Tucker McKnight <tucker.mcknight@gmail.com> | Tue Sep 02 2025
Add patch.njk page Fix some references to static files that weren't using the reposPath variable. Put main.css in the vendors folder, which should be renamed at some point, but for now it gets main.css into a dist/ location that can be used by the virutal templates. Change the way the light/dark theme switcher works; just replace "prism.css" or "prism_dark.css" instead of replacing the full path. This avoids having to know the reposPath variable in a JS file.
31 32 33 34 35 36 37
}
const link: any = document.getElementById("prism-theme")
const preferred = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
const stylesheet = window['currentTheme'] === 'dark' || (window['currentTheme'] === 'auto' && preferred === 'dark') ? "/vendor/prism_dark.css" : "/vendor/prism.css"
link.href = stylesheet
}
window['toggleDarkMode'] = (button) => {
const clickedOption = button.dataset.themePref31 32 33 34 35 36 37
}
const link: any = document.getElementById("prism-theme")
const preferred = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
const stylesheet = window['currentTheme'] === 'dark' || (window['currentTheme'] === 'auto' && preferred === 'dark') ? "prism_dark.css" : "prism.css"
link.href = link.href.split("/").slice(0, -1).concat(stylesheet).join("/")
}
window['toggleDarkMode'] = (button) => {
const clickedOption = button.dataset.themePref3 4 5 6 7
import repos from './src/repos.ts'
import branches from './src/branches.ts'
import flatFiles from './src/flatFiles.ts'
import paginatedPatches from './src/paginatedPatches.ts'
import {getLocation} from './src/helpers.ts'
import repoOperations from './src/vcses/operations.ts'3 4 5 6 7 8
import repos from './src/repos.ts'
import branches from './src/branches.ts'
import flatFiles from './src/flatFiles.ts'
import flatPatches from './src/flatPatches.ts'
import paginatedPatches from './src/paginatedPatches.ts'
import {getLocation} from './src/helpers.ts'
import repoOperations from './src/vcses/operations.ts'33 34 35 36 37 38 39
const frontend = `${__dirname}/frontend`
eleventyConfig.addPassthroughCopy({
[vendor]: "vendor",
[frontend]: "frontend"
})
eleventyConfig.on(33 34 35 36 37 38 39
const frontend = `${__dirname}/frontend`
eleventyConfig.addPassthroughCopy({
[vendor]: `${reposPath}/vendor`,
[frontend]: `${reposPath}/frontend`,
})
eleventyConfig.on(176 177 178 179 180 181 182 183 184
return res.stdout
})
eleventyConfig.addAsyncFilter("getReadMe", async (repoName, branchName) => {
console.log("getreadme")
console.log(branchName)
console.log(repoName)
const location = getLocation(reposConfiguration, branchName, repoName)
const config = reposConfiguration.repos[repoName]
let command = ''176 177 178 179 180 181 182 183 184 185 186 187 188 189
return res.stdout
})
eleventyConfig.addFilter("pagesJustForBranch", (pages, repoName, branchName) => {
return pages.filter(page => page.repoName === repoName && page.branchName === branchName)
})
eleventyConfig.addFilter("date", (dateString) => {
return new Date(dateString).toDateString()
})
eleventyConfig.addAsyncFilter("getReadMe", async (repoName, branchName) => {
const location = getLocation(reposConfiguration, branchName, repoName)
const config = reposConfiguration.repos[repoName]
let command = ''303 304 305 306 307 308
size: 1,
alias: "branch",
},
branches: branchesData,
permalink: (data) => {
const repoName = data.branch.repoName
const branchName = data.branch.branchName303 304 305 306 307
size: 1,
alias: "branch",
},
permalink: (data) => {
const repoName = data.branch.repoName
const branchName = data.branch.branchName324 325 326 327 328 329
const paginatedPatchesData = await paginatedPatches(reposData)
eleventyConfig.addTemplate(
`repos/patches.njk`,
topLayoutPartial + repoTemplate + bottomLayoutPartial,
{
pagination: {
data: "paginatedPatches",324 325 326 327 328 329
const paginatedPatchesData = await paginatedPatches(reposData)
eleventyConfig.addTemplate(
`repos/patches.njk`,
topLayoutPartial + patchesTemplate + bottomLayoutPartial,
{
pagination: {
data: "paginatedPatches",335 336 337 338 339 340
permalink: (data) => {
const repoName = data.patchPage.repoName
const branchName = data.patchPage.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches/page${data.patchPage.pageNumber}`
},
eleventyComputed: {
nav: {335 336 337 338 339 340
permalink: (data) => {
const repoName = data.patchPage.repoName
const branchName = data.patchPage.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches/page${data.patchPage.pageNumber}/`
},
eleventyComputed: {
nav: {346 347 348
navTab: "patches",
}
)
}346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
navTab: "patches",
}
)
// PATCH.NJK
const patchTemplate = fsImport.readFileSync(`${__dirname}/templates/patch.njk`).toString()
const flatPatchesData = await flatPatches(reposConfiguration)
eleventyConfig.addTemplate(
`repos/patch.njk`,
topLayoutPartial + patchTemplate + bottomLayoutPartial,
{
pagination: {
data: "flatPatches",
size: 1,
alias: "patchInfo",
},
flatPatches: flatPatchesData,
permalink: (data) => {
const repoName = data.patchInfo.repoName
const branchName = data.patchInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches/${data.patchInfo.patch.hash}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchInfo.repoName,
branchName: (data) => data.patchInfo.branchName,
}
},
width: "full",
navTab: "patches",
}
)
}20 21 22 23 24 25 26 27
const selectBranch = (e) => {
const values = e.value.split(",")
window.location = `/repos/${values[0]}/branches/${values[1]}/${values[2]}`
}
</script>
<script src="/frontend/main.js"></script>
</body>
</html>20 21 22 23 24 25 26 27
const selectBranch = (e) => {
const values = e.value.split(",")
window.location = `{{reposPath}}/${values[0]}/branches/${values[1]}/${values[2]}`
}
</script>
<script src="{{reposPath}}/frontend/main.js"></script>
</body>
</html>28 29 30 31 32 33 34 35 36 37 38
{% endif %}
</script>
<script src="/frontend/top.js"></script>
<link rel="stylesheet" id="prism-theme" type="text/css" href="/vendor/prism.css" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script>
<link rel="stylesheet" type="text/css" href="/static/main.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
28 29 30 31 32 33 34 35 36 37 38
{% endif %}
</script>
<script src="{{reposPath}}/frontend/top.js"></script>
<link rel="stylesheet" id="prism-theme" type="text/css" href="{{reposPath}}/vendor/prism.css" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script>
<link rel="stylesheet" type="text/css" href="{{reposPath}}/vendor/main.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
136 137 138 139 140 141
}
const getLocation = (reposConfig: any, branchName: string, repoName: string): string => {
console.log(repoName)
const config = reposConfig.repos[repoName]
if (config._type === "darcs") {
return config.branches[branchName].location136 137 138 139 140
}
const getLocation = (reposConfig: any, branchName: string, repoName: string): string => {
const config = reposConfig.repos[repoName]
if (config._type === "darcs") {
return config.branches[branchName].location
-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
<div class="container-lg">
<div class="row">
<div class="col-auto">
<h1>{{patchInfo.patch.name}}</h2>
<p>{{patchInfo.patch.date | date }}</p>
<p>{{patchInfo.patch.author }}</p>
<pre>{{patchInfo.patch.description}}</pre>
</div>
</div>
<div class="row">
<div class="col-auto">
<p class="font-monospace fw-bold text-secondary">{{patchInfo.patch.hash}}</p>
{% if reposConfig.repos[patchInfo.repoName]._type == "darcs" %}
<div class="input-group mb-3 d-flex flex-nowrap">
<span id="clone-command" class="clone input-group-text overflow-hidden">
{% set url = [reposConfig.baseUrl, "/repos/", patchInfo.repoName | slugify, "/branches/", patchInfo.branchName | slugify] | join | url %}
darcs pull {{ url }} -h {{patchInfo.patch.hash}}
</span>
<button class="btn btn-primary" id="clone-button" onclick="copyCommand()">Copy</button>
</div>
{% endif %}
</div>
</div>
</div>
<div class="container-fluid border-top">
<div class="row my-2">
<div class="col-auto d-flex align-items-center fs-4">
<i class="bi bi-layout-split"></i><span class="fs-6 mx-1">Side-by-side</span>
<div class="form-check form-switch mb-1 px-2">
<input class="form-check-input mx-0" type="checkbox" id="unifiedModeSwitch" value="unified" onchange="toggleUnifiedMode(this)" aria-label="Toggle switch for stacked or side by side diff view">
</div>
<i class="bi bi-hr"></i><span class="fs-6 mx-1">Stacked</span>
</div>
</div>
<div class="row" id="diffs">
{% set patchHunks = patchInfo.patch.diffs %}
{% for hunk in patchHunks %}
<div class=hunk>
<span class="font-monospace fw-bold"><a href="/repos/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/files/{{ hunk.file | slugify}}.html">{{ hunk.file }}:{{ hunk.lineNumber }}</a></span>
<div class="diff d-flex">
<div class="flex-grow-1 diff-left pe-2">
<span class='font-monospace text-secondary'>Before</span>
<div class="row">
<div class="col-auto border-end">
<code>
{%- for lineNumber in hunk.previousText | lineNumbers -%}
{{ lineNumber + hunk.lineNumber - 1}}
{% endfor -%}
</code>
</div>
<div class="col overflow-scroll">
<pre data-start="{{hunk.lineNumber}}"><code data-type="before" class="line-numbers language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{hunk.previousText | safe}}</code></pre>
</div>
</div>
</div>
<div class="diff-right flex-grow-1 ps-2 border-start">
<span class='font-monospace text-secondary'>After</span>
<div class="row">
<div class="col-auto border-end">
<code>
{%- for lineNumber in hunk.afterText | lineNumbers -%}
{{ lineNumber + hunk.lineNumber - 1}}
{% endfor -%}
</code>
</div>
<div class="col overflow-scroll">
<pre data-start="{{hunk.lineNumber}}"><code data-type="after" class="line-numbers language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{ hunk.afterText | safe}}</code></pre>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<script>
const copyCommand = () => {
const text = document.getElementById("clone-command").innerText
const button = document.getElementById("clone-button")
navigator.clipboard.writeText(text).then(() => {
button.innerText = "Copied"
})
}
</script>11 12 13 14 15 16
{% for patch in patchPage.patches %}
<li class="patch">
<div>
<span class="patch-name"><a href="/repos/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName | slugify}}/patches/{{patch.hash}}">{{patch.name}}</a></span>
<br />
<span>{{patch.date | date}}</span>
<br />11 12 13 14 15 16
{% for patch in patchPage.patches %}
<li class="patch">
<div>
<span class="patch-name"><a href="{{reposPath}}/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName | slugify}}/patches/{{patch.hash}}">{{patch.name}}</a></span>
<br />
<span>{{patch.date | date}}</span>
<br />26 27 28 29 30 31
<ul class="pagination">
{% for pageObj in (paginatedPatches | pagesJustForBranch(patchPage.repoName, patchPage.branchName)) %}
<li class="page-item">
<a class="page-link {% if pageObj.pageNumber == patchPage.pageNumber %}active{% endif %}" href="/repos/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName}}/patches/page{{pageObj.pageNumber}}">{{ pageObj.pageNumber }}</a>
</li>
{% endfor %}
</ul>26 27 28 29 30 31
<ul class="pagination">
{% for pageObj in (paginatedPatches | pagesJustForBranch(patchPage.repoName, patchPage.branchName)) %}
<li class="page-item">
<a class="page-link {% if pageObj.pageNumber == patchPage.pageNumber %}active{% endif %}" href="{{reposPath}}/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName}}/patches/page{{pageObj.pageNumber}}">{{ pageObj.pageNumber }}</a>
</li>
{% endfor %}
</ul>
-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
pre {
font-size: 14px !important;
}
code {
white-space: pre;
}
blockquote {
margin-left: 0.25rem;
padding-left: 0.5rem;
border-left: 3px solid royalblue;
background: rgba(125, 125, 125, 0.07);
overflow: hidden;
}
blockquote p {
margin: 0.5rem;
}
.hunk {
box-sizing: border-box;
margin-bottom: 25px;
}
[data-type=before] mark {
background-color: rgba(252, 78, 78, .15);
}
[data-type=after] mark {
background-color: rgba(151, 247, 203, .4);
}
[data-bs-theme=dark] [data-type=after] mark {
background-color: rgba(51, 247, 160, .1); /* different green for dark mode; looks better */
}
.unified .diff {
flex-wrap: wrap;
}
.diff-left, .diff-right {
flex: 1;
overflow: hidden;
}
.unified .diff-left, .unified .diff-right {
flex-basis: 100%;
}
.patch {
margin: 12px 0;
}
.patch-name {
font-size: 18px;
font-weight: bold;
}
.patch-hash {
font-size: 14px;
margin: 6px 0;
}
.clone {
font-family: monospace;
font-size: 12px;
}
.popover {
max-width: 100%;
}