Make themes work, some color tweaks, make directory nav links work on file page

ec91a0a6fb00a4ac5162093c3c8b372fc2475123

Tucker McKnight <tucker@pangolin.lan> | Sun Dec 21 2025

Make themes work, some color tweaks, make directory nav links work on file page
frontend/main.js:1
Before
0
1
2
import {branchesListItems} from '../dist/js_templates/common/htmlPage.js'

const setCheckbox = window.setCheckbox
const currentTheme = window.currentTheme
After
0
1
2
import branchesListItems from '../dist/js_templates/common/branchesListItems.js'

const setCheckbox = window.setCheckbox
const currentTheme = window.currentTheme
frontend/top.js:16
Before
15
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'
After
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>&#x1F4A1;</span>`
    }
    if (mode === 'dark') {
      element.innerHTML = `<span>&#x1F319;</span>`
    }
    if (mode === 'auto') {
      element.innerHTML = `<span>&#x1F5A5;&#xFE0F;</span>`
    }
    const link = document.getElementById("prism-theme")
    const preferred = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
js_templates/common/branchesListItems.ts:0
Before






⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
After
-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('')
}
js_templates/common/htmlPage.ts:1
Before
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
    }
After
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
    }
js_templates/common/htmlPage.ts:26
Before
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(),
      }
    })
After
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(),
      }
    })
js_templates/common/htmlPage.ts:39
Before
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}";
After
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}";
js_templates/common/htmlPage.ts:47
Before
46
47
48

49
50
            window.cloneUrl = "${repo.cloneUrl}";
          </script>
          <script src="${nav.rootPath()}frontend/top.js"></script>
⁣
        </head>
        <body>
          <div class="container">
After
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">
js_templates/common/htmlPage.ts:63
Before
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">
After
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">
js_templates/common/htmlPage.ts:101
Before
100
101
102












103
104
                  </ul>
                </nav>
              </div>
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
            </div>
            <div class="row">
              <div class="col">
After
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>&#xFE0F;</span>
                  </button>
                  <ul class="dropdown-menu">
                    <li><button class="btn shadow-none" data-theme-pref="light" onclick="toggleDarkMode(this)"><span class="me-1">&#x1F4A1;</span>Light</button></li>
                    <li><button class="btn shadow-none" data-theme-pref="dark" onclick="toggleDarkMode(this)"><span class="me-1">&#x1F319;</span>Dark</button></li>
                    <li><button class="btn shadow-none" data-theme-pref="auto" onclick="toggleDarkMode(this)"><span class="me-1">&#x1F5A5;&#xFE0F;</span>Match OS</button></li>
                  </ul>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="col">
js_templates/file.ts:14
Before
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="#">&#x1F3E0; ./</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('')
          }
After
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">&#x1F3E0; ./</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('')
          }
js_templates/file.ts:39
Before
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>&#x1F4C1;</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>
After
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>&#x1F4C1;</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>
js_templates/files.ts:25
Before
24
25
26
27
28
29
        return `
          <li class="list-group-item">
            ${file.isDirectory ? '<span>&#x1F4C1;</span>' : ''}
            <a href="${data.reposPath}/${slugify(data.branchInfo.repoName)}/branches/${slugify(data.branchInfo.branchName)}/files/${slugify(file.fullPath)}.html">${file.name}</a>
          </li>
        `
    }).join('')}
After
24
25
26
27
28
29
        return `
          <li class="list-group-item">
            ${file.isDirectory ? '<span>&#x1F4C1;</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('')}
main.ts:310
Before
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.repoName
After
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/${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.repoName
main.ts:405
Before
404
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.repoName
After
404
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.repoName
main.ts:444
Before
443
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.repoName
After
443
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.repoName
main.ts:481
Before
480
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.repoName
After
480
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.repoName
scss/custom.scss:1
Before
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
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;
}
After














































































































⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
scss/design-board.scss:20
Before
19
20
21

22
23

$body-bg: $gray;
$primary: $blue;
⁣
$info: $teal;
$warning: $magenta;
After
19
20
21
22
23
24

$body-bg: $gray;
$primary: $blue;
$secondary: $purple;
$info: $teal;
$warning: $magenta;
scss/design-board.scss:43
Before
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%);
After
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%);
scss/design-board.scss:52
Before
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 {
After
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 {
scss/design-board.scss:83
Before
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;
After
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;