Make language percent breakdown work; add optional config for extra buttons

073785ab9fa931c3d89e9631c36b2ff05e1b14ee

Tucker McKnight <tucker@pangolin.lan> | Wed Dec 24 2025

Make language percent breakdown work; add optional config for extra buttons
frontend/top.js:23
Before
22
23
24
25
26
27
  window['setCheckbox'] = (mode, element) => {
    if (element === null) { return }
    if (mode === 'light') {
      element.innerHTML = `<span>&#x1F4A1;</span>`
    }
    if (mode === 'dark') {
      element.innerHTML = `<span>&#x1F319;</span>`
After
22
23
24
25
26
27
  window['setCheckbox'] = (mode, element) => {
    if (element === null) { return }
    if (mode === 'light') {
      element.innerHTML = `<span>&#x1F31E;</span>`
    }
    if (mode === 'dark') {
      element.innerHTML = `<span>&#x1F319;</span>`
js_templates/common/htmlPage.ts:96
Before
95
96
97
98
99
100
101
102
103
104
              </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>
After
95
96
97
98
99
100
101
102
103
104
              </div>
              <div class="col-auto d-flex align-items-center">
                <div class="dropdown">
                  <button class="dropdown-toggle btn btn-bg" 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">&#x1F31E;</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>
js_templates/helpers/nav.ts:36
Before
35
36
37

38
39
    repoBranchCommitsBase: () => repoBranchCommitsBase,
    repoCurrentBranchCommits: () => `${repoBranchCommitsBase}page1`,
    repoCurrentBranchBranches: () => `${currentBranchPath}/branches`,
⁣
  }
}
After
35
36
37
38
39
40
    repoBranchCommitsBase: () => repoBranchCommitsBase,
    repoCurrentBranchCommits: () => `${repoBranchCommitsBase}page1`,
    repoCurrentBranchBranches: () => `${currentBranchPath}/branches`,
    homepageButtons: reposConfig.repos[repoName].defaultTemplateConfiguration?.homepageButtons
  }
}
js_templates/repo.ts:61
Before
60
61
62
63
64
65
66
          </noscript>
          <div class="row mt-2">
            <div class="col">
              <p class="font-monospace text-white text-truncate">
                Latest commit: ${latestCommit.date.toDateString()} <a class="fw-bold link-info" href="${nav.repoBranchCommitsBase()}${latestCommit.hash}">${latestCommit.hash.substr(0, 6)}</a> ${latestCommit.message.split('\n')[0]}
              </p>
            </div>
          </div>
After
60
61
62
63
64
65
66
          </noscript>
          <div class="row mt-2">
            <div class="col">
              <p class="font-monospace text-white">
                Latest commit: ${latestCommit.date.toDateString()} <a class="fw-bold link-info" href="${nav.repoBranchCommitsBase()}${latestCommit.hash}">${latestCommit.hash.substr(0, 6)}</a> ${latestCommitMessage}
              </p>
            </div>
          </div>
schemas/ReposConfiguration.json:57
Before
56
57
58































59
60
        "defaultBranch": {
          "type": "string"
        },
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
        "description": {
          "type": "string"
        },
After
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
        "defaultBranch": {
          "type": "string"
        },
        "defaultTemplateConfiguration": {
          "additionalProperties": false,
          "properties": {
            "homepageButtons": {
              "items": {
                "additionalProperties": false,
                "properties": {
                  "newTab": {
                    "type": "boolean"
                  },
                  "text": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                },
                "required": [
                  "url",
                  "text"
                ],
                "type": "object"
              },
              "type": "array"
            }
          },
          "required": [
            "homepageButtons"
          ],
          "type": "object"
        },
        "description": {
          "type": "string"
        },
schemas/ReposConfiguration.json:121
Before
120
121
122
123



124
            "description": "An object containing the configuration for your repositories. Each key in this object is a repository name, and the value has several config options for that repository. The required config options describe the path to the repository and which branches should be pulled. See the specific definition of  {@link  GitConfig }  for more details about what goes in these configuration objects."
          },
          "type": "object"
        }
⁣
⁣
⁣
      },
      "required": [
After
120
121
122
123
124
125
126
127
            "description": "An object containing the configuration for your repositories. Each key in this object is a repository name, and the value has several config options for that repository. The required config options describe the path to the repository and which branches should be pulled. See the specific definition of  {@link  GitConfig }  for more details about what goes in these configuration objects."
          },
          "type": "object"
        },
        "useDefaultTemplate": {
          "type": "boolean"
        }
      },
      "required": [
scss/design-board.scss:40
Before
39
40
41
42
43
44
45
46
47
48
49
  background-color: $lightblue;
  border-top: 6px solid color.adjust($lightblue, $lightness: 15%);
  border-left: 6px solid color.adjust($lightblue, $lightness: 13%);
  border-bottom: 6px solid color.adjust($lightblue, $lightness: -10%);
  border-right: 6px solid color.adjust($lightblue, $lightness: -13%);
}

#dark-mode-switch span {
  text-shadow: 0 0 12px $black;
}

@include color-mode(dark) {
After
39
40
41
42
43
44
45



  background-color: $lightblue;
  border-top: 6px solid color.adjust($lightblue, $lightness: 15%);
  border-left: 6px solid color.adjust($lightblue, $lightness: 13%);
  border-bottom: 6px solid color.adjust($lightblue, $lightness: -30%);
  border-right: 6px solid color.adjust($lightblue, $lightness: -40%);
}

⁣
⁣
⁣
⁣
@include color-mode(dark) {
scss/design-board.scss:78
Before
77
78
79
80

81
82
83
84
85
86
87
    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%);
After
77
78
79
80
81
82
83
84
85
86
87
88
    border-bottom: 6px solid color.adjust($body-bg-dark, $lightness: -5%);
    border-right: 6px solid color.adjust($body-bg-dark, $lightness: -9%);
  }
}

.btn-bg, .btn-bg:hover {
  border: 1px solid $body-color;
}


.bezel-purple {
  background-color: $purple;
  border-top: 6px solid color.adjust($purple, $lightness: 15%);
scss/design-board.scss:165
Before
164
165
166



167
168
169
170





171
172
173
174
175

176
177



  width: 1rem;
}

⁣
⁣
⁣
.language-percentages {
  font-size: 0;
}

⁣
⁣
⁣
⁣
⁣
.language-percent {
  display: inline-block;
  margin: 0;
  padding: 0;
  height: 2rem;
⁣
}

⁣
⁣
⁣
⁣
.language-name {
After
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  width: 1rem;
}

.language-percent-row {
  max-height: 60%;
}
.language-percentages {
  font-size: 0;
}
.language-col {
  display: flex;
  flex-direction: column;
  justify-content: end;
  min-height: 4rem;
}
.language-percent {
  display: inline-block;
  margin: 0;
  padding: 0;
  background-color: $lilac;
  // background-color: #E273FA;
}
.language-col:nth-of-type(2n) .language-percent {
  background-color: $magenta;
  // background-color: #852CDF;
}

.language-name {
scss/design-board.scss:181
Before
180
181
182
183
184
185
186
187
188
  font-size: 1rem;
}

.branches, .branches:hover {
  border: 1px solid $body-color;
}

.dropdown-branches {
  list-style: none;
  padding: 0;
After
180
181
182




183
184
  font-size: 1rem;
}

⁣
⁣
⁣
⁣
.dropdown-branches {
  list-style: none;
  padding: 0;
scss/design-board.scss:210
Before
209
210





















.sortRadioButton {
  display: inline-block;
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
}
After
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
.sortRadioButton {
  display: inline-block;
}

.badge.bg-primary {
  background: linear-gradient(color.adjust($primary, $lightness: 10%), $primary);
  box-shadow: -1px -3px 4px inset color.adjust($primary, $lightness: -30%);
}

.badge.bg-info {
  background: linear-gradient(color.adjust($info, $lightness: 10%), $info);
  box-shadow: -1px -3px 4px inset color.adjust($info, $lightness: -30%);
}

.header-button-container {
  display: flex;
  flex-wrap: wrap;
  margin: 10px -10px;

  .btn {
    flex: 1;
    flex-basis: 200px;
    margin: 10px 10px;
  }
}
src/configTypes.ts:40
Before
39
40
41

42
43
  * @example path: "/repos"
  */
  path?: string,
⁣
}

export type GitConfig = {
After
39
40
41
42
43
44
  * @example path: "/repos"
  */
  path?: string,
  useDefaultTemplate?: boolean,
}

export type GitConfig = {
src/configTypes.ts:58
Before
57
58
59
60






      command: string,
      copyFrom: string,
      copyTo: string,
  }[]
⁣
⁣
⁣
⁣
⁣
⁣
⁣
}
After
57
58
59
60
61
62
63
64
65
66
67
      command: string,
      copyFrom: string,
      copyTo: string,
  }[],
  defaultTemplateConfiguration?: {
    homepageButtons: Array<{
      url: string,
      text: string,
      newTab?: boolean,
    }>
  },
}