Move to custom SSG instead of eleventy.

This commit is contained in:
Joshua Bemenderfer
2023-03-04 22:36:08 -05:00
parent 31bb42e985
commit f42225bd13
59 changed files with 337 additions and 1784 deletions

20
docs/renderer/layout.njk Normal file
View File

@@ -0,0 +1,20 @@
<!doctype html>
{% from "nodes/Node.njk" import Node %}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ page.title }}</title>
<meta name="description" content="{{ page.description | join(' ') }}"/>
<link rel="stylesheet" href="/main.css"/>
<link rel="stylesheet" href="/public/styles/highlightjs-theme.css"/>
{{ googleFonts('https://fonts.googleapis.com/css2?family=Fredoka:wght@300;400;500&display=swap') }}
</head>
<body class="text-base pt-16">
{{ Node('Navbar', {}, { headings: page.headings, url: url }) }}
{% for child in page.children %}
{{ Node(child.type, child, { headings: page.headings, url: url }) }}
{% endfor %}
</body>
</html>

View File

@@ -0,0 +1,9 @@
{% from "./Node.njk" import Node %}
{% macro render(node, page) %}
<div class="{{ node.class }}">
{% for child in node.children %}
{{ Node(child.type, child, page) }}
{% endfor %}
</div>
{% endmacro %}

View File

@@ -0,0 +1,14 @@
{% set commonClasses = "px-6 py-2 md:px-12 md:py-3 text-white rounded-md w-auto whitespace-nowrap not-prose" %}
{% macro render(node) %}
<a
href="{{ node.href }}"
target="{{ node.target }}"
{% if node.variant == "primary" %}
class="{{ node.class }} {{ commonClasses }} bg-gradient-to-b from-primary-500 to-primary-600 hover:from-primary-400 hover:to-primary-600 active:from-primary-600 active:to-primary-500"
{% elseif node.variant == 'neutral' %}
class="{{ node.class }} {{ commonClasses }} bg-gradient-to-b from-neutral-500 to-neutral-800"
{% endif %}
>
{{ node.text | safe }}
</a>
{% endmacro %}

View File

@@ -0,0 +1,9 @@
{% macro render(node) %}
<pre
class="
my-8 p-4
bg-neutral-900 text-neutral-50 rounded-md whitespace-pre-wrap text-sm
{{ node.class }}
"
>{{ node.text | highlight(node.language) | safe }}</pre>
{% endmacro %}

View File

@@ -0,0 +1,52 @@
{%
set languageMeta = {
'terrace': { name: 'Terrace', icon: '<svg viewBox="0 0 64 64"><g clip-path="url(#a)"><rect width="64" height="64" fill="#fff" rx="8"/><mask id="b" width="64" height="64" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M0 0h64v64H0V0Z"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0h64v64H0V0Z"/><circle cx="4" cy="14" r="16" fill="url(#d)"/><circle cx="16" cy="27" r="15" fill="url(#e)"/><circle cx="30" cy="50" r="25" fill="url(#f)"/><circle cx="52" cy="57" r="16" fill="url(#g)"/><circle cx=".5" cy="30.5" r="11.5" fill="url(#h)"/><circle cx="16" cy="35" r="8" fill="url(#i)"/><circle cx="37" cy="45" r="13" fill="url(#j)"/><circle cx="15.5" cy="61.5" r="28.5" fill="url(#k)"/><circle cx="40" cy="61" r="8" fill="url(#l)"/><circle cx="23" cy="73" r="23" fill="url(#m)"/></g></g><defs><radialGradient id="d" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 5.218 12.225) scale(23.8636 26.8465)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="e" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 1.693 26.631) scale(22.3721 25.1686)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="f" cx="0" cy="0" r="1" gradientTransform="matrix(10.71426 35.71429 -40.17862 12.05355 47.857 33.929)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="g" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 .322 65.982) scale(23.8636 26.8465)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="h" cx="0" cy="0" r="1" gradientTransform="matrix(4.92855 16.42854 -18.48212 5.54462 8.714 23.107)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="i" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 -9.207 29.52) scale(11.9318 13.4232)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="j" cx="0" cy="0" r="1" gradientTransform="matrix(5.5714 18.5714 -20.89288 6.26785 46.286 36.643)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="k" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 -11.088 45.686) scale(42.507 47.8203)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="l" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 -14.68 58.65) scale(11.9318 13.4232)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><radialGradient id="m" cx="0" cy="0" r="1" gradientTransform="rotate(73.301 -19.407 55.604) scale(34.3039 38.5918)" gradientUnits="userSpaceOnUse"><stop stop-color="#5CCA53"/><stop offset="1" stop-color="#22701B"/></radialGradient><linearGradient id="c" x1="31" x2="32" y1="0" y2="64" gradientUnits="userSpaceOnUse"><stop stop-color="#70B8FB"/><stop offset="1" stop-color="#99C9F5" stop-opacity=".27"/></linearGradient><clipPath id="a"><rect width="64" height="64" fill="#fff" rx="8"/></clipPath></defs></svg>' },
'json': { name: 'JSON', icon: '<svg viewBox="0 0 160 160"><defs><linearGradient id="linearGradient8385"><stop offset="0"/><stop stop-color="#fff" offset="1"/></linearGradient><linearGradient id="linearGradient3002" x1="-553.27" x2="-666.12" y1="525.91" y2="413.05" gradientTransform="matrix(.99884 0 0 .9987 689.01 -388.84)" gradientUnits="userSpaceOnUse" xlink:href="#linearGradient8385"/><linearGradient id="linearGradient3005" x1="-666.12" x2="-553.27" y1="413.04" y2="525.91" gradientTransform="matrix(.99884 0 0 .9987 689.01 -388.84)" gradientUnits="userSpaceOnUse" xlink:href="#linearGradient8385"/></defs><g fill-rule="evenodd"><path d="m79.865 119.1c35.398 48.255 70.04-13.469 69.989-50.587-0.0602-43.886-44.541-68.414-70.018-68.414-40.892 0-79.836 33.796-79.836 80.036 0 51.396 44.64 79.865 79.836 79.865-7.9645-1.1468-34.506-6.834-34.863-67.967-0.23987-41.347 13.488-57.866 34.805-50.599 0.47743 0.17707 23.514 9.2645 23.514 38.951 0 29.56-23.427 38.715-23.427 38.715z" color="#000000" fill="url(#linearGradient3005)"/><path d="m79.823 41.401c-23.39-8.0619-52.043 11.216-52.043 49.829 0 63.048 46.721 68.77 52.384 68.77 40.892 0 79.836-33.796 79.836-80.036 0-51.396-44.64-79.865-79.836-79.865 9.7481-1.35 52.541 10.55 52.541 69.037 0 38.141-31.953 58.905-52.735 50.033-0.47743-0.17707-23.514-9.2645-23.514-38.951 0-29.56 23.367-38.818 23.367-38.818z" color="#000000" fill="url(#linearGradient3002)"/></g></svg>' },
'yaml': { name: 'YAML', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 470.647" class="bg-white p-[1px]"><polygon id="Y" points="235.793 0 143.978 137.674 143.978 224.949 87.702 224.949 87.702 137.674 0 0 63.25 0 119.018 88.646 175.243 0 235.793 0 235.793 0"/><path id="A" fill="#cb171e" d="M330.294,175.451h-101.861l-20.717,50.024h-45.106l95.38,-224.949h46.137l91.51,224.949h-48.2l-17.144,-50.024zm-16.92,-44.911l-31.226,-82.55l-34.837,82.55h66.063z"/><polygon id="M" points="87.701 250.177 87.701 470.647 135.004 470.647 135.004 318.569 184.509 420.789 221.743 420.789 272.939 314.976 272.939 470.602 318.318 470.602 318.318 250.177 256.358 250.177 201.381 349.883 149.021 250.177 87.701 250.177 87.701 250.177"/><polygon id="L" points="512 422.735 395.638 422.735 395.638 250.125 347.442 250.125 347.442 469.647 512 469.647 512 422.737 512 422.735"/></svg>' },
'toml': { name: 'TOML', icon: '<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><g><polygon class="fill-current text-orange-800" points="99.2359551 -7.08546829e-15 99.2359551 14.3820225 112.179775 14.3820225 112.179775 113.617978 99.2359551 113.617978 99.2359551 128 128 128 128 0"></polygon><polygon class="fill-current text-white" points="32.3595506 41.7078652 32.3595506 25.8876404 95.6404494 25.8876404 95.6404494 41.7078652 71.9101124 41.7078652 71.9101124 110.741573 56.0898876 110.741573 56.0898876 41.7078652"></polygon><polygon class="fill-current text-orange-800" points="28.7640449 0 28.7640449 14.3820225 15.8202247 14.3820225 15.8202247 113.617978 28.7640449 113.617978 28.7640449 128 0 128 0 0"></polygon></g></svg>' },
'c': { name: 'C', icon: '<svg viewBox="0 0 128 128"><path fill="#659AD3" d="M115.4 30.7L67.1 2.9c-.8-.5-1.9-.7-3.1-.7-1.2 0-2.3.3-3.1.7l-48 27.9c-1.7 1-2.9 3.5-2.9 5.4v55.7c0 1.1.2 2.4 1 3.5l106.8-62c-.6-1.2-1.5-2.1-2.4-2.7z"></path><path fill="#03599C" d="M10.7 95.3c.5.8 1.2 1.5 1.9 1.9l48.2 27.9c.8.5 1.9.7 3.1.7 1.2 0 2.3-.3 3.1-.7l48-27.9c1.7-1 2.9-3.5 2.9-5.4V36.1c0-.9-.1-1.9-.6-2.8l-106.6 62z"></path><path fill="#fff" d="M85.3 76.1C81.1 83.5 73.1 88.5 64 88.5c-13.5 0-24.5-11-24.5-24.5s11-24.5 24.5-24.5c9.1 0 17.1 5 21.3 12.5l13-7.5c-6.8-11.9-19.6-20-34.3-20-21.8 0-39.5 17.7-39.5 39.5s17.7 39.5 39.5 39.5c14.6 0 27.4-8 34.2-19.8l-12.9-7.6z"></path></svg>' },
'javascript': { name: 'JavaScript', icon: '<svg viewBox="0 0 128 128"><path fill="#F0DB4F" d="M1.408 1.408h125.184v125.185H1.408z"></path><path fill="#323330" d="M116.347 96.736c-.917-5.711-4.641-10.508-15.672-14.981-3.832-1.761-8.104-3.022-9.377-5.926-.452-1.69-.512-2.642-.226-3.665.821-3.32 4.784-4.355 7.925-3.403 2.023.678 3.938 2.237 5.093 4.724 5.402-3.498 5.391-3.475 9.163-5.879-1.381-2.141-2.118-3.129-3.022-4.045-3.249-3.629-7.676-5.498-14.756-5.355l-3.688.477c-3.534.893-6.902 2.748-8.877 5.235-5.926 6.724-4.236 18.492 2.975 23.335 7.104 5.332 17.54 6.545 18.873 11.531 1.297 6.104-4.486 8.08-10.234 7.378-4.236-.881-6.592-3.034-9.139-6.949-4.688 2.713-4.688 2.713-9.508 5.485 1.143 2.499 2.344 3.63 4.26 5.795 9.068 9.198 31.76 8.746 35.83-5.176.165-.478 1.261-3.666.38-8.581zM69.462 58.943H57.753l-.048 30.272c0 6.438.333 12.34-.714 14.149-1.713 3.558-6.152 3.117-8.175 2.427-2.059-1.012-3.106-2.451-4.319-4.485-.333-.584-.583-1.036-.667-1.071l-9.52 5.83c1.583 3.249 3.915 6.069 6.902 7.901 4.462 2.678 10.459 3.499 16.731 2.059 4.082-1.189 7.604-3.652 9.448-7.401 2.666-4.915 2.094-10.864 2.07-17.444.06-10.735.001-21.468.001-32.237z"></path></svg>' },
'typescript': { name: 'TypeScript', icon: '<svg viewBox="0 0 128 128"><path fill="#fff" d="M22.67 47h99.67v73.67H22.67z"></path><path data-name="original" fill="#007acc" d="M1.5 63.91v62.5h125v-125H1.5zm100.73-5a15.56 15.56 0 017.82 4.5 20.58 20.58 0 013 4c0 .16-5.4 3.81-8.69 5.85-.12.08-.6-.44-1.13-1.23a7.09 7.09 0 00-5.87-3.53c-3.79-.26-6.23 1.73-6.21 5a4.58 4.58 0 00.54 2.34c.83 1.73 2.38 2.76 7.24 4.86 8.95 3.85 12.78 6.39 15.16 10 2.66 4 3.25 10.46 1.45 15.24-2 5.2-6.9 8.73-13.83 9.9a38.32 38.32 0 01-9.52-.1 23 23 0 01-12.72-6.63c-1.15-1.27-3.39-4.58-3.25-4.82a9.34 9.34 0 011.15-.73L82 101l3.59-2.08.75 1.11a16.78 16.78 0 004.74 4.54c4 2.1 9.46 1.81 12.16-.62a5.43 5.43 0 00.69-6.92c-1-1.39-3-2.56-8.59-5-6.45-2.78-9.23-4.5-11.77-7.24a16.48 16.48 0 01-3.43-6.25 25 25 0 01-.22-8c1.33-6.23 6-10.58 12.82-11.87a31.66 31.66 0 019.49.26zm-29.34 5.24v5.12H56.66v46.23H45.15V69.26H28.88v-5a49.19 49.19 0 01.12-5.17C29.08 59 39 59 51 59h21.83z"></path></svg>' },
'python': { name: 'Python', icon: '<svg viewBox="0 0 128 128"><linearGradient id="python-original-a" gradientUnits="userSpaceOnUse" x1="70.252" y1="1237.476" x2="170.659" y2="1151.089" gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"><stop offset="0" stop-color="#5A9FD4"></stop><stop offset="1" stop-color="#306998"></stop></linearGradient><linearGradient id="python-original-b" gradientUnits="userSpaceOnUse" x1="209.474" y1="1098.811" x2="173.62" y2="1149.537" gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"><stop offset="0" stop-color="#FFD43B"></stop><stop offset="1" stop-color="#FFE873"></stop></linearGradient><path fill="url(#python-original-a)" d="M63.391 1.988c-4.222.02-8.252.379-11.8 1.007-10.45 1.846-12.346 5.71-12.346 12.837v9.411h24.693v3.137H29.977c-7.176 0-13.46 4.313-15.426 12.521-2.268 9.405-2.368 15.275 0 25.096 1.755 7.311 5.947 12.519 13.124 12.519h8.491V67.234c0-8.151 7.051-15.34 15.426-15.34h24.665c6.866 0 12.346-5.654 12.346-12.548V15.833c0-6.693-5.646-11.72-12.346-12.837-4.244-.706-8.645-1.027-12.866-1.008zM50.037 9.557c2.55 0 4.634 2.117 4.634 4.721 0 2.593-2.083 4.69-4.634 4.69-2.56 0-4.633-2.097-4.633-4.69-.001-2.604 2.073-4.721 4.633-4.721z" transform="translate(0 10.26)"></path><path fill="url(#python-original-b)" d="M91.682 28.38v10.966c0 8.5-7.208 15.655-15.426 15.655H51.591c-6.756 0-12.346 5.783-12.346 12.549v23.515c0 6.691 5.818 10.628 12.346 12.547 7.816 2.297 15.312 2.713 24.665 0 6.216-1.801 12.346-5.423 12.346-12.547v-9.412H63.938v-3.138h37.012c7.176 0 9.852-5.005 12.348-12.519 2.578-7.735 2.467-15.174 0-25.096-1.774-7.145-5.161-12.521-12.348-12.521h-9.268zM77.809 87.927c2.561 0 4.634 2.097 4.634 4.692 0 2.602-2.074 4.719-4.634 4.719-2.55 0-4.633-2.117-4.633-4.719 0-2.595 2.083-4.692 4.633-4.692z" transform="translate(0 10.26)"></path><radialGradient id="python-original-c" cx="1825.678" cy="444.45" r="26.743" gradientTransform="matrix(0 -.24 -1.055 0 532.979 557.576)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#B8B8B8" stop-opacity=".498"></stop><stop offset="1" stop-color="#7F7F7F" stop-opacity="0"></stop></radialGradient><path opacity=".444" fill="url(#python-original-c)" d="M97.309 119.597c0 3.543-14.816 6.416-33.091 6.416-18.276 0-33.092-2.873-33.092-6.416 0-3.544 14.815-6.417 33.092-6.417 18.275 0 33.091 2.872 33.091 6.417z"></path></svg>' },
'sh': { name: 'shell' }
}
%}
{% macro render(node) %}
<div class="text-white text-sm md:text-base relative flex w-full {{ node.class }}">
{% macro Language(node, example, isFirst) %}
<details
{{ 'open' if isFirst else '' }}
class="group first:rounded-tl-md last:rounded-tr-md overflow-hidden"
onclick="[...this.parentElement.children].forEach(c => (c.open = false))"
>
<summary
class="
flex gap-2 items-center px-4 py-2 cursor-pointer
text-neutral-50 group-open:text-primary-500 bg-neutral-800 border-b-2 border-b-neutral-50/25 group-open:border-b-primary-600
h-12 {{ node.summaryClass }}
"
>
{% if languageMeta[example.language].icon %}
<div class="w-[16px] h-[16px] hidden md:block">{{ languageMeta[example.language].icon | safe }}</div>
{% endif %}
{{ example.name or languageMeta[example.language].name }}
</summary>
<pre
class="
absolute top-12 left-0 right-0 w-full
bg-neutral-900 text-neutral-50 p-4 rounded-bl-md rounded-br-md rounded-tr-md whitespace-pre-wrap text-sm
overflow-x-none overflow-y-auto {{ node.preClass }}
"
onclick="event.preventDefault(); event.stopPropagation();"
>{{ example.code | highlight(example.language) | safe }}</pre>
</details>
{% endmacro %}
{% set isFirst = true %}
{% for example in node.examples %}
{{ Language(node, example, true if isFirst else false) }}
{% set isFirst = false %}
{% endfor %}
</div>
{% endmacro %}

View File

@@ -0,0 +1,29 @@
{% set levelClasses = {
'1': 'text-5xl text-transparent bg-clip-text bg-gradient-to-b from-primary-400 to-primary-600 mb-12',
'2': 'text-4xl text-transparent bg-clip-text bg-gradient-to-b from-primary-400 to-primary-600 mb-12',
'3': 'text-lg md:text-2xl font-light'
} %}
{% set levelLinkClasses = {
'1': 'border-transparent hover:border-primary-600',
'2': 'border-transparent hover:border-primary-600',
'3': 'border-transparent hover:border-current'
} %}
{% macro render(node) %}
<h{{ node.level }}
id="{{ node.slug }}"
{% if levelClasses[node.level] %}
class="
{{ levelClasses[node.level] }}
{{ node.class }}
"
{% endif %}
>
{% if node.href %}
<a href="{{ node.href }}" class="border-b-2 {{ levelLinkClasses[node.level] }}">{{ node.text | safe }}</a>
{% else %}
<a href="#{{ node.slug }}">{{ node.text | safe }}</a>
{% endif %}
</h{{ node.level }}>
{% endmacro %}

View File

@@ -0,0 +1,3 @@
{% macro render(node) %}
{{ featherIcons(node.icon) }}
{% endmacro %}

View File

@@ -0,0 +1,7 @@
{% from "./Node.njk" import Node %}
{% macro render(node, page) %}
{% for child in node.children %}
{{ Node(child.type, child, page) }}
{% endfor %}
{% endmacro %}

View File

@@ -0,0 +1,13 @@
{% macro render(node) %}
{% if node.variant == 'small' %}
<div class="flex gap-2 items-center {{node.class}}">
<img src="/public/logo.png" class="w-8 h-8" alt=""/>
<span class="text-3xl text-transparent bg-clip-text bg-gradient-to-b from-primary-400 to-primary-600">Terrace</span>
</div>
{% else %}
<div class="flex gap-4 items-center {{node.class}}">
<img src="/public/logo.png" class="w-8 h-8 md:w-16 md:h-16" alt=""/>
<h1 class="text-xl md:text-5xl text-transparent bg-clip-text bg-gradient-to-b from-primary-400 to-primary-600">Terrace</h1>
</div>
{% endif %}
{% endmacro %}

View File

@@ -0,0 +1,11 @@
{% macro render(node) %}
<div class="
prose text-base
prose-p:mb-2 prose-ul:my-1 prose-ul:pl-0 prose-ul:ml-6
md:prose-p:mb-4 md:prose-ul:pl-4 md:prose-ul:ml-2 md:prose-ul:my-2
prose-li:my-0 text-current
{{node.class}}
">
{{ node.text | safe }}
</div>
{% endmacro %}

View File

@@ -0,0 +1,48 @@
{% from "./Node.njk" import Node %}
{% macro navlink(href, text, active) %}
<a href="{{ href }}" class="flex items-center hover:text-primary-400 {{ 'text-primary-400' if href in active }}">{{ text }}</a>
{% endmacro %}
{% macro render(node, ctx) %}
<nav class="fixed left-0 right-0 top-0 lg:absolute bg-neutral-800 text-neutral-50 h-20 flex items-center z-10">
<div class="container mx-auto px-8 flex items-center space-between">
<a id="nav" class="peer" href="#nav" aria-hidden="true" tabindex="-1"></a>
<div class="absolute z-10 bg-neutral-800 w-full h-full top-0 left-0 lg:hidden"></div>
<a href="/" class="relative w-full z-20">
{{ Node('Logo', { variant: 'small' }) }}
</a>
<div class="
group
fixed flex flex-col gap-8 items-stretch w-full bg-neutral-800 text-neutral-50 top-20 bottom-0 left-0 px-7
transition-transform
-translate-y-full
pointer-events-none
peer-target:translate-y-0
peer-target:pointer-events-auto
lg:translate-y-0
lg:pointer-events-auto
lg:relative lg:top-0 lg:px-0 lg:flex-row lg:flex-1 lg:h-20 lg:bg-neutral-800 lg:text-white
">
{{ navlink("/docs/javascript/", "Docs", ctx.url) }}
{{ navlink("/examples/", "Examples", ctx.url) }}
{{ navlink("/about/", "About", ctx.url) }}
{{ navlink("/contribute/", "Contribute", ctx.url) }}
<div class="-order-1 lg:order-2 flex items-center">{{ Node('SearchBox', { class: 'w-full lg:w-72' }) }}</div>
<a href="https://git.thederf.com/thederf/Terrace" target="_blank" class="flex items-center hover:text-primary-400 order-3">
{{ Node('Icon', { icon: 'github' }) }}<div class="ml-4 lg:hidden">Code</div>
</a>
</div>
<a href="#nav" title="Open Menu" class="absolute right-4 flex peer-target:hidden lg:hidden h-16 w-16 items-center justify-center z-20">
{{ Node('Icon', { icon: 'menu' }) }}
</a>
<a href="#" title="Close Menu" class="absolute right-4 hidden peer-target:flex lg:hidden lg:peer-target:hidden h-16 w-16 items-center justify-center text-primary-500 z-20">
{{ Node('Icon', { icon: 'x' }) }}
</a>
</div>
</nav>
{% endmacro %}

View File

@@ -0,0 +1,4 @@
{% macro Node(name, params, ctx) %}
{% from "./" + name + ".njk" import render %}
{{ render(params, ctx) }}
{% endmacro %}

View File

@@ -0,0 +1,8 @@
{% from "./Node.njk" import Node %}
{% macro render(node) %}
<div class="flex gap-4 px-4 py-2 rounded-md border-2 border-neutral-50/75 focus-within:border-primary-400 focus-within:text-primary-400 bg-transparent text-neutral-50 {{ node.class }}">
<input type="text" placeholder="Search" class="w-full bg-transparent outline-none"/>
{{ Node('Icon', { icon: 'search' }) }}
</div>
{% endmacro %}

View File

@@ -0,0 +1,20 @@
{% from "./Node.njk" import Node %}
{% set variants = {
light: "bg-gradient-to-b from-neutral-50 to-neutral-50/50 text-neutral-900",
dark: "bg-gradient-to-b from-neutral-800 to-neutral-900 text-neutral-50"
}
%}
{% macro render(node, ctx) %}
<svg class="w-full -mt-[55px] {{ 'text-neutral-50' if node.variant == 'light' else 'text-neutral-800' }}" viewBox="0 0 1920 55">
<path d="M0 55H1920V6.99994C1811 55 1150 55 403.5 6.99994C141.541 -9.84407 0 6.99996 0 55Z" fill="currentColor"/>
</svg>
<section class="group/{{node.variant}} {{ variants[node.variant] }}">
<div class="{{ node.class }} container mx-auto px-8 pt-16 pb-48">
{% for child in node.children %}
{{ Node(child.type, child, ctx) }}
{% endfor %}
</div>
</section>
{% endmacro %}

View File

@@ -0,0 +1,19 @@
{% macro renderHeadings(headings) %}
<ul class="pl-4">
{% for heading in headings %}
<li class="my-4">
<a href="#{{ heading.slug }}" class="hover:text-primary-400">{{ heading.text }}</a>
{% if heading.children %}
{{ renderHeadings(heading.children)}}
{% endif %}
</li>
{% endfor %}
</ul>
{% endmacro %}
{% macro render(node, ctx) %}
<div class="my-4 font-medium text-2xl">Table of Contents</div>
<div class="-ml-3">
{{ renderHeadings(ctx.headings) }}
</div>
{% endmacro %}

50
docs/renderer/render.js Normal file
View File

@@ -0,0 +1,50 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import nunjucks from 'nunjucks'
import HighlightJS from 'highlight.js'
import readPage from '../read-page/index.js'
import googleFonts from './util/google-fonts.js'
import featherIcons from './util/feather-icons.js'
const pages = {
'/': './pages/index.tce',
'/about/': './pages/about.tce'
}
async function render() {
// await fs.rm('dist', { recursive: true })
for (const [urlPath, filePath] of Object.entries(pages)) {
const env = nunjucks.configure('renderer/', { autoescape: false })
env.addGlobal('googleFonts', googleFonts)
env.addGlobal('featherIcons', featherIcons)
HighlightJS.registerLanguage('terrace', () => ({
name: 'Terrace',
contains: [
{
className: 'keyword',
begin: /^\s*(.*?)(?:\s|$)/,
relevance: 1
}
]
}))
env.addFilter("highlight", (value, language) => {
return HighlightJS.highlight(value, { language }).value
})
const page = await readPage(filePath)
const result = env.render('layout.njk', { page, url: urlPath, file: filePath })
await fs.mkdir(path.join('dist', urlPath), { recursive: true })
await fs.writeFile(path.join('dist', urlPath, 'index.html'), result)
}
// await fs.cp('node_modules/highlight.js/styles/atom-one-dark.css', '');
await fs.cp('./public/', './dist/public/', { recursive: true })
await fs.cp('./favicon.ico', './dist/favicon.ico', { recursive: true })
}
render()

View File

@@ -0,0 +1,19 @@
import feather from 'feather-icons'
const defaultAttributes = {
"class": "feather feather-x",
"xmlns": "http://www.w3.org/2000/svg",
"width": 24,
"height": 24,
"viewBox": "0 0 24 24",
"fill": "none",
"stroke": "currentColor",
"stroke-width": 2,
"stroke-linecap": "round",
"stroke-linejoin": "round",
}
export default (iconName, attributes = {}) => {
attributes = { ...defaultAttributes, ...attributes }
return feather.icons[iconName].toSvg(attributes)
}

View File

@@ -0,0 +1,45 @@
import https from 'node:https'
const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'
const isValidURL = url => {
return /fonts.googleapis.com/.test(url)
}
const downloadFont = url => {
return new Promise((resolve) => {
let rawData = ''
https.get(
url,
{
headers: {
'user-agent': UA,
},
},
res => {
res.on('data', chunk => {
rawData += chunk
})
res.on('end', () => {
resolve(rawData.toString('utf8'))
})
}
)
})
}
const createInlineCss = async url => {
if (!isValidURL(url)) {
throw new Error('Invalid Google Fonts URL')
}
const content = await downloadFont(url)
return (
`<link rel="preconnect" href="https://fonts.gstatic.com">`+
`<link data-href="${url}" rel="stylesheet">`+
`<style data-href='${url}'>${content}</style>`
)
}
export default createInlineCss