Tucker McKnight <tucker@pangolin.lan> | Sun Dec 21 2025
Make themes work, some color tweaks, make directory nav links work on file page
0 1 2
import {branchesListItems} from '../dist/js_templates/common/htmlPage.js'
const setCheckbox = window.setCheckbox
const currentTheme = window.currentTheme0 1 2
import branchesListItems from '../dist/js_templates/common/branchesListItems.js'
const setCheckbox = window.setCheckbox
const currentTheme = window.currentTheme15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
window['currentTheme'] = mode
};
// window['currentTheme'] = localStorage.getItem("theme") || "auto"
window['currentTheme'] = "light"
window['setMode'](window['currentTheme'])
window['setCheckbox'] = (mode, element) => {
if (element === null) { return }
if (mode === 'light') {
element.innerHTML = `<i class="bi bi-brightness-high"></i>`
}
if (mode === 'dark') {
element.innerHTML = `<i class="bi bi-moon"></i>`
}
if (mode === 'auto') {
element.innerHTML = `<i class="bi bi-yin-yang"></i>`
}
const link = document.getElementById("prism-theme")
const preferred = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
window['currentTheme'] = mode
};
window['currentTheme'] = localStorage.getItem("theme") || "auto"
window['setMode'](window['currentTheme'])
window['setCheckbox'] = (mode, element) => {
if (element === null) { return }
if (mode === 'light') {
element.innerHTML = `<span>💡</span>`
}
if (mode === 'dark') {
element.innerHTML = `<span>🌙</span>`
}
if (mode === 'auto') {
element.innerHTML = `<span>🖥️</span>`
}
const link = document.getElementById("prism-theme")
const preferred = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
-1 0 1 2 3 4 5 6
export default (branches: Array<{name: string, href: string, date: string}>, defaultBranch: string, currentBranch: string): string => {
return branches.map((branch) => {
const currentBadge = currentBranch === branch.name ? '<div class="badge rounded-pill bg-primary mx-1">current</div>' : ''
const defaultBadge = defaultBranch === branch.name ? '<div class="badge rounded-pill bg-info text-dark mx-1">default</div>' : ''
return `<a href='${branch.href}' class='dropdown-item my-1'><span class="branch-dropdown-branch-name me-1">${branch.name}</span>${currentBadge}${defaultBadge}<span class="text-body d-block ms-2">updated ${branch.date}</span></a>`
}).join('')
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import { type ReposConfiguration } from '../../src/configTypes.ts'
import { type Repository } from '../../src/dataTypes.ts'
import { NavHelper } from '../helpers/nav.ts'
export const branchesListItems = (branches: Array<{name: string, href: string, date: string}>, defaultBranch: string, currentBranch: string): string => {
return branches.map((branch) => {
const currentBadge = currentBranch === branch.name ? '<div class="badge rounded-pill bg-primary mx-1">current</div>' : ''
const defaultBadge = defaultBranch === branch.name ? '<div class="badge rounded-pill bg-info text-body mx-1">default</div>' : ''
return `<a href='${branch.href}' class='dropdown-item my-1'><span class="branch-dropdown-branch-name me-1">${branch.name}</span>${currentBadge}${defaultBadge}<span class="text-body d-block ms-2">updated ${branch.date}</span></a>`
}).join('')
}
export default async (reposConfig: ReposConfiguration, eleventyConfig: any, pageContent: Function) => {
return async (data) => {
if (data.currentRepo === '') {
return
}0 1 2 3 4 5 6 7 8 9
import { type ReposConfiguration } from '../../src/configTypes.ts'
import { type Repository } from '../../src/dataTypes.ts'
import { NavHelper } from '../helpers/nav.ts'
import branchesListItems from './branchesListItems.ts'
export default async (reposConfig: ReposConfiguration, eleventyConfig: any, pageContent: Function) => {
return async (data) => {
// this still necessary?
if (data.currentRepo === '') {
return
}25 26 27 28 29 30
const branchesWithHrefs = repo.branches.map((branch) => {
return {
name: branch.name,
href: nav.repoBranchHome(branch.name),
date: repo.commits.get(branch.head).date.toDateString(),
}
})25 26 27 28 29 30
const branchesWithHrefs = repo.branches.map((branch) => {
return {
name: branch.name,
href: nav.repoBranchHome(branch.name) + '/' + data.nav.path,
date: repo.commits.get(branch.head).date.toDateString(),
}
})38 39 40 41 42 43
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${repo.name}</title>
<link rel="stylesheet" href="/css/design-board.css">
<link href="https://unpkg.com/prismjs@1.20.0/themes/prism.css" rel="stylesheet" />
<script>
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";38 39 40 41 42
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${repo.name}</title>
<link rel="stylesheet" href="/css/design-board.css">
<script>
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";46 47 48 49 50
window.cloneUrl = "${repo.cloneUrl}";
</script>
<script src="${nav.rootPath()}frontend/top.js"></script>
</head>
<body>
<div class="container">46 47 48 49 50 51
window.cloneUrl = "${repo.cloneUrl}";
</script>
<script src="${nav.rootPath()}frontend/top.js"></script>
<link rel="stylesheet" id="prism-theme" type="text/css" href="${data.reposPath}/vendor/prism.css" />
</head>
<body>
<div class="container">62 63 64 65 66 67
<li class="nav-item">
<span class="nav-link d-inline-block">Branch:</span>
<div class="branch-selector dropdown-center d-inline-block">
<button class="branches nav-link d-inline-block btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">
${branch.name}
</button>
<div class="dropdown-menu">62 63 64 65 66 67
<li class="nav-item">
<span class="nav-link d-inline-block">Branch:</span>
<div class="branch-selector dropdown-center d-inline-block">
<button class="branches nav-link d-inline-block btn btn-bg dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">
${branch.name}
</button>
<div class="dropdown-menu">100 101 102 103 104
</ul>
</nav>
</div>
</div>
<div class="row">
<div class="col">100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
</ul>
</nav>
</div>
<div class="col-auto d-flex align-items-center">
<div class="dropdown">
<button class="dropdown-toggle btn shadow-none" id="dark-mode-switch" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<span>️</span>
</button>
<ul class="dropdown-menu">
<li><button class="btn shadow-none" data-theme-pref="light" onclick="toggleDarkMode(this)"><span class="me-1">💡</span>Light</button></li>
<li><button class="btn shadow-none" data-theme-pref="dark" onclick="toggleDarkMode(this)"><span class="me-1">🌙</span>Dark</button></li>
<li><button class="btn shadow-none" data-theme-pref="auto" onclick="toggleDarkMode(this)"><span class="me-1">🖥️</span>Match OS</button></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col">13 14 15 16 17 18 19 20 21 22 23 24
<div class="row my-3">
<div class="col">
<h3>
<span class="bezel-gray px-1"><a href="#">🏠 ./</a></span>${
data.fileInfo.file.split('/').map((dir, index, arr) => {
if (index === arr.length - 1) {
return `<span class="px-2">${dir}</span>`
}
else {
return `<span class="bezel-gray px-1"><a href="#">${dir}</a></span>`
}
}).join('')
}13 14 15 16 17 18 19 20 21 22 23 24
<div class="row my-3">
<div class="col">
<h3>
<span class="bezel-gray px-1"><a href="${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files">🏠 ./</a></span>${
data.fileInfo.file.split('/').map((dir, index, arr) => {
if (index === arr.length - 1) {
return `<span class="px-2">${dir}</span>`
}
else {
return `<span class="bezel-gray px-1"><a href="${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files/${arr.slice(0, index + 1).join('/')}">${dir}</a></span>`
}
}).join('')
}38 39 40 41 42 43
${topLevelFilesOnly(getDirectoryContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file), data.fileInfo.file + '/').map((dir) => {
return `<li class="list-group-item">
${dir.isDirectory ? `<span>📁</span>` : ''}
<a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/files/${slugify(dir.fullPath)}.html">${getRelativePath(data.fileInfo.file, dir.name)}</a>
</li>`
}).join('')}
</ul>38 39 40 41 42 43
${topLevelFilesOnly(getDirectoryContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file), data.fileInfo.file + '/').map((dir) => {
return `<li class="list-group-item">
${dir.isDirectory ? `<span>📁</span>` : ''}
<a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => slugify(pathPart)).join('/')}">${getRelativePath(data.fileInfo.file, dir.name)}</a>
</li>`
}).join('')}
</ul>24 25 26 27 28 29
return `
<li class="list-group-item">
${file.isDirectory ? '<span>📁</span>' : ''}
<a href="${data.reposPath}/${slugify(data.branchInfo.repoName)}/branches/${slugify(data.branchInfo.branchName)}/files/${slugify(file.fullPath)}.html">${file.name}</a>
</li>
`
}).join('')}24 25 26 27 28 29
return `
<li class="list-group-item">
${file.isDirectory ? '<span>📁</span>' : ''}
<a href="${data.reposPath}/${slugify(data.branchInfo.repoName)}/branches/${slugify(data.branchInfo.branchName)}/files/${file.fullPath.split('/').map((pathPart) => slugify(pathPart)).join('/')}">${file.name}</a>
</li>
`
}).join('')}309 310 311 312 313 314 315 316 317 318 319 320
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/${eleventyConfig.getFilter("slugify")(data.fileInfo.file)}.html`
},
eleventyComputed: {
nav: {
repoName: (data) => data.fileInfo.repoName,
branchName: (data) => data.fileInfo.branchName,
path: "files",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName309 310 311 312 313 314 315 316 317 318 319 320
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/${data.fileInfo.file.split('/').map((filePart) => eleventyConfig.getFilter("slugify")(filePart)).join('/')}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.fileInfo.repoName,
branchName: (data) => data.fileInfo.branchName,
path: 'files',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName404 405 406 407 408 409
nav: {
repoName: (data) => data.branch.repoName,
branchName: (data) => data.branch.branchName,
path: "",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.branch.repoName404 405 406 407 408 409
nav: {
repoName: (data) => data.branch.repoName,
branchName: (data) => data.branch.branchName,
path: (data) => '', // ask about why empty string here shows up as a function
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.branch.repoName443 444 445 446 447 448
nav: {
repoName: (data) => data.patchPage.repoName,
branchName: (data) => data.patchPage.branchName,
path: "patches/page1",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchPage.repoName443 444 445 446 447 448
nav: {
repoName: (data) => data.patchPage.repoName,
branchName: (data) => data.patchPage.branchName,
path: 'patches/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchPage.repoName480 481 482 483 484 485
nav: {
repoName: (data) => data.patchInfo.repoName,
branchName: (data) => data.patchInfo.branchName,
path: "patches/page1",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchInfo.repoName480 481 482 483 484 485
nav: {
repoName: (data) => data.patchInfo.repoName,
branchName: (data) => data.patchInfo.branchName,
path: 'patches/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchInfo.repoName0 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
@use 'sass:color';
$body-color: #262626;
$font-family-base: serif;
$border-radius: 0;
$border-radius-sm: 0;
$border-radius-lg: 0;
$enable-transitions: false;
$gray: #EFF2E4;
$blue: #7388FA;
$lightblue: #73B2FA;
$purple: #B673FA;
$magenta: #E273FA;
$teal: #80F8D4;
$body-bg: $gray;
$primary: $blue;
$info: $teal;
$warning: $magenta;
$body-tertiary-bg: color.adjust($gray, $lightness: -5%);
$border-color: $lightblue;
$border-width: 2px;
@import "../node_modules/bootstrap/scss/bootstrap";
.bezel {
background-color: $blue;
border-top: 6px solid color.adjust($blue, $lightness: 15%);
border-left: 6px solid color.adjust($blue, $lightness: 13%);
border-bottom: 6px solid color.adjust($blue, $lightness: -10%);
border-right: 6px solid color.adjust($blue, $lightness: -13%);
}
.bezel-purple {
background-color: $purple;
border-top: 6px solid color.adjust($purple, $lightness: 15%);
border-left: 6px solid color.adjust($purple, $lightness: 13%);
border-bottom: 6px solid color.adjust($purple, $lightness: -10%);
border-right: 6px solid color.adjust($purple, $lightness: -13%);
}
.bezel-gray {
background-color: $gray;
border-top: 6px solid color.adjust($gray, $lightness: 15%);
border-left: 6px solid color.adjust($gray, $lightness: 13%);
border-bottom: 6px solid color.adjust($gray, $lightness: -10%);
border-right: 6px solid color.adjust($gray, $lightness: -13%);
}
.bezel-inset {
background-color: $gray;
border-bottom: 6px solid color.adjust($gray, $lightness: 15%);
border-right: 6px solid color.adjust($gray, $lightness: 13%);
border-top: 6px solid color.adjust($gray, $lightness: -10%);
border-left: 6px solid color.adjust($gray, $lightness: -13%);
}
.bezel-inset-purple {
background-color: $purple;
border-bottom: 6px solid color.adjust($purple, $lightness: 15%);
border-right: 6px solid color.adjust($purple, $lightness: 13%);
border-top: 6px solid color.adjust($purple, $lightness: -10%);
border-left: 6px solid color.adjust($purple, $lightness: -13%);
}
.no-right {
border-right: none !important;
}
.no-left {
border-left: none !important;
}
a {
color: $teal;
}
.btn {
font-family: sans-serif;
box-shadow: rgba(0, 0, 0, .6) 4px 6px;
}
.btn:hover:not(.shadow-none) {
transform: translateX(2px) translateY(4px);
box-shadow: rgba(0, 0, 0, 1) 2px 2px;
}
.btn:active:not(.shadow-none) {
transform: translateX(3px) translateY(5px);
box-shadow: rgba(0, 0, 0, 1) 1px 1px;
}
h1.blue {
color: $blue;
}
nav a, nav span {
font-family: sans-serif;
}
.nav-item a {
color: $purple;
}
.navbar-nav .nav-link.active {
background-color: $purple;
color: white;
}
19 20 21 22 23
$body-bg: $gray;
$primary: $blue;
$info: $teal;
$warning: $magenta;
19 20 21 22 23 24
$body-bg: $gray;
$primary: $blue;
$secondary: $purple;
$info: $teal;
$warning: $magenta;
42 43 44 45 46
border-right: 6px solid color.adjust($lightblue, $lightness: -13%);
}
.bezel-purple {
background-color: $purple;
border-top: 6px solid color.adjust($purple, $lightness: 15%);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 83 84 85 86
border-right: 6px solid color.adjust($lightblue, $lightness: -13%);
}
#dark-mode-switch span {
text-shadow: 0 0 12px $black;
}
@include color-mode(dark) {
$blue: #354BC6;
.bezel {
background-color: $blue;
border-top: 6px solid color.adjust($blue, $lightness: 10%);
border-left: 6px solid color.adjust($blue, $lightness: 7%);
border-bottom: 6px solid color.adjust($blue, $lightness: -15%);
border-right: 6px solid color.adjust($blue, $lightness: -18%);
}
.nav-item a {
color: $lightblue;
}
.btn-bg {
background-color: color.adjust($body-bg-dark, $lightness: 10%);
}
.list-group {
--bs-list-group-border-color: #404448;
}
.bezel-gray {
background-color: color.adjust($body-bg-dark, $lightness: 10%);
border-top: 6px solid color.adjust($body-bg-dark, $lightness: 15%);
border-left: 6px solid color.adjust($body-bg-dark, $lightness: 13%);
border-bottom: 6px solid color.adjust($body-bg-dark, $lightness: -5%);
border-right: 6px solid color.adjust($body-bg-dark, $lightness: -9%);
}
#dark-mode-switch span {
text-shadow: 0 0 12px $white;
}
}
.bezel-purple {
background-color: $purple;
border-top: 6px solid color.adjust($purple, $lightness: 15%);51 52 53 54 55 56 57 58 59 60
}
.bezel-gray {
background-color: $gray;
border-top: 6px solid color.adjust($gray, $lightness: 15%);
border-left: 6px solid color.adjust($gray, $lightness: 13%);
border-bottom: 6px solid color.adjust($gray, $lightness: -10%);
border-right: 6px solid color.adjust($gray, $lightness: -13%);
}
.bezel-inset {51 52 53 54 55 56 57 58 59
}
.bezel-gray {
border-top: 6px solid color.adjust($body-bg, $lightness: 15%);
border-left: 6px solid color.adjust($body-bg, $lightness: 13%);
border-bottom: 6px solid color.adjust($body-bg, $lightness: -10%);
border-right: 6px solid color.adjust($body-bg, $lightness: -13%);
}
.bezel-inset {82 83 84 85 86 87 88 89
border-left: none !important;
}
a {
color: $blue;
}
.btn {
font-family: sans-serif;
box-shadow: rgba(0, 0, 0, .6) 4px 6px;82 83 84 85 86 87 88 89
border-left: none !important;
}
$link-color: $blue;
$link-color-dark: $lightblue;
.btn {
font-family: sans-serif;
box-shadow: rgba(0, 0, 0, .6) 4px 6px;