cerrado @ e9098e00fb6339b759df5b0df2e086cef8a7ce83

feat: Rework some pages
   1diff --git a/Makefile b/Makefile
   2index 53b076749dac0fd0399fed31a3857ea3c338db01..8f6d462fa6a51fdf81da416dae3f1807c54019c5 100644
   3--- a/Makefile
   4+++ b/Makefile
   5@@ -12,9 +12,11 @@
   6 test:
   7 	go test -v --tags=unit ./...
   8 
   9+# this is meant for "prod" build
  10 sass-slug:
  11 	mkdir -p static
  12 	sassc \
  13+		--style compressed \
  14 		-I scss scss/main.scss static/main.$(COMMIT).css
  15 
  16 sass:
  17diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
  18index 28cc99e1ffe8564b7d63b31a3452134a51dc33aa..7bdf3729016402aa7f8f1e462d9b34c209109835 100644
  19--- a/pkg/handler/git/handler.go
  20+++ b/pkg/handler/git/handler.go
  21@@ -2,8 +2,10 @@ package git
  22 
  23 import (
  24 	"bytes"
  25+	"io"
  26 	"log/slog"
  27 	"net/http"
  28+	"os"
  29 	"path/filepath"
  30 
  31 	"git.gabrielgio.me/cerrado/pkg/ext"
  32@@ -15,11 +17,15 @@ 	"github.com/alecthomas/chroma/v2/lexers"
  33 	"github.com/alecthomas/chroma/v2/styles"
  34 	"github.com/go-git/go-git/v5/plumbing"
  35 	"github.com/go-git/go-git/v5/plumbing/object"
  36+	"github.com/gomarkdown/markdown"
  37+	markdownhtml "github.com/gomarkdown/markdown/html"
  38+	"github.com/gomarkdown/markdown/parser"
  39 )
  40 
  41 type (
  42 	GitHandler struct {
  43 		gitService gitService
  44+		readmePath string
  45 	}
  46 
  47 	gitService interface {
  48@@ -30,12 +36,17 @@ 		GetTree(name, ref, path string) (*object.Tree, error)
  49 		GetFileContent(name, ref, path string) (string, error)
  50 		ListTags(name string) ([]*object.Tag, error)
  51 		ListBranches(name string) ([]*plumbing.Reference, error)
  52+	}
  53+
  54+	configurationRepository interface {
  55+		GetRootReadme() string
  56 	}
  57 )
  58 
  59-func NewGitHandler(gitService gitService) *GitHandler {
  60+func NewGitHandler(gitService gitService, confRepo configurationRepository) *GitHandler {
  61 	return &GitHandler{
  62 		gitService: gitService,
  63+		readmePath: confRepo.GetRootReadme(),
  64 	}
  65 }
  66 
  67@@ -46,7 +57,32 @@ 		slog.Error("Error listing repo", "error", err)
  68 		return
  69 	}
  70 
  71-	gitList := &templates.GitListPage{repos}
  72+	f, err := os.Open(g.readmePath)
  73+	if err != nil {
  74+		slog.Error("Error loading readme file", "error", err)
  75+		return
  76+	}
  77+
  78+	bs, err := io.ReadAll(f)
  79+	if err != nil {
  80+		slog.Error("Error reading readme file bytes", "error", err)
  81+		return
  82+	}
  83+
  84+	extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
  85+	p := parser.NewWithExtensions(extensions)
  86+	doc := p.Parse(bs)
  87+
  88+	htmlFlag := markdownhtml.CommonFlags | markdownhtml.HrefTargetBlank
  89+	opts := markdownhtml.RendererOptions{Flags: htmlFlag}
  90+	renderer := markdownhtml.NewRenderer(opts)
  91+
  92+	bs = markdown.Render(doc, renderer)
  93+
  94+	gitList := &templates.GitListPage{
  95+		Respositories: repos,
  96+		About:         bs,
  97+	}
  98 	templates.WritePageTemplate(w, gitList)
  99 }
 100 
 101diff --git a/pkg/handler/router.go b/pkg/handler/router.go
 102index de5117c9e0a6b8836117e40c1b237bdb1afa6346..bf13ad5f8821c8fa48e6c932afddacd8e2f964f6 100644
 103--- a/pkg/handler/router.go
 104+++ b/pkg/handler/router.go
 105@@ -20,7 +20,7 @@ 	gitService *service.GitService,
 106 	configRepo *serverconfig.ConfigurationRepository,
 107 ) (http.Handler, error) {
 108 	var (
 109-		gitHandler   = git.NewGitHandler(gitService)
 110+		gitHandler   = git.NewGitHandler(gitService, configRepo)
 111 		aboutHandler = about.NewAboutHandler(configRepo)
 112 		configHander = config.ConfigFile(configRepo)
 113 	)
 114diff --git a/pkg/service/git.go b/pkg/service/git.go
 115index f8867850cd8a76933da262156fdbac70134a27ff..31a1cbb47d7c74f552ce0ff097735481613a1aac 100644
 116--- a/pkg/service/git.go
 117+++ b/pkg/service/git.go
 118@@ -1,21 +1,24 @@
 119 package service
 120 
 121 import (
 122+	"log/slog"
 123+	"os"
 124 	"path"
 125 
 126 	"git.gabrielgio.me/cerrado/pkg/config"
 127 	"git.gabrielgio.me/cerrado/pkg/git"
 128+	"git.gabrielgio.me/cerrado/pkg/u"
 129 	"github.com/go-git/go-git/v5/plumbing"
 130 	"github.com/go-git/go-git/v5/plumbing/object"
 131 )
 132 
 133 type (
 134 	Repository struct {
 135-		Name              string
 136-		Title             string
 137-		LastCommitMessage string
 138-		LastCommitDate    string
 139-		Ref               string
 140+		Name           string
 141+		Title          string
 142+		Description    string
 143+		LastCommitDate string
 144+		Ref            string
 145 	}
 146 
 147 	GitService struct {
 148@@ -46,9 +49,6 @@ 		repo, err := git.OpenRepository(r.Path)
 149 		if err != nil {
 150 			return nil, err
 151 		}
 152-		if err != nil {
 153-			return nil, err
 154-		}
 155 
 156 		obj, err := repo.LastCommit()
 157 		if err != nil {
 158@@ -60,13 +60,23 @@ 		if err != nil {
 159 			return nil, err
 160 		}
 161 
 162+		d := path.Join(r.Path, "description")
 163+		description := ""
 164+		if u.FileExist(d) {
 165+			if b, err := os.ReadFile(d); err == nil {
 166+				description = string(b)
 167+			} else {
 168+				slog.Error("Error loading description file", "err", err)
 169+			}
 170+		}
 171+
 172 		baseName := path.Base(r.Path)
 173 		repos[i] = &Repository{
 174-			Name:              baseName,
 175-			Title:             baseName,
 176-			LastCommitMessage: obj.Message,
 177-			LastCommitDate:    obj.Author.When.Format(timeFormat),
 178-			Ref:               head.Name().Short(),
 179+			Name:           baseName,
 180+			Title:          baseName,
 181+			Description:    description,
 182+			LastCommitDate: obj.Author.When.Format(timeFormat),
 183+			Ref:            head.Name().Short(),
 184 		}
 185 	}
 186 
 187diff --git a/scss/main.scss b/scss/main.scss
 188index bb7d7f08ab56d7d605e119b1a4bf85817ed8a76f..b3ba6498c55ad5e4cc20a568fc62b9bb8000a829 100644
 189--- a/scss/main.scss
 190+++ b/scss/main.scss
 191@@ -19,8 +19,31 @@ @import "bootstrap/scss/_containers.scss";
 192 @import "bootstrap/scss/_nav.scss";
 193 @import "bootstrap/scss/_navbar.scss";
 194 @import "bootstrap/scss/_grid.scss";
 195+@import "tree.scss";
 196+
 197+// overwrite to reduce the ammount of css generated by loading all utilities
 198+$utilities: (
 199+    "order": (
 200+      responsive: true,
 201+      property: order,
 202+      values: (
 203+        first: -1,
 204+        0: 0,
 205+        1: 1,
 206+        2: 2,
 207+        3: 3,
 208+        4: 4,
 209+        5: 5,
 210+        last: 6,
 211+      ),
 212+    ),
 213+);
 214+
 215+@import "bootstrap/scss/utilities/_api.scss";
 216 
 217 body {
 218+    // prevents wierd font resizing on overflow
 219+    -webkit-text-size-adjust: 100%;
 220     font-family: $font-family-monospace;
 221     font-size: $base-font-size;
 222     margin: 0;
 223@@ -63,23 +86,27 @@     overflow-x: auto;
 224 }
 225 
 226 .logs {
 227-  >div:nth-child(odd) {
 228+  >div {
 229     background: #f8f9fa;
 230   }
 231 
 232   >div {
 233-    padding: 10px;
 234+    padding: 5px;
 235+    margin: $spacer;
 236+  }
 237+
 238+  @include media-breakpoint-down(md) {
 239+    >div {
 240+      margin: $spacer 0 $spacer 0;
 241+    }
 242   }
 243 
 244   pre {
 245-    white-space: break-spaces;
 246+    font-size: $base-font-size;
 247     margin: 0;
 248   }
 249 }
 250 
 251-.logs pre::first-line {
 252-  font-weight: bold;
 253-}
 254 .logs>div>div:first-child {
 255   margin-bottom: 15px;
 256 }
 257@@ -87,3 +114,14 @@ .logs>div>div:last-child {
 258   margin-top: 15px;
 259 }
 260 
 261+#about {
 262+  padding: 0 $spacer $spacer $spacer;
 263+  > p:first-child {
 264+    margin-top: 0
 265+  }
 266+
 267+  @include media-breakpoint-down(md) { 
 268+    padding: $spacer;
 269+    max-width: calc(100% - calc(2 * #{$spacer}));
 270+  }
 271+}
 272diff --git a/scss/tree.scss b/scss/tree.scss
 273new file mode 100644
 274index 0000000000000000000000000000000000000000..bbca16231973265e12f95b9b1d97274288bdce1d
 275--- /dev/null
 276+++ b/scss/tree.scss
 277@@ -0,0 +1,59 @@
 278+// TODO: refer to sourcehut code AGPL
 279+.tree-list {
 280+  display: grid;
 281+  // mode name
 282+  grid-template-columns: auto 1fr fit-content(40em) auto auto;
 283+  font-family: $font-family-monospace;
 284+
 285+  svg {
 286+    color: #777;
 287+  }
 288+
 289+  .size {
 290+    text-align: right;
 291+  }
 292+
 293+  .name.blob a {
 294+    color: $gray-900;
 295+  }
 296+
 297+  .mode, .commit, .commit a, .date, .size {
 298+    color: $gray-700;
 299+  }
 300+
 301+  .name.blob {
 302+    text-overflow: ellipsis;
 303+    white-space: nowrap;
 304+    overflow: hidden;
 305+  }
 306+
 307+  .commit {
 308+    text-overflow: ellipsis;
 309+    white-space: nowrap;
 310+    overflow: hidden;
 311+  }
 312+
 313+  & > div {
 314+    padding: 0.1rem 0.5rem;
 315+    background: transparent;
 316+
 317+    &.id {
 318+      text-align: right;
 319+    }
 320+
 321+    &.comments {
 322+      text-align: center;
 323+    }
 324+
 325+    @for $i from 1 through 5 {
 326+      &:nth-child(5n+#{$i}) {
 327+        grid-column-start: $i;
 328+      }
 329+
 330+      // Striped rows
 331+      &:nth-child(10n+#{$i}) {
 332+        background: rgba(0,0,0,.05);
 333+      }
 334+    }
 335+  }
 336+}
 337diff --git a/templates/base.qtpl b/templates/base.qtpl
 338index 497aa6d59d5fa49671e8ad4da524712bf62c40e8..9b0c4f57081d1861c77a7a75a4b25cafef57b6f4 100644
 339--- a/templates/base.qtpl
 340+++ b/templates/base.qtpl
 341@@ -26,7 +26,7 @@     }
 342 %}
 343 
 344 {% code func TimeFormat(t time.Time) string {
 345-        return t.Format("2006-01-02")
 346+        return t.Format("02.01.2006")
 347     }
 348 %}
 349 
 350diff --git a/templates/base.qtpl.go b/templates/base.qtpl.go
 351index 5f39e8dd4040b5dbe896fc5c44dc5b423636f9eb..d2bcc7369a0587ee3edf2f641a333666d598e33e 100644
 352--- a/templates/base.qtpl.go
 353+++ b/templates/base.qtpl.go
 354@@ -68,7 +68,7 @@ }
 355 
 356 //line base.qtpl:28
 357 func TimeFormat(t time.Time) string {
 358-	return t.Format("2006-01-02")
 359+	return t.Format("02.01.2006")
 360 }
 361 
 362 //line base.qtpl:33
 363diff --git a/templates/gititemlog.qtpl b/templates/gititemlog.qtpl
 364index e037c52718fa440432f3e361b61625729fed8edb..ef473b73835002fc162ae4c115d4f4967c9f4d32 100644
 365--- a/templates/gititemlog.qtpl
 366+++ b/templates/gititemlog.qtpl
 367@@ -15,11 +15,11 @@   <div class="row">
 368       <div class="col-xxl-2">
 369        {%s TimeFormat(c.Committer.When) %}
 370       </div>
 371-      <div class="col-xxl-7">
 372+      <div class="col-xxl-7 code-view">
 373        <pre>{%s c.Message %}</pre>
 374       </div>
 375       <div class="col-xxl-3">
 376-       <small>{%s c.Committer.Name %}</small>
 377+       <small>{%s c.Committer.Name %} &lt;{%s c.Committer.Email %}&gt;</small>
 378       </div>
 379   </div>
 380   {% endfor %}
 381diff --git a/templates/gititemlog.qtpl.go b/templates/gititemlog.qtpl.go
 382index 47e700d343dbf7be77ef68410f4d907ba0bd29fc..e3bac4102910c7b96b8da4235d43ac530b02726a 100644
 383--- a/templates/gititemlog.qtpl.go
 384+++ b/templates/gititemlog.qtpl.go
 385@@ -76,7 +76,7 @@ 		qw422016.E().S(TimeFormat(c.Committer.When))
 386 //line gititemlog.qtpl:16
 387 		qw422016.N().S(`
 388       </div>
 389-      <div class="col-xxl-7">
 390+      <div class="col-xxl-7 code-view">
 391        <pre>`)
 392 //line gititemlog.qtpl:19
 393 		qw422016.E().S(c.Message)
 394@@ -88,7 +88,11 @@        <small>`)
 395 //line gititemlog.qtpl:22
 396 		qw422016.E().S(c.Committer.Name)
 397 //line gititemlog.qtpl:22
 398-		qw422016.N().S(`</small>
 399+		qw422016.N().S(` &lt;`)
 400+//line gititemlog.qtpl:22
 401+		qw422016.E().S(c.Committer.Email)
 402+//line gititemlog.qtpl:22
 403+		qw422016.N().S(`&gt;</small>
 404       </div>
 405   </div>
 406   `)
 407diff --git a/templates/gititemtree.qtpl b/templates/gititemtree.qtpl
 408index be7a27de050be204b7d90bac4fcffe073c44a921..68b98566d92fb065d6413d1236d0ee0deb6401cb 100644
 409--- a/templates/gititemtree.qtpl
 410+++ b/templates/gititemtree.qtpl
 411@@ -14,15 +14,21 @@
 412 {% func (g *GitItemTreePage) Nav(name, ref string) %}{%= GitItemNav(name, ref, Tree) %}{% endfunc %}
 413 
 414 {% func (g *GitItemTreePage) GitContent() %}
 415-{% for _, e := range g.Tree.Entries %}
 416 <div class="row">
 417-  <div class="col">{%s Ignore(e.Mode.ToOSFileMode()).String() %}</div>
 418-  {% if e.Mode.IsFile() %}
 419-  <div class="col-md"><a href="/{%s g.Name %}/blob/{%s g.Ref%}/{%s g.CurrentPath %}/{%s e.Name %}">{%s e.Name %}</a></div>
 420-  {% else %}
 421-  <div class="col-md"><a href="./{%s g.CurrentPath %}/{%s e.Name %}">{%s e.Name %}</a></div>
 422-  {% endif %}
 423-  <div class="col-md">{%dl Ignore(g.Tree.Size(e.Name))%} KiB</div>
 424+  <div class="col-md-12">
 425+    <div class="tree-list">
 426+      {% for _, e := range g.Tree.Entries %}
 427+          <div class="mode">{%s Ignore(e.Mode.ToOSFileMode()).String() %}</div>
 428+          {% if e.Mode.IsFile() %}
 429+          <div class="name blob"><a href="/{%s g.Name %}/blob/{%s g.Ref%}/{%s g.CurrentPath %}/{%s e.Name %}">{%s e.Name %}</a></div>
 430+          {% else %}
 431+          <div class="name tree"><a href="./{%s g.CurrentPath %}/{%s e.Name %}">{%s e.Name %}</a></div>
 432+          {% endif %}
 433+          <div class="commit"></div>
 434+          <div class="date"></div>
 435+          <div class="size">{%dl Ignore(g.Tree.Size(e.Name))%} KiB</div>
 436+      {% endfor %}
 437+    </div>
 438+  </div>
 439 </div>
 440-{% endfor %}
 441 {% endfunc %}
 442diff --git a/templates/gititemtree.qtpl.go b/templates/gititemtree.qtpl.go
 443index cdc374fef67dd51aedbd48aad507ea3ff515e48f..546cb13e1253fa4c9183c0b804b35d0b12e2ac0f 100644
 444--- a/templates/gititemtree.qtpl.go
 445+++ b/templates/gititemtree.qtpl.go
 446@@ -67,103 +67,109 @@ //line gititemtree.qtpl:16
 447 func (g *GitItemTreePage) StreamGitContent(qw422016 *qt422016.Writer) {
 448 //line gititemtree.qtpl:16
 449 	qw422016.N().S(`
 450-`)
 451-//line gititemtree.qtpl:17
 452+<div class="row">
 453+  <div class="col-md-12">
 454+    <div class="tree-list">
 455+      `)
 456+//line gititemtree.qtpl:20
 457 	for _, e := range g.Tree.Entries {
 458-//line gititemtree.qtpl:17
 459+//line gititemtree.qtpl:20
 460 		qw422016.N().S(`
 461-<div class="row">
 462-  <div class="col">`)
 463-//line gititemtree.qtpl:19
 464+          <div class="mode">`)
 465+//line gititemtree.qtpl:21
 466 		qw422016.E().S(Ignore(e.Mode.ToOSFileMode()).String())
 467-//line gititemtree.qtpl:19
 468+//line gititemtree.qtpl:21
 469 		qw422016.N().S(`</div>
 470-  `)
 471-//line gititemtree.qtpl:20
 472+          `)
 473+//line gititemtree.qtpl:22
 474 		if e.Mode.IsFile() {
 475-//line gititemtree.qtpl:20
 476+//line gititemtree.qtpl:22
 477 			qw422016.N().S(`
 478-  <div class="col-md"><a href="/`)
 479-//line gititemtree.qtpl:21
 480+          <div class="name blob"><a href="/`)
 481+//line gititemtree.qtpl:23
 482 			qw422016.E().S(g.Name)
 483-//line gititemtree.qtpl:21
 484+//line gititemtree.qtpl:23
 485 			qw422016.N().S(`/blob/`)
 486-//line gititemtree.qtpl:21
 487+//line gititemtree.qtpl:23
 488 			qw422016.E().S(g.Ref)
 489-//line gititemtree.qtpl:21
 490+//line gititemtree.qtpl:23
 491 			qw422016.N().S(`/`)
 492-//line gititemtree.qtpl:21
 493+//line gititemtree.qtpl:23
 494 			qw422016.E().S(g.CurrentPath)
 495-//line gititemtree.qtpl:21
 496+//line gititemtree.qtpl:23
 497 			qw422016.N().S(`/`)
 498-//line gititemtree.qtpl:21
 499+//line gititemtree.qtpl:23
 500 			qw422016.E().S(e.Name)
 501-//line gititemtree.qtpl:21
 502+//line gititemtree.qtpl:23
 503 			qw422016.N().S(`">`)
 504-//line gititemtree.qtpl:21
 505+//line gititemtree.qtpl:23
 506 			qw422016.E().S(e.Name)
 507-//line gititemtree.qtpl:21
 508+//line gititemtree.qtpl:23
 509 			qw422016.N().S(`</a></div>
 510-  `)
 511-//line gititemtree.qtpl:22
 512+          `)
 513+//line gititemtree.qtpl:24
 514 		} else {
 515-//line gititemtree.qtpl:22
 516+//line gititemtree.qtpl:24
 517 			qw422016.N().S(`
 518-  <div class="col-md"><a href="./`)
 519-//line gititemtree.qtpl:23
 520+          <div class="name tree"><a href="./`)
 521+//line gititemtree.qtpl:25
 522 			qw422016.E().S(g.CurrentPath)
 523-//line gititemtree.qtpl:23
 524+//line gititemtree.qtpl:25
 525 			qw422016.N().S(`/`)
 526-//line gititemtree.qtpl:23
 527+//line gititemtree.qtpl:25
 528 			qw422016.E().S(e.Name)
 529-//line gititemtree.qtpl:23
 530+//line gititemtree.qtpl:25
 531 			qw422016.N().S(`">`)
 532-//line gititemtree.qtpl:23
 533+//line gititemtree.qtpl:25
 534 			qw422016.E().S(e.Name)
 535-//line gititemtree.qtpl:23
 536+//line gititemtree.qtpl:25
 537 			qw422016.N().S(`</a></div>
 538-  `)
 539-//line gititemtree.qtpl:24
 540+          `)
 541+//line gititemtree.qtpl:26
 542 		}
 543-//line gititemtree.qtpl:24
 544+//line gititemtree.qtpl:26
 545 		qw422016.N().S(`
 546-  <div class="col-md">`)
 547-//line gititemtree.qtpl:25
 548+          <div class="commit"></div>
 549+          <div class="date"></div>
 550+          <div class="size">`)
 551+//line gititemtree.qtpl:29
 552 		qw422016.N().DL(Ignore(g.Tree.Size(e.Name)))
 553-//line gititemtree.qtpl:25
 554+//line gititemtree.qtpl:29
 555 		qw422016.N().S(` KiB</div>
 556-</div>
 557-`)
 558-//line gititemtree.qtpl:27
 559+      `)
 560+//line gititemtree.qtpl:30
 561 	}
 562-//line gititemtree.qtpl:27
 563+//line gititemtree.qtpl:30
 564 	qw422016.N().S(`
 565+    </div>
 566+  </div>
 567+</div>
 568 `)
 569-//line gititemtree.qtpl:28
 570+//line gititemtree.qtpl:34
 571 }
 572 
 573-//line gititemtree.qtpl:28
 574+//line gititemtree.qtpl:34
 575 func (g *GitItemTreePage) WriteGitContent(qq422016 qtio422016.Writer) {
 576-//line gititemtree.qtpl:28
 577+//line gititemtree.qtpl:34
 578 	qw422016 := qt422016.AcquireWriter(qq422016)
 579-//line gititemtree.qtpl:28
 580+//line gititemtree.qtpl:34
 581 	g.StreamGitContent(qw422016)
 582-//line gititemtree.qtpl:28
 583+//line gititemtree.qtpl:34
 584 	qt422016.ReleaseWriter(qw422016)
 585-//line gititemtree.qtpl:28
 586+//line gititemtree.qtpl:34
 587 }
 588 
 589-//line gititemtree.qtpl:28
 590+//line gititemtree.qtpl:34
 591 func (g *GitItemTreePage) GitContent() string {
 592-//line gititemtree.qtpl:28
 593+//line gititemtree.qtpl:34
 594 	qb422016 := qt422016.AcquireByteBuffer()
 595-//line gititemtree.qtpl:28
 596+//line gititemtree.qtpl:34
 597 	g.WriteGitContent(qb422016)
 598-//line gititemtree.qtpl:28
 599+//line gititemtree.qtpl:34
 600 	qs422016 := string(qb422016.B)
 601-//line gititemtree.qtpl:28
 602+//line gititemtree.qtpl:34
 603 	qt422016.ReleaseByteBuffer(qb422016)
 604-//line gititemtree.qtpl:28
 605+//line gititemtree.qtpl:34
 606 	return qs422016
 607-//line gititemtree.qtpl:28
 608+//line gititemtree.qtpl:34
 609 }
 610diff --git a/templates/gitlist.qtpl b/templates/gitlist.qtpl
 611index 3d7ef822b9bae8b6debebe51796799f2417513ed..937ba224e5709efc8c22623cf9b382ada77c2ba9 100644
 612--- a/templates/gitlist.qtpl
 613+++ b/templates/gitlist.qtpl
 614@@ -3,6 +3,7 @@
 615 {% code
 616 type GitListPage struct {
 617     Respositories []*service.Repository
 618+    About []byte
 619 }
 620 %}
 621 
 622@@ -12,7 +13,7 @@ {% func (p *GitListPage) Navbar() %}{%= Navbar(Git) %}{% endfunc %}
 623 
 624 {% func (p *GitListPage) Content() %}
 625 <div class="row">
 626-  <div class="col-md-8 offset-md-2">
 627+  <div class="col-md-6 order-last order-md-first">
 628     <div class="event-list">
 629       {% for _, r := range p.Respositories %}
 630       <div class="event">
 631@@ -20,8 +21,7 @@         <h4>
 632           <a href="/{%s r.Name %}">{%s r.Name %}</a>
 633         </h4>
 634         </hr>
 635-        <p>{%s r.LastCommitMessage %}</p>
 636-        <p><small>{%s r.LastCommitDate %}</small></p>
 637+        <p>{%s r.Description %}</p>
 638         <p>
 639           <a href="/{%s r.Name %}/log/{%s r.Ref %}">log</a>
 640           <a href="/{%s r.Name %}/tree/{%s r.Ref %}">tree</a>
 641@@ -30,9 +30,12 @@         </p>
 642       </div>
 643       {% endfor %}
 644     </div>
 645-  {% endfunc %}
 646+  </div>
 647+  <div id="about" class="col-md-4 order-first order-md-last">
 648+    {%z= p.About %}
 649   </div>
 650 </div>
 651+{% endfunc %}
 652 
 653 {% func (p *GitListPage) Script() %}
 654 {% endfunc %}
 655diff --git a/templates/gitlist.qtpl.go b/templates/gitlist.qtpl.go
 656index d9f7ec1ec45568e18d549fa56015753b5511221a..435626eee0d43f1f780dcae81de5fea21a7f8017 100644
 657--- a/templates/gitlist.qtpl.go
 658+++ b/templates/gitlist.qtpl.go
 659@@ -23,109 +23,105 @@
 660 //line gitlist.qtpl:4
 661 type GitListPage struct {
 662 	Respositories []*service.Repository
 663+	About         []byte
 664 }
 665 
 666-//line gitlist.qtpl:9
 667+//line gitlist.qtpl:10
 668 func (p *GitListPage) StreamTitle(qw422016 *qt422016.Writer) {
 669-//line gitlist.qtpl:9
 670+//line gitlist.qtpl:10
 671 	qw422016.N().S(`Git | List`)
 672-//line gitlist.qtpl:9
 673+//line gitlist.qtpl:10
 674 }
 675 
 676-//line gitlist.qtpl:9
 677+//line gitlist.qtpl:10
 678 func (p *GitListPage) WriteTitle(qq422016 qtio422016.Writer) {
 679-//line gitlist.qtpl:9
 680+//line gitlist.qtpl:10
 681 	qw422016 := qt422016.AcquireWriter(qq422016)
 682-//line gitlist.qtpl:9
 683+//line gitlist.qtpl:10
 684 	p.StreamTitle(qw422016)
 685-//line gitlist.qtpl:9
 686+//line gitlist.qtpl:10
 687 	qt422016.ReleaseWriter(qw422016)
 688-//line gitlist.qtpl:9
 689+//line gitlist.qtpl:10
 690 }
 691 
 692-//line gitlist.qtpl:9
 693+//line gitlist.qtpl:10
 694 func (p *GitListPage) Title() string {
 695-//line gitlist.qtpl:9
 696+//line gitlist.qtpl:10
 697 	qb422016 := qt422016.AcquireByteBuffer()
 698-//line gitlist.qtpl:9
 699+//line gitlist.qtpl:10
 700 	p.WriteTitle(qb422016)
 701-//line gitlist.qtpl:9
 702+//line gitlist.qtpl:10
 703 	qs422016 := string(qb422016.B)
 704-//line gitlist.qtpl:9
 705+//line gitlist.qtpl:10
 706 	qt422016.ReleaseByteBuffer(qb422016)
 707-//line gitlist.qtpl:9
 708+//line gitlist.qtpl:10
 709 	return qs422016
 710-//line gitlist.qtpl:9
 711+//line gitlist.qtpl:10
 712 }
 713 
 714-//line gitlist.qtpl:11
 715+//line gitlist.qtpl:12
 716 func (p *GitListPage) StreamNavbar(qw422016 *qt422016.Writer) {
 717-//line gitlist.qtpl:11
 718+//line gitlist.qtpl:12
 719 	StreamNavbar(qw422016, Git)
 720-//line gitlist.qtpl:11
 721+//line gitlist.qtpl:12
 722 }
 723 
 724-//line gitlist.qtpl:11
 725+//line gitlist.qtpl:12
 726 func (p *GitListPage) WriteNavbar(qq422016 qtio422016.Writer) {
 727-//line gitlist.qtpl:11
 728+//line gitlist.qtpl:12
 729 	qw422016 := qt422016.AcquireWriter(qq422016)
 730-//line gitlist.qtpl:11
 731+//line gitlist.qtpl:12
 732 	p.StreamNavbar(qw422016)
 733-//line gitlist.qtpl:11
 734+//line gitlist.qtpl:12
 735 	qt422016.ReleaseWriter(qw422016)
 736-//line gitlist.qtpl:11
 737+//line gitlist.qtpl:12
 738 }
 739 
 740-//line gitlist.qtpl:11
 741+//line gitlist.qtpl:12
 742 func (p *GitListPage) Navbar() string {
 743-//line gitlist.qtpl:11
 744+//line gitlist.qtpl:12
 745 	qb422016 := qt422016.AcquireByteBuffer()
 746-//line gitlist.qtpl:11
 747+//line gitlist.qtpl:12
 748 	p.WriteNavbar(qb422016)
 749-//line gitlist.qtpl:11
 750+//line gitlist.qtpl:12
 751 	qs422016 := string(qb422016.B)
 752-//line gitlist.qtpl:11
 753+//line gitlist.qtpl:12
 754 	qt422016.ReleaseByteBuffer(qb422016)
 755-//line gitlist.qtpl:11
 756+//line gitlist.qtpl:12
 757 	return qs422016
 758-//line gitlist.qtpl:11
 759+//line gitlist.qtpl:12
 760 }
 761 
 762-//line gitlist.qtpl:13
 763+//line gitlist.qtpl:14
 764 func (p *GitListPage) StreamContent(qw422016 *qt422016.Writer) {
 765-//line gitlist.qtpl:13
 766+//line gitlist.qtpl:14
 767 	qw422016.N().S(`
 768 <div class="row">
 769-  <div class="col-md-8 offset-md-2">
 770+  <div class="col-md-6 order-last order-md-first">
 771     <div class="event-list">
 772       `)
 773-//line gitlist.qtpl:17
 774+//line gitlist.qtpl:18
 775 	for _, r := range p.Respositories {
 776-//line gitlist.qtpl:17
 777+//line gitlist.qtpl:18
 778 		qw422016.N().S(`
 779       <div class="event">
 780         <h4>
 781           <a href="/`)
 782-//line gitlist.qtpl:20
 783+//line gitlist.qtpl:21
 784 		qw422016.E().S(r.Name)
 785-//line gitlist.qtpl:20
 786+//line gitlist.qtpl:21
 787 		qw422016.N().S(`">`)
 788-//line gitlist.qtpl:20
 789+//line gitlist.qtpl:21
 790 		qw422016.E().S(r.Name)
 791-//line gitlist.qtpl:20
 792+//line gitlist.qtpl:21
 793 		qw422016.N().S(`</a>
 794         </h4>
 795         </hr>
 796         <p>`)
 797-//line gitlist.qtpl:23
 798-		qw422016.E().S(r.LastCommitMessage)
 799-//line gitlist.qtpl:23
 800-		qw422016.N().S(`</p>
 801-        <p><small>`)
 802 //line gitlist.qtpl:24
 803-		qw422016.E().S(r.LastCommitDate)
 804+		qw422016.E().S(r.Description)
 805 //line gitlist.qtpl:24
 806-		qw422016.N().S(`</small></p>
 807+		qw422016.N().S(`</p>
 808         <p>
 809           <a href="/`)
 810 //line gitlist.qtpl:26
 811@@ -158,70 +154,75 @@ 	}
 812 //line gitlist.qtpl:31
 813 	qw422016.N().S(`
 814     </div>
 815-  `)
 816-//line gitlist.qtpl:33
 817+  </div>
 818+  <div id="about" class="col-md-4 order-first order-md-last">
 819+    `)
 820+//line gitlist.qtpl:35
 821+	qw422016.N().Z(p.About)
 822+//line gitlist.qtpl:35
 823+	qw422016.N().S(`
 824+  </div>
 825+</div>
 826+`)
 827+//line gitlist.qtpl:38
 828 }
 829 
 830-//line gitlist.qtpl:33
 831+//line gitlist.qtpl:38
 832 func (p *GitListPage) WriteContent(qq422016 qtio422016.Writer) {
 833-//line gitlist.qtpl:33
 834+//line gitlist.qtpl:38
 835 	qw422016 := qt422016.AcquireWriter(qq422016)
 836-//line gitlist.qtpl:33
 837+//line gitlist.qtpl:38
 838 	p.StreamContent(qw422016)
 839-//line gitlist.qtpl:33
 840+//line gitlist.qtpl:38
 841 	qt422016.ReleaseWriter(qw422016)
 842-//line gitlist.qtpl:33
 843+//line gitlist.qtpl:38
 844 }
 845 
 846-//line gitlist.qtpl:33
 847+//line gitlist.qtpl:38
 848 func (p *GitListPage) Content() string {
 849-//line gitlist.qtpl:33
 850+//line gitlist.qtpl:38
 851 	qb422016 := qt422016.AcquireByteBuffer()
 852-//line gitlist.qtpl:33
 853+//line gitlist.qtpl:38
 854 	p.WriteContent(qb422016)
 855-//line gitlist.qtpl:33
 856+//line gitlist.qtpl:38
 857 	qs422016 := string(qb422016.B)
 858-//line gitlist.qtpl:33
 859+//line gitlist.qtpl:38
 860 	qt422016.ReleaseByteBuffer(qb422016)
 861-//line gitlist.qtpl:33
 862+//line gitlist.qtpl:38
 863 	return qs422016
 864-//line gitlist.qtpl:33
 865+//line gitlist.qtpl:38
 866 }
 867 
 868-//   </div>
 869-// </div>
 870-//
 871-
 872-//line gitlist.qtpl:37
 873+//line gitlist.qtpl:40
 874 func (p *GitListPage) StreamScript(qw422016 *qt422016.Writer) {
 875-//line gitlist.qtpl:37
 876+//line gitlist.qtpl:40
 877 	qw422016.N().S(`
 878 `)
 879-//line gitlist.qtpl:38
 880+//line gitlist.qtpl:41
 881 }
 882 
 883-//line gitlist.qtpl:38
 884+//line gitlist.qtpl:41
 885 func (p *GitListPage) WriteScript(qq422016 qtio422016.Writer) {
 886-//line gitlist.qtpl:38
 887+//line gitlist.qtpl:41
 888 	qw422016 := qt422016.AcquireWriter(qq422016)
 889-//line gitlist.qtpl:38
 890+//line gitlist.qtpl:41
 891 	p.StreamScript(qw422016)
 892-//line gitlist.qtpl:38
 893+//line gitlist.qtpl:41
 894 	qt422016.ReleaseWriter(qw422016)
 895-//line gitlist.qtpl:38
 896+//line gitlist.qtpl:41
 897 }
 898 
 899-//line gitlist.qtpl:38
 900+//line gitlist.qtpl:41
 901 func (p *GitListPage) Script() string {
 902-//line gitlist.qtpl:38
 903+//line gitlist.qtpl:41
 904 	qb422016 := qt422016.AcquireByteBuffer()
 905-//line gitlist.qtpl:38
 906+//line gitlist.qtpl:41
 907 	p.WriteScript(qb422016)
 908-//line gitlist.qtpl:38
 909+//line gitlist.qtpl:41
 910 	qs422016 := string(qb422016.B)
 911-//line gitlist.qtpl:38
 912+//line gitlist.qtpl:41
 913 	qt422016.ReleaseByteBuffer(qb422016)
 914-//line gitlist.qtpl:38
 915+//line gitlist.qtpl:41
 916 	return qs422016
 917-//line gitlist.qtpl:38
 918+//line gitlist.qtpl:41
 919 }
 920diff --git a/templates/navbar.qtpl b/templates/navbar.qtpl
 921index 9681fa4a5fd873e89621c1cdad5151f49661716b..4d2a6a93af93586389c21b685b1df89c818c39f5 100644
 922--- a/templates/navbar.qtpl
 923+++ b/templates/navbar.qtpl
 924@@ -29,7 +29,10 @@ {% comment %}
 925 Add this back once needed
 926             <a class="nav-link{%= insertIfEqual(s, List) %}" href="/list">list</a>
 927 {% endcomment %}
 928+{% comment %}
 929+Add this back if needed
 930             <a class="nav-link{%= insertIfEqual(s, About) %}" href="/about">about</a>
 931+{% endcomment %}
 932             <a class="nav-link{%= insertIfEqual(s, Config) %}" href="/config">config</a>
 933           </div>
 934         </nav>
 935diff --git a/templates/navbar.qtpl.go b/templates/navbar.qtpl.go
 936index a2989db1381d0e7ff34c0499825bc27a2b303271..1eacd6a7b0142adb82f16595cfc41d4dd9fafac5 100644
 937--- a/templates/navbar.qtpl.go
 938+++ b/templates/navbar.qtpl.go
 939@@ -89,162 +89,160 @@ 	qw422016.N().S(`" href="/">git</a>
 940 `)
 941 //line navbar.qtpl:31
 942 	qw422016.N().S(`
 943+`)
 944+//line navbar.qtpl:35
 945+	qw422016.N().S(`
 946             <a class="nav-link`)
 947-//line navbar.qtpl:32
 948-	streaminsertIfEqual(qw422016, s, About)
 949-//line navbar.qtpl:32
 950-	qw422016.N().S(`" href="/about">about</a>
 951-            <a class="nav-link`)
 952-//line navbar.qtpl:33
 953+//line navbar.qtpl:36
 954 	streaminsertIfEqual(qw422016, s, Config)
 955-//line navbar.qtpl:33
 956+//line navbar.qtpl:36
 957 	qw422016.N().S(`" href="/config">config</a>
 958           </div>
 959         </nav>
 960 `)
 961-//line navbar.qtpl:36
 962+//line navbar.qtpl:39
 963 }
 964 
 965-//line navbar.qtpl:36
 966+//line navbar.qtpl:39
 967 func WriteNavbar(qq422016 qtio422016.Writer, s Selection) {
 968-//line navbar.qtpl:36
 969+//line navbar.qtpl:39
 970 	qw422016 := qt422016.AcquireWriter(qq422016)
 971-//line navbar.qtpl:36
 972+//line navbar.qtpl:39
 973 	StreamNavbar(qw422016, s)
 974-//line navbar.qtpl:36
 975+//line navbar.qtpl:39
 976 	qt422016.ReleaseWriter(qw422016)
 977-//line navbar.qtpl:36
 978+//line navbar.qtpl:39
 979 }
 980 
 981-//line navbar.qtpl:36
 982+//line navbar.qtpl:39
 983 func Navbar(s Selection) string {
 984-//line navbar.qtpl:36
 985+//line navbar.qtpl:39
 986 	qb422016 := qt422016.AcquireByteBuffer()
 987-//line navbar.qtpl:36
 988+//line navbar.qtpl:39
 989 	WriteNavbar(qb422016, s)
 990-//line navbar.qtpl:36
 991+//line navbar.qtpl:39
 992 	qs422016 := string(qb422016.B)
 993-//line navbar.qtpl:36
 994+//line navbar.qtpl:39
 995 	qt422016.ReleaseByteBuffer(qb422016)
 996-//line navbar.qtpl:36
 997+//line navbar.qtpl:39
 998 	return qs422016
 999-//line navbar.qtpl:36
1000+//line navbar.qtpl:39
1001 }
1002 
1003-//line navbar.qtpl:38
1004+//line navbar.qtpl:41
1005 func StreamGitItemNav(qw422016 *qt422016.Writer, name, ref string, s GitSelection) {
1006-//line navbar.qtpl:38
1007+//line navbar.qtpl:41
1008 	qw422016.N().S(`
1009 <div class="row">
1010     <h3>`)
1011-//line navbar.qtpl:40
1012+//line navbar.qtpl:43
1013 	qw422016.E().S(name)
1014-//line navbar.qtpl:40
1015+//line navbar.qtpl:43
1016 	qw422016.N().S(` `)
1017-//line navbar.qtpl:40
1018+//line navbar.qtpl:43
1019 	if ref != "" && (s == Log || s == Tree) {
1020-//line navbar.qtpl:40
1021+//line navbar.qtpl:43
1022 		qw422016.N().S(`@ `)
1023-//line navbar.qtpl:40
1024+//line navbar.qtpl:43
1025 		qw422016.E().S(ref)
1026-//line navbar.qtpl:40
1027+//line navbar.qtpl:43
1028 	}
1029-//line navbar.qtpl:40
1030+//line navbar.qtpl:43
1031 	qw422016.N().S(`</h3>
1032 </div>
1033 <div class="row">
1034   <ul class="nav">
1035     <li class="nav-item">
1036       <a class="nav-link`)
1037-//line navbar.qtpl:45
1038-	streaminsertIfEqual(qw422016, s, Readme)
1039-//line navbar.qtpl:45
1040-	qw422016.N().S(`" aria-current="page" href="/`)
1041-//line navbar.qtpl:45
1042-	qw422016.E().S(name)
1043-//line navbar.qtpl:45
1044-	qw422016.N().S(`/about">about</a>
1045-    </li>
1046-    <li class="nav-item">
1047-      <a class="nav-link`)
1048 //line navbar.qtpl:48
1049-	streaminsertIfEqual(qw422016, s, Log)
1050+	streaminsertIfEqual(qw422016, s, Readme)
1051 //line navbar.qtpl:48
1052 	qw422016.N().S(`" aria-current="page" href="/`)
1053 //line navbar.qtpl:48
1054 	qw422016.E().S(name)
1055 //line navbar.qtpl:48
1056-	qw422016.N().S(`/log/`)
1057-//line navbar.qtpl:48
1058-	qw422016.E().S(ref)
1059-//line navbar.qtpl:48
1060-	qw422016.N().S(`">log</a>
1061+	qw422016.N().S(`/about">about</a>
1062     </li>
1063     <li class="nav-item">
1064       <a class="nav-link`)
1065 //line navbar.qtpl:51
1066-	streaminsertIfEqual(qw422016, s, Summary)
1067+	streaminsertIfEqual(qw422016, s, Log)
1068 //line navbar.qtpl:51
1069 	qw422016.N().S(`" aria-current="page" href="/`)
1070 //line navbar.qtpl:51
1071 	qw422016.E().S(name)
1072 //line navbar.qtpl:51
1073-	qw422016.N().S(`">summary</a>
1074+	qw422016.N().S(`/log/`)
1075+//line navbar.qtpl:51
1076+	qw422016.E().S(ref)
1077+//line navbar.qtpl:51
1078+	qw422016.N().S(`">log</a>
1079     </li>
1080     <li class="nav-item">
1081       <a class="nav-link`)
1082 //line navbar.qtpl:54
1083-	streaminsertIfEqual(qw422016, s, Refs)
1084+	streaminsertIfEqual(qw422016, s, Summary)
1085 //line navbar.qtpl:54
1086 	qw422016.N().S(`" aria-current="page" href="/`)
1087 //line navbar.qtpl:54
1088 	qw422016.E().S(name)
1089 //line navbar.qtpl:54
1090-	qw422016.N().S(`/refs">refs</a>
1091+	qw422016.N().S(`">summary</a>
1092     </li>
1093     <li class="nav-item">
1094       <a class="nav-link`)
1095 //line navbar.qtpl:57
1096-	streaminsertIfEqual(qw422016, s, Tree)
1097+	streaminsertIfEqual(qw422016, s, Refs)
1098 //line navbar.qtpl:57
1099 	qw422016.N().S(`" aria-current="page" href="/`)
1100 //line navbar.qtpl:57
1101 	qw422016.E().S(name)
1102 //line navbar.qtpl:57
1103+	qw422016.N().S(`/refs">refs</a>
1104+    </li>
1105+    <li class="nav-item">
1106+      <a class="nav-link`)
1107+//line navbar.qtpl:60
1108+	streaminsertIfEqual(qw422016, s, Tree)
1109+//line navbar.qtpl:60
1110+	qw422016.N().S(`" aria-current="page" href="/`)
1111+//line navbar.qtpl:60
1112+	qw422016.E().S(name)
1113+//line navbar.qtpl:60
1114 	qw422016.N().S(`/tree/`)
1115-//line navbar.qtpl:57
1116+//line navbar.qtpl:60
1117 	qw422016.E().S(ref)
1118-//line navbar.qtpl:57
1119+//line navbar.qtpl:60
1120 	qw422016.N().S(`">tree</a>
1121     </li>
1122   </ul>
1123 </div>
1124 `)
1125-//line navbar.qtpl:61
1126+//line navbar.qtpl:64
1127 }
1128 
1129-//line navbar.qtpl:61
1130+//line navbar.qtpl:64
1131 func WriteGitItemNav(qq422016 qtio422016.Writer, name, ref string, s GitSelection) {
1132-//line navbar.qtpl:61
1133+//line navbar.qtpl:64
1134 	qw422016 := qt422016.AcquireWriter(qq422016)
1135-//line navbar.qtpl:61
1136+//line navbar.qtpl:64
1137 	StreamGitItemNav(qw422016, name, ref, s)
1138-//line navbar.qtpl:61
1139+//line navbar.qtpl:64
1140 	qt422016.ReleaseWriter(qw422016)
1141-//line navbar.qtpl:61
1142+//line navbar.qtpl:64
1143 }
1144 
1145-//line navbar.qtpl:61
1146+//line navbar.qtpl:64
1147 func GitItemNav(name, ref string, s GitSelection) string {
1148-//line navbar.qtpl:61
1149+//line navbar.qtpl:64
1150 	qb422016 := qt422016.AcquireByteBuffer()
1151-//line navbar.qtpl:61
1152+//line navbar.qtpl:64
1153 	WriteGitItemNav(qb422016, name, ref, s)
1154-//line navbar.qtpl:61
1155+//line navbar.qtpl:64
1156 	qs422016 := string(qb422016.B)
1157-//line navbar.qtpl:61
1158+//line navbar.qtpl:64
1159 	qt422016.ReleaseByteBuffer(qb422016)
1160-//line navbar.qtpl:61
1161+//line navbar.qtpl:64
1162 	return qs422016
1163-//line navbar.qtpl:61
1164+//line navbar.qtpl:64
1165 }