Collection of themes/skins for the Fossil SCM. Public write access, just make an account.

⌈⌋ branch:  Fossil Skins Extra


Artifact [10dd719f86]

Artifact 10dd719f8638df88787290e72d42742217fd5381:

  • File github.txt — part of check-in [5608fdba7e] at 2014-03-16 23:32:57 on branch trunk — GitHub-style template, implements language-bar using `fx_stats` table, includes th1x.tcl, requires fossil-search-table.php being run beforehand. (user: mario size: 27113)

# The "skin" configuration exported from
# repository "/home/mario/fossil.d/skins/../fossil-skins.fossil"
# on 2014-03-16 23:30:38
config /config 8499
1395012511 'css' value '     /* fonts */
     @import url(http://fonts.googleapis.com/css?family=Viga);
     @import url(http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700);

     /* no body spacing */
     html, body {
        border: 0; padding: 0; margin: 0;
        background: #fefefe;
        font: normal normal 400 10pt/16pt Arial,sans-serif;
     }
     html, * {
        box-sizing: border-box; 
     }
     /* general text settings */
     body, article, p {
        color: #333;
        line-height: 135%;
     }

     /* layout features */
     .width-container {
        padding-left: 15% !important;
        padding-right: 15% !important; 
     }
     .text-shadow {
        text-shadow: 0px 1px 0px #888;
     }

     /* menu layout */
     #menu-header, #submenu-header, #project-header, #fossil-footer {
        width: 100%;
        height: 50pt;
        padding: 10pt;
        padding-top: 14pt;
        border-bottom: 1px solid #dfdfdf;
        display: block;
        clear: both;
        vertical-align: middle;
     }
     #menu-header h1 {
        font-family: Viga;
        font-weight: 700;
        display: inline;
        font-size: 23pt;
        color: #333;
        margin: 3pt 20pt 0 0;
        float: left;
     }
     /* search bar */
     #menu-header input, #menu-header select {
        display: inline;
        margin: 0;
        padding: 1pt;
        border: 1px solid #aaa;
     }
     #menu-header select {
        border: 1px solid #ccc;
        background: #f3f3f3;
        color: #555;
        font-weight: 100;
        font-size: 10pt;
        padding: 2pt;
        margin: 0;
     }
     /* menu links */
     #menu-header a {
        color: #444;
        font-size: 11pt;
        font-weight: 600;
        padding: 7pt;
     }

     /* buttons */
     #menu-header a.button {
        top: 2pt;
        border-radius: 5px;
        font-weight: 700;
        padding: 6pt 10pt;
        position: relative;
     }
     .button.green {
        border: 1px solid #493;
        background: #7e5 linear-gradient(180deg,#8e7,#593);
        color: #fff !important;
     }
     .button.green:hover {
        background: #5c3 linear-gradient(180deg,#6d5,#482);
     }
     .button.white {
        border: 1px solid #ddd;
        background: #eee;
        background: linear-gradient(180deg,#fff,#ddd);
        color: #333;
     }
     .button.white:hover, .submenu .label:hover {
        background: #ccc linear-gradient(180deg,#eee,#ccc);
     }
     .button.red {
        border: 1px solid #d97;
        background: #fdc;
        background: linear-gradient(180deg,#fdc,#eba);
        color: #333;
     }
     .button.red:hover {
        background: #eba linear-gradient(180deg,#ecb,#c97);
     }
     .download.button {
        display: block;
        padding: 4pt 10pt;
        border-radius: 4px;
        margin-top: 10pt;
     }
     .branch.button {
        font-family: sans-serif;
        padding: 2pt 6pt;
        border-radius: 4px;
     }
     
     /* fossil action buttons */
     .submenu a.label {
        padding: 2px 3pt;
        border-radius: 3px;
        border: 1px solid #d7d7d7;
        background: #fff linear-gradient(0deg, #eee 0%, #fff 20%);
        color: #333;
     }
     main .submenu {
        float: right;
        position: relative;
        top: -40px;
     }

     /* links */
     a {
        text-decoration: none;
        color: #57c;
     }

     /* project info */
     #project-header a {
        font-size: 16pt;
        color: #48c;
        font-weight: 100;
     }
     #public-prefix {
        font-size: 13pt;
        font-weight: 100;
        color: #ccc;
        position: relative;
        left: -53pt;
        top: 5pt;
     }
     #project-header .share-button {
        padding: 3pt 6pt;
        font-weight: 600;
        border: 1px solid #ddd;
        border-radius: 4px 0 0 4px;
        background: #eee;
        background:linear-gradient(180deg,#fff,#e7e7e7);
        color: #222;
     }
     #project-header .share-button a {
        font-size: 12pt;
     }
     #project-header .share-button-number {
        padding: 3pt 6pt;
        border: 1px solid #ddd;
        border-left: 0;
        border-radius: 0 4px 4px 0;
        background: #fcfcfc;
        color: #444;
     }

     /* project main pane */
     #project-content {
        font-size: 12.25pt;
        padding-top: 16pt;
        color: #666;
        width: 82%;  /************** layout *************/
     }
     #project-stats {
        margin-top: 10pt;
        border: 1px solid #ccc;
        border-radius: 4pt;
        height: 48px;
        overflow: hidden;
        background: #fcfcfc;
        font-family: sans-serif;
     }
     #project-stats-alternate {
        height: 32px;
        text-align: center;
        overflow: hidden;
     }
     #project-stats-alternate div {
        height: 32px;
        padding: 5pt;
     }
     #project-stats-alternate a {
        padding: 1pt 25pt;
        color: #999;
     }
     #project-stats-alternate a b {
        color: #111;
     }

     /* language color graph */
     #language-bar {
        width: 100% !important;
        height: 15px !important;
        border-radius: 0 0 4pt 4pt;
        background: #999;
        padding: 0;
        margin: 0;
     }
     #language-bar span:first-child {
        border-radius: 0 0 0 4pt;
     }
     #language-bar span:last-child {
        border-radius: 0 0 4pt 0;
     }

     /* sidebar */
     #function-sidebar {
        width: 15%;
        float: right;   /************** layout *************/
        overflow: hidden;
     }
     #function-sidebar ul {
        padding: 0;
        margin: 20pt 0 0 0;
     }
     #function-sidebar li {
        border-left: 1px solid #ececec;
        box-shadow: #f4f4f4 4px 0 0 0 inset;
        display: block;
        border-bottom: 1px solid #fcfcfc;
        list-stype-type: none;
        margin: 0;
        padding: 8pt 20pt;
     }
     #function-sidebar li.current, #function-sidebar li a[href="current_page"] {
        border: 0;
        border-top: 1px solid #eee;
        border-bottom: 1px solid #eee;
        border-right: 3px solid #b53;
        box-shadow: #f7f7f7 0 3px 0 0 inset;
     }
     #function-sidebar li:hover {
        background: linear-gradient(90deg, #bbb 0%, #eee 2%, #f7f7f7 15%, #fff 50%);
     }
     #function-sidebar li a b {
        color: #333;
     }
     
     /* file list box */
     #recent-files {
        border: 1px solid #edf;
        box-shadow: #f0f4f8 0 0 6px 4px;
        padding: 0;
     }
     #recent-files table {
        background: #f7f8f9;
        width: 100%;
        margin: 0; padding: 0; border-spacing: 0;
        border-collapse: collapse;
     }
     #recent-files tr {
        width: 100%;
        border-bottom: 1px solid #e4e8ea;
        font-size: 90%;
        padding: 0 !important; margin: 0 !important;
     }
     #recent-files th {
        padding: 5pt;
        text-align: left;
        font-weight: 300;
        background: #dee;
        border-bottom: 1px solid #f3f5f7;
     }
     #recent-files td {
        padding: 5pt;
        text-align: left;
     }
     
     /* actual fossil content (wiki/timeline/files/etc.) */
     main {
        display: block;
        min-height: 500pt;
        border: 1px solid #eee;
        box-shadow: #f7f7f7 0 0 5px 3px;
     }
     main h2.page-title {
        display: block;
        margin: 0;
        padding: 9pt;
        background: #eee;
        background: linear-gradient(180deg, #fff 0%, #fcfcfc 33%, #f7f7f7 75%, #ebebeb 100%);
     }

     /* end */
     #fossil-footer {
        margin-top: 30pt;
        border-top: 1px solid #eee;
        height: 175pt;
     }
     #fossil-footer .button {
        padding: 3pt 6pt;
        border-radius: 2pt;
     }


     /* have different layout on frontpage than elsewhere */
                     .optional { display: none; }
     body.page-index .optional { display: inherit; }
                     #function-sidebar { width: 6%; }
     body.page-index #function-sidebar { width: 15%; }
                     #project-content { width: 91%; }
     body.page-index #project-content { width: 82%; }


'
config /config 7544
1395009355 'header' value '<th1>
   if {[regexp {^(index|home)[?]?} $current_page]} {
      set pagecat "index"
   } else {
      set pagecat $current_page
   }
</th1>

<html>
<head>

  <title>$<project_name>: $<title></title>
  <base href="$<baseurl>/$current_page" />
  <meta http-equiv=Content-Type content="text/html; charset=UTF-8; version=5">
  <link rel=alternate type="application/rss+xml" title=Timeline href="$<baseurl>/timeline.rss">
  <link rel=stylesheet href="$<baseurl>/style.css?gitlike" type="text/css" media=screen>

  <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
  <link rel=stylesheet href="//google-code-prettify.googlecode.com/svn/loader/prettify.css" type="text/css" media="screen">

  <script language=JavaScript>
     function searchform_target(nm) {
        var form = $("#search_form");
        var baseurl = "$<baseurl>";
        form.find("input[name=as_sitesearch]").remove().parent();
        switch (nm) {
           case "fx_src": // utilize th1x and `fx_search` table
               form.attr("action", "wiki");
               form.find("input[name=q]").attr("name", "name");
               break;
           case "manual":
               baseurl = "http://www.fossil-scm.org/";
           case "google":
               form.attr("action", "https://google.com/search");
               form.find("input[name=name]").attr("name", "q");
               form.append("<input type=hidden name=as_sitesearch value=''" + baseurl + "''>");
               break;
        }
     }
  </script>

</head>
<body
   class="page-$pagecat"
   onLoad="
      $(''code,pre'').addClass(''prettyprint'');
      $.getScript(''//google-code-prettify.googlecode.com/svn/loader/run_prettify.js?autoload=true'')
   "
>

  <footer id=menu-header class=width-container>
      <h1>Fossil</h1>
      <form id=search_form action=wiki method=get style=display:inline>
        <select>
           <option onClick="searchform_target(''fx_src'')">This repository
           <option onClick="searchform_target(''google'')">via Google
           <option onClick="searchform_target(''manual'')">Fossil help
        </select>
        <input type=search name=name placeholder="Search through files and wiki" size=30>
      </form>
      <a href=#>Explore</a>
      <a href=#>Features</a>
      <a href=#>Enterprise</a>
      <a href=#>Blog</a>
      <span style=float:right>
        <th1>
            if {[hascap s]} { html {<a href=setup class="button red text-shadow">Admin</a>}
            } else { html {<a href=setup class="button green text-shadow">Sign up</a>} }
        </th1>
        <a href=login class="button white text-shadow"><th1>if {[info exists login]} { puts $login } else { puts "Sign in" }</th1></a>
      </span>
  </footer>


  <footer id=project-header class=width-container>
      <span style=position:absolute><span id=public-prefix>PUBLIC&nbsp;</span></span>
      <img src=https://fossil.include-once.org/fossil-icon.png align=middle height=32 width=32>
      <a href="../..">example.org</a> / <a href=index><b>$project_name</b></a>
      <span style="float:right;">
         <span class=share-button id=share-button onclick="$(''#share-button>span'').toggle(''75'')">&#9733; Star<span style=display:none>&nbsp;
           <a href="https://plus.google.com/share?url=$baseurl" title=google+>g+</a> &middot;
           <a href="https://www.facebook.com/sharer/sharer.php?u=$baseurl" title=facebook>fb</a> &middot;
           <a href="https://twitter.com/intent/tweet?url=$baseurl" title=twitter>tw</a> &middot;
           <a href="http://reddit.com/submit?url=$baseurl" title=reddit>rd</a> &middot;
           <a href="https://www.linkedin.com/shareArticle?mini=true&amp;url=$baseurl" title=linkedin>in</a> &middot;
           <a href="https://www.stumbleupon.com/submit?url=$baseurl" title=stumbleupon>su</a> &middot;
           <a href="https://del.icio.us/post?url=$baseurl" title=delicious>dl</a>
         </span></span><span class=share-button-number><img src="http://api.include-once.org/links/social.img.php?url=http://reddit.com/" align=middle valign=middle></span>
         <span class=share-button>&#9988; Fork</span><span class=share-button-number>1</span>
      </span>
  </footer>
  

  <section id=main-content class=width-container>
  
    <aside id=function-sidebar>
       <ul>
          <li style=padding:5pt>
          <li class=current><a href=tree>  <b>&lt;&gt;</b>  <span class=optional> Code </span></a>
          <li><a href=reportlist title=Tickets>  <b>!</b>   <span class=optional> Issues </span></a>
          <li><a href=wcontent title=Wiki>       <b>#</b>   <span class=optional> Wiki </span></a>
          <li><a href=timeline title=Time>       <b>~</b>   <span class=optional> Pulse </span></a>
          <li><a href=reports title=Graphs>      <b>=</b>   <span class=optional> Graphs </span></a>
          <li><a href=taglist title=Tags>        <b>/</b>   <span class=optional> Tags </span></a>
          <li style=padding:3pt>
       </ul>
       <p class=optional>
          <b>HTTP</b>S sync URL<br>
          <input type=url size=15 value="$<baseurl>/xfer" style="border: 1px solid #ddd; border-radius: 3px" onClick="select()"><br>
          <a class="download button white text-shadow" href="$<baseurl>/zip/$<project_name>.zip?uuid=trunk">➱&nbsp;Download ZIP</a>
          <a class="download button white text-shadow" href="$<baseurl>/zip/$<project_name>.tgz?uuid=trunk">➱&nbsp;Download TGZ</a>
       </p>
    </aside>

    <section id=project-content>
       <article class=optional>
          This project does this and that. Please check out the tickets, then make a sync request and whathaveyou!
       </article>
       
       <section id=project-stats class=optional>
  <th1>set stats_checkins [set stats_branches [set stats_releases [set stats_developers 0]]] ; catch { ui::stats } </th1>
           <div id=project-stats-alternate>
               <div>
                  <a href="timeline?y=ci">◶ <b>$stats_checkins</b> commits</a>
                  <a href=brlist>         ⎇  <b>$stats_branches</b> branches</a>
                  <a href=taglist>        ⌫  <b>$stats_releases</b> releases</a>
                  <a href="timeline?u=*"> ♙ <b>$stats_developers</b> developers</a>
               </div>
               <div>
                  <a href=#><b>100%</b> open source</a>
               </div>
           </div>
           <div onclick="$(''#project-stats-alternate div:eq(0)'').toggle(''slow'')" id=language-bar><th1> ui::lang_stats </th1></div>
       </section>

       <br class=optional>
       <section id=branch-info>
         <a class="branch button green" href=diff>⌈⌋</a>
         <a class="branch button white" href=brlist>⎇  <small>branch:</small> <b>trunk</b></a>
         &nbsp;<a href=index><b>$project_name</b></a>
       </section>

       <br class=optional>
       <section id=recent-files class=optional>
         <table>
            <colgroup><col style="width:25%"><col style="width:60%"><col style="width:15%"></colgroup>
            <tbody><th1>catch {
               ui::last_commit
               ui::recent_files
            }</th1></tbody>
         </table>
       </section>


       <br>
       <main>
          <h2 class=page-title>$title</h2>
          <article style=padding:7pt>
<th1>catch { ui::search_on_wiki }</th1>



'
config /config 1942
1395012544 'footer' value '             <br><br><br>


          </article>
       </main>

    </section>

  </section>




  <section class=width-container>
    <footer id=fossil-footer>
        <p><a href=http://fossil-scm.org/>Fossil</a> version <tt>$release_version</tt> | Manifest: $manifest_version | Manifest date: $manifest_date
        </p>
        <p>
           <a class="button green" href=login>Account</a>
           <a class="button white" href=Index>Index</a>
        </p>
           <a class="button red" href=setup>Setup</a>
           <a href=setup_ulist>Users</a> /
           <a href=setup_access>Access</a> /
           <a href=setup_config>Config</a> /
           <a href=setup_timeline>Timeline</a> /
           <a href=setup_editcss>CSS</a> /
           <a href=setup_header>Header</a> /
           <a href=setup_footer>Footer</a> /
           <a href=rcvfromlist>Log</a> /
           <a href=admin_sql>SQL</a> /
           <a href=stat>Stats</a>
        </p>
        <p>
           <a class="button white" href=wiki>Wiki</a>
           <a href=wcontent>All Pages</a> /
           <a href=timeline?y=w>Recent Changes</a> /
           <a href=wikinew>New Page</a> /
           <a href=eventedit>New Event</a> /
           <a href=modreq>Moderation</a>
        </p>
        <p>
           <a class="button white" href="tree?ci=tip">Files</a>
           <a class="button white" href=zip/trunk.zip?uuid=trunk>ZIP</a>
           <a class="button white" href=tarball/trunk.zip?uuid=trunk>TGZ</a>
           <a class="button white" href=timeline>Timeline</a>
           <a class="button white" href=brlist>Branches</a>
           <a class="button white" href=taglist>Tags</a>
           <a class="button white" href=reportlist>Tickets</a>
           <a class="button white" href=reports>Reports</a>
        </p>
        
    </footer>
  </section>

</body>
</html>
'
config /config 8913
1395008681 'th1-setup' value '


# -------------------------------------------------------------------
# Control structures
#
#   ++ varname
#   ?: [if] {then} {else}
#   isset varname
#   eq str1 str2
#
#   while {cond} {code}
#   foreach varname {list} {code}
#   switch value { val1 {code1} val2 {code2} ... }
#



#-- Pre-increment  [++ varname]
proc ++ {varname} {
   upvar 1 $varname i
   return [uplevel 1 "set {$varname} [expr 1+$i]"]
}


#-- ternary / if-shorthand (cond/then/else may be literals, or {[expressions]} themselves)
proc ?: {cond then else} {
   uplevel 1 "if {$cond} { return $then; } else { return $else; }"
}


#-- info exists shorthand
proc isset {varname} {
   return [uplevel 1 "info exists {$varname}"]
}


#-- string equality shorthand
proc eq {str1 str2} {
   return [expr {$str1 eq $str2}]
}


#-- while loop
proc while {condition code} {
   return [uplevel 1 "for {} {$condition} {} {$code}"]
}


#-- foreach list
#
# foreach VAR "abc xyz 123" { puts "($VAR) " }
#
proc foreach {varname list code} {
   upvar 1 $varname val
   for {set i 0}  {$i < [llength $list]}  {++ i} {
      set val [lindex $list $i]
      uplevel 1 "$code"
   }
}


#-- A switch statement.
#
# switch "val" {
#        "cmp1" {code1}
#        "cmp2" {code2}
#        "cmp3" {code3}
#   {{default}} {codeN}
# }
#
proc switch {compare_value val_code_pairs} {
   set len [llength $val_code_pairs]
   # loop over compare values + code pairs
   for  {set n 0}  {$n < $len}  {++ n} {
      set cmp [lindex $val_code_pairs $n];
      if {[expr $cmp eq $compare_value || $cmp eq {{default}} ]} {
         return [uplevel 1 [lindex $val_code_pairs [++ n]]];
      }
   }
}






# -------------------------------------------------------------------
# String functions
#
#   str::contains needle haystack
#   str::next needle haystack startindex
#   str::wrap haystack needle addbefore addafter
#


   
#-- returns true if string contained in another string
proc str::contains {needle haystack} {
   return [expr {-1 != [string first $needle $haystack]}]
}


#-- wrapper for [string first ...] to support startindex
proc str::next {search content start} {
   # cut out $content at $start before searching
   set p [string first $search [string range $content $start [string length $content]]]
   if [expr $p>=0] {
      set p [expr $start+$p]
   }
   return $p
}


#-- enclose string in e.g. html tags
proc str::wrap {content search before after} {
   set len [string length $search]
   set p 0
   while {[expr [set p [str::next $search $content $p]]>=0]} {
      set content "[string range $content 0 [expr $p-1]]$before$search$after[string range $content [expr $p+$len] 2000]";
      set p [expr $p+[string length "$before+$search+$after"]]; # skip a little further
   }
   return $content
}


#-- Split string into list on delimiter character
# (basically just turns delimiter into space)
#
proc str::explode {delim str} {
   set r ""
   set len [string length $str]
   while {-1 != [set p [string first $delim $str]]} {
      set r "$r [string range $str 0 [expr $p-1]]"
      set str [string range $str [++ p] $len]
   }
   return [list [string trim "$r $str"]]
}   






# -------------------------------------------------------------------
# User Interface utility code
#
#   sql::allowed safe_string
#   ui::page_exists WikiPage
#   ui::file_exists file.name
#
#   ui::search terms baseurl
#   ui::search_on_wiki   
#   ui::stats
#   ui::lang_stats
#



#-- Whitelist permissible characters for SQL context
# * A workaround for the lack of SQL escaping here (or the new query API branch)
# * Used in LIKE context, so ? and % are not allowed
# * And '' and \ or " not included in the whitelist for obvious reasons.
proc sql::allowed {str} {
   return [regexp {^[a-zA-Z0-9 !$&/(){}=<>,.;:-_+#*@]+$} $str]
}


#-- Search function
# * Requires fossil-search.php to build the according table
#   (reading from the raw blobs is impossible) as cronjob
# * And a patched `fossil` binary src/report.c to allow
#   SELECTs on the `search` table.
proc ui::search {terms baseurl} {

   # cleanup $terms
   if [sql::allowed $terms] {
   
      # prepare search query
      set WHERE "1"
      foreach search $terms {
         set WHERE "$WHERE AND content LIKE ''%$search%''"
      }

      # perform search
      query "SELECT path, type, name, content FROM fx_search WHERE $WHERE" {

         # conent excerpt and highlighting      
         set p [string first $terms $content]
         set excerpt [string range $content [expr $p-50] [expr $p+450]]
         foreach search $terms {
            set excerpt [str::wrap "$excerpt" $search <ins> </ins>]
         }
      
         # format result list
         html "
         <div class=search-result>
            <b>[htmlize $type]</b>
            <a href=[htmlize $path]>[htmlize $name]</a> <br>
            <span class=search-link><a href=[htmlize $path]>$baseurl/[htmlize $path]</a></span> <br>
            <small class=search-excerpt>$excerpt</small>
         </div>\n";
      }
   }
}


#-- Check for existence of wiki page
proc ui::page_exists {name} {
   if [sql::allowed $name] {
      query "SELECT 1 FROM tag WHERE tagname = ''wiki-$name''" {
         return 1
      }
   }
   return 0
}


#-- Check if file exists in repository
proc ui::file_exists {name} {
   if [sql::allowed $name] {
      query "SELECT 1 FROM filename WHERE name = ''$name''" {
         return 1
      }
   }
   return 0
}


#-- Call ui::search on non-existant wiki pages
# * We can get $<title> as search terms
#   (no way to access query string parameters otherwise)
# * But this is also convenient, as it doubles as wiki page search
proc ui::search_on_wiki {} {
   upvar 1  title title  baseurl baseurl  current_page current_page
   if [expr {[regexp {^wiki[?]name=} $current_page] && ! [ui::page_exists $title]}] {
      html "<h2 class=search-headline>Search</h2>";
      ui::search $title $baseurl
      html "<br><br><br><br>"
   }
}


#-- Ordered list of project statistics (will populate global $stats() array)
proc ui::stats {} {
   uplevel 1 { query {SELECT
     (SELECT count(objid) FROM event WHERE type=''ci'' LIMIT 1) AS `stats_checkins`,
     (SELECT count(name) FROM filename LIMIT 1) AS `stats_files`,
     (SELECT count(status) FROM ticket LIMIT 1) AS `stats_tickets`,
     (SELECT count(DISTINCT user) FROM event LIMIT 1) AS `stats_developers`,
     (SELECT count(DISTINCT value) FROM tagxref WHERE tagid=8) AS `stats_branches`,
     (SELECT count(tagname) FROM tag WHERE tagname LIKE ''sym-%'') AS `stats_tags`,
     (SELECT count(tagname) FROM tag WHERE tagname REGEXP ''^sym[\\-\\w_.]+\\d+\\.\\d+'') AS `stats_releases`
   } {} }
}


#-- Language/Content statistics (outputs colored bar graph)
proc ui::lang_stats {} {
   # fetch $lang(js/...), $lang_color(js), $lang_list, $total_size
   query {SELECT name, value FROM fx_stats ORDER by VALUE DESC} {
      set $name $value
   }
   # output color bar for language proportions
   #html "<div class=language-bar style=''width:100%; height:3pt; box-sizing:border-box;''>"
   foreach name $lang_list {
      set percent "[expr $lang($name)*100]%"
      html "<span class=code-rate-$name style=''height:100%; width:$percent; display:inline-block; background-color:#$lang_color($name)'' title=''$percent $name''></span>";
   }
   #html "</div>";
}



#-- print two table rows for last commit
proc ui::last_commit {} {
   query {SELECT *, round(julianday(''now'')-mtime) AS age, substr(comment,0,199) AS msg, substr(uuid, 0, 10) AS short_uuid
          FROM event JOIN blob ON blob.rid=event.objid
          WHERE type=''ci'' ORDER BY mtime DESC LIMIT 1
   } {
      html "  <tr><th colspan=3>$msg</th></tr>";
      html "  <tr><th colspan=3 style=background:#fff><a href=''timeline?u=$user'' class=user>$user</a> authored $age days ago
            <span style=float:right>last checkin <a href=''ci/$uuid''>$short_uuid</a></span></th></tr>";
   }
}


#-- outputs table rows containing top-level filenames and recent checkin comments
proc ui::recent_files {} {

   # files
   query {
       SELECT DISTINCT
             instr(name, ''/'') as dir,
             (CASE instr(name,''/'') WHEN 0 THEN name
              ELSE substr(name,0,instr(name, ''/'')) END) AS name,
             substr(comment, 0, 50) AS comment,
             uuid,
             round(julianday(''now'')-mtime) AS age
       FROM filename
       JOIN mlink ON filename.fnid=mlink.fnid
       JOIN event ON mlink.mid=event.objid
       JOIN blob ON blob.rid=event.objid
       GROUP BY name ORDER BY mtime
   } {
      html "               <tr><td>";
      if {$dir > 0} {
          html "<a class=dir href=''tree?name=[htmlize $name]''>[htmlize $name]</a>";
      } else {
          html "<a class=file href=''ci/$uuid?sbs=0''>[htmlize $name]</a>";
      }
      html "</td> <td>[htmlize $comment]</td> <td>[htmlize $age] days ago</td></tr>\n";
   }
}'