forked from supersonicbi/supersonicbi.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathen.search-data.min.7fcf3e759772e937d79bb15024ac51a7ffa254c787edad48250ccbb3d3c2db7f.json
1 lines (1 loc) · 94.1 KB
/
en.search-data.min.7fcf3e759772e937d79bb15024ac51a7ffa254c787edad48250ccbb3d3c2db7f.json
1
[{"id":0,"href":"/docs/%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%B7%AF/","title":"设计思路","section":"Docs","content":"通过自然语言界面(Natural Language Interface)访问数据是数据库上古大神们就开始畅想的情境,在学术界也一直是专门的研究方向。对我们影响比较大的两篇论文是IBM在2016年发表的ATHENA和谷歌在2017年发表的Analyza,但它是纯基于规则的工程实现。2017年之后,随着大规模数据集Seq2SQL和Spider发布,基于AI模型的解决方案如雨后春笋般涌现,从seq2seq到slot filling,从schema linking到intermediate representation,各种奇淫技巧不一而足。直到ChatGPT横空出世,基于prompting来实现Text-to-SQL几乎成了大家的共识。\n在项目初期,我们也曾尝试通过prompt engineering让ChatGPT直接生成SQL,但经过多轮迭代,在稳定性和可靠性方面始终无法达到生产可用的期望,总的来说有如下问题:\n语义复杂\n如果涉及多表关联、运算公式、时间转换等复杂语义情况,SQL生成难度变高,LLM输出可靠性会显著降低。 涉及指标计算的场景,如果依赖用户问询来描述,无法保证口径的一致性与确定性。 输出幻觉\n为了让LLM理解schema,需要将所有字段的名称和描述作为context输入,如果schema字段数量多,可能会超过context window限制。因此,基数过大的字段取值一般不会全部放入context,使得LLM无法识别到专有领域的术语。即便将schema全部输入且告知LLM不要随意猜测,仍然有一定几率会预测出错误的字段,甚至可能幻觉出不存在的字段。 相同的语义,不同的语言表达,可能会导致大相径庭的输出结果,无法保证一致性。 推理效率\n当前LLM推理速度还处在10秒+量级,再加上底层数据查询的耗时,同时还无法像纯文本那样的流式输出,非常考验用户的耐心。 当前LLM主流是按token计费,如果所有查询都需要走LLM,MaaS成本会随着查询量线性增长。 我们逐渐意识到,LLM只是看作是意图识别和文本生成的引擎,它还需要其他的组件来配套,才构成一个完整的系统解决方案。可与此类比的是传统OLAP引擎,需要有transformation层的清洗、关联、聚合等建模步骤来配套,才能形成高效稳定的数据服务。\n因此,在SuperSonic项目中我们围绕LLM引入与之配套的工程化组件,来增强语义解析和SQL生成的可靠性。架构图里黄色背景块就是引入的配套组件,下面的篇幅将展开介绍设计思考。\nSemantic Layer\r#\r当AI领域的LLM满级输出吸走大部份聚光灯的时候,BI领域也有一位召唤师在猥琐发育——它就是Semantic Layer(也有称为是Headless BI或者Metric Store)。所以它的核心价值是什么?我们可以用两个拟人化的角色来类比:\n翻译官:将技术名词(表/字段)翻译成业务术语(维度/指标/标签),便于业务用户理解。 大管家:将技术口径(关联关系/运算公式)统一化、精细化地管理起来,便于查帐比对,消除口径混乱。 两类角色的存在都可以提升数据服务质量,增强业务对数据的理解和信任。而随着企业内数据规模和使用场景的不断扩展,对于数据的组织能力和传播能力越来越成为数据驱动落地的关键因素(这似乎跟企业本身的发展是一个道理),所以semantic layer得到越来越多的关注与认可。\n接下来的问题,semantic layer和LLM又有什么联系?我们还是从它的两类角色上来分析:\n翻译官将技术名词翻译成更有意义的业务术语,便于业务用户理解的同时,同样能增强LLM对数据的语义理解。 大管家将技术口径封装起来,LLM不需要考虑关联关系和运算公式,生成复杂度极大地被简化。 因此,既然semantic layer可以帮助人更好地把数据用起来,为什么不顺便帮一下LLM,举手之劳而已。\nSchema Mapper\r#\r前文有提到LLM的token限制问题,主要是因为想要暴力求解,将全部字段的名称和取值一股脑都丢给LLM。直观分析,可以在前置环节增加映射机制,只保留能在输入文本中映射上的字段,可以极大地减少token使用,即便不超过限制,也能节省推理成本。这就是Schema Mapper组件的由来。\n当前的设计方案是,定期从semantic model中抽取名称、别名、取值来构建词典,形成内部的knowledge base。查询解析阶段,先对输入文本分词,再采用n-gram词典探测的机制。经过前期调研,中文NLP框架HanLP相对成熟,可以满足需要。\nSchema mapper会将所有达到匹配阈值要求的schema项及其相似度得分一并保存到info对象,传递给后续semantic parsing环节引用,最终会由parser挑选输入给LLM。\n与此同时,schema mapper可以定制扩展,并通过Java SPI配置的方式替换或补充SuperSonic的默认实现。\nSemantic Corrector\r#\r针对前文提到的推测错误和幻觉问题,一个方向是对prompt不断试验打磨,但现阶段仍然是玄学成分居多。另一个方向是,在LLM输出的后置环节增加修正机制,将明显错误的表达予以修复。这就是Semantic Corrector组件的由来。\n当前的设计方案是,从LLM生成的SQL中解析出表、字段、取值等名词,逐个检查合法性,将不合法名词通过类似schema mapping的方式去knowledge base尝试找到正确的匹配。比如,LLM可能将取值映射到了错误的字段,通过corrector尝试找到正确的字段映射,并改写SQL。\n与此同时,semantic corrector可以定制扩展,并通过Java SPI配置的方式替换或补充SuperSonic的默认实现。\nRule-based Parser\r#\r针对前文提到的效率问题,除了做时间的朋友,等待LLM进化突破之外,还可以想办法在应用层规避。比如,对于一些简单的问题输入,可以尝试基于规则来做语义解析,这也是从Google Analyza论文里带来的启发。这就是Rule-based Parser的由来。\n当前的设计方案是,通过经验整理一份表单,如下表所示,明确列举每类语义对象(指标/维度/取值/时间/算子)的组合会映射到哪一种查询意图。Rule-based parser再根据schema mapping的结果,从表单中查到对应的查询意图,这个有点类似slot filling的思路。\n引入Semantic Parser的抽象,分别有rule-based和LLM-based的实现。输入问题首先经过rule-based parser,如果有查询意图命中,则根据启发性算法来决定是否可以跳过LLM-based parser。当前,启发性算法会根据schema mapping命中的词汇总长度除以问题总长度来判断,超过配置的阈值则认为规则可以满足需要,决定跳过LLM,如果跳过那么提升效率的目的就达到了。\n与此同时,semantic parser可以定制扩展,并通过Java SPI配置的方式替换或补充SuperSonic的默认实现,也可以选择完全去掉rule-based parser配置。\nChat Plugin\r#\rSuperSonic的主链路主要涉及两个步骤:1、LLM解析语义,生成逻辑SQL,提交semantic layer;2、semantic layer生成物理SQL,提交底层OLAP引擎执行。但是,既然已经有了统一的ChatBI界面,是否能兼容其他的场景,比如文档知识库、数据看板,它们的执行过程不同于SuperSonic主链路。这就是Chat Plugin组件的由来。\n当前的设计方案是,三方插件可以通过WebPage(将直接IFrame嵌入展现)或者WebService(HTTP调用获取返回)两种方式来注册,后续会当作是一种工具来使用。当用户输入问题时,如何选择出合适的插件工具?经典的方式有以下两种:\n向量召回:插件在注册时可以设定一些示例问题,提前通过LLM向量化存储到向量数据库。解析输入问题时,将输入文本同样经过LLM向量化后,从向量数据库根据相似度来召回,通过最匹配的示例问题找到相对应的插件; 函数调用:插件在注册时可以配置函数名称和描述,利用LLM的function call能力来直接根据输入问题选择函数,并找到对应的插件。 两种方式可以结合使用,优先走向量召回流程,如果相似度没有达到配置的阈值,则继续用函数调用来选择。\n"},{"id":1,"href":"/posts/creating-a-new-theme/","title":"Creating a New Theme","section":"Blog","content":"\rIntroduction\r#\rThis tutorial will show you how to create a simple theme in Hugo. I assume that you are familiar with HTML, the bash command line, and that you are comfortable using Markdown to format content. I\u0026rsquo;ll explain how Hugo uses templates and how you can organize your templates to create a theme. I won\u0026rsquo;t cover using CSS to style your theme.\nWe\u0026rsquo;ll start with creating a new site with a very basic template. Then we\u0026rsquo;ll add in a few pages and posts. With small variations on that, you will be able to create many different types of web sites.\nIn this tutorial, commands that you enter will start with the \u0026ldquo;$\u0026rdquo; prompt. The output will follow. Lines that start with \u0026ldquo;#\u0026rdquo; are comments that I\u0026rsquo;ve added to explain a point. When I show updates to a file, the \u0026ldquo;:wq\u0026rdquo; on the last line means to save the file.\nHere\u0026rsquo;s an example:\n## this is a comment\r$ echo this is a command\rthis is a command\r## edit the file\r$ vi foo.md\r+++\rdate = \u0026#34;2014-09-28\u0026#34;\rtitle = \u0026#34;creating a new theme\u0026#34;\r+++\rbah and humbug\r:wq\r## show it\r$ cat foo.md\r+++\rdate = \u0026#34;2014-09-28\u0026#34;\rtitle = \u0026#34;creating a new theme\u0026#34;\r+++\rbah and humbug\r$ Some Definitions\r#\rThere are a few concepts that you need to understand before creating a theme.\nSkins\r#\rSkins are the files responsible for the look and feel of your site. It’s the CSS that controls colors and fonts, it’s the Javascript that determines actions and reactions. It’s also the rules that Hugo uses to transform your content into the HTML that the site will serve to visitors.\nYou have two ways to create a skin. The simplest way is to create it in the layouts/ directory. If you do, then you don’t have to worry about configuring Hugo to recognize it. The first place that Hugo will look for rules and files is in the layouts/ directory so it will always find the skin.\nYour second choice is to create it in a sub-directory of the themes/ directory. If you do, then you must always tell Hugo where to search for the skin. It’s extra work, though, so why bother with it?\nThe difference between creating a skin in layouts/ and creating it in themes/ is very subtle. A skin in layouts/ can’t be customized without updating the templates and static files that it is built from. A skin created in themes/, on the other hand, can be and that makes it easier for other people to use it.\nThe rest of this tutorial will call a skin created in the themes/ directory a theme.\nNote that you can use this tutorial to create a skin in the layouts/ directory if you wish to. The main difference will be that you won’t need to update the site’s configuration file to use a theme.\nThe Home Page\r#\rThe home page, or landing page, is the first page that many visitors to a site see. It is the index.html file in the root directory of the web site. Since Hugo writes files to the public/ directory, our home page is public/index.html.\nSite Configuration File\r#\rWhen Hugo runs, it looks for a configuration file that contains settings that override default values for the entire site. The file can use TOML, YAML, or JSON. I prefer to use TOML for my configuration files. If you prefer to use JSON or YAML, you’ll need to translate my examples. You’ll also need to change the name of the file since Hugo uses the extension to determine how to process it.\nHugo translates Markdown files into HTML. By default, Hugo expects to find Markdown files in your content/ directory and template files in your themes/ directory. It will create HTML files in your public/ directory. You can change this by specifying alternate locations in the configuration file.\nContent\r#\rContent is stored in text files that contain two sections. The first section is the “front matter,” which is the meta-information on the content. The second section contains Markdown that will be converted to HTML.\nFront Matter\r#\rThe front matter is information about the content. Like the configuration file, it can be written in TOML, YAML, or JSON. Unlike the configuration file, Hugo doesn’t use the file’s extension to know the format. It looks for markers to signal the type. TOML is surrounded by “+++”, YAML by “---”, and JSON is enclosed in curly braces. I prefer to use TOML, so you’ll need to translate my examples if you prefer YAML or JSON.\nThe information in the front matter is passed into the template before the content is rendered into HTML.\nMarkdown\r#\rContent is written in Markdown which makes it easier to create the content. Hugo runs the content through a Markdown engine to create the HTML which will be written to the output file.\nTemplate Files\r#\rHugo uses template files to render content into HTML. Template files are a bridge between the content and presentation. Rules in the template define what content is published, where it\u0026rsquo;s published to, and how it will rendered to the HTML file. The template guides the presentation by specifying the style to use.\nThere are three types of templates: single, list, and partial. Each type takes a bit of content as input and transforms it based on the commands in the template.\nHugo uses its knowledge of the content to find the template file used to render the content. If it can’t find a template that is an exact match for the content, it will shift up a level and search from there. It will continue to do so until it finds a matching template or runs out of templates to try. If it can’t find a template, it will use the default template for the site.\nPlease note that you can use the front matter to influence Hugo’s choice of templates.\nSingle Template\r#\rA single template is used to render a single piece of content. For example, an article or post would be a single piece of content and use a single template.\nList Template\r#\rA list template renders a group of related content. That could be a summary of recent postings or all articles in a category. List templates can contain multiple groups.\nThe homepage template is a special type of list template. Hugo assumes that the home page of your site will act as the portal for the rest of the content in the site.\nPartial Template\r#\rA partial template is a template that can be included in other templates. Partial templates must be called using the “partial” template command. They are very handy for rolling up common behavior. For example, your site may have a banner that all pages use. Instead of copying the text of the banner into every single and list template, you could create a partial with the banner in it. That way if you decide to change the banner, you only have to change the partial template.\nCreate a New Site\r#\rLet\u0026rsquo;s use Hugo to create a new web site. I\u0026rsquo;m a Mac user, so I\u0026rsquo;ll create mine in my home directory, in the Sites folder. If you\u0026rsquo;re using Linux, you might have to create the folder first.\nThe \u0026ldquo;new site\u0026rdquo; command will create a skeleton of a site. It will give you the basic directory structure and a useable configuration file.\n$ hugo new site ~/Sites/zafta\r$ cd ~/Sites/zafta\r$ ls -l\rtotal 8\rdrwxr-xr-x 7 quoha staff 238 Sep 29 16:49 .\rdrwxr-xr-x 3 quoha staff 102 Sep 29 16:49 ..\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 archetypes\r-rw-r--r-- 1 quoha staff 82 Sep 29 16:49 config.toml\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 content\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 layouts\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 static\r$ Take a look in the content/ directory to confirm that it is empty.\nThe other directories (archetypes/, layouts/, and static/) are used when customizing a theme. That\u0026rsquo;s a topic for a different tutorial, so please ignore them for now.\nGenerate the HTML For the New Site\r#\rRunning the hugo command with no options will read all the available content and generate the HTML files. It will also copy all static files (that\u0026rsquo;s everything that\u0026rsquo;s not content). Since we have an empty site, it won\u0026rsquo;t do much, but it will do it very quickly.\n$ hugo --verbose\rINFO: 2014/09/29 Using config file: config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rWARN: 2014/09/29 Unable to locate layout: [index.html _default/list.html _default/single.html]\rWARN: 2014/09/29 Unable to locate layout: [404.html]\r0 draft content 0 future content 0 pages created 0 tags created\r0 categories created\rin 2 ms\r$ The \u0026ldquo;--verbose\u0026rdquo; flag gives extra information that will be helpful when we build the template. Every line of the output that starts with \u0026ldquo;INFO:\u0026rdquo; or \u0026ldquo;WARN:\u0026rdquo; is present because we used that flag. The lines that start with \u0026ldquo;WARN:\u0026rdquo; are warning messages. We\u0026rsquo;ll go over them later.\nWe can verify that the command worked by looking at the directory again.\n$ ls -l\rtotal 8\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 archetypes\r-rw-r--r-- 1 quoha staff 82 Sep 29 16:49 config.toml\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 content\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 layouts\rdrwxr-xr-x 4 quoha staff 136 Sep 29 17:02 public\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 static\r$ See that new public/ directory? Hugo placed all generated content there. When you\u0026rsquo;re ready to publish your web site, that\u0026rsquo;s the place to start. For now, though, let\u0026rsquo;s just confirm that we have what we\u0026rsquo;d expect from a site with no content.\n$ ls -l public\rtotal 16\r-rw-r--r-- 1 quoha staff 416 Sep 29 17:02 index.xml\r-rw-r--r-- 1 quoha staff 262 Sep 29 17:02 sitemap.xml\r$ Hugo created two XML files, which is standard, but there are no HTML files.\nTest the New Site\r#\rVerify that you can run the built-in web server. It will dramatically shorten your development cycle if you do. Start it by running the \u0026ldquo;server\u0026rdquo; command. If it is successful, you will see output similar to the following:\n$ hugo server --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rWARN: 2014/09/29 Unable to locate layout: [index.html _default/list.html _default/single.html]\rWARN: 2014/09/29 Unable to locate layout: [404.html]\r0 draft content 0 future content 0 pages created 0 tags created\r0 categories created\rin 2 ms\rServing pages from /Users/quoha/Sites/zafta/public\rWeb Server is available at http://localhost:1313\rPress Ctrl+C to stop Connect to the listed URL (it\u0026rsquo;s on the line that starts with \u0026ldquo;Web Server\u0026rdquo;). If everything is working correctly, you should get a page that shows the following:\nindex.xml\rsitemap.xml That\u0026rsquo;s a listing of your public/ directory. Hugo didn\u0026rsquo;t create a home page because our site has no content. When there\u0026rsquo;s no index.html file in a directory, the server lists the files in the directory, which is what you should see in your browser.\nLet’s go back and look at those warnings again.\nWARN: 2014/09/29 Unable to locate layout: [index.html _default/list.html _default/single.html]\rWARN: 2014/09/29 Unable to locate layout: [404.html] That second warning is easier to explain. We haven’t created a template to be used to generate “page not found errors.” The 404 message is a topic for a separate tutorial.\nNow for the first warning. It is for the home page. You can tell because the first layout that it looked for was “index.html.” That’s only used by the home page.\nI like that the verbose flag causes Hugo to list the files that it\u0026rsquo;s searching for. For the home page, they are index.html, _default/list.html, and _default/single.html. There are some rules that we\u0026rsquo;ll cover later that explain the names and paths. For now, just remember that Hugo couldn\u0026rsquo;t find a template for the home page and it told you so.\nAt this point, you\u0026rsquo;ve got a working installation and site that we can build upon. All that’s left is to add some content and a theme to display it.\nCreate a New Theme\r#\rHugo doesn\u0026rsquo;t ship with a default theme. There are a few available (I counted a dozen when I first installed Hugo) and Hugo comes with a command to create new themes.\nWe\u0026rsquo;re going to create a new theme called \u0026ldquo;zafta.\u0026rdquo; Since the goal of this tutorial is to show you how to fill out the files to pull in your content, the theme will not contain any CSS. In other words, ugly but functional.\nAll themes have opinions on content and layout. For example, Zafta uses \u0026ldquo;post\u0026rdquo; over \u0026ldquo;blog\u0026rdquo;. Strong opinions make for simpler templates but differing opinions make it tougher to use themes. When you build a theme, consider using the terms that other themes do.\nCreate a Skeleton\r#\rUse the hugo \u0026ldquo;new\u0026rdquo; command to create the skeleton of a theme. This creates the directory structure and places empty files for you to fill out.\n$ hugo new theme zafta\r$ ls -l\rtotal 8\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 archetypes\r-rw-r--r-- 1 quoha staff 82 Sep 29 16:49 config.toml\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 content\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 layouts\rdrwxr-xr-x 4 quoha staff 136 Sep 29 17:02 public\rdrwxr-xr-x 2 quoha staff 68 Sep 29 16:49 static\rdrwxr-xr-x 3 quoha staff 102 Sep 29 17:31 themes\r$ find themes -type f | xargs ls -l\r-rw-r--r-- 1 quoha staff 1081 Sep 29 17:31 themes/zafta/LICENSE.md\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/archetypes/default.md\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/_default/list.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/_default/single.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/partials/footer.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/partials/header.html\r-rw-r--r-- 1 quoha staff 93 Sep 29 17:31 themes/zafta/theme.toml\r$ The skeleton includes templates (the files ending in .html), license file, a description of your theme (the theme.toml file), and an empty archetype.\nPlease take a minute to fill out the theme.toml and LICENSE.md files. They\u0026rsquo;re optional, but if you\u0026rsquo;re going to be distributing your theme, it tells the world who to praise (or blame). It\u0026rsquo;s also nice to declare the license so that people will know how they can use the theme.\n$ vi themes/zafta/theme.toml\rauthor = \u0026#34;michael d henderson\u0026#34;\rdescription = \u0026#34;a minimal working template\u0026#34;\rlicense = \u0026#34;MIT\u0026#34;\rname = \u0026#34;zafta\u0026#34;\rsource_repo = \u0026#34;\u0026#34;\rtags = [\u0026#34;tags\u0026#34;, \u0026#34;categories\u0026#34;]\r:wq\r## also edit themes/zafta/LICENSE.md and change\r## the bit that says \u0026#34;YOUR_NAME_HERE\u0026#34; Note that the the skeleton\u0026rsquo;s template files are empty. Don\u0026rsquo;t worry, we\u0026rsquo;ll be changing that shortly.\n$ find themes/zafta -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/_default/list.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/_default/single.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/partials/footer.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/partials/header.html\r$ Update the Configuration File to Use the Theme\r#\rNow that we\u0026rsquo;ve got a theme to work with, it\u0026rsquo;s a good idea to add the theme name to the configuration file. This is optional, because you can always add \u0026ldquo;-t zafta\u0026rdquo; on all your commands. I like to put it the configuration file because I like shorter command lines. If you don\u0026rsquo;t put it in the configuration file or specify it on the command line, you won\u0026rsquo;t use the template that you\u0026rsquo;re expecting to.\nEdit the file to add the theme, add a title for the site, and specify that all of our content will use the TOML format.\n$ vi config.toml\rtheme = \u0026#34;zafta\u0026#34;\rbaseurl = \u0026#34;\u0026#34;\rlanguageCode = \u0026#34;en-us\u0026#34;\rtitle = \u0026#34;zafta - totally refreshing\u0026#34;\rMetaDataFormat = \u0026#34;toml\u0026#34;\r:wq\r$ Generate the Site\r#\rNow that we have an empty theme, let\u0026rsquo;s generate the site again.\n$ hugo --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 0 pages created 0 tags created\r0 categories created\rin 2 ms\r$ Did you notice that the output is different? The warning message for the home page has disappeared and we have an additional information line saying that Hugo is syncing from the theme\u0026rsquo;s directory.\nLet\u0026rsquo;s check the public/ directory to see what Hugo\u0026rsquo;s created.\n$ ls -l public\rtotal 16\rdrwxr-xr-x 2 quoha staff 68 Sep 29 17:56 css\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:56 index.html\r-rw-r--r-- 1 quoha staff 407 Sep 29 17:56 index.xml\rdrwxr-xr-x 2 quoha staff 68 Sep 29 17:56 js\r-rw-r--r-- 1 quoha staff 243 Sep 29 17:56 sitemap.xml\r$ Notice four things:\nHugo created a home page. This is the file public/index.html. Hugo created a css/ directory. Hugo created a js/ directory. Hugo claimed that it created 0 pages. It created a file and copied over static files, but didn\u0026rsquo;t create any pages. That\u0026rsquo;s because it considers a \u0026ldquo;page\u0026rdquo; to be a file created directly from a content file. It doesn\u0026rsquo;t count things like the index.html files that it creates automatically. The Home Page\r#\rHugo supports many different types of templates. The home page is special because it gets its own type of template and its own template file. The file, layouts/index.html, is used to generate the HTML for the home page. The Hugo documentation says that this is the only required template, but that depends. Hugo\u0026rsquo;s warning message shows that it looks for three different templates:\nWARN: 2014/09/29 Unable to locate layout: [index.html _default/list.html _default/single.html] If it can\u0026rsquo;t find any of these, it completely skips creating the home page. We noticed that when we built the site without having a theme installed.\nWhen Hugo created our theme, it created an empty home page template. Now, when we build the site, Hugo finds the template and uses it to generate the HTML for the home page. Since the template file is empty, the HTML file is empty, too. If the template had any rules in it, then Hugo would have used them to generate the home page.\n$ find . -name index.html | xargs ls -l\r-rw-r--r-- 1 quoha staff 0 Sep 29 20:21 ./public/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 ./themes/zafta/layouts/index.html\r$ The Magic of Static\r#\rHugo does two things when generating the site. It uses templates to transform content into HTML and it copies static files into the site. Unlike content, static files are not transformed. They are copied exactly as they are.\nHugo assumes that your site will use both CSS and JavaScript, so it creates directories in your theme to hold them. Remember opinions? Well, Hugo\u0026rsquo;s opinion is that you\u0026rsquo;ll store your CSS in a directory named css/ and your JavaScript in a directory named js/. If you don\u0026rsquo;t like that, you can change the directory names in your theme directory or even delete them completely. Hugo\u0026rsquo;s nice enough to offer its opinion, then behave nicely if you disagree.\n$ find themes/zafta -type d | xargs ls -ld\rdrwxr-xr-x 7 quoha staff 238 Sep 29 17:38 themes/zafta\rdrwxr-xr-x 3 quoha staff 102 Sep 29 17:31 themes/zafta/archetypes\rdrwxr-xr-x 5 quoha staff 170 Sep 29 17:31 themes/zafta/layouts\rdrwxr-xr-x 4 quoha staff 136 Sep 29 17:31 themes/zafta/layouts/_default\rdrwxr-xr-x 4 quoha staff 136 Sep 29 17:31 themes/zafta/layouts/partials\rdrwxr-xr-x 4 quoha staff 136 Sep 29 17:31 themes/zafta/static\rdrwxr-xr-x 2 quoha staff 68 Sep 29 17:31 themes/zafta/static/css\rdrwxr-xr-x 2 quoha staff 68 Sep 29 17:31 themes/zafta/static/js\r$ The Theme Development Cycle\r#\rWhen you\u0026rsquo;re working on a theme, you will make changes in the theme\u0026rsquo;s directory, rebuild the site, and check your changes in the browser. Hugo makes this very easy:\nPurge the public/ directory. Run the built in web server in watch mode. Open your site in a browser. Update the theme. Glance at your browser window to see changes. Return to step 4. I’ll throw in one more opinion: never work on a theme on a live site. Always work on a copy of your site. Make changes to your theme, test them, then copy them up to your site. For added safety, use a tool like Git to keep a revision history of your content and your theme. Believe me when I say that it is too easy to lose both your mind and your changes.\nCheck the main Hugo site for information on using Git with Hugo.\nPurge the public/ Directory\r#\rWhen generating the site, Hugo will create new files and update existing ones in the public/ directory. It will not delete files that are no longer used. For example, files that were created in the wrong directory or with the wrong title will remain. If you leave them, you might get confused by them later. I recommend cleaning out your site prior to generating it.\nNote: If you\u0026rsquo;re building on an SSD, you should ignore this. Churning on a SSD can be costly.\nHugo\u0026rsquo;s Watch Option\r#\rHugo\u0026rsquo;s \u0026ldquo;--watch\u0026rdquo; option will monitor the content/ and your theme directories for changes and rebuild the site automatically.\nLive Reload\r#\rHugo\u0026rsquo;s built in web server supports live reload. As pages are saved on the server, the browser is told to refresh the page. Usually, this happens faster than you can say, \u0026ldquo;Wow, that\u0026rsquo;s totally amazing.\u0026rdquo;\nDevelopment Commands\r#\rUse the following commands as the basis for your workflow.\n## purge old files. hugo will recreate the public directory.\r##\r$ rm -rf public\r##\r## run hugo in watch mode\r##\r$ hugo server --watch --verbose Here\u0026rsquo;s sample output showing Hugo detecting a change to the template for the home page. Once generated, the web browser automatically reloaded the page. I\u0026rsquo;ve said this before, it\u0026rsquo;s amazing.\n$ rm -rf public\r$ hugo server --watch --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 0 pages created 0 tags created\r0 categories created\rin 2 ms\rWatching for changes in /Users/quoha/Sites/zafta/content\rServing pages from /Users/quoha/Sites/zafta/public\rWeb Server is available at http://localhost:1313\rPress Ctrl+C to stop\rINFO: 2014/09/29 File System Event: [\u0026#34;/Users/quoha/Sites/zafta/themes/zafta/layouts/index.html\u0026#34;: MODIFY|ATTRIB]\rChange detected, rebuilding site\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 0 pages created 0 tags created\r0 categories created\rin 1 ms Update the Home Page Template\r#\rThe home page is one of a few special pages that Hugo creates automatically. As mentioned earlier, it looks for one of three files in the theme\u0026rsquo;s layout/ directory:\nindex.html _default/list.html _default/single.html We could update one of the default templates, but a good design decision is to update the most specific template available. That\u0026rsquo;s not a hard and fast rule (in fact, we\u0026rsquo;ll break it a few times in this tutorial), but it is a good generalization.\nMake a Static Home Page\r#\rRight now, that page is empty because we don\u0026rsquo;t have any content and we don\u0026rsquo;t have any logic in the template. Let\u0026rsquo;s change that by adding some text to the template.\n$ vi themes/zafta/layouts/index.html\r\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;hugo says hello!\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; :wq\r$ Build the web site and then verify the results.\n$ hugo --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 0 pages created 0 tags created\r0 categories created\rin 2 ms\r$ find public -type f -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-r--r-- 1 quoha staff 78 Sep 29 21:26 public/index.html\r$ cat public/index.html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;hugo says hello!\u0026lt;/p\u0026gt; \u0026lt;/html\u0026gt; Live Reload\r#\rNote: If you\u0026rsquo;re running the server with the --watch option, you\u0026rsquo;ll see different content in the file:\n$ cat public/index.html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;hugo says hello!\u0026lt;/p\u0026gt; \u0026lt;script\u0026gt;document.write(\u0026#39;\u0026lt;script src=\u0026#34;http://\u0026#39; + (location.host || \u0026#39;localhost\u0026#39;).split(\u0026#39;:\u0026#39;)[0] + \u0026#39;:1313/livereload.js?mindelay=10\u0026#34;\u0026gt;\u0026lt;/\u0026#39; + \u0026#39;script\u0026gt;\u0026#39;)\u0026lt;/script\u0026gt;\u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; When you use --watch, the Live Reload script is added by Hugo. Look for live reload in the documentation to see what it does and how to disable it.\nBuild a \u0026ldquo;Dynamic\u0026rdquo; Home Page\r#\r\u0026ldquo;Dynamic home page?\u0026rdquo; Hugo\u0026rsquo;s a static web site generator, so this seems an odd thing to say. I mean let\u0026rsquo;s have the home page automatically reflect the content in the site every time Hugo builds it. We\u0026rsquo;ll use iteration in the template to do that.\nCreate New Posts\r#\rNow that we have the home page generating static content, let\u0026rsquo;s add some content to the site. We\u0026rsquo;ll display these posts as a list on the home page and on their own page, too.\nHugo has a command to generate a skeleton post, just like it does for sites and themes.\n$ hugo --verbose new post/first.md\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 attempting to create post/first.md of post\rINFO: 2014/09/29 curpath: /Users/quoha/Sites/zafta/themes/zafta/archetypes/default.md\rERROR: 2014/09/29 Unable to Cast \u0026lt;nil\u0026gt; to map[string]interface{}\r$ That wasn\u0026rsquo;t very nice, was it?\nThe \u0026ldquo;new\u0026rdquo; command uses an archetype to create the post file. Hugo created an empty default archetype file, but that causes an error when there\u0026rsquo;s a theme. For me, the workaround was to create an archetypes file specifically for the post type.\n$ vi themes/zafta/archetypes/post.md\r+++\rDescription = \u0026#34;\u0026#34;\rTags = []\rCategories = []\r+++\r:wq\r$ find themes/zafta/archetypes -type f | xargs ls -l\r-rw-r--r-- 1 quoha staff 0 Sep 29 21:53 themes/zafta/archetypes/default.md\r-rw-r--r-- 1 quoha staff 51 Sep 29 21:54 themes/zafta/archetypes/post.md\r$ hugo --verbose new post/first.md\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 attempting to create post/first.md of post\rINFO: 2014/09/29 curpath: /Users/quoha/Sites/zafta/themes/zafta/archetypes/post.md\rINFO: 2014/09/29 creating /Users/quoha/Sites/zafta/content/post/first.md\r/Users/quoha/Sites/zafta/content/post/first.md created\r$ hugo --verbose new post/second.md\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 attempting to create post/second.md of post\rINFO: 2014/09/29 curpath: /Users/quoha/Sites/zafta/themes/zafta/archetypes/post.md\rINFO: 2014/09/29 creating /Users/quoha/Sites/zafta/content/post/second.md\r/Users/quoha/Sites/zafta/content/post/second.md created\r$ ls -l content/post\rtotal 16\r-rw-r--r-- 1 quoha staff 104 Sep 29 21:54 first.md\r-rw-r--r-- 1 quoha staff 105 Sep 29 21:57 second.md\r$ cat content/post/first.md +++\rCategories = []\rDescription = \u0026#34;\u0026#34;\rTags = []\rdate = \u0026#34;2014-09-29T21:54:53-05:00\u0026#34;\rtitle = \u0026#34;first\u0026#34;\r+++\rmy first post\r$ cat content/post/second.md +++\rCategories = []\rDescription = \u0026#34;\u0026#34;\rTags = []\rdate = \u0026#34;2014-09-29T21:57:09-05:00\u0026#34;\rtitle = \u0026#34;second\u0026#34;\r+++\rmy second post\r$ Build the web site and then verify the results.\n$ rm -rf public\r$ hugo --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 found taxonomies: map[string]string{\u0026#34;category\u0026#34;:\u0026#34;categories\u0026#34;, \u0026#34;tag\u0026#34;:\u0026#34;tags\u0026#34;}\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 2 pages created 0 tags created\r0 categories created\rin 4 ms\r$ The output says that it created 2 pages. Those are our new posts:\n$ find public -type f -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-r--r-- 1 quoha staff 78 Sep 29 22:13 public/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:13 public/post/first/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:13 public/post/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:13 public/post/second/index.html\r$ The new files are empty because because the templates used to generate the content are empty. The homepage doesn\u0026rsquo;t show the new content, either. We have to update the templates to add the posts.\nList and Single Templates\r#\rIn Hugo, we have three major kinds of templates. There\u0026rsquo;s the home page template that we updated previously. It is used only by the home page. We also have \u0026ldquo;single\u0026rdquo; templates which are used to generate output for a single content file. We also have \u0026ldquo;list\u0026rdquo; templates that are used to group multiple pieces of content before generating output.\nGenerally speaking, list templates are named \u0026ldquo;list.html\u0026rdquo; and single templates are named \u0026ldquo;single.html.\u0026rdquo;\nThere are three other types of templates: partials, content views, and terms. We will not go into much detail on these.\nAdd Content to the Homepage\r#\rThe home page will contain a list of posts. Let\u0026rsquo;s update its template to add the posts that we just created. The logic in the template will run every time we build the site.\n$ vi themes/zafta/layouts/index.html \u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;body\u0026gt;\r{{ range first 10 .Data.Pages }}\r\u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt;\r{{ end }}\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r:wq\r$ Hugo uses the Go template engine. That engine scans the template files for commands which are enclosed between \u0026ldquo;{{\u0026rdquo; and \u0026ldquo;}}\u0026rdquo;. In our template, the commands are:\nrange .Title end The \u0026ldquo;range\u0026rdquo; command is an iterator. We\u0026rsquo;re going to use it to go through the first ten pages. Every HTML file that Hugo creates is treated as a page, so looping through the list of pages will look at every file that will be created.\nThe \u0026ldquo;.Title\u0026rdquo; command prints the value of the \u0026ldquo;title\u0026rdquo; variable. Hugo pulls it from the front matter in the Markdown file.\nThe \u0026ldquo;end\u0026rdquo; command signals the end of the range iterator. The engine loops back to the top of the iteration when it finds \u0026ldquo;end.\u0026rdquo; Everything between the \u0026ldquo;range\u0026rdquo; and \u0026ldquo;end\u0026rdquo; is evaluated every time the engine goes through the iteration. In this file, that would cause the title from the first ten pages to be output as heading level one.\nIt\u0026rsquo;s helpful to remember that some variables, like .Data, are created before any output files. Hugo loads every content file into the variable and then gives the template a chance to process before creating the HTML files.\nBuild the web site and then verify the results.\n$ rm -rf public\r$ hugo --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 found taxonomies: map[string]string{\u0026#34;tag\u0026#34;:\u0026#34;tags\u0026#34;, \u0026#34;category\u0026#34;:\u0026#34;categories\u0026#34;}\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 2 pages created 0 tags created\r0 categories created\rin 4 ms\r$ find public -type f -name \u0026#39;*.html\u0026#39; | xargs ls -l -rw-r--r-- 1 quoha staff 94 Sep 29 22:23 public/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:23 public/post/first/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:23 public/post/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:23 public/post/second/index.html\r$ cat public/index.html \u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;second\u0026lt;/h1\u0026gt;\r\u0026lt;h1\u0026gt;first\u0026lt;/h1\u0026gt;\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r$ Congratulations, the home page shows the title of the two posts. The posts themselves are still empty, but let\u0026rsquo;s take a moment to appreciate what we\u0026rsquo;ve done. Your template now generates output dynamically. Believe it or not, by inserting the range command inside of those curly braces, you\u0026rsquo;ve learned everything you need to know to build a theme. All that\u0026rsquo;s really left is understanding which template will be used to generate each content file and becoming familiar with the commands for the template engine.\nAnd, if that were entirely true, this tutorial would be much shorter. There are a few things to know that will make creating a new template much easier. Don\u0026rsquo;t worry, though, that\u0026rsquo;s all to come.\nAdd Content to the Posts\r#\rWe\u0026rsquo;re working with posts, which are in the content/post/ directory. That means that their section is \u0026ldquo;post\u0026rdquo; (and if we don\u0026rsquo;t do something weird, their type is also \u0026ldquo;post\u0026rdquo;).\nHugo uses the section and type to find the template file for every piece of content. Hugo will first look for a template file that matches the section or type name. If it can\u0026rsquo;t find one, then it will look in the _default/ directory. There are some twists that we\u0026rsquo;ll cover when we get to categories and tags, but for now we can assume that Hugo will try post/single.html, then _default/single.html.\nNow that we know the search rule, let\u0026rsquo;s see what we actually have available:\n$ find themes/zafta -name single.html | xargs ls -l\r-rw-r--r-- 1 quoha staff 132 Sep 29 17:31 themes/zafta/layouts/_default/single.html We could create a new template, post/single.html, or change the default. Since we don\u0026rsquo;t know of any other content types, let\u0026rsquo;s start with updating the default.\nRemember, any content that we haven\u0026rsquo;t created a template for will end up using this template. That can be good or bad. Bad because I know that we\u0026rsquo;re going to be adding different types of content and we\u0026rsquo;re going to end up undoing some of the changes we\u0026rsquo;ve made. It\u0026rsquo;s good because we\u0026rsquo;ll be able to see immediate results. It\u0026rsquo;s also good to start here because we can start to build the basic layout for the site. As we add more content types, we\u0026rsquo;ll refactor this file and move logic around. Hugo makes that fairly painless, so we\u0026rsquo;ll accept the cost and proceed.\nPlease see the Hugo documentation on template rendering for all the details on determining which template to use. And, as the docs mention, if you\u0026rsquo;re building a single page application (SPA) web site, you can delete all of the other templates and work with just the default single page. That\u0026rsquo;s a refreshing amount of joy right there.\nUpdate the Template File\r#\r$ vi themes/zafta/layouts/_default/single.html \u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;head\u0026gt;\r\u0026lt;title\u0026gt;{{ .Title }}\u0026lt;/title\u0026gt;\r\u0026lt;/head\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt;\r{{ .Content }}\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r:wq\r$ Build the web site and verify the results.\n$ rm -rf public\r$ hugo --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 found taxonomies: map[string]string{\u0026#34;tag\u0026#34;:\u0026#34;tags\u0026#34;, \u0026#34;category\u0026#34;:\u0026#34;categories\u0026#34;}\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 2 pages created 0 tags created\r0 categories created\rin 4 ms\r$ find public -type f -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-r--r-- 1 quoha staff 94 Sep 29 22:40 public/index.html\r-rw-r--r-- 1 quoha staff 125 Sep 29 22:40 public/post/first/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:40 public/post/index.html\r-rw-r--r-- 1 quoha staff 128 Sep 29 22:40 public/post/second/index.html\r$ cat public/post/first/index.html \u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;head\u0026gt;\r\u0026lt;title\u0026gt;first\u0026lt;/title\u0026gt;\r\u0026lt;/head\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;first\u0026lt;/h1\u0026gt;\r\u0026lt;p\u0026gt;my first post\u0026lt;/p\u0026gt;\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r$ cat public/post/second/index.html \u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;head\u0026gt;\r\u0026lt;title\u0026gt;second\u0026lt;/title\u0026gt;\r\u0026lt;/head\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;second\u0026lt;/h1\u0026gt;\r\u0026lt;p\u0026gt;my second post\u0026lt;/p\u0026gt;\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r$ Notice that the posts now have content. You can go to localhost:1313/post/first to verify.\nLinking to Content\r#\rThe posts are on the home page. Let\u0026rsquo;s add a link from there to the post. Since this is the home page, we\u0026rsquo;ll update its template.\n$ vi themes/zafta/layouts/index.html\r\u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;body\u0026gt;\r{{ range first 10 .Data.Pages }}\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt;{{ .Title }}\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r{{ end }}\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt; Build the web site and verify the results.\n$ rm -rf public\r$ hugo --verbose\rINFO: 2014/09/29 Using config file: /Users/quoha/Sites/zafta/config.toml\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/themes/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 syncing from /Users/quoha/Sites/zafta/static/ to /Users/quoha/Sites/zafta/public/\rINFO: 2014/09/29 found taxonomies: map[string]string{\u0026#34;tag\u0026#34;:\u0026#34;tags\u0026#34;, \u0026#34;category\u0026#34;:\u0026#34;categories\u0026#34;}\rWARN: 2014/09/29 Unable to locate layout: [404.html theme/404.html]\r0 draft content 0 future content 2 pages created 0 tags created\r0 categories created\rin 4 ms\r$ find public -type f -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-r--r-- 1 quoha staff 149 Sep 29 22:44 public/index.html\r-rw-r--r-- 1 quoha staff 125 Sep 29 22:44 public/post/first/index.html\r-rw-r--r-- 1 quoha staff 0 Sep 29 22:44 public/post/index.html\r-rw-r--r-- 1 quoha staff 128 Sep 29 22:44 public/post/second/index.html\r$ cat public/index.html \u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;/post/second/\u0026#34;\u0026gt;second\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;/post/first/\u0026#34;\u0026gt;first\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r$ Create a Post Listing\r#\rWe have the posts displaying on the home page and on their own page. We also have a file public/post/index.html that is empty. Let\u0026rsquo;s make it show a list of all posts (not just the first ten).\nWe need to decide which template to update. This will be a listing, so it should be a list template. Let\u0026rsquo;s take a quick look and see which list templates are available.\n$ find themes/zafta -name list.html | xargs ls -l\r-rw-r--r-- 1 quoha staff 0 Sep 29 17:31 themes/zafta/layouts/_default/list.html As with the single post, we have to decide to update _default/list.html or create post/list.html. We still don\u0026rsquo;t have multiple content types, so let\u0026rsquo;s stay consistent and update the default list template.\nCreating Top Level Pages\r#\rLet\u0026rsquo;s add an \u0026ldquo;about\u0026rdquo; page and display it at the top level (as opposed to a sub-level like we did with posts).\nThe default in Hugo is to use the directory structure of the content/ directory to guide the location of the generated html in the public/ directory. Let\u0026rsquo;s verify that by creating an \u0026ldquo;about\u0026rdquo; page at the top level:\n$ vi content/about.md +++\rtitle = \u0026#34;about\u0026#34;\rdescription = \u0026#34;about this site\u0026#34;\rdate = \u0026#34;2014-09-27\u0026#34;\rslug = \u0026#34;about time\u0026#34;\r+++\r## about us\ri\u0026#39;m speechless\r:wq Generate the web site and verify the results.\n$ find public -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-rw-r-- 1 mdhender staff 334 Sep 27 15:08 public/about-time/index.html\r-rw-rw-r-- 1 mdhender staff 527 Sep 27 15:08 public/index.html\r-rw-rw-r-- 1 mdhender staff 358 Sep 27 15:08 public/post/first-post/index.html\r-rw-rw-r-- 1 mdhender staff 0 Sep 27 15:08 public/post/index.html\r-rw-rw-r-- 1 mdhender staff 342 Sep 27 15:08 public/post/second-post/index.html Notice that the page wasn\u0026rsquo;t created at the top level. It was created in a sub-directory named \u0026lsquo;about-time/\u0026rsquo;. That name came from our slug. Hugo will use the slug to name the generated content. It\u0026rsquo;s a reasonable default, by the way, but we can learn a few things by fighting it for this file.\nOne other thing. Take a look at the home page.\n$ cat public/index.html\r\u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;http://localhost:1313/post/theme/\u0026#34;\u0026gt;creating a new theme\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;http://localhost:1313/about-time/\u0026#34;\u0026gt;about\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;http://localhost:1313/post/second-post/\u0026#34;\u0026gt;second\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r\u0026lt;h1\u0026gt;\u0026lt;a href=\u0026#34;http://localhost:1313/post/first-post/\u0026#34;\u0026gt;first\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt;\r\u0026lt;script\u0026gt;document.write(\u0026#39;\u0026lt;script src=\u0026#34;http://\u0026#39;\r+ (location.host || \u0026#39;localhost\u0026#39;).split(\u0026#39;:\u0026#39;)[0]\r+ \u0026#39;:1313/livereload.js?mindelay=10\u0026#34;\u0026gt;\u0026lt;/\u0026#39;\r+ \u0026#39;script\u0026gt;\u0026#39;)\u0026lt;/script\u0026gt;\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt; Notice that the \u0026ldquo;about\u0026rdquo; link is listed with the posts? That\u0026rsquo;s not desirable, so let\u0026rsquo;s change that first.\n$ vi themes/zafta/layouts/index.html\r\u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;body\u0026gt;\r\u0026lt;h1\u0026gt;posts\u0026lt;/h1\u0026gt;\r{{ range first 10 .Data.Pages }}\r{{ if eq .Type \u0026#34;post\u0026#34;}}\r\u0026lt;h2\u0026gt;\u0026lt;a href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt;{{ .Title }}\u0026lt;/a\u0026gt;\u0026lt;/h2\u0026gt;\r{{ end }}\r{{ end }}\r\u0026lt;h1\u0026gt;pages\u0026lt;/h1\u0026gt;\r{{ range .Data.Pages }}\r{{ if eq .Type \u0026#34;page\u0026#34; }}\r\u0026lt;h2\u0026gt;\u0026lt;a href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt;{{ .Title }}\u0026lt;/a\u0026gt;\u0026lt;/h2\u0026gt;\r{{ end }}\r{{ end }}\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r:wq Generate the web site and verify the results. The home page has two sections, posts and pages, and each section has the right set of headings and links in it.\nBut, that about page still renders to about-time/index.html.\n$ find public -name \u0026#39;*.html\u0026#39; | xargs ls -l\r-rw-rw-r-- 1 mdhender staff 334 Sep 27 15:33 public/about-time/index.html\r-rw-rw-r-- 1 mdhender staff 645 Sep 27 15:33 public/index.html\r-rw-rw-r-- 1 mdhender staff 358 Sep 27 15:33 public/post/first-post/index.html\r-rw-rw-r-- 1 mdhender staff 0 Sep 27 15:33 public/post/index.html\r-rw-rw-r-- 1 mdhender staff 342 Sep 27 15:33 public/post/second-post/index.html Knowing that hugo is using the slug to generate the file name, the simplest solution is to change the slug. Let\u0026rsquo;s do it the hard way and change the permalink in the configuration file.\n$ vi config.toml\r[permalinks]\rpage = \u0026#34;/:title/\u0026#34;\rabout = \u0026#34;/:filename/\u0026#34; Generate the web site and verify that this didn\u0026rsquo;t work. Hugo lets \u0026ldquo;slug\u0026rdquo; or \u0026ldquo;URL\u0026rdquo; override the permalinks setting in the configuration file. Go ahead and comment out the slug in content/about.md, then generate the web site to get it to be created in the right place.\nSharing Templates\r#\rIf you\u0026rsquo;ve been following along, you probably noticed that posts have titles in the browser and the home page doesn\u0026rsquo;t. That\u0026rsquo;s because we didn\u0026rsquo;t put the title in the home page\u0026rsquo;s template (layouts/index.html). That\u0026rsquo;s an easy thing to do, but let\u0026rsquo;s look at a different option.\nWe can put the common bits into a shared template that\u0026rsquo;s stored in the themes/zafta/layouts/partials/ directory.\nCreate the Header and Footer Partials\r#\rIn Hugo, a partial is a sugar-coated template. Normally a template reference has a path specified. Partials are different. Hugo searches for them along a TODO defined search path. This makes it easier for end-users to override the theme\u0026rsquo;s presentation.\n$ vi themes/zafta/layouts/partials/header.html\r\u0026lt;!DOCTYPE html\u0026gt;\r\u0026lt;html\u0026gt;\r\u0026lt;head\u0026gt;\r\u0026lt;title\u0026gt;{{ .Title }}\u0026lt;/title\u0026gt;\r\u0026lt;/head\u0026gt;\r\u0026lt;body\u0026gt;\r:wq\r$ vi themes/zafta/layouts/partials/footer.html\r\u0026lt;/body\u0026gt;\r\u0026lt;/html\u0026gt;\r:wq Update the Home Page Template to Use the Partials\r#\rThe most noticeable difference between a template call and a partials call is the lack of path:\n{{ template \u0026#34;theme/partials/header.html\u0026#34; . }} versus\n{{ partial \u0026#34;header.html\u0026#34; . }} Both pass in the context.\nLet\u0026rsquo;s change the home page template to use these new partials.\n$ vi themes/zafta/layouts/index.html\r{{ partial \u0026#34;header.html\u0026#34; . }}\r\u0026lt;h1\u0026gt;posts\u0026lt;/h1\u0026gt;\r{{ range first 10 .Data.Pages }}\r{{ if eq .Type \u0026#34;post\u0026#34;}}\r\u0026lt;h2\u0026gt;\u0026lt;a href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt;{{ .Title }}\u0026lt;/a\u0026gt;\u0026lt;/h2\u0026gt;\r{{ end }}\r{{ end }}\r\u0026lt;h1\u0026gt;pages\u0026lt;/h1\u0026gt;\r{{ range .Data.Pages }}\r{{ if or (eq .Type \u0026#34;page\u0026#34;) (eq .Type \u0026#34;about\u0026#34;) }}\r\u0026lt;h2\u0026gt;\u0026lt;a href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt;{{ .Type }} - {{ .Title }} - {{ .RelPermalink }}\u0026lt;/a\u0026gt;\u0026lt;/h2\u0026gt;\r{{ end }}\r{{ end }}\r{{ partial \u0026#34;footer.html\u0026#34; . }}\r:wq Generate the web site and verify the results. The title on the home page is now \u0026ldquo;your title here\u0026rdquo;, which comes from the \u0026ldquo;title\u0026rdquo; variable in the config.toml file.\nUpdate the Default Single Template to Use the Partials\r#\r$ vi themes/zafta/layouts/_default/single.html\r{{ partial \u0026#34;header.html\u0026#34; . }}\r\u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt;\r{{ .Content }}\r{{ partial \u0026#34;footer.html\u0026#34; . }}\r:wq Generate the web site and verify the results. The title on the posts and the about page should both reflect the value in the markdown file.\nAdd “Date Published” to Posts\r#\rIt\u0026rsquo;s common to have posts display the date that they were written or published, so let\u0026rsquo;s add that. The front matter of our posts has a variable named \u0026ldquo;date.\u0026rdquo; It\u0026rsquo;s usually the date the content was created, but let\u0026rsquo;s pretend that\u0026rsquo;s the value we want to display.\nAdd “Date Published” to the Template\r#\rWe\u0026rsquo;ll start by updating the template used to render the posts. The template code will look like:\n{{ .Date.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; }} Posts use the default single template, so we\u0026rsquo;ll change that file.\n$ vi themes/zafta/layouts/_default/single.html\r{{ partial \u0026#34;header.html\u0026#34; . }}\r\u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt;\r\u0026lt;h2\u0026gt;{{ .Date.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; }}\u0026lt;/h2\u0026gt;\r{{ .Content }}\r{{ partial \u0026#34;footer.html\u0026#34; . }}\r:wq Generate the web site and verify the results. The posts now have the date displayed in them. There\u0026rsquo;s a problem, though. The \u0026ldquo;about\u0026rdquo; page also has the date displayed.\nAs usual, there are a couple of ways to make the date display only on posts. We could do an \u0026ldquo;if\u0026rdquo; statement like we did on the home page. Another way would be to create a separate template for posts.\nThe \u0026ldquo;if\u0026rdquo; solution works for sites that have just a couple of content types. It aligns with the principle of \u0026ldquo;code for today,\u0026rdquo; too.\nLet\u0026rsquo;s assume, though, that we\u0026rsquo;ve made our site so complex that we feel we have to create a new template type. In Hugo-speak, we\u0026rsquo;re going to create a section template.\nLet\u0026rsquo;s restore the default single template before we forget.\n$ mkdir themes/zafta/layouts/post\r$ vi themes/zafta/layouts/_default/single.html\r{{ partial \u0026#34;header.html\u0026#34; . }}\r\u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt;\r{{ .Content }}\r{{ partial \u0026#34;footer.html\u0026#34; . }}\r:wq Now we\u0026rsquo;ll update the post\u0026rsquo;s version of the single template. If you remember Hugo\u0026rsquo;s rules, the template engine will use this version over the default.\n$ vi themes/zafta/layouts/post/single.html\r{{ partial \u0026#34;header.html\u0026#34; . }}\r\u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt;\r\u0026lt;h2\u0026gt;{{ .Date.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; }}\u0026lt;/h2\u0026gt;\r{{ .Content }}\r{{ partial \u0026#34;footer.html\u0026#34; . }}\r:wq Note that we removed the date logic from the default template and put it in the post template. Generate the web site and verify the results. Posts have dates and the about page doesn\u0026rsquo;t.\nDon\u0026rsquo;t Repeat Yourself\r#\rDRY is a good design goal and Hugo does a great job supporting it. Part of the art of a good template is knowing when to add a new template and when to update an existing one. While you\u0026rsquo;re figuring that out, accept that you\u0026rsquo;ll be doing some refactoring. Hugo makes that easy and fast, so it\u0026rsquo;s okay to delay splitting up a template.\n"},{"id":2,"href":"/posts/migrate-from-jekyll/","title":"Migrating from Jekyll","section":"Blog","content":"\rMove static content to static\r#\rJekyll has a rule that any directory not starting with _ will be copied as-is to the _site output. Hugo keeps all static content under static. You should therefore move it all there. With Jekyll, something that looked like\n▾ \u0026lt;root\u0026gt;/\r▾ images/\rlogo.png\rshould become\n▾ \u0026lt;root\u0026gt;/\r▾ static/\r▾ images/\rlogo.png\rAdditionally, you\u0026rsquo;ll want any files that should reside at the root (such as CNAME) to be moved to static.\nCreate your Hugo configuration file\r#\rHugo can read your configuration as JSON, YAML or TOML. Hugo supports parameters custom configuration too. Refer to the Hugo configuration documentation for details.\nSet your configuration publish folder to _site\r#\rThe default is for Jekyll to publish to _site and for Hugo to publish to public. If, like me, you have _site mapped to a git submodule on the gh-pages branch, you\u0026rsquo;ll want to do one of two alternatives:\nChange your submodule to point to map gh-pages to public instead of _site (recommended).\ngit submodule deinit _site\rgit rm _site\rgit submodule add -b gh-pages [email protected]:your-username/your-repo.git public\rOr, change the Hugo configuration to use _site instead of public.\n{\r..\r\u0026quot;publishdir\u0026quot;: \u0026quot;_site\u0026quot;,\r..\r}\rConvert Jekyll templates to Hugo templates\r#\rThat\u0026rsquo;s the bulk of the work right here. The documentation is your friend. You should refer to Jekyll\u0026rsquo;s template documentation if you need to refresh your memory on how you built your blog and Hugo\u0026rsquo;s template to learn Hugo\u0026rsquo;s way.\nAs a single reference data point, converting my templates for heyitsalex.net took me no more than a few hours.\nConvert Jekyll plugins to Hugo shortcodes\r#\rJekyll has plugins; Hugo has shortcodes. It\u0026rsquo;s fairly trivial to do a port.\nImplementation\r#\rAs an example, I was using a custom image_tag plugin to generate figures with caption when running Jekyll. As I read about shortcodes, I found Hugo had a nice built-in shortcode that does exactly the same thing.\nJekyll\u0026rsquo;s plugin:\nmodule Jekyll\rclass ImageTag \u0026lt; Liquid::Tag\r@url = nil\r@caption = nil\r@class = nil\r@link = nil\r// Patterns\rIMAGE_URL_WITH_CLASS_AND_CAPTION =\rIMAGE_URL_WITH_CLASS_AND_CAPTION_AND_LINK = /(\\w+)(\\s+)((https?:\\/\\/|\\/)(\\S+))(\\s+)\u0026quot;(.*?)\u0026quot;(\\s+)-\u0026gt;((https?:\\/\\/|\\/)(\\S+))(\\s*)/i\rIMAGE_URL_WITH_CAPTION = /((https?:\\/\\/|\\/)(\\S+))(\\s+)\u0026quot;(.*?)\u0026quot;/i\rIMAGE_URL_WITH_CLASS = /(\\w+)(\\s+)((https?:\\/\\/|\\/)(\\S+))/i\rIMAGE_URL = /((https?:\\/\\/|\\/)(\\S+))/i\rdef initialize(tag_name, markup, tokens)\rsuper\rif markup =~ IMAGE_URL_WITH_CLASS_AND_CAPTION_AND_LINK\r@class = $1\r@url = $3\r@caption = $7\r@link = $9\relsif markup =~ IMAGE_URL_WITH_CLASS_AND_CAPTION\r@class = $1\r@url = $3\r@caption = $7\relsif markup =~ IMAGE_URL_WITH_CAPTION\r@url = $1\r@caption = $5\relsif markup =~ IMAGE_URL_WITH_CLASS\r@class = $1\r@url = $3\relsif markup =~ IMAGE_URL\r@url = $1\rend\rend\rdef render(context)\rif @class\rsource = \u0026quot;\u0026lt;figure class='#{@class}'\u0026gt;\u0026quot;\relse\rsource = \u0026quot;\u0026lt;figure\u0026gt;\u0026quot;\rend\rif @link\rsource += \u0026quot;\u0026lt;a href=\\\u0026quot;#{@link}\\\u0026quot;\u0026gt;\u0026quot;\rend\rsource += \u0026quot;\u0026lt;img src=\\\u0026quot;#{@url}\\\u0026quot;\u0026gt;\u0026quot;\rif @link\rsource += \u0026quot;\u0026lt;/a\u0026gt;\u0026quot;\rend\rsource += \u0026quot;\u0026lt;figcaption\u0026gt;#{@caption}\u0026lt;/figcaption\u0026gt;\u0026quot; if @caption\rsource += \u0026quot;\u0026lt;/figure\u0026gt;\u0026quot;\rsource\rend\rend\rend\rLiquid::Template.register_tag('image', Jekyll::ImageTag)\ris written as this Hugo shortcode:\n\u0026lt;!-- image --\u0026gt;\r\u0026lt;figure {{ with .Get \u0026quot;class\u0026quot; }}class=\u0026quot;{{.}}\u0026quot;{{ end }}\u0026gt;\r{{ with .Get \u0026quot;link\u0026quot;}}\u0026lt;a href=\u0026quot;{{.}}\u0026quot;\u0026gt;{{ end }}\r\u0026lt;img src=\u0026quot;{{ .Get \u0026quot;src\u0026quot; }}\u0026quot; {{ if or (.Get \u0026quot;alt\u0026quot;) (.Get \u0026quot;caption\u0026quot;) }}alt=\u0026quot;{{ with .Get \u0026quot;alt\u0026quot;}}{{.}}{{else}}{{ .Get \u0026quot;caption\u0026quot; }}{{ end }}\u0026quot;{{ end }} /\u0026gt;\r{{ if .Get \u0026quot;link\u0026quot;}}\u0026lt;/a\u0026gt;{{ end }}\r{{ if or (or (.Get \u0026quot;title\u0026quot;) (.Get \u0026quot;caption\u0026quot;)) (.Get \u0026quot;attr\u0026quot;)}}\r\u0026lt;figcaption\u0026gt;{{ if isset .Params \u0026quot;title\u0026quot; }}\r{{ .Get \u0026quot;title\u0026quot; }}{{ end }}\r{{ if or (.Get \u0026quot;caption\u0026quot;) (.Get \u0026quot;attr\u0026quot;)}}\u0026lt;p\u0026gt;\r{{ .Get \u0026quot;caption\u0026quot; }}\r{{ with .Get \u0026quot;attrlink\u0026quot;}}\u0026lt;a href=\u0026quot;{{.}}\u0026quot;\u0026gt; {{ end }}\r{{ .Get \u0026quot;attr\u0026quot; }}\r{{ if .Get \u0026quot;attrlink\u0026quot;}}\u0026lt;/a\u0026gt; {{ end }}\r\u0026lt;/p\u0026gt; {{ end }}\r\u0026lt;/figcaption\u0026gt;\r{{ end }}\r\u0026lt;/figure\u0026gt;\r\u0026lt;!-- image --\u0026gt;\rUsage\r#\rI simply changed:\n{% image full http://farm5.staticflickr.com/4136/4829260124_57712e570a_o_d.jpg \u0026quot;One of my favorite touristy-type photos. I secretly waited for the good light while we were \u0026quot;having fun\u0026quot; and took this. Only regret: a stupid pole in the top-left corner of the frame I had to clumsily get rid of at post-processing.\u0026quot; -\u0026gt;http://www.flickr.com/photos/alexnormand/4829260124/in/set-72157624547713078/ %}\rto this (this example uses a slightly extended version named fig, different than the built-in figure):\n{{% fig class=\u0026quot;full\u0026quot; src=\u0026quot;http://farm5.staticflickr.com/4136/4829260124_57712e570a_o_d.jpg\u0026quot; title=\u0026quot;One of my favorite touristy-type photos. I secretly waited for the good light while we were having fun and took this. Only regret: a stupid pole in the top-left corner of the frame I had to clumsily get rid of at post-processing.\u0026quot; link=\u0026quot;http://www.flickr.com/photos/alexnormand/4829260124/in/set-72157624547713078/\u0026quot; %}}\rAs a bonus, the shortcode named parameters are, arguably, more readable.\nFinishing touches\r#\rFix content\r#\rDepending on the amount of customization that was done with each post with Jekyll, this step will require more or less effort. There are no hard and fast rules here except that hugo server --watch is your friend. Test your changes and fix errors as needed.\nClean up\r#\rYou\u0026rsquo;ll want to remove the Jekyll configuration at this point. If you have anything else that isn\u0026rsquo;t used, delete it.\nA practical example in a diff\r#\rHey, it\u0026rsquo;s Alex was migrated in less than a father-with-kids day from Jekyll to Hugo. You can see all the changes (and screw-ups) by looking at this diff.\n"},{"id":3,"href":"/docs/%E7%B3%BB%E7%BB%9F%E6%9E%84%E5%BB%BA%E5%92%8C%E5%90%AF%E5%8A%A8/%E7%8E%AF%E5%A2%83%E4%BE%9D%E8%B5%96/","title":"环境依赖","section":"系统构建和启动","content":"Python 服务 (如果需要用到LLM的能力, 需要启动python服务)\nPython版本: 3.9+ pip版本: 3.9+\n其它需要用到的包(启动服务的时候, 会默认检测安装):\nlangchain==0.0.207 openai==0.27.4 fastapi==0.95.1\nchromadb==0.3.21 tiktoken==0.3.3 uvicorn[standard]==0.21.1(windows: uvicorn==0.21.1)\npandas==1.5.3\n注意事项:\n前往supersonic/chat/core/src/main/python/llm/run_config.py配置LLM key 服务host、port指定: 在supersonic/chat/core/src/main/python/llm/run_config.py中修改 python、pip环境指定: pip路径:在supersonic-common.sh中修改\npython路径:在supersonic-common.sh中修改 注意:0.7.4release版本:在supersonic/chat/core/src/main/python/bin/env.sh中修改\n默认pip路径为/usr/local/bin/pip3,python路径为/usr/local/bin/python3, 若不在这个路径,需要在修改成自己的路径\nWindows下需要将python和pip指令配置好环境变量\n后端服务\nJDK版本: 1.8+\n前端服务\nNode版本: 16+ pnpm\n"},{"id":4,"href":"/docs/%E7%B3%BB%E7%BB%9F%E6%9E%84%E5%BB%BA%E5%92%8C%E5%90%AF%E5%8A%A8/%E6%9E%84%E5%BB%BA%E5%92%8C%E5%90%AF%E5%8A%A8/","title":"构建和启动","section":"系统构建和启动","content":"Release包启动\n下载最新release包 sh bin/supersonic-daemon.sh start http://localhost:9080 IDE启动(后端直接访问前端资源的方式) sh assembly/bin/supersonic-build.sh 执行构建 IDE本地启动Java类StandaloneLauncher http://localhost:9080 IDE启动(前后端分开启动的方式) IDE本地启动Java类StandaloneLauncher 进入webapp目录,执行sh start-fe-dev.sh直接启动前端服务 http://localhost:9000 注意这里是9000端口 Source Code启动 sh assembly/bin/supersonic-build.sh 执行构建 sh assembly/bin/supersonic-daemon.sh start http://localhost:9080 PS:\nWindows环境均有提供对应的bat脚本, 执行即可\n系统目前支持两种访问LLM和向量库的方式:\na. Java服务通过langchain4j直接访问LLM的方式, 只需要按如上方式启动服务即可\nb. 另起Python服务的方式来访问LLM, 执行sh assembly/bin/supersonic-daemon.sh start pyllm即可, 这条命令会同时启动Python服务和Java服务, 只不过是通过Python服务 来访问LLM\nJava和Python两种访问LLM的相关配置均可参考LLM与text2sql配置\n执行构建和启动之前, 请先查看环境依赖\nUbuntu环境, 启动方式同上, 若出现报错, 可尝试https://support.huaweicloud.com/intl/zh-cn/deployman_faq/deployman_faq_1016.html\n启动之后, 可以到logs目录下查看日志, llm解析服务相关日志可以到llm/logs目录下查看\n数据库表结构可直接参考launcher/standalone下的sql脚本, h2数据库schema-h2.sql, data-h2.sql, mysql数据库schema-mysql.sql, data-mysql.sql, 这两个脚本均为最新表结构, 每次发版更新的sql会放到sql-update.sql (第一次启动不需要管sql-update.sql, 只是已经在mysql上跑过, 如果不想重新建表导数据, 就需要对照sql-update.sql中的sql执行日期来进行表结构更新)\n系统默认对h2数据库支持样例数据, 若需要基于本地mysql查看样例数据, 可执行schema-mysql.sql来创建最新表结构, 并执行data-mysql.sql把样例数据写入mysql, 之后系统在启动的时候会自动把系统元数据和会话数据写入mysql表,系统启动成功后可在页面看到样例数据, 详细可参考DemoLoader。若需要从h2切换至mysql, 按如下正常配置即可\nspring:\rdatasource:\rurl: jdbc:mysql://localhost:3306/your_database?useUnicode=true\u0026amp;characterEncoding=UTF-8\u0026amp;useSSL=false\rusername: your_username\rpassword: your_password\rdriver-class-name: com.mysql.jdbc.Driver\rdemo:\renabled:\rtrue "},{"id":5,"href":"/docs/%E7%B3%BB%E7%BB%9F%E6%9E%84%E5%BB%BA%E5%92%8C%E5%90%AF%E5%8A%A8/llm%E4%B8%8Etext2sql%E9%85%8D%E7%BD%AE/","title":"LLM与text2sql配置","section":"系统构建和启动","content":"语言模型的使用是SuperSonic的重要一环。能显著增强对用户的问题的理解能力,是通过对话形式与用户交互的基石之一。在本项目中对语言模型能力的应用主要在 LLM 和 Embedding 两方面, 且支持Java和Python两种访问语言模型的方式, 下面分别介绍这两种访问方式的配置。\nJavaLLMProxy\r#\r服务默认启动方式就为Java访问语言模型的方式, 语言模型相关配置可直接通过YAML文件来配置, 目前在配置中支持配置访问open-ai的key和LLM模型的名称 PythonLLMProxy\r#\rPython访问LLM的方式需要通过sh assembly/bin/supersonic-daemon.sh start pyllm来启动Python服务, 该命令同时也会启动Java服务, 但通过Python服务来访问LLM。 Python服务默认使用的模型中,LLM选用闭源模型 gpt-3.5-turbo-16k,Embedding模型选用开源模型 GanymedeNil/text2vec-large-chinese。用户可以根据自己实际需求进行配置更改。\n配置方式 LLM模型的配置\r#\rLLM模型相关的配置,在 supersonic/chat/core/src/main/python/config/run_config.ini 进行配置。 LLM默认采用OpenAI的闭源模型 gpt-3.5-turbo-16k,用户可以根据自己的实际需要选择LLM模型的提供方,例如Azure、文心一言等。通过[LLMProvider]下的LLM_PROVIDER_NAME 变量进行配置。需要注意的是,现阶段支持配置的模型提供方必须能够被langchain所支持,提供方的名字可以在langchain文档中查询。 LLM的相关变量在[LLMModel]下进行配置,例如openAI的模型,需要提供 MODEL_NAME、OPENAI_API_KEY、TEMPERATURE 等参数配置。不同的LLM提供方需要的配置各不相同,用户可以根据实际情况配置相关变量。 Embedding模型配置\r#\rEmbedding模型默认采用开源模型 GanymedeNil/text2vec-large-chinese。用户可以根据实际需要配置适合的Embedding模型;通过[Text2Vec]下 HF_TEXT2VEC_MODEL_NAME 变量进行配置,为了使用方便采用托管在HuggingFace的源,初次启动时自动下载模型文件。 LLM与embedding配置 FAQ\r#\r可以用开源的LLM模型替代OpenAI的GPT模型吗? 暂时不能。我们测试过大部分主流的开源LLM,在实际使用中,在本项目需要LLM提供的逻辑推理和代码生成场景上,开源模型还不能满足需求。 我们会持续跟进开源LLM的最新进展,在有满足要求的开源LLM后,在项目中集成私有化部署开源LLM的能力。 可以用国产的闭源模型替代OpenAI的GPT模型吗? 据部分用户反馈,在他们的场景下文心一言、混元等国产闭源模型的效果与GPT3.5差距不大;整体而言GPT3.5及GPT4适用的场景会更广泛一些。用户可以在自己的场景下修改LLM的相应配置,试一试实际效果。 GPT4、GPT3.5、GPT3.5-16k 这几个模型用哪个比较好? GPT3.5、GPT3.5-16k 均能基本满足要求,但会有输出结果不稳定的情况;GPT3.5的token长度限制为4k,在现有CoT策略下,容易出现超过长度限制的情况。 GPT4的输出更稳定,但费用成本远超GPT3.5,可以根据实际使用场景进行选择。 Embedding模型用其他的可以吗? 可以。可以以该项目text2vec的榜单作为参考,然后在HuggingFace找到对应模型的model card,修改HF_TEXT2VEC_MODEL_NAME变量的取值。 启动时,首次下载Embedding模型需要会链接HuggingFace的源进行下载,如果网络不通怎么办? 可以到HuggingFace的官网找到对应的model card,然后将模型下载到本地。在supersonic/chat/core/src/main/python/config/run_config.ini 中将HF_TEXT2VEC_MODEL_NAME变量配置为模型所在的绝对路径。 LLM在text2sql中的应用\r#\rtext2sql的功能实现,高度依赖对LLM的应用。通过LLM生成SQL的过程中,利用小样本(few-shots-examples)通过思维链(chain-of-thoughts)的方式对LLM in-context-learning的能力进行引导,对于生成较为稳定且符合下游语法解析规则的SQL非常重要。用户可以根据自身需要,对样本池及样本的数量进行配置,使其更加符合自身业务特点。\ntext2sql运行中更新配置的脚本\r#\r如果在启动项目后,用户需要对text2sql功能的相关配置进行调试,可以在修改相关配置文件后,通过以下2种方式让配置在项目运行中让配置生效。 执行 supersonic-daemon.sh reload llmparser 执行 python examples_reload_run.py "},{"id":6,"href":"/docs/%E4%BA%A7%E5%93%81%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C/%E6%9E%84%E5%BB%BA%E8%AF%AD%E4%B9%89%E6%A8%A1%E5%9E%8B/","title":"构建语义模型","section":"产品使用手册","content":"构建语义模型是使用超音数的第一步。在这个模块中, 它可以连接上你的数据库引擎, 并通过简单方便的方式来帮助你将物理数据建模为维度和指标等逻辑概念。建模完成后, 你就可以在问答中通过自然语言的方式来和你的物理数据交互啦~\n问题示例\n为了帮助你更好地理解建模的过程, 我们通过一个问题示例来进行介绍: 超音数本身作为一个产品, 那么如何用语义建模来统计它的埋点访问数据呢?比如超音数在一段时间内的访问用户数是多少?这些用户的访问次数和停留时长是怎样的?这些用户来自哪些部门?这些用户看了哪些页面?分别看了多少次?等我们建模完成, 这些问题的答案也就浮出水面了。\n在超音数中, 模型归属于主题域, 主题域类似于一个分类的概念, 用户可以按自己的业务场景去创建主题域, 然后在主题域下面创建模型。\n1. 创建一个主题域\r#\r如图1-1所示, 为了统计超音数的埋点访问情况, 我们创建了一个叫\u0026quot;超音数\u0026quot;的主题域作为示例:\n2. 创建一个数据库连接\r#\r为了进行数据查询, 我们首先需要创建一个数据库连接, 创建一个数据库连接主要分为三个步骤,\n填写连接信息 点击测试连接, 若连接测试通过, 则可点击保存。否则, 返回步骤1 点击保存。 如图1-1所示, 由于超音数的埋点访问数据被存放在H2数据库中, 因此我们创建了一个H2数据库实例作为例子。除了H2数据库以外, 我们还支持MySQL, ClickHouse等多种常见数据库。\n需要说明的是, 在这里创建数据库之后, 并不是所有人都可以查询这个数据库链接的数据, 需要在图2-1表单上进行授权。\n管理员: 可以编辑这个数据库链接的人\n使用者: 可以使用这个数据库链接查询数据的人\n3. 创建数据模型\r#\r有了数据库连接之后, 我们就可以把物理数据抽象为一个个数据模型。在超音数中, 数据模型是对数据库中数据的一种逻辑层面上的抽象, 它既可以直接指代一张物理表, 也可以由一段SQL逻辑表示而成。数据模型中涉及的字段可被指定为维度或者度量, 而这些维度和度量又可以衍生出更复杂的维度和指标。如图3-1, 超音数提供了两种创建数据模型的方式。\n其中, 快速创建 可以直接指定一张物理表来把它创建为数据模型, 而SQL脚本 则提供了更为灵活的数据模型创建方式, 我们可以通过写一条逻辑SQL来把它指定为数据模型\n如图3-2为通过SQL脚本的方式创建数据模型, 首先需要我们填写一些基本信息, 如数据模型名称和描述。\n然后我们写一条SQL来表达我们的数据模型逻辑, 然后点击运行, 就可以看到这条SQL查询出来的数据, 校验数据无误之后, 我们可以点击生成数据模型, 需要注意的是, 这里创建数据模型选择数据库链接的时候, 需要有数据库的使用者权限.\n如图3-4所示, 执行完SQL之后, 根据SQL执行结果, 需要填写一些字段信息, 把数据模型的字段指定为维度或者度量, 其中日期和主键为特殊的维度。\n维度主要用于筛选和分组\n度量主要标识数值类型字段, 用来进行聚合计算\n日期字段主要用于标识, 方便问答进行数据查询。\n主键则用于不同数据模型之间的连接字段, 有了连接字段后,就可以在画布进行连接关系的配置, 配置完成, 在查询模型数据的时候, 多个模型之间就可以进行Join连接了\n把字段指定为维度/度量之后, 还可选择是否勾选快速创建单选框。若勾选, 则会直接把选中的维度/度量批量创建到维度/指标列表。 若不勾选, 但字段已被选定为维度/度量, 该字段虽然不会直接被创建到维度/指标列表, 但是后续在创建衍生维度/指标的时候也可作为表达式中的字段被用上。 如图3-5所示, 为我们创建的3个数据模型示例, 分别为超音数用户停留时长统计, 访问次数和访问人数统计, 用户部门统计。\n4. 创建维度\r#\r如图4-1, 为刚刚创建数据模型时, 通过勾选快速创建按钮创建出来的维度。分别为用户名, 用户所在的部门, 用户浏览过的超音数页面。\n若我们需要更复杂的维度, 如我们需要根据页面来划分模块, 那我们可以点击创建按钮来创建一个更复杂的维度。如图4-2所示\n5. 创建指标\r#\r和维度类似, 如图5-1为通过快速创建按钮创建出来的指标, 分别为访问超音数的次数, 人数和停留时长。这几个指标都可以在用户、部门 、页面等分组粒度上进行计算。\n到此为止, 我们就成功把超音数的访问统计数据建模成了相关的数据模型、维度和指标。通过在问答中直接对这些维度和指标进行提问, 就可以回答我们在介绍开头提到的那些问题啦!\n"},{"id":7,"href":"/posts/goisforlovers/","title":"(Hu)go Template Primer","section":"Blog","content":"Hugo uses the excellent Go html/template library for its template engine. It is an extremely lightweight engine that provides a very small amount of logic. In our experience that it is just the right amount of logic to be able to create a good static website. If you have used other template systems from different languages or frameworks you will find a lot of similarities in Go templates.\nThis document is a brief primer on using Go templates. The Go docs provide more details.\nIntroduction to Go Templates\r#\rGo templates provide an extremely simple template language. It adheres to the belief that only the most basic of logic belongs in the template or view layer. One consequence of this simplicity is that Go templates parse very quickly.\nA unique characteristic of Go templates is they are content aware. Variables and content will be sanitized depending on the context of where they are used. More details can be found in the Go docs.\nBasic Syntax\r#\rGolang templates are HTML files with the addition of variables and functions.\nGo variables and functions are accessible within {{ }}\nAccessing a predefined variable \u0026ldquo;foo\u0026rdquo;:\n{{ foo }}\rParameters are separated using spaces\nCalling the add function with input of 1, 2:\n{{ add 1 2 }}\rMethods and fields are accessed via dot notation\nAccessing the Page Parameter \u0026ldquo;bar\u0026rdquo;\n{{ .Params.bar }}\rParentheses can be used to group items together\n{{ if or (isset .Params \u0026quot;alt\u0026quot;) (isset .Params \u0026quot;caption\u0026quot;) }} Caption {{ end }}\rVariables\r#\rEach Go template has a struct (object) made available to it. In hugo each template is passed either a page or a node struct depending on which type of page you are rendering. More details are available on the variables page.\nA variable is accessed by referencing the variable name.\n\u0026lt;title\u0026gt;{{ .Title }}\u0026lt;/title\u0026gt;\rVariables can also be defined and referenced.\n{{ $address := \u0026quot;123 Main St.\u0026quot;}}\r{{ $address }}\rFunctions\r#\rGo template ship with a few functions which provide basic functionality. The Go template system also provides a mechanism for applications to extend the available functions with their own. Hugo template functions provide some additional functionality we believe are useful for building websites. Functions are called by using their name followed by the required parameters separated by spaces. Template functions cannot be added without recompiling hugo.\nExample:\n{{ add 1 2 }}\rIncludes\r#\rWhen including another template you will pass to it the data it will be able to access. To pass along the current context please remember to include a trailing dot. The templates location will always be starting at the /layout/ directory within Hugo.\nExample:\n{{ template \u0026quot;chrome/header.html\u0026quot; . }}\rLogic\r#\rGo templates provide the most basic iteration and conditional logic.\nIteration\r#\rJust like in Go, the Go templates make heavy use of range to iterate over a map, array or slice. The following are different examples of how to use range.\nExample 1: Using Context\n{{ range array }}\r{{ . }}\r{{ end }}\rExample 2: Declaring value variable name\n{{range $element := array}}\r{{ $element }}\r{{ end }}\rExample 2: Declaring key and value variable name\n{{range $index, $element := array}}\r{{ $index }}\r{{ $element }}\r{{ end }}\rConditionals\r#\rIf, else, with, or, \u0026amp; and provide the framework for handling conditional logic in Go Templates. Like range, each statement is closed with end.\nGo Templates treat the following values as false:\nfalse 0 any array, slice, map, or string of length zero Example 1: If\n{{ if isset .Params \u0026quot;title\u0026quot; }}\u0026lt;h4\u0026gt;{{ index .Params \u0026quot;title\u0026quot; }}\u0026lt;/h4\u0026gt;{{ end }}\rExample 2: If -\u0026gt; Else\n{{ if isset .Params \u0026quot;alt\u0026quot; }}\r{{ index .Params \u0026quot;alt\u0026quot; }}\r{{else}}\r{{ index .Params \u0026quot;caption\u0026quot; }}\r{{ end }}\rExample 3: And \u0026amp; Or\n{{ if and (or (isset .Params \u0026quot;title\u0026quot;) (isset .Params \u0026quot;caption\u0026quot;)) (isset .Params \u0026quot;attr\u0026quot;)}}\rExample 4: With\nAn alternative way of writing \u0026ldquo;if\u0026rdquo; and then referencing the same value is to use \u0026ldquo;with\u0026rdquo; instead. With rebinds the context . within its scope, and skips the block if the variable is absent.\nThe first example above could be simplified as:\n{{ with .Params.title }}\u0026lt;h4\u0026gt;{{ . }}\u0026lt;/h4\u0026gt;{{ end }}\rExample 5: If -\u0026gt; Else If\n{{ if isset .Params \u0026quot;alt\u0026quot; }}\r{{ index .Params \u0026quot;alt\u0026quot; }}\r{{ else if isset .Params \u0026quot;caption\u0026quot; }}\r{{ index .Params \u0026quot;caption\u0026quot; }}\r{{ end }}\rPipes\r#\rOne of the most powerful components of Go templates is the ability to stack actions one after another. This is done by using pipes. Borrowed from unix pipes, the concept is simple, each pipeline\u0026rsquo;s output becomes the input of the following pipe.\nBecause of the very simple syntax of Go templates, the pipe is essential to being able to chain together function calls. One limitation of the pipes is that they only can work with a single value and that value becomes the last parameter of the next pipeline.\nA few simple examples should help convey how to use the pipe.\nExample 1 :\n{{ if eq 1 1 }} Same {{ end }}\ris the same as\n{{ eq 1 1 | if }} Same {{ end }}\rIt does look odd to place the if at the end, but it does provide a good illustration of how to use the pipes.\nExample 2 :\n{{ index .Params \u0026quot;disqus_url\u0026quot; | html }}\rAccess the page parameter called \u0026ldquo;disqus_url\u0026rdquo; and escape the HTML.\nExample 3 :\n{{ if or (or (isset .Params \u0026quot;title\u0026quot;) (isset .Params \u0026quot;caption\u0026quot;)) (isset .Params \u0026quot;attr\u0026quot;)}}\rStuff Here\r{{ end }}\rCould be rewritten as\n{{ isset .Params \u0026quot;caption\u0026quot; | or isset .Params \u0026quot;title\u0026quot; | or isset .Params \u0026quot;attr\u0026quot; | if }}\rStuff Here\r{{ end }}\rContext (aka. the dot)\r#\rThe most easily overlooked concept to understand about Go templates is that {{ . }} always refers to the current context. In the top level of your template this will be the data set made available to it. Inside of a iteration it will have the value of the current item. When inside of a loop the context has changed. . will no longer refer to the data available to the entire page. If you need to access this from within the loop you will likely want to set it to a variable instead of depending on the context.\nExample:\n{{ $title := .Site.Title }}\r{{ range .Params.tags }}\r\u0026lt;li\u0026gt; \u0026lt;a href=\u0026quot;{{ $baseurl }}/tags/{{ . | urlize }}\u0026quot;\u0026gt;{{ . }}\u0026lt;/a\u0026gt; - {{ $title }} \u0026lt;/li\u0026gt;\r{{ end }}\rNotice how once we have entered the loop the value of {{ . }} has changed. We have defined a variable outside of the loop so we have access to it from within the loop.\nHugo Parameters\r#\rHugo provides the option of passing values to the template language through the site configuration (for sitewide values), or through the meta data of each specific piece of content. You can define any values of any type (supported by your front matter/config format) and use them however you want to inside of your templates.\nUsing Content (page) Parameters\r#\rIn each piece of content you can provide variables to be used by the templates. This happens in the front matter.\nAn example of this is used in this documentation site. Most of the pages benefit from having the table of contents provided. Sometimes the TOC just doesn\u0026rsquo;t make a lot of sense. We\u0026rsquo;ve defined a variable in our front matter of some pages to turn off the TOC from being displayed.\nHere is the example front matter:\n---\rtitle: \u0026#34;Permalinks\u0026#34;\rdate: \u0026#34;2013-11-18\u0026#34;\raliases:\r- \u0026#34;/doc/permalinks/\u0026#34;\rgroups: [\u0026#34;extras\u0026#34;]\rgroups_weight: 30\rnotoc: true\r--- Here is the corresponding code inside of the template:\n{{ if not .Params.notoc }}\r\u0026lt;div id=\u0026quot;toc\u0026quot; class=\u0026quot;well col-md-4 col-sm-6\u0026quot;\u0026gt;\r{{ .TableOfContents }}\r\u0026lt;/div\u0026gt;\r{{ end }}\rUsing Site (config) Parameters\r#\rIn your top-level configuration file (eg, config.yaml) you can define site parameters, which are values which will be available to you in chrome.\nFor instance, you might declare:\nparams: CopyrightHTML: \u0026#34;Copyright \u0026amp;#xA9; 2013 John Doe. All Rights Reserved.\u0026#34; TwitterUser: \u0026#34;spf13\u0026#34; SidebarRecentLimit: 5 Within a footer layout, you might then declare a \u0026lt;footer\u0026gt; which is only provided if the CopyrightHTML parameter is provided, and if it is given, you would declare it to be HTML-safe, so that the HTML entity is not escaped again. This would let you easily update just your top-level config file each January 1st, instead of hunting through your templates.\n{{if .Site.Params.CopyrightHTML}}\u0026lt;footer\u0026gt;\r\u0026lt;div class=\u0026#34;text-center\u0026#34;\u0026gt;{{.Site.Params.CopyrightHTML | safeHtml}}\u0026lt;/div\u0026gt;\r\u0026lt;/footer\u0026gt;{{end}} An alternative way of writing the \u0026ldquo;if\u0026rdquo; and then referencing the same value is to use \u0026ldquo;with\u0026rdquo; instead. With rebinds the context . within its scope, and skips the block if the variable is absent:\n{{with .Site.Params.TwitterUser}}\u0026lt;span class=\u0026#34;twitter\u0026#34;\u0026gt;\r\u0026lt;a href=\u0026#34;https://twitter.com/{{.}}\u0026#34; rel=\u0026#34;author\u0026#34;\u0026gt;\r\u0026lt;img src=\u0026#34;/images/twitter.png\u0026#34; width=\u0026#34;48\u0026#34; height=\u0026#34;48\u0026#34; title=\u0026#34;Twitter: {{.}}\u0026#34;\ralt=\u0026#34;Twitter\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\r\u0026lt;/span\u0026gt;{{end}} Finally, if you want to pull \u0026ldquo;magic constants\u0026rdquo; out of your layouts, you can do so, such as in this example:\n\u0026lt;nav class=\u0026#34;recent\u0026#34;\u0026gt;\r\u0026lt;h1\u0026gt;Recent Posts\u0026lt;/h1\u0026gt;\r\u0026lt;ul\u0026gt;{{range first .Site.Params.SidebarRecentLimit .Site.Recent}}\r\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;{{.RelPermalink}}\u0026#34;\u0026gt;{{.Title}}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\r{{end}}\u0026lt;/ul\u0026gt;\r\u0026lt;/nav\u0026gt; "},{"id":8,"href":"/posts/hugoisforlovers/","title":"Getting Started with Hugo","section":"Blog","content":"\rStep 1. Install Hugo\r#\rGo to Hugo releases and download the appropriate version for your OS and architecture.\nSave it somewhere specific as we will be using it in the next step.\nMore complete instructions are available at Install Hugo\nStep 2. Build the Docs\r#\rHugo has its own example site which happens to also be the documentation site you are reading right now.\nFollow the following steps:\nClone the Hugo repository Go into the repo Run hugo in server mode and build the docs Open your browser to http://localhost:1313 Corresponding pseudo commands:\ngit clone https://github.com/spf13/hugo\rcd hugo\r/path/to/where/you/installed/hugo server --source=./docs\r\u0026gt; 29 pages created\r\u0026gt; 0 tags index created\r\u0026gt; in 27 ms\r\u0026gt; Web Server is available at http://localhost:1313\r\u0026gt; Press ctrl+c to stop\rOnce you\u0026rsquo;ve gotten here, follow along the rest of this page on your local build.\nStep 3. Change the docs site\r#\rStop the Hugo process by hitting Ctrl+C.\nNow we are going to run hugo again, but this time with hugo in watch mode.\n/path/to/hugo/from/step/1/hugo server --source=./docs --watch\r\u0026gt; 29 pages created\r\u0026gt; 0 tags index created\r\u0026gt; in 27 ms\r\u0026gt; Web Server is available at http://localhost:1313\r\u0026gt; Watching for changes in /Users/spf13/Code/hugo/docs/content\r\u0026gt; Press ctrl+c to stop\rOpen your favorite editor and change one of the source content pages. How about changing this very file to fix the typo. How about changing this very file to fix the typo.\nContent files are found in docs/content/. Unless otherwise specified, files are located at the same relative location as the url, in our case docs/content/overview/quickstart.md.\nChange and save this file.. Notice what happened in your terminal.\n\u0026gt; Change detected, rebuilding site\r\u0026gt; 29 pages created\r\u0026gt; 0 tags index created\r\u0026gt; in 26 ms\rRefresh the browser and observe that the typo is now fixed.\nNotice how quick that was. Try to refresh the site before it\u0026rsquo;s finished building. I double dare you. Having nearly instant feedback enables you to have your creativity flow without waiting for long builds.\nStep 4. Have fun\r#\rThe best way to learn something is to play with it.\n"}]