Overview page

To generate the overview page with all projects the template ./config/overview.template will be sourced.

This file is used as a bash include. Here are these variables:

  • html_page the html code of the html page. You can add links to a custom css or javascript - and must put your own files to the ./public_html/ directory. It has these placeholders:

    • ${__page_title__} - title of the page for h1 and html header
    • ${__page_descr__} - Hint text
    • ${CONTENT} - to insert all elements for groups and
  • html_group start a section for a new group. It has these placeholders:

    • ${__group__} - name of the group
    • ${__groupinfo__} - Hint text
  • html_element This is the html code per project. The combined html code of all projects will be used as ${__CONTENT__} in the variable $html_page (see above) Placeholders are the following variables:

    • ${__author__} - description of the project; it is taken from config.json - entry “author”
    • ${__commit__} - last commit message; it is a automatically read info of author and date from git log
    • ${__descr__} - description of the project; it is taken from config.json - entry “tagline”
    • ${__group__} - name of the group
    • ${__label__} - label for the project; it is taken from config.json - entry “title”; if it is missing it is taken from config/repos.cfg.
    • ${__url_repo__} - repo url; taken from config/repos.cfg
    • ${__url_doc__} - url to the generated docs
  • html_group_close optional html code to close a group

Because it is Bash: quote the " char with a backslash in the variables.

Distributed template: boxes

This is a template to use a table where you get group sections … and inside a group is one boxes per project.

In th folder ./config/ copy overview.template.boxes.dist to overview.template and start the script ./generate_box.sh.

# ================================================================================
# >>> boxes
# --------------------------------------------------------------------------------
# 2022-01-19  v0.1   ahahn  init
# 2022-01-20  v0.2   ahahn  added groups and filter
# 2022-01-21  v0.3   ahahn  update filter box: counter of visible items
# 2022-04-01  v0.4   ahahn  add onclick attribute on a box
# 2022-04-01  v0.5   ahahn  css update; add intro text
# 2022-04-03  v0.6   ahahn  add section; add title from json
# 2022-04-05  v0.7   ahahn  darker box header
# 2022-04-14  v0.8   ahahn  different main colors for sections
# 2022-08-13  v0.9   ahahn  add a href link to docpages for spiders
# 2022-08-17  v0.10  ahahn  dimmed sections if folder has no match
# 2022-08-18  v0.11  ahahn  add menu to jump to existing sections
# ================================================================================

# --------------------------------------------------------------------------------
# html page
# --------------------------------------------------------------------------------
html_page="<!doctype html>
        <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"/>
                --color: #345;
                --colorA: #23a;
                --colorAhover: #56f;
                --colorH1: #abc;
                --colorInfo: #679;
                --colorInput: #c52;

                --colorSectionA: #569;
                --colorSectionB: #589;
                --colorSectionC: #956;
                --colorSectionD: #769;
            body{background:var(--bgBody); color: var(--color); margin: 1% 3% 2%; font-family: arial;}
            a{color: var(--colorA); }
            a:hover{color: var(--colorAhover); }
            a.doclink{display: none;}
            footer{position: fixed; bottom: 1em; right: 1em; background: rgba(0,0,0,0.05); padding: 0.5em; opacity: 0.3;}
            footer:hover{opacity: 0.6;}
            h1{margin: 0; color:var(--colorH1); font-size: 190%;}
            h1>span{font-size: 160%}
            h2{margin: 0; color:var(--colorSectionA); }
            h2>span{font-size: 160%}
            div#filterbox{text-align: center; background: #fff; top: 0; right: 0; padding: 0.5em; position: fixed; border-bottom-left-radius: 1em; z-index: 100;}
            div#filterbox input{font-size: 130%; border-radius: 1em; border: 1px solid #ddd; color: var(--colorInput); padding: 0.1em 0.6em; margin-bottom: 0.5em;}
            div#filterbox input:focus{border: 1px solid #abc !important;}
            div#filterbox:hover nav{display: block;}

            div.box{float: left; width: 23%; min-width: 20em; border: 2px solid rgba(0,0,0,0.1); border-radius: 0.5em; margin: 0 1% 2em 0; background:var(--bgBox); height: 13em; box-shadow: none; opacity: 1; transition: all 0.3s}
            div.hiddenboxclass{height:0; width: 0; display: none;}
            div.box:hover{box-shadow: 0 0 2em #ccc;border-color: rgba(0,0,0,0.15); cursor:pointer; opacity: 1;}
            div.boxheader{background: var(--colorSectionA); padding: 0.5em 1em; border-radius: 0.3em 0.3em 0 0 ; transition: all 1s}
            div.boxheader>span{font-size: 130%}
            div.box:hover div.boxheader{background: var(--bgBoxHeadHover);}
            div.boxheader a{color:var(--colorBoxHead); font-weight: bold; text-decoration: none;}
            div.boxcontent{margin: 1em;}
            nav{text-align: left; display: none; padding: 1em;}
            nav a {display: block;}
            nav a:hover{background: #fed;}
            p.groupinfo{color: var(--colorInfo); font-style: italic;  margin: 0 0 1em;}
            section {display: flow-root; padding: 0.7em; box-shadow: 0 1em 2em #fff inset; border-top-left-radius: 1em; margin-bottom: 1.5em;}
            section.hidden {opacity: 0.4; margin: 0; padding: 0 0.7em; }
            section.hidden p.groupinfo{display: none;}

            section:nth-child(1) {box-shadow: none;}

            section:nth-child(4n+2) div.boxheader{background:var(--colorSectionA);}
            section:nth-child(4n+2) h2{color:var(--colorSectionA);}

            section:nth-child(4n+3) div.boxheader{background:var(--colorSectionB);}
            section:nth-child(4n+3) h2{color:var(--colorSectionB);}

            section:nth-child(4n+4) div.boxheader{background:var(--colorSectionC);}
            section:nth-child(4n+4) h2{color:var(--colorSectionC);}

            section:nth-child(4n+5) div.boxheader{background:var(--colorSectionD);}
            section:nth-child(4n+5) h2{color:var(--colorSectionD);}
            var idMenu='divMenu';
            var idInput='eFilter';
            var idCount='idCount';
            var sClassHiddenSection='hidden';
            var sClassHidden='hiddenboxclass';

            function generateMenu(){
                // return true;
                var sHtml='';
                var obj = document.getElementsByTagName('h2');
                for (var i=0; i<obj.length; i++){
                    var sH2Id='h2-'+i;
                    sHtml+='<a href=#'+sH2Id+'>'+obj[i].innerText+'</a>';
                sHtml+='<br><a href=#>top</a>';

                return true;
            function filter(){

                // ---------- show / hide boxes based on search text
                var sText=document.getElementById(idInput).value;
                var obj = document.getElementsByClassName('box');
                var regex = RegExp(sText, 'i');

                for (i = 0; i < obj.length; i++) {
                    obj[i].className=obj[i].className.replace(sClassHidden, '');
                    if ( sText && !regex.test(obj[i].innerText) ){
                        obj[i].className+=' '+sClassHidden;
                    obj[i].className=obj[i].className.replace('  ', ' ');

                // ---------- show message based on visible boxes
                var sVisText='';
                var iCount=obj.length;

                var objHidden = document.getElementsByClassName(sClassHidden);
                // console.log(objHidden);
                var iVisible=iCount-objHidden.length;

                if(iVisible==iCount){ sVisText=iCount+' (all)'; }
                if(iVisible<iCount){ sVisText=iVisible+'/ '+iCount; }
                if(iVisible<1){ sVisText='(none)'; }
                document.getElementById(idCount).innerText='Visible: '+sVisText;

                // ---------- show message based on visible boxes
                var sections=document.querySelectorAll('section');
                for(var i=0; i < sections.length; i++){
                    sections[i].className=sections[i].className.replace(' '+sClassHiddenSection, '');
                    var sectionBoxes = sections[i].getElementsByClassName('box');
                    var hiddenBoxes = sections[i].getElementsByClassName(sClassHidden);
                    // console.log('section #'+i + ' -- ' + sectionBoxes.length + ', ' + hiddenBoxes.length);
                    if(sectionBoxes.length && sectionBoxes.length-hiddenBoxes.length==0){
                        sections[i].className+=' '+sClassHiddenSection;


            window.setTimeout('generateMenu();filter()', 20);
            <div id=\"filterbox\">
                🔎 Filter:
                <input type=\"text\" id=\"eFilter\" autofocus 
                    onchange=\"filter();\" onkeyup=\"filter();\" onchange=\"filter();\" 
                    value=\"\" size=\"20\" placeholder=\"Start to type ...\"><br>
                <span id=\"idCount\"></span>
                <nav id=\"divMenu\"></nav>
            <h1><span>🗃️ </span> ${__page_title__}</h1>


        <div style=\"clear: both; margin-bottom: 5em;\"></div>

# --------------------------------------------------------------------------------
# The replacement for ${__CONTENT__} is multiple concatination of a
# group ...
# --------------------------------------------------------------------------------
        <h2><span>🗂️</span> ${__group__}</h2>
        <p class="groupinfo">${__groupinfo__}</p>

# --------------------------------------------------------------------------------
# and html snippets per project
# --------------------------------------------------------------------------------
            <div class="box" onclick=\"window.open('${__url_doc__}');\">
                <div class="boxheader">
                    <span>📙</span> <a href=\"#\"><strong>${__label__}</strong></a>
                <div class="boxcontent">
                    🌐 <a href=\"${__url_repo__}\" target=\"_blank\" onclick=\"event.stopImmediatePropagation();\">${__url_repo__}</a>
                    <a href=\"${__url_doc__}\" class="doclink">${__url_doc__}</a>


# --------------------------------------------------------------------------------

The result is something like that:

Screenshot: Boxes