Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
95a20df58b | |||
0452c5fcf0 | |||
2636e42533 | |||
1a09a51780 | |||
beb80145a4 | |||
0076cbb971 | |||
bd9945b24b | |||
760af891c5 | |||
f8b15f0ff4 | |||
20e585e708 | |||
3da2c28b13 | |||
35a2080714 | |||
e99170f015 | |||
f48d0a6194 | |||
92a9fe5685 | |||
d0166b1db9 | |||
821456c226 | |||
5ff9cee8de | |||
df4545a7f1 | |||
34c4144c3d | |||
fbe142f12f | |||
bcd60473b6 | |||
46f7424bc9 | |||
dc3b8ea5d8 | |||
5d36def32c | |||
e3bf8dc139 | |||
7297f9cd7a | |||
2dd300f4dc | |||
17a406e546 | |||
5149be7c8c |
40
.gitea/workflows/build-and-pubilsh.yaml
Normal file
40
.gitea/workflows/build-and-pubilsh.yaml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
name: Build and publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
description: Semver to publish
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
NPM_PUBLISH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Login to package registry
|
||||||
|
run: |
|
||||||
|
npm config set @doc-utils:registry https://gitea.jbrumond.me/api/packages/doc-utils/npm/
|
||||||
|
npm config set -- '//gitea.jbrumond.me/api/packages/doc-utils/npm/:_authToken' "$NPM_PUBLISH_TOKEN"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Compile TypeScript
|
||||||
|
run: npm run tsc
|
||||||
|
|
||||||
|
- name: Publish package
|
||||||
|
run: |
|
||||||
|
npm version ${{ inputs.version }} --allow-same-version --git-tag-version false
|
||||||
|
npm publish
|
40
.gitea/workflows/build-and-test.yaml
Normal file
40
.gitea/workflows/build-and-test.yaml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
name: Build and test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [18.x, 20.x]
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- name: Login to package registry
|
||||||
|
run: |
|
||||||
|
npm config set @doc-utils:registry https://gitea.jbrumond.me/api/packages/doc-utils/npm/
|
||||||
|
npm config set -- '//gitea.jbrumond.me/api/packages/doc-utils/npm/:_authToken' "$NPM_PUBLISH_TOKEN"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Compile TypeScript
|
||||||
|
run: npm run tsc
|
||||||
|
|
||||||
|
# todo: tests
|
||||||
|
- name: Run tests
|
||||||
|
run: exit 0
|
@ -116,9 +116,16 @@
|
|||||||
const outline = [ ];
|
const outline = [ ];
|
||||||
|
|
||||||
for (const heading of headings) {
|
for (const heading of headings) {
|
||||||
|
const content = heading.cloneNode(true);
|
||||||
|
const anchor = content.querySelector('a.heading-anchor');
|
||||||
|
|
||||||
|
if (anchor) {
|
||||||
|
anchor.parentNode.removeChild(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
outline.push(`
|
outline.push(`
|
||||||
<li data-depth="${heading.tagName.toLowerCase()}">
|
<li data-depth="${heading.tagName.toLowerCase()}">
|
||||||
<a href="#${heading.id}">${heading.innerText}</a>
|
<a href="#${heading.id}">${content.innerHTML}</a>
|
||||||
</li>
|
</li>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
@ -92,9 +92,16 @@
|
|||||||
const outline = [ ];
|
const outline = [ ];
|
||||||
|
|
||||||
for (const heading of headings) {
|
for (const heading of headings) {
|
||||||
|
const content = heading.cloneNode(true);
|
||||||
|
const anchor = content.querySelector('a.heading-anchor');
|
||||||
|
|
||||||
|
if (anchor) {
|
||||||
|
anchor.parentNode.removeChild(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
outline.push(`
|
outline.push(`
|
||||||
<li data-depth="${heading.tagName.toLowerCase()}">
|
<li data-depth="${heading.tagName.toLowerCase()}">
|
||||||
<a href="#${heading.id}">${heading.innerText}</a>
|
<a href="#${heading.id}">${content.innerHTML}</a>
|
||||||
</li>
|
</li>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
163
extras/figures.css
Normal file
163
extras/figures.css
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
|
||||||
|
figure[data-lang] {
|
||||||
|
margin-block: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure a.view-svg {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang] svg {
|
||||||
|
display: block;
|
||||||
|
margin-inline: auto;
|
||||||
|
margin-block: 2rem;
|
||||||
|
|
||||||
|
/* The auto-scaling font-size from typography/*.css does bad things to a lot of SVGs.
|
||||||
|
* The SVGs themselves are inherently scalable, so there is no need for it here anyway. */
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-size='medium']:has(svg) {
|
||||||
|
margin-block: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-size='large'] {
|
||||||
|
margin-block: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-size='small'] svg {
|
||||||
|
max-width: 40rem;
|
||||||
|
max-height: min(20rem, 50vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-size='medium'] svg {
|
||||||
|
max-width: 60rem;
|
||||||
|
max-height: min(40rem, 50vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-size='large'] svg {
|
||||||
|
max-height: min(60rem, 80vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* figure[data-lang].big {
|
||||||
|
background: var(--theme-bg-main);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
border: 0.25rem var(--theme-line) solid;
|
||||||
|
margin: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang].big svg {
|
||||||
|
max-width: none !important;
|
||||||
|
max-height: none !important;
|
||||||
|
} */
|
||||||
|
|
||||||
|
/* figure:is([data-lang='pikchr'], [data-lang='nomnoml'], [data-lang='bytefield']) svg text:not([fill^='var']) { */
|
||||||
|
figure:is([data-lang='pikchr'], [data-lang='nomnoml'], [data-lang='bytefield']) svg text {
|
||||||
|
fill: var(--theme-text-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure:is([data-lang='pikchr'], [data-lang='nomnoml'], [data-lang='bytefield']) svg text:not([font-family~='Courier']) {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-lang='bash:samp'] samp {
|
||||||
|
display: block;
|
||||||
|
margin-block-start: 0.5rem;
|
||||||
|
padding-block-start: 0.5rem;
|
||||||
|
border-block-start: 0.1rem solid var(--theme-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === KaTeX === */
|
||||||
|
|
||||||
|
.katex-display {
|
||||||
|
color: var(--theme-text-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
.katex-display .katex {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* using "body" here to add specificity, to override styles from katex.min.css */
|
||||||
|
body .katex-display {
|
||||||
|
margin: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body figure.align-left .katex-display > .katex {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
body figure.align-right .katex-display > .katex {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(.katex-display) > .katex {
|
||||||
|
font-size: inherit;
|
||||||
|
margin-inline: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.katex span[style~='color:transparent;'] {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Pikchr */
|
||||||
|
|
||||||
|
/* boxes */
|
||||||
|
figure[data-lang='pikchr'] svg path[style*='fill:none;'] {
|
||||||
|
fill: var(--theme-bg-light) !important;
|
||||||
|
transition: fill linear .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lines and boxes */
|
||||||
|
figure[data-lang='pikchr'] svg path[style*='stroke:rgb(0,0,0);'] {
|
||||||
|
stroke: var(--theme-text-body) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* circles */
|
||||||
|
figure[data-lang='pikchr'] svg circle[style*='stroke:rgb(0,0,0);'] {
|
||||||
|
stroke: var(--theme-text-body) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* arrow heads */
|
||||||
|
figure[data-lang='pikchr'] svg polygon[style='fill:rgb(0,0,0)'] {
|
||||||
|
fill: var(--theme-text-body) !important;
|
||||||
|
transition: fill linear .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Bytefield === */
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg line[stroke-width='1'] {
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg :is(text, tspan)[font-size='11'] {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg :is(text, tspan)[font-size='18'] {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg :is(text, tspan)[font-family~='Courier'] {
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg :is(text, tspan)[font-family~='Times'] {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg line[stroke-dasharray='1,1'] {
|
||||||
|
stroke-dasharray: 4px, 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-lang='clojure:bytefield'] svg line[stroke-dasharray='1,3'] {
|
||||||
|
stroke-dasharray: 2px, 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
58
extras/svg-links.js
Normal file
58
extras/svg-links.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
(() => {
|
||||||
|
|
||||||
|
if (document.readyState === 'complete') {
|
||||||
|
add_svg_links();
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
window.addEventListener('DOMContentLoaded', add_svg_links);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_svg_links() {
|
||||||
|
const svg_elems = document.querySelectorAll('figure > svg');
|
||||||
|
|
||||||
|
for (const svg_elem of svg_elems) {
|
||||||
|
const figure_elem = svg_elem.parentElement;
|
||||||
|
const anchor_elem = document.createElement('a');
|
||||||
|
const lang = figure_elem.getAttribute('data-lang') || '';
|
||||||
|
|
||||||
|
let svg_html;
|
||||||
|
|
||||||
|
if ('svg_link_css' in window) {
|
||||||
|
const svg_clone = svg_elem.cloneNode(true);
|
||||||
|
const style_elem = document.createElement('style');
|
||||||
|
svg_clone.insertBefore(style_elem, svg_clone.firstChild);
|
||||||
|
|
||||||
|
const css
|
||||||
|
= (window.svg_link_css['*'] || '')
|
||||||
|
+ (lang ? window.svg_link_css[lang] || '' : '');
|
||||||
|
|
||||||
|
style_elem.setAttribute('type', 'text/css');
|
||||||
|
style_elem.appendChild(document.createTextNode(css));
|
||||||
|
|
||||||
|
svg_html = svg_clone.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
svg_html = svg_elem.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
const box = document.createElement('div');
|
||||||
|
const svg_xml = '<?xml version="1.0" standalone="no" ?>\r\n'
|
||||||
|
+ svg_html.replace(/(&(?!(amp|gt|lt|quot|apos))[^;]+;)/g, ($0, $1) => {
|
||||||
|
box.innerHTML = $0;
|
||||||
|
return box.textContent;
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg_blob = new Blob([ svg_xml ], { type: 'image/svg+xml; charset=utf-8' });
|
||||||
|
const svg_object_url = URL.createObjectURL(svg_blob);
|
||||||
|
|
||||||
|
anchor_elem.href = svg_object_url;
|
||||||
|
anchor_elem.className = 'view-svg';
|
||||||
|
anchor_elem.textContent = 'View / Download Graphic';
|
||||||
|
|
||||||
|
figure_elem.insertBefore(anchor_elem, svg_elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
@ -52,3 +52,50 @@ li {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin-block: 0.25rem;
|
margin-block: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd + dt {
|
||||||
|
margin-block-start: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(dt, dd) > p {
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Figure Captions ===== */
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
margin-block-start: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Pre-formatted Blocks ===== */
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-block: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Note Blocks ===== */
|
||||||
|
|
||||||
|
:is(aside, section):is([role='note'], .info, .highlight, .warning, .problem) > :first-child {
|
||||||
|
margin-block-start: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(aside, section):is([role='note'], .info, .highlight, .warning, .problem) > :last-child {
|
||||||
|
margin-block-end: 0.25rem;
|
||||||
|
}
|
||||||
|
@ -80,12 +80,23 @@ p {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Inline Styles ===== */
|
||||||
|
|
||||||
|
del {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ===== Links ===== */
|
/* ===== Links ===== */
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--theme-text-link);
|
color: var(--theme-text-link);
|
||||||
|
display: inline;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:active,
|
a:active,
|
||||||
@ -98,7 +109,8 @@ a:visited {
|
|||||||
color: var(--theme-text-link-visited);
|
color: var(--theme-text-link-visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.icon-link {
|
a.icon-link,
|
||||||
|
a.inline-flex {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
column-gap: 0.3rem;
|
column-gap: 0.3rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -108,6 +120,10 @@ a.icon-link svg.icon {
|
|||||||
--icon-size: 1rem;
|
--icon-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
del a {
|
||||||
|
text-decoration: line-through underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -137,38 +153,10 @@ ul[role='doc-endnotes'] {
|
|||||||
background: var(--theme-bg-heavy);
|
background: var(--theme-bg-heavy);
|
||||||
}
|
}
|
||||||
|
|
||||||
dl {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt, dd {
|
|
||||||
/* */
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
dt {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
dt p {
|
|
||||||
margin-block-end: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:not(:first-of-type) {
|
|
||||||
margin-block-start: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dd {
|
|
||||||
margin-inline-start: 1rem;
|
|
||||||
padding-inline-start: 1rem;
|
|
||||||
padding-block: 0.125rem;
|
|
||||||
border-inline-start: 0.5rem var(--theme-line) solid;
|
|
||||||
background: var(--theme-bg-heavy)
|
|
||||||
} */
|
|
||||||
|
|
||||||
dd p {
|
|
||||||
margin-block: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cite-label {
|
.cite-label {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@ -205,64 +193,64 @@ mark {
|
|||||||
|
|
||||||
/* ===== Tables ===== */
|
/* ===== Tables ===== */
|
||||||
|
|
||||||
table, .table, td, .td {
|
table, .faux-table, td, .faux-td {
|
||||||
color: var(--theme-text-body);
|
color: var(--theme-text-body);
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
}
|
}
|
||||||
|
|
||||||
table, .table {
|
table, .faux-table {
|
||||||
display: table;
|
display: table;
|
||||||
margin-block: 2rem;
|
margin-block: 2rem;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
table, th, td,
|
table, th, td,
|
||||||
.table, .th, .td {
|
.faux-table, .faux-th, .faux-td {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.75;
|
line-height: 1.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead, .thead {
|
thead, .faux-thead {
|
||||||
display: table-header-group;
|
display: table-header-group;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody, .tbody {
|
tbody, .faux-tbody {
|
||||||
display: table-row-group;
|
display: table-row-group;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr,
|
tbody tr,
|
||||||
.tbody .tr {
|
.faux-tbody .faux-tr {
|
||||||
border-top: 1px var(--theme-line) solid;
|
border-top: 1px var(--theme-line) solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr, .tr {
|
tr, .faux-tr {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
th, .th {
|
th, .faux-th {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--theme-text-heading);
|
color: var(--theme-text-heading);
|
||||||
}
|
}
|
||||||
|
|
||||||
th, .th,
|
th, .faux-th,
|
||||||
td, .td {
|
td, .faux-td {
|
||||||
padding-block: 0.5rem;
|
padding-block: 0.5rem;
|
||||||
padding-inline: 1rem;
|
padding-inline: 1rem;
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
td, .td {
|
td, .faux-td {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(td, .td):not(:last-of-type) {
|
:is(td, .faux-td):not(:last-of-type) {
|
||||||
border-inline-end: 0.1rem solid var(--theme-line);
|
border-inline-end: 0.1rem solid var(--theme-line);
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(table, .table) :is(input, select) {
|
:is(table, .faux-table) :is(input, select) {
|
||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,24 +268,28 @@ table dl {
|
|||||||
pre {
|
pre {
|
||||||
color: var(--theme-code-normal);
|
color: var(--theme-code-normal);
|
||||||
font-family: var(--font-monospace);
|
font-family: var(--font-monospace);
|
||||||
margin-block: 3rem;
|
margin-inline-start: 1rem;
|
||||||
margin-inline: 2rem;
|
margin-inline-end: 5rem;
|
||||||
padding-block: 0.5rem;
|
|
||||||
padding-inline: 1rem;
|
|
||||||
border: 0.1rem solid var(--theme-line);
|
border: 0.1rem solid var(--theme-line);
|
||||||
border-radius: 1rem;
|
border-radius: 0.5rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background: var(--theme-bg-light);
|
background: var(--theme-bg-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 60rem) {
|
||||||
|
pre {
|
||||||
|
margin-inline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ===== Code / Sample Output ===== */
|
/* ===== Code / Sample Output ===== */
|
||||||
|
|
||||||
code, samp {
|
code, samp {
|
||||||
font-size: 1rem;
|
font-size: inherit;
|
||||||
color: var(--theme-code-normal);
|
color: var(--theme-code-normal);
|
||||||
font-family: var(--font-monospace);
|
font-family: var(--font-monospace);
|
||||||
}
|
}
|
||||||
@ -305,8 +297,11 @@ code, samp {
|
|||||||
:not(pre) > :is(code, samp) {
|
:not(pre) > :is(code, samp) {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
background: var(--theme-bg-light);
|
background: var(--theme-bg-light);
|
||||||
|
margin-inline: 0.15rem;
|
||||||
|
padding-block: 0.1rem;
|
||||||
padding-inline: 0.25rem;
|
padding-inline: 0.25rem;
|
||||||
border: 0.1rem solid var(--theme-line);
|
border: 0.1rem solid var(--theme-line);
|
||||||
|
border-radius: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -351,7 +346,6 @@ figcaption {
|
|||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
margin-block-start: 1rem;
|
|
||||||
color: var(--theme-text-light);
|
color: var(--theme-text-light);
|
||||||
max-width: 60vw;
|
max-width: 60vw;
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
|
@ -45,6 +45,20 @@ p {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Note Blocks ===== */
|
||||||
|
|
||||||
|
:is(aside, section):is([role='note'], .info, .highlight, .warning, .problem) > :first-child {
|
||||||
|
margin-block-start: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(aside, section):is([role='note'], .info, .highlight, .warning, .problem) > :last-child {
|
||||||
|
margin-block-end: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ===== Lists ===== */
|
/* ===== Lists ===== */
|
||||||
|
|
||||||
@ -52,3 +66,36 @@ li {
|
|||||||
line-height: 1.75;
|
line-height: 1.75;
|
||||||
margin-block: 0.5rem;
|
margin-block: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd + dt {
|
||||||
|
margin-block-start: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(dt, dd) > p {
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Figure Captions ===== */
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
margin-block-start: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Pre-formatted Blocks ===== */
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin-block: 2rem;
|
||||||
|
padding-block: 0.5rem;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
}
|
||||||
|
35
package-lock.json
generated
35
package-lock.json
generated
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@doc-utils/docs2website",
|
"name": "@doc-utils/docs2website",
|
||||||
"version": "0.1.5",
|
"version": "0.2.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@doc-utils/docs2website",
|
"name": "@doc-utils/docs2website",
|
||||||
"version": "0.1.5",
|
"version": "0.2.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@doc-utils/color-themes": "^0.1.14",
|
"@doc-utils/color-themes": "^0.2.0",
|
||||||
"@doc-utils/jsonschema2markdown": "^0.1.1",
|
"@doc-utils/jsonschema2markdown": "^0.1.1",
|
||||||
"@doc-utils/markdown2html": "^0.2.1",
|
"@doc-utils/markdown2html": "^0.3.6",
|
||||||
"glob": "^10.2.3",
|
"glob": "^10.2.3",
|
||||||
"ical": "^0.8.0",
|
"ical": "^0.8.0",
|
||||||
"ical-generator": "^4.1.0",
|
"ical-generator": "^4.1.0",
|
||||||
@ -33,9 +33,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@doc-utils/color-themes": {
|
"node_modules/@doc-utils/color-themes": {
|
||||||
"version": "0.1.14",
|
"version": "0.2.0",
|
||||||
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fcolor-themes/-/0.1.14/color-themes-0.1.14.tgz",
|
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fcolor-themes/-/0.2.0/color-themes-0.2.0.tgz",
|
||||||
"integrity": "sha512-j0U8v8Y+9zAm9D7pbCheTQYGEKt9FSpKSZQNGsogxWl95S9Z7QMjtmJns6QPgdOsSDss7sjMLFS5Gm50GCMzNA=="
|
"integrity": "sha512-UtjY25B8m4qdMvrmTPK3I1JXckbc1cvCOmIygHFBexpSWBQmb+sdoXfdCsoSpgPncurz1kwlEXffgPELCGtP8g=="
|
||||||
},
|
},
|
||||||
"node_modules/@doc-utils/jsonschema2markdown": {
|
"node_modules/@doc-utils/jsonschema2markdown": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
@ -51,9 +51,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@doc-utils/markdown2html": {
|
"node_modules/@doc-utils/markdown2html": {
|
||||||
"version": "0.2.6",
|
"version": "0.3.6",
|
||||||
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fmarkdown2html/-/0.2.6/markdown2html-0.2.6.tgz",
|
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fmarkdown2html/-/0.3.6/markdown2html-0.3.6.tgz",
|
||||||
"integrity": "sha512-6cQNzthYOOlkT6rr6E1lpSJ4Zq991+chblSRxv69SVT8y5cIs6c9tFu75MXoIKX4+H68Q4i10gtMrrsXD+bkaA==",
|
"integrity": "sha512-LCt5HAAcHz6lE+IqV2igQP7bbTq1juNbyn4KWqn3xe9eSslIyl7EvSaEH68WQtqEzX/iVFOvZz7qSoqsQeggXA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytefield-svg": "^1.6.1",
|
"bytefield-svg": "^1.6.1",
|
||||||
"dompurify": "^2.3.6",
|
"dompurify": "^2.3.6",
|
||||||
@ -66,6 +66,9 @@
|
|||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"vega": "^5.22.1",
|
"vega": "^5.22.1",
|
||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"markdown2html": "bin/markdown2html"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
@ -2674,9 +2677,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@doc-utils/color-themes": {
|
"@doc-utils/color-themes": {
|
||||||
"version": "0.1.14",
|
"version": "0.2.0",
|
||||||
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fcolor-themes/-/0.1.14/color-themes-0.1.14.tgz",
|
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fcolor-themes/-/0.2.0/color-themes-0.2.0.tgz",
|
||||||
"integrity": "sha512-j0U8v8Y+9zAm9D7pbCheTQYGEKt9FSpKSZQNGsogxWl95S9Z7QMjtmJns6QPgdOsSDss7sjMLFS5Gm50GCMzNA=="
|
"integrity": "sha512-UtjY25B8m4qdMvrmTPK3I1JXckbc1cvCOmIygHFBexpSWBQmb+sdoXfdCsoSpgPncurz1kwlEXffgPELCGtP8g=="
|
||||||
},
|
},
|
||||||
"@doc-utils/jsonschema2markdown": {
|
"@doc-utils/jsonschema2markdown": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
@ -2692,9 +2695,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@doc-utils/markdown2html": {
|
"@doc-utils/markdown2html": {
|
||||||
"version": "0.2.6",
|
"version": "0.3.6",
|
||||||
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fmarkdown2html/-/0.2.6/markdown2html-0.2.6.tgz",
|
"resolved": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/%40doc-utils%2Fmarkdown2html/-/0.3.6/markdown2html-0.3.6.tgz",
|
||||||
"integrity": "sha512-6cQNzthYOOlkT6rr6E1lpSJ4Zq991+chblSRxv69SVT8y5cIs6c9tFu75MXoIKX4+H68Q4i10gtMrrsXD+bkaA==",
|
"integrity": "sha512-LCt5HAAcHz6lE+IqV2igQP7bbTq1juNbyn4KWqn3xe9eSslIyl7EvSaEH68WQtqEzX/iVFOvZz7qSoqsQeggXA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bytefield-svg": "^1.6.1",
|
"bytefield-svg": "^1.6.1",
|
||||||
"dompurify": "^2.3.6",
|
"dompurify": "^2.3.6",
|
||||||
|
10
package.json
10
package.json
@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@doc-utils/docs2website",
|
"name": "@doc-utils/docs2website",
|
||||||
"version": "0.1.5",
|
"version": "0.2.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://gitea.home.jbrumond.me/api/packages/doc-utils/npm/"
|
"registry": "https://gitea.jbrumond.me/api/packages/doc-utils/npm/"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"tsc": "tsc",
|
"tsc": "tsc",
|
||||||
"clean": "rm -rf ./build ./www"
|
"clean": "rm -rf ./build ./www"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"docs2website": "./bin/docs2website.js"
|
"docs2website": "./bin/docs2website"
|
||||||
},
|
},
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -22,9 +22,9 @@
|
|||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@doc-utils/color-themes": "^0.1.14",
|
"@doc-utils/color-themes": "^0.2.0",
|
||||||
"@doc-utils/jsonschema2markdown": "^0.1.1",
|
"@doc-utils/jsonschema2markdown": "^0.1.1",
|
||||||
"@doc-utils/markdown2html": "^0.2.1",
|
"@doc-utils/markdown2html": "^0.3.6",
|
||||||
"glob": "^10.2.3",
|
"glob": "^10.2.3",
|
||||||
"ical": "^0.8.0",
|
"ical": "^0.8.0",
|
||||||
"ical-generator": "^4.1.0",
|
"ical-generator": "^4.1.0",
|
||||||
|
@ -5,11 +5,12 @@ import { BuildState } from './state';
|
|||||||
import { mkdirp, write_text } from '../fs';
|
import { mkdirp, write_text } from '../fs';
|
||||||
import { CalendarConfig, RSSConfig } from '../conf';
|
import { CalendarConfig, RSSConfig } from '../conf';
|
||||||
import { render_theme_css_properties } from '../themes';
|
import { render_theme_css_properties } from '../themes';
|
||||||
import { load_partials, FrontMatter, Context, load_layout, render_template } from '../template';
|
import { load_partials, FrontMatter, Context, load_layout, render_template, EventFrontmatter } from '../template';
|
||||||
import { render_markdown_to_html, render_markdown_to_html_inline_sync } from '@doc-utils/markdown2html';
|
import { render_markdown_to_html, render_markdown_to_html_inline_sync } from '@doc-utils/markdown2html';
|
||||||
import { RSSEntry } from './rss';
|
import { RSSEntry } from './rss';
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { EventEntry } from './icalendar';
|
import { EventEntry } from './icalendar';
|
||||||
|
import { as_context_time, from_iso } from '../time';
|
||||||
|
import { FileMetadata } from '../metadata';
|
||||||
|
|
||||||
export interface OutFileURL {
|
export interface OutFileURL {
|
||||||
base_url: string;
|
base_url: string;
|
||||||
@ -29,9 +30,9 @@ export function map_output_file_to_url(state: BuildState, out_file: string, inde
|
|||||||
rel_path = '/' + rel_path;
|
rel_path = '/' + rel_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const base_url = state.conf.base_url.endsWith('/')
|
const base_url = state.conf.base_url?.endsWith('/')
|
||||||
? state.conf.base_url.slice(0, -1)
|
? state.conf.base_url.slice(0, -1)
|
||||||
: state.conf.base_url;
|
: (state.conf.base_url ?? '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
base_url,
|
base_url,
|
||||||
@ -81,14 +82,42 @@ export async function build_partials(state: BuildState) {
|
|||||||
Object.assign(state.partials, state.extras);
|
Object.assign(state.partials, state.extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mustache_context(state: BuildState, page_url: string, frontmatter?: FrontMatter) : Context {
|
export function mustache_context(state: BuildState, page_url: string, metadata: FileMetadata, frontmatter?: FrontMatter) : Context {
|
||||||
|
let event: Context['event'];
|
||||||
|
|
||||||
|
if (frontmatter?.event) {
|
||||||
|
event = Array.isArray(frontmatter.event)
|
||||||
|
? frontmatter.event.map(to_context_event)
|
||||||
|
: to_context_event(frontmatter.event);
|
||||||
|
|
||||||
|
function to_context_event(event_fm: EventFrontmatter) {
|
||||||
|
const start = from_iso(event_fm.start, event_fm.time_zone);
|
||||||
|
const end = from_iso(event_fm.end, event_fm.time_zone);
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: as_context_time(start),
|
||||||
|
end: as_context_time(end),
|
||||||
|
time_zone: event_fm.time_zone,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const has_been_updated = metadata.first_seen_time !== metadata.last_updated_time;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
env: state.env,
|
env: state.env,
|
||||||
page: frontmatter,
|
page: frontmatter,
|
||||||
base_url: state.conf.base_url,
|
base_url: state.conf.base_url ?? '/',
|
||||||
page_url: page_url,
|
page_url: page_url,
|
||||||
|
page_published: as_context_time(from_iso(metadata.first_seen_time), 'dt-published'),
|
||||||
|
page_updated: has_been_updated ? as_context_time(from_iso(metadata.last_updated_time), 'dt-updated') : null,
|
||||||
site_title: state.conf.title,
|
site_title: state.conf.title,
|
||||||
author: get_author(state, frontmatter),
|
author: get_author(state, frontmatter),
|
||||||
|
event: event,
|
||||||
|
event_series: Array.isArray(event) && {
|
||||||
|
start: event[0].start,
|
||||||
|
end: event[event.length - 1].end,
|
||||||
|
},
|
||||||
build_time: state.build_time,
|
build_time: state.build_time,
|
||||||
icons: icons,
|
icons: icons,
|
||||||
rss_feeds: state.conf.rss || [ ],
|
rss_feeds: state.conf.rss || [ ],
|
||||||
@ -109,10 +138,10 @@ export function mustache_context(state: BuildState, page_url: string, frontmatte
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function render_page(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, text: string, render_as_markdown: boolean, frontmatter?: any) {
|
export async function render_page(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, text: string, render_as_markdown: boolean, hash: string, frontmatter?: any) {
|
||||||
if (render_as_markdown) {
|
if (render_as_markdown) {
|
||||||
const opts = Object.assign({ }, state.conf.markdown, {
|
const opts = Object.assign({ }, state.conf.markdown, {
|
||||||
base_url: out_url.abs_url
|
// base_url: out_url.abs_url,
|
||||||
});
|
});
|
||||||
|
|
||||||
text = await render_markdown_to_html(text, opts);
|
text = await render_markdown_to_html(text, opts);
|
||||||
@ -129,11 +158,21 @@ export async function render_page(state: BuildState, in_file: string, out_file:
|
|||||||
layout = state.layouts[layout_file];
|
layout = state.layouts[layout_file];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hash_matches = file_hash_matches(state, in_file, hash);
|
||||||
|
const rel_in_file = in_file.slice(state.conf.input.root.length);
|
||||||
|
const old_metadata = state.old_metadata?.files?.[rel_in_file];
|
||||||
|
const new_metadata = hash_matches ? structuredClone(old_metadata) : {
|
||||||
|
first_seen_time: old_metadata?.first_seen_time || state.build_time.iso,
|
||||||
|
last_build_hash: hash,
|
||||||
|
last_updated_time: state.build_time.iso,
|
||||||
|
};
|
||||||
|
|
||||||
const tags = state.conf.templates?.tags;
|
const tags = state.conf.templates?.tags;
|
||||||
const context = mustache_context(state, out_url.abs_url, frontmatter);
|
const context = mustache_context(state, out_url.abs_url, new_metadata, frontmatter);
|
||||||
const rendered = render_template(text, context, layout, structuredClone(state.partials), tags);
|
const rendered = render_template(text, context, layout, structuredClone(state.partials), tags);
|
||||||
await write_text(out_file, rendered);
|
await write_text(out_file, rendered);
|
||||||
|
|
||||||
|
state.new_metadata.files[rel_in_file] = new_metadata;
|
||||||
handle_page_side_effects(state, in_file, out_file, out_url, text, frontmatter);
|
handle_page_side_effects(state, in_file, out_file, out_url, text, frontmatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +212,12 @@ function handle_page_side_effects(state: BuildState, in_file: string, out_file:
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handle_rss(state: BuildState, rss_conf: RSSConfig, entries: RSSEntry[], in_file: string, out_url: OutFileURL, text: string, frontmatter: FrontMatter) {
|
function handle_rss(state: BuildState, rss_conf: RSSConfig, entries: RSSEntry[], in_file: string, out_url: OutFileURL, text: string, frontmatter: FrontMatter) {
|
||||||
|
if (frontmatter?.rss?.skip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const author_or_authors = get_author(state, frontmatter);
|
const author_or_authors = get_author(state, frontmatter);
|
||||||
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
|
const authors = author_or_authors && (Array.isArray(author_or_authors) ? author_or_authors : [ author_or_authors ]);
|
||||||
|
|
||||||
entries.push({
|
entries.push({
|
||||||
url: out_url.abs_url,
|
url: out_url.abs_url,
|
||||||
@ -182,12 +225,16 @@ function handle_rss(state: BuildState, rss_conf: RSSConfig, entries: RSSEntry[],
|
|||||||
html_content: text,
|
html_content: text,
|
||||||
title: frontmatter?.title,
|
title: frontmatter?.title,
|
||||||
description: frontmatter?.description,
|
description: frontmatter?.description,
|
||||||
author_name: author?.name,
|
authors: authors,
|
||||||
tags: frontmatter?.tags,
|
tags: frontmatter?.tags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_sitemap(state: BuildState, out_url: OutFileURL, frontmatter: FrontMatter) {
|
function handle_sitemap(state: BuildState, out_url: OutFileURL, frontmatter: FrontMatter) {
|
||||||
|
if (frontmatter?.sitemap?.skip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state.sitemap.push({
|
state.sitemap.push({
|
||||||
url: out_url.abs_url,
|
url: out_url.abs_url,
|
||||||
lastmod: state.build_time.iso,
|
lastmod: state.build_time.iso,
|
||||||
@ -204,16 +251,30 @@ function handle_event(state: BuildState, in_file: string, out_url: OutFileURL, f
|
|||||||
const author_or_authors = get_author(state, frontmatter);
|
const author_or_authors = get_author(state, frontmatter);
|
||||||
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
|
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
|
||||||
|
|
||||||
|
if (Array.isArray(frontmatter.event)) {
|
||||||
|
state.event_series.push({
|
||||||
|
url: out_url.abs_url,
|
||||||
|
in_file: in_file,
|
||||||
|
title: frontmatter.title,
|
||||||
|
description: frontmatter.description,
|
||||||
|
author_name: author?.name,
|
||||||
|
author_email: author?.email,
|
||||||
|
entries: frontmatter.event.slice(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state.events.push({
|
state.events.push({
|
||||||
url: out_url.abs_url,
|
url: out_url.abs_url,
|
||||||
in_file: in_file,
|
in_file: in_file,
|
||||||
title: frontmatter.title,
|
title: frontmatter.event.title || frontmatter.title,
|
||||||
description: frontmatter.description,
|
description: frontmatter.description,
|
||||||
author_name: author?.name,
|
author_name: author?.name,
|
||||||
author_email: author?.email,
|
author_email: author?.email,
|
||||||
start_time: frontmatter.event?.start_time,
|
start: frontmatter.event.start,
|
||||||
end_time: frontmatter.event?.end_time,
|
end: frontmatter.event.end,
|
||||||
time_zone: frontmatter.event?.time_zone,
|
time_zone: frontmatter.event.time_zone,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +282,12 @@ function handle_calendar(state: BuildState, cal_conf: CalendarConfig, entries: E
|
|||||||
const author_or_authors = get_author(state, frontmatter);
|
const author_or_authors = get_author(state, frontmatter);
|
||||||
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
|
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
|
||||||
|
|
||||||
|
if (Array.isArray(frontmatter.event)) {
|
||||||
|
// todo: add each event to the calendar
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
entries.push({
|
entries.push({
|
||||||
url: out_url.abs_url,
|
url: out_url.abs_url,
|
||||||
in_file: in_file,
|
in_file: in_file,
|
||||||
@ -228,8 +295,8 @@ function handle_calendar(state: BuildState, cal_conf: CalendarConfig, entries: E
|
|||||||
description: frontmatter.description,
|
description: frontmatter.description,
|
||||||
author_name: author?.name,
|
author_name: author?.name,
|
||||||
author_email: author?.email,
|
author_email: author?.email,
|
||||||
start_time: frontmatter.event?.start_time,
|
start: frontmatter.event?.start,
|
||||||
end_time: frontmatter.event?.end_time,
|
end: frontmatter.event?.end,
|
||||||
time_zone: frontmatter.event?.time_zone,
|
time_zone: frontmatter.event?.time_zone,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -269,20 +336,6 @@ export function skip_file(state: BuildState, in_file: string, out_file: string,
|
|||||||
handle_page_side_effects(state, in_file, out_file, out_url, frontmatter);
|
handle_page_side_effects(state, in_file, out_file, out_url, frontmatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copy_metadata(state: BuildState, in_file: string) {
|
|
||||||
in_file = in_file.slice(state.conf.input.root.length);
|
|
||||||
state.new_metadata.files[in_file] = structuredClone(state.old_metadata?.files?.[in_file]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function update_metadata(state: BuildState, in_file: string, hash: string) {
|
|
||||||
in_file = in_file.slice(state.conf.input.root.length);
|
|
||||||
state.new_metadata.files[in_file] = {
|
|
||||||
first_seen_time: state.old_metadata?.files?.[in_file]?.first_seen_time || state.build_time.iso,
|
|
||||||
last_build_hash: hash,
|
|
||||||
last_updated_time: state.build_time.iso,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_author(state: BuildState, frontmatter?: FrontMatter) {
|
export function get_author(state: BuildState, frontmatter?: FrontMatter) {
|
||||||
if (! frontmatter?.author) {
|
if (! frontmatter?.author) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -2,11 +2,28 @@
|
|||||||
import { write_text } from '../fs';
|
import { write_text } from '../fs';
|
||||||
import { BuildState } from './state';
|
import { BuildState } from './state';
|
||||||
import { map_input_file_to_output_file } from './helpers';
|
import { map_input_file_to_output_file } from './helpers';
|
||||||
import { parseICS, CalendarComponent } from 'ical';
|
// import { parseICS, CalendarComponent } from 'ical';
|
||||||
import create_calendar, { ICalEventData, ICalCalendarData } from 'ical-generator';
|
import create_calendar, { ICalEventData, ICalCalendarData } from 'ical-generator';
|
||||||
|
import { FrontMatterLocation } from '../template';
|
||||||
|
|
||||||
export type { ICalEventData, ICalCalendarData, ICalAttendeeData, ICalAttendeeStatus } from 'ical-generator';
|
export type { ICalEventData, ICalCalendarData, ICalAttendeeData, ICalAttendeeStatus } from 'ical-generator';
|
||||||
|
|
||||||
|
export interface EventSeries {
|
||||||
|
url: string;
|
||||||
|
in_file: string;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
author_name?: string;
|
||||||
|
author_email?: string;
|
||||||
|
entries: {
|
||||||
|
title?: string;
|
||||||
|
start?: string;
|
||||||
|
end?: string;
|
||||||
|
time_zone?: `${string}/${string}`;
|
||||||
|
location?: FrontMatterLocation;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface EventEntry {
|
export interface EventEntry {
|
||||||
url: string;
|
url: string;
|
||||||
in_file: string;
|
in_file: string;
|
||||||
@ -14,9 +31,10 @@ export interface EventEntry {
|
|||||||
description?: string;
|
description?: string;
|
||||||
author_name?: string;
|
author_name?: string;
|
||||||
author_email?: string;
|
author_email?: string;
|
||||||
start_time?: string;
|
start?: string;
|
||||||
end_time?: string;
|
end?: string;
|
||||||
time_zone?: `${string}/${string}`;
|
time_zone?: `${string}/${string}`;
|
||||||
|
location?: FrontMatterLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function write_events_and_calendars_if_needed(state: BuildState) {
|
export async function write_events_and_calendars_if_needed(state: BuildState) {
|
||||||
@ -33,7 +51,40 @@ export async function write_events_and_calendars_if_needed(state: BuildState) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const calendar = create_icalendar(cal_data, event);
|
const calendar = create_icalendar(cal_data, event);
|
||||||
const out_file = await map_input_file_to_output_file(state, entry.in_file, [ '.html', '.md', '.markdown' ], '.isc');
|
const out_file = await map_input_file_to_output_file(state, entry.in_file, [ '.html', '.md', '.markdown' ], '.ics');
|
||||||
|
await write_text(out_file, calendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const series of state.event_series) {
|
||||||
|
const events = series.entries.map((event) => {
|
||||||
|
return icalendar_event(state, {
|
||||||
|
url: series.url,
|
||||||
|
in_file: series.in_file,
|
||||||
|
title: event.title || series.title,
|
||||||
|
description: series.description,
|
||||||
|
author_name: series.author_name,
|
||||||
|
author_email: series.author_email,
|
||||||
|
start: event.start,
|
||||||
|
end: event.end,
|
||||||
|
time_zone: event.time_zone,
|
||||||
|
location: event.location,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const cal_data: ICalCalendarData = {
|
||||||
|
name: series.title,
|
||||||
|
description: series.description,
|
||||||
|
url: series.url,
|
||||||
|
prodId: {
|
||||||
|
company: 'jbrumond.me',
|
||||||
|
product: 'docs2website',
|
||||||
|
language: 'EN',
|
||||||
|
},
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
const calendar = create_icalendar(cal_data, events);
|
||||||
|
const out_file = await map_input_file_to_output_file(state, series.in_file, [ '.html', '.md', '.markdown' ], '.ics');
|
||||||
await write_text(out_file, calendar);
|
await write_text(out_file, calendar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,16 +113,16 @@ export async function write_events_and_calendars_if_needed(state: BuildState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parse_icalendar(contents: string) {
|
// export function parse_icalendar(contents: string) {
|
||||||
const parsed = parseICS(contents);
|
// const parsed = parseICS(contents);
|
||||||
const calendar: CalendarComponent[] = [ ];
|
// const calendar: CalendarComponent[] = [ ];
|
||||||
|
|
||||||
for (const data of Object.values(parsed)) {
|
// for (const data of Object.values(parsed)) {
|
||||||
calendar.push(data);
|
// calendar.push(data);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return calendar;
|
// return calendar;
|
||||||
}
|
// }
|
||||||
|
|
||||||
export function create_icalendar(cal: ICalCalendarData, events: ICalEventData | ICalEventData[]) {
|
export function create_icalendar(cal: ICalCalendarData, events: ICalEventData | ICalEventData[]) {
|
||||||
const calendar = create_calendar(cal);
|
const calendar = create_calendar(cal);
|
||||||
@ -97,8 +148,8 @@ export function icalendar_event(state: BuildState, entry: EventEntry) : ICalEven
|
|||||||
id: entry.url,
|
id: entry.url,
|
||||||
summary: entry.title,
|
summary: entry.title,
|
||||||
description: entry.description || void 0,
|
description: entry.description || void 0,
|
||||||
start: entry.start_time,
|
start: entry.start,
|
||||||
end: entry.end_time,
|
end: entry.end,
|
||||||
url: entry.url,
|
url: entry.url,
|
||||||
timezone: entry.time_zone,
|
timezone: entry.time_zone,
|
||||||
created: metadata.first_seen_time,
|
created: metadata.first_seen_time,
|
||||||
@ -107,6 +158,7 @@ export function icalendar_event(state: BuildState, entry: EventEntry) : ICalEven
|
|||||||
name: entry.author_name || 'Unknown',
|
name: entry.author_name || 'Unknown',
|
||||||
email: entry.author_email,
|
email: entry.author_email,
|
||||||
},
|
},
|
||||||
|
location: format_location(entry.location),
|
||||||
// attendees: post.mentions.flatMap((mention) : ICalAttendeeData | ICalAttendeeData[] => {
|
// attendees: post.mentions.flatMap((mention) : ICalAttendeeData | ICalAttendeeData[] => {
|
||||||
// if (mention.is_rsvp && mention.is_reply_to_this) {
|
// if (mention.is_rsvp && mention.is_reply_to_this) {
|
||||||
// const ext = mention.external as ExternalEntry;
|
// const ext = mention.external as ExternalEntry;
|
||||||
@ -129,3 +181,29 @@ export function icalendar_event(state: BuildState, entry: EventEntry) : ICalEven
|
|||||||
// }),
|
// }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function format_location(loc: FrontMatterLocation) {
|
||||||
|
if (! loc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.description) {
|
||||||
|
if (loc.address) {
|
||||||
|
return `${loc.description} (${loc.address})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.lat && loc.long) {
|
||||||
|
return `${loc.description} [${loc.lat}, ${loc.long}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loc.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.address) {
|
||||||
|
return loc.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.lat && loc.long) {
|
||||||
|
return `[${loc.lat}, ${loc.long}]`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { render_json_schema_files } from './jsonschema';
|
|||||||
import { write_sitemap_if_needed } from './sitemap';
|
import { write_sitemap_if_needed } from './sitemap';
|
||||||
import { write_rss_if_needed } from './rss';
|
import { write_rss_if_needed } from './rss';
|
||||||
import { write_events_and_calendars_if_needed } from './icalendar';
|
import { write_events_and_calendars_if_needed } from './icalendar';
|
||||||
|
import { as_context_time, as_html_time } from '../time';
|
||||||
|
|
||||||
export { BuildState, ThemeGroups } from './state';
|
export { BuildState, ThemeGroups } from './state';
|
||||||
|
|
||||||
@ -60,11 +61,9 @@ export async function build_docs_project(conf: Config) {
|
|||||||
rss: [ ],
|
rss: [ ],
|
||||||
sitemap: [ ],
|
sitemap: [ ],
|
||||||
events: [ ],
|
events: [ ],
|
||||||
|
event_series: [ ],
|
||||||
calendars: [ ],
|
calendars: [ ],
|
||||||
build_time: {
|
build_time: as_context_time(now),
|
||||||
iso: now.toISO(),
|
|
||||||
rfc2822: now.toRFC2822(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const theme of Object.values(themes)) {
|
for (const theme of Object.values(themes)) {
|
||||||
|
@ -4,7 +4,7 @@ import { BuildState } from './state';
|
|||||||
import { read_json, write_text, read_yaml } from '../fs';
|
import { read_json, write_text, read_yaml } from '../fs';
|
||||||
import { build_markdown_from_json_schema } from '@doc-utils/jsonschema2markdown';
|
import { build_markdown_from_json_schema } from '@doc-utils/jsonschema2markdown';
|
||||||
import { stringify as to_yaml } from 'yaml';
|
import { stringify as to_yaml } from 'yaml';
|
||||||
import { build_partials, copy_metadata, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers';
|
import { build_partials, map_input_file_to_output_file, map_output_file_to_url, render_page } from './helpers';
|
||||||
|
|
||||||
export async function render_json_schema_files(state: BuildState) {
|
export async function render_json_schema_files(state: BuildState) {
|
||||||
const promises: Promise<any>[] = [ ];
|
const promises: Promise<any>[] = [ ];
|
||||||
@ -124,14 +124,8 @@ export async function render_json_schema(state: BuildState, schema: unknown, in_
|
|||||||
}
|
}
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
render_page(state, in_file, out_file, out_url, markdown, true, frontmatter)
|
render_page(state, in_file, out_file, out_url, markdown, true, hash, frontmatter)
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
if (file_hash_matches(state, in_file, hash)) {
|
|
||||||
return copy_metadata(state, in_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_metadata(state, in_file, hash);
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { glob } from 'glob';
|
import { glob } from 'glob';
|
||||||
import { read_text } from '../fs';
|
import { read_text } from '../fs';
|
||||||
import { BuildState } from './state';
|
import { BuildState } from './state';
|
||||||
import { build_partials, copy_metadata, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers';
|
import { build_partials, map_input_file_to_output_file, map_output_file_to_url, render_page } from './helpers';
|
||||||
|
|
||||||
export async function render_markdown_files(state: BuildState) {
|
export async function render_markdown_files(state: BuildState) {
|
||||||
const promises: Promise<any>[] = [ ];
|
const promises: Promise<any>[] = [ ];
|
||||||
@ -34,16 +34,10 @@ export async function render_markdown_file(state: BuildState, in_file: string) {
|
|||||||
const out_file = await map_input_file_to_output_file(state, in_file, [ '.md', '.markdown' ], '.html');
|
const out_file = await map_input_file_to_output_file(state, in_file, [ '.md', '.markdown' ], '.html');
|
||||||
const out_url = map_output_file_to_url(state, out_file);
|
const out_url = map_output_file_to_url(state, out_file);
|
||||||
const { frontmatter, text, hash } = await read_text(in_file);
|
const { frontmatter, text, hash } = await read_text(in_file);
|
||||||
|
|
||||||
if (frontmatter?.skip) {
|
if (frontmatter?.skip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await render_page(state, in_file, out_file, out_url, text, true, frontmatter);
|
await render_page(state, in_file, out_file, out_url, text, true, hash, frontmatter);
|
||||||
|
|
||||||
if (file_hash_matches(state, in_file, hash)) {
|
|
||||||
return copy_metadata(state, in_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_metadata(state, in_file, hash);
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { glob } from 'glob';
|
import { glob } from 'glob';
|
||||||
import { read_text } from '../fs';
|
import { read_text } from '../fs';
|
||||||
import { BuildState } from './state';
|
import { BuildState } from './state';
|
||||||
import { build_partials, copy_metadata, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers';
|
import { build_partials, map_input_file_to_output_file, map_output_file_to_url, render_page } from './helpers';
|
||||||
|
|
||||||
export async function render_text_file_templates(state: BuildState) {
|
export async function render_text_file_templates(state: BuildState) {
|
||||||
const promises: Promise<any>[] = [ ];
|
const promises: Promise<any>[] = [ ];
|
||||||
@ -39,11 +39,5 @@ export async function render_text_file_template(state: BuildState, in_file: stri
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await render_page(state, in_file, out_file, out_url, text, false, frontmatter);
|
await render_page(state, in_file, out_file, out_url, text, false, hash, frontmatter);
|
||||||
|
|
||||||
if (file_hash_matches(state, in_file, hash)) {
|
|
||||||
return copy_metadata(state, in_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_metadata(state, in_file, hash);
|
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,17 @@ import type { XMLBuilder } from 'xmlbuilder2/lib/interfaces';
|
|||||||
import { create as create_xml } from 'xmlbuilder2';
|
import { create as create_xml } from 'xmlbuilder2';
|
||||||
import { BuildState } from './state';
|
import { BuildState } from './state';
|
||||||
import { write_text } from '../fs';
|
import { write_text } from '../fs';
|
||||||
import { app_version } from '../conf';
|
import { AuthorConfig, app_version } from '../conf';
|
||||||
import { map_output_file_to_url } from './helpers';
|
import { map_output_file_to_url } from './helpers';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { FileMetadata } from '../metadata';
|
||||||
|
|
||||||
export interface RSSEntry {
|
export interface RSSEntry {
|
||||||
url: string;
|
url: string;
|
||||||
in_file: string;
|
in_file: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
author_name?: string;
|
authors?: AuthorConfig[];
|
||||||
html_content?: string;
|
html_content?: string;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
}
|
}
|
||||||
@ -84,11 +85,21 @@ export async function write_rss_if_needed(state: BuildState) {
|
|||||||
// channel.ele('image').txt('');
|
// channel.ele('image').txt('');
|
||||||
// channel.ele('skipHours').txt('');
|
// channel.ele('skipHours').txt('');
|
||||||
// channel.ele('skipDays').txt('');
|
// channel.ele('skipDays').txt('');
|
||||||
|
|
||||||
|
let entries: (RSSEntry & { metadata: FileMetadata, first_seen: number })[] = [ ];
|
||||||
|
|
||||||
for (const entry of state.rss[index] || [ ]) {
|
for (const entry of state.rss[index] || [ ]) {
|
||||||
const item = channel.ele('item');
|
|
||||||
const in_file = entry.in_file.slice(state.conf.input.root.length);
|
const in_file = entry.in_file.slice(state.conf.input.root.length);
|
||||||
const metadata = state.new_metadata.files[in_file];
|
const metadata = state.new_metadata.files[in_file];
|
||||||
|
entries.push({ ...entry, metadata, first_seen: DateTime.fromISO(metadata.first_seen_time).toUnixInteger() });
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = entries.sort((a, b) => {
|
||||||
|
return b.first_seen - a.first_seen;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const item = channel.ele('item');
|
||||||
|
|
||||||
item.ele('link').txt(entry.url);
|
item.ele('link').txt(entry.url);
|
||||||
|
|
||||||
@ -100,8 +111,10 @@ export async function write_rss_if_needed(state: BuildState) {
|
|||||||
item.ele('description').txt(entry.description);
|
item.ele('description').txt(entry.description);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.author_name) {
|
if (entry.authors) {
|
||||||
item.ele('dc:creator').ele({ $: entry.author_name });
|
for (const author of entry.authors) {
|
||||||
|
item.ele('dc:creator').ele({ $: author.name || author.url || author.email });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.tags) {
|
if (entry.tags) {
|
||||||
@ -109,7 +122,7 @@ export async function write_rss_if_needed(state: BuildState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.ele('guid').txt(entry.url);
|
item.ele('guid').txt(entry.url);
|
||||||
item.ele('pubDate').txt(DateTime.fromISO(metadata.first_seen_time).toRFC2822());
|
item.ele('pubDate').txt(DateTime.fromISO(entry.metadata.first_seen_time).toRFC2822());
|
||||||
|
|
||||||
if (entry.html_content) {
|
if (entry.html_content) {
|
||||||
item.ele('content:encoded').ele({ $: entry.html_content });
|
item.ele('content:encoded').ele({ $: entry.html_content });
|
||||||
|
@ -4,7 +4,8 @@ import type { Metadata } from '../metadata';
|
|||||||
import type { ColorTheme } from '@doc-utils/color-themes';
|
import type { ColorTheme } from '@doc-utils/color-themes';
|
||||||
import type { SitemapEntry } from './sitemap';
|
import type { SitemapEntry } from './sitemap';
|
||||||
import { RSSEntry } from './rss';
|
import { RSSEntry } from './rss';
|
||||||
import { EventEntry } from './icalendar';
|
import { EventEntry, EventSeries } from './icalendar';
|
||||||
|
import { ContextTime } from '../template';
|
||||||
|
|
||||||
export interface BuildState {
|
export interface BuildState {
|
||||||
conf: Config;
|
conf: Config;
|
||||||
@ -21,11 +22,9 @@ export interface BuildState {
|
|||||||
rss: RSSEntry[][];
|
rss: RSSEntry[][];
|
||||||
sitemap: SitemapEntry[];
|
sitemap: SitemapEntry[];
|
||||||
events: EventEntry[];
|
events: EventEntry[];
|
||||||
|
event_series: EventSeries[];
|
||||||
calendars: EventEntry[][];
|
calendars: EventEntry[][];
|
||||||
build_time: {
|
build_time: ContextTime;
|
||||||
iso: string;
|
|
||||||
rfc2822: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ThemeGroups {
|
export interface ThemeGroups {
|
||||||
|
@ -14,22 +14,48 @@ export interface Context {
|
|||||||
page?: FrontMatter;
|
page?: FrontMatter;
|
||||||
base_url: string;
|
base_url: string;
|
||||||
page_url: string;
|
page_url: string;
|
||||||
|
page_published: ContextTime;
|
||||||
|
page_updated: ContextTime;
|
||||||
site_title: string;
|
site_title: string;
|
||||||
author: AuthorConfig | AuthorConfig[];
|
author: AuthorConfig | AuthorConfig[];
|
||||||
|
event?: ContextEvent | ContextEvent[];
|
||||||
|
event_series?: {
|
||||||
|
start?: ContextTime;
|
||||||
|
end?: ContextTime;
|
||||||
|
};
|
||||||
icons: Record<string, string>;
|
icons: Record<string, string>;
|
||||||
themes: ColorTheme[];
|
themes: ColorTheme[];
|
||||||
theme_groups: ThemeGroups;
|
theme_groups: ThemeGroups;
|
||||||
rss_feeds: RSSConfig[];
|
rss_feeds: RSSConfig[];
|
||||||
calendars: CalendarConfig[];
|
calendars: CalendarConfig[];
|
||||||
build_time: {
|
build_time: ContextTime;
|
||||||
iso: string;
|
|
||||||
rfc2822: string;
|
|
||||||
};
|
|
||||||
markdown: {
|
markdown: {
|
||||||
render_inline(): MustacheRenderer;
|
render_inline(): MustacheRenderer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ContextTime {
|
||||||
|
iso: string;
|
||||||
|
rfc2822: string;
|
||||||
|
html: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContextEvent {
|
||||||
|
title?: string;
|
||||||
|
start?: ContextTime;
|
||||||
|
end?: ContextTime;
|
||||||
|
time_zone?: `${string}/${string}`;
|
||||||
|
location?: ContextLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContextLocation {
|
||||||
|
description?: string;
|
||||||
|
lat?: string;
|
||||||
|
long?: string;
|
||||||
|
// todo: represent this better?
|
||||||
|
address?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FrontMatter {
|
export interface FrontMatter {
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
layout?: string;
|
layout?: string;
|
||||||
@ -39,23 +65,34 @@ export interface FrontMatter {
|
|||||||
author?: string | string[];
|
author?: string | string[];
|
||||||
rss?: RSSFrontmatter;
|
rss?: RSSFrontmatter;
|
||||||
sitemap?: SitemapFrontmatter;
|
sitemap?: SitemapFrontmatter;
|
||||||
event?: EventFrontmatter;
|
event?: EventFrontmatter | EventFrontmatter[];
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SitemapFrontmatter {
|
export interface SitemapFrontmatter {
|
||||||
|
skip?: boolean;
|
||||||
change_freq?: ChangeFreq;
|
change_freq?: ChangeFreq;
|
||||||
priority?: number;
|
priority?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventFrontmatter {
|
export interface EventFrontmatter {
|
||||||
start_time?: string;
|
title?: string;
|
||||||
end_time?: string;
|
start?: string;
|
||||||
|
end?: string;
|
||||||
time_zone?: `${string}/${string}`;
|
time_zone?: `${string}/${string}`;
|
||||||
|
location?: FrontMatterLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RSSFrontmatter {
|
export interface FrontMatterLocation {
|
||||||
//
|
description?: string;
|
||||||
|
lat?: string;
|
||||||
|
long?: string;
|
||||||
|
// todo: represent this better?
|
||||||
|
address?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RSSFrontmatter {
|
||||||
|
skip?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function render_template(template: string, context: Context, layout?: string, partials: Record<string, string> = { }, tags?: [ string, string ]) {
|
export function render_template(template: string, context: Context, layout?: string, partials: Record<string, string> = { }, tags?: [ string, string ]) {
|
||||||
@ -67,6 +104,7 @@ export async function load_extras() {
|
|||||||
const extras: Record<string, string> = Object.create(null);
|
const extras: Record<string, string> = Object.create(null);
|
||||||
const extras_dir = resolve_path(__dirname, '../extras');
|
const extras_dir = resolve_path(__dirname, '../extras');
|
||||||
const extras_files = [
|
const extras_files = [
|
||||||
|
'svg-links.js',
|
||||||
'components/color-scheme-toggle-button.js',
|
'components/color-scheme-toggle-button.js',
|
||||||
'components/outline-button.js',
|
'components/outline-button.js',
|
||||||
'components/outline-inline.js',
|
'components/outline-inline.js',
|
||||||
@ -78,6 +116,7 @@ export async function load_extras() {
|
|||||||
'forms-inputs/spacious.css',
|
'forms-inputs/spacious.css',
|
||||||
'forms-inputs/compact.css',
|
'forms-inputs/compact.css',
|
||||||
'forms-inputs/general.css',
|
'forms-inputs/general.css',
|
||||||
|
'figures.css',
|
||||||
];
|
];
|
||||||
|
|
||||||
const promises = extras_files.map((file) => load_from_dir(extras_dir, file));
|
const promises = extras_files.map((file) => load_from_dir(extras_dir, file));
|
||||||
|
52
src/time.ts
52
src/time.ts
@ -13,42 +13,20 @@ export function from_iso(time: string, zone?: string) {
|
|||||||
: DateTime.fromISO(time);
|
: DateTime.fromISO(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function date_formatters(lang: string, time_zone: string) {
|
export function as_html_time(time: DateTime, classname = '', lang?: string, config?: Intl.DateTimeFormatOptions) {
|
||||||
// return {
|
if (lang && config) {
|
||||||
// date() {
|
const formatter = new Intl.DateTimeFormat(lang, config);
|
||||||
// return (text, render) => {
|
const formatted = formatter.format(new Date(time.toISO()));
|
||||||
// return format_with_config(render(text), {
|
return `<time datetime="${time.toISO()}">${formatted}</time>`;
|
||||||
// dateStyle: 'short',
|
}
|
||||||
// timeZone: time_zone,
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// time() {
|
|
||||||
// return (text, render) => {
|
|
||||||
// return format_with_config(render(text), {
|
|
||||||
// timeStyle: 'long',
|
|
||||||
// timeZone: time_zone,
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// datetime() {
|
|
||||||
// return (text, render) => {
|
|
||||||
// return format_with_config(render(text), {
|
|
||||||
// dateStyle: 'short',
|
|
||||||
// timeStyle: 'long',
|
|
||||||
// timeZone: time_zone,
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// function format_with_config(text: string, config: Intl.DateTimeFormatOptions) {
|
|
||||||
// const date = new Date(text.trim());
|
|
||||||
// const formatter = new Intl.DateTimeFormat(lang, config);
|
|
||||||
// return formatter.format(date);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return `<time class="${classname}" datetime="${time.toISO()}">${time.toRFC2822()}</time>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function as_context_time(time: DateTime, classname = '', lang?: string, config?: Intl.DateTimeFormatOptions) {
|
||||||
|
return {
|
||||||
|
iso: time.toISO(),
|
||||||
|
rfc2822: time.toRFC2822(),
|
||||||
|
html: as_html_time(time, classname, lang, config),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user