[WIP] starting work on a tags page

eafb15f67e96d4be72e6e52aaa3146a55e85cebd

Tucker McKnight <tmcknight@instructure.com> | Sun Mar 29 2026

[WIP] starting work on a tags page

Currently it's mostly a copy-paste of the branches page, with
parts removed that tags don't have.

Still need to do:
- Changes all "branches" links to have either "branch" or "tag" in the
  URL, since the site is now being generated for all branches and all tags.
js_templates/branches.ts:18
Before
17
18
19
20
21
22
      return branch.repoName === data.flatRel.repoName ?
        m('li', [
          m('a', {
            href: `${data.reposPath}/${slugify(branch.repoName)}/branches/${slugify(branch.relName)}/branches`
          }, branch.relName),
          branch.relName === data.flatRel.relName
            ? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
After
17
18
19
20
21
22
      return branch.repoName === data.flatRel.repoName ?
        m('li', [
          m('a', {
            href: `${data.reposPath}/${slugify(branch.repoName)}/branch/${slugify(branch.relName)}/branches`
          }, branch.relName),
          branch.relName === data.flatRel.relName
            ? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
js_templates/commit.ts:56
Before
55
56
57
58
59
60
      return m('div', {class: 'hunk'}, [
        m('span', {class: "font-monospace fw-bold"},
          m('a', {
            href: `${data.reposPath}/${slugify(data.patchInfo.repoName)}/branches/${slugify(data.patchInfo.branchName)}/files/${hunk.fileName.split('/').map((filePart) => { return filePart.split('.').map((subpart) => { return slugify(subpart)}).join('.')}).join('/')}.html`
          }, `${hunk.fileName}:${hunk.lineNumber}`)
        ),
        m('div', {class: "diff d-flex"}, [
After
55
56
57
58
59
60
      return m('div', {class: 'hunk'}, [
        m('span', {class: "font-monospace fw-bold"},
          m('a', {
            href: `${data.reposPath}/${slugify(data.patchInfo.repoName)}/branch/${slugify(data.patchInfo.branchName)}/files/${hunk.fileName.split('/').map((filePart) => { return filePart.split('.').map((subpart) => { return slugify(subpart)}).join('.')}).join('/')}.html`
          }, `${hunk.fileName}:${hunk.lineNumber}`)
        ),
        m('div', {class: "diff d-flex"}, [
js_templates/common/htmlPage.ts:22
Before
21
22
23
24
25
26
27
28
29
30
31
32
33
34
      href: nav.repoBranchHome(branch.name) + '/' + data.nav.path,
      date: repo.commits.get(branch.sha).date.toISOString(),
    }
    }).concat(repo.tags.map((tag) => {
      return {
        name: tag.name,
        href: "tbd",
        date: repo.commits.get(tag.sha).date.toISOString(),
      }
    }))

    console.log(branchesWithHrefs)

  return {
    rootPath: nav.rootPath(),
After
21
22
23
24
25
26
27
28
29
30
31


32
      href: nav.repoBranchHome(branch.name) + '/' + data.nav.path,
      date: repo.commits.get(branch.sha).date.toISOString(),
    }
  }).concat(repo.tags.map((tag) => {
    return {
      name: tag.name,
      href: nav.repoTagHome(tag.name) + '/' + data.nav.path,
      date: repo.commits.get(tag.sha).date.toISOString(),
    }
  }))

⁣
⁣
  return {
    rootPath: nav.rootPath(),
js_templates/common/htmlPage.ts:118
Before
117
118
119
120



121
              ),
              m('li', {class: "nav-item"},
                m('a', {class: `nav-link ${data.navTab === 'branches' ? 'active' : ''}`, href: nav.repoCurrentBranchBranches()}, 'Branches')
              )
⁣
⁣
⁣
            ])
          )
After
117
118
119
120
121
122
123
124
              ),
              m('li', {class: "nav-item"},
                m('a', {class: `nav-link ${data.navTab === 'branches' ? 'active' : ''}`, href: nav.repoCurrentBranchBranches()}, 'Branches')
              ),
              m('li', {class: "nav-item"},
                m('a', {class: `nav-link ${data.navTab === 'tags' ? 'active' : ''}`, href: nav.repoCurrentBranchTags()}, 'Tags')
              )
            ])
          )
js_templates/file.ts:31
Before
30
31
32
33
34
35
      m('div', {class: "col"},
        m('h3', [
          m('span', {class: "bezel-gray p-1 my-1 d-inline-block"},
            m('a', {href: `${data.reposPath}/${fileInfo.repoName}/branches/${fileInfo.branchName}/files`}, './')
          ),
          fileInfo.file.split('/').map((dir, index, arr) => {
            if (index === arr.length - 1) {
After
30
31
32
33
34
35
      m('div', {class: "col"},
        m('h3', [
          m('span', {class: "bezel-gray p-1 my-1 d-inline-block"},
            m('a', {href: `${data.reposPath}/${fileInfo.repoName}/branch/${fileInfo.branchName}/files`}, './')
          ),
          fileInfo.file.split('/').map((dir, index, arr) => {
            if (index === arr.length - 1) {
js_templates/file.ts:40
Before
39
40
41
42
43
44
            else {
              return m('span', {class: "bezel-gray p-1 my-1 d-inline-block"}, [
                m('a', {
                  href: `${data.reposPath}/${fileInfo.repoName}/branches/${fileInfo.branchName}/files/${arr.slice(0, index + 1).map((part) => slugify(part)).join('/')}.html`}, `${dir}/`)
              ])
            }
          })
After
39
40
41
42
43
44
            else {
              return m('span', {class: "bezel-gray p-1 my-1 d-inline-block"}, [
                m('a', {
                  href: `${data.reposPath}/${fileInfo.repoName}/branch/${fileInfo.branchName}/files/${arr.slice(0, index + 1).map((part) => slugify(part)).join('/')}.html`}, `${dir}/`)
              ])
            }
          })
js_templates/file.ts:55
Before
54
55
56
57
58
59
              return m('li', {class: 'list-group-item'}, [
                dir.isDirectory ? m('span', m.trust('&#x1F4C1;&nbsp;')) : null,
                m('a', {
                  href: `${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => {
                    return pathPart.split('.').map((subPart) => {
                      return slugify(subPart)
                    }).join('.')
After
54
55
56
57
58
59
              return m('li', {class: 'list-group-item'}, [
                dir.isDirectory ? m('span', m.trust('&#x1F4C1;&nbsp;')) : null,
                m('a', {
                  href: `${data.reposPath}/${slugify(fileInfo.repoName)}/branch/${slugify(fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => {
                    return pathPart.split('.').map((subPart) => {
                      return slugify(subPart)
                    }).join('.')
js_templates/file.ts:71
Before
70
71
72
73
74
75
        m('div', {class: "row my-3"},
          m('div', {class: "col"},
            m('span', m('a', {
              href: `${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/raw/${fileInfo.file.split('.').map(filePart => slugify(filePart)).join('.')}`}, 'View raw file'))
          )
        ),
        (fileInfo.file.endsWith(".md") ?
After
70
71
72
73
74
75
        m('div', {class: "row my-3"},
          m('div', {class: "col"},
            m('span', m('a', {
              href: `${data.reposPath}/${slugify(fileInfo.repoName)}/branch/${slugify(fileInfo.branchName)}/raw/${fileInfo.file.split('.').map(filePart => slugify(filePart)).join('.')}`}, 'View raw file'))
          )
        ),
        (fileInfo.file.endsWith(".md") ?
js_templates/file.ts:126
Before
125
126
127
128
129
130
                m('code', {style: "white-space: pre;"}, [
                  m('pre', {class: "language-text"}, m.trust(
                    currentRel.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
                      return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
                    }).join('\n')))
                ])
              ),
After
125
126
127
128
129
130
                m('code', {style: "white-space: pre;"}, [
                  m('pre', {class: "language-text"}, m.trust(
                    currentRel.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
                      return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/branch/${slugify(fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
                    }).join('\n')))
                ])
              ),
js_templates/files.ts:27
Before
26
27
28
29
30
31
      return m('li', {class: 'list-group-item'}, [
        file.isDirectory ? m.trust('<span>&#x1F4C1;&nbsp;</span>') : null,
        m('a', {
          href: `${data.reposPath}/${slugify(data.flatRel.repoName)}/branches/${slugify(data.flatRel.relName)}/files/${
            file.fullPath.split('/')
            .map((pathPart) => {
              return pathPart.split('.').map((subPart) => {
After
26
27
28
29
30
31
      return m('li', {class: 'list-group-item'}, [
        file.isDirectory ? m.trust('<span>&#x1F4C1;&nbsp;</span>') : null,
        m('a', {
          href: `${data.reposPath}/${slugify(data.flatRel.repoName)}/branch/${slugify(data.flatRel.relName)}/files/${
            file.fullPath.split('/')
            .map((pathPart) => {
              return pathPart.split('.').map((subPart) => {
js_templates/helpers/nav.ts:7
Before
6
7
8
9
10

11
  // every page URL. E.g. all of them start with 'repos/my-repo-name'
  // or 'repos/my-repo-name/branches.'
  const repoBasePath = `${reposPath}/${slugify(repoName)}`
  const rootBasePathBranches = `${repoBasePath}/branches`

⁣
  const currentBranchPath = `${rootBasePathBranches}/${slugify(branchName)}`
  const repoBranchCommitsBase = `${currentBranchPath}/commits/`
After
6
7
8
9
10
11
12
  // every page URL. E.g. all of them start with 'repos/my-repo-name'
  // or 'repos/my-repo-name/branches.'
  const repoBasePath = `${reposPath}/${slugify(repoName)}`
  const rootBasePathBranches = `${repoBasePath}/branch`
  const rootBasePathTags = `${repoBasePath}/tag`

  const currentBranchPath = `${rootBasePathBranches}/${slugify(branchName)}`
  const repoBranchCommitsBase = `${currentBranchPath}/commits/`
js_templates/helpers/nav.ts:30
Before
29
30
31



32
33
34
35
36
37


38
39
    repoBranchHome: (branchName: string) => {
      return `${rootBasePathBranches}/${slugify(branchName)}`
    },
⁣
⁣
⁣
    repoCurrentBranchFiles: () => {
      return `${currentBranchPath}/files`
    },
    repoBranchCommitsBase: () => repoBranchCommitsBase,
    repoCurrentBranchCommits: () => `${repoBranchCommitsBase}page1`,
    repoCurrentBranchBranches: () => `${currentBranchPath}/branches`,
⁣
⁣
    repoCurrentBranchRssFeed: () => `${currentBranchPath}/commits.xml`,
    homepageButtons: reposConfig.repos[repoName].defaultTemplateConfiguration?.homepageButtons || []
  }
After
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    repoBranchHome: (branchName: string) => {
      return `${rootBasePathBranches}/${slugify(branchName)}`
    },
    repoTagHome: (tagName: string) => {
      return `${rootBasePathTags}/${slugify(tagName)}`
    },
    repoCurrentBranchFiles: () => {
      return `${currentBranchPath}/files`
    },
    repoBranchCommitsBase: () => repoBranchCommitsBase,
    repoCurrentBranchCommits: () => `${repoBranchCommitsBase}page1`,
    repoCurrentBranchBranches: () => `${currentBranchPath}/branches`,
    // TODO: change these "currentBranch"es to "currentRel"s
    repoCurrentBranchTags: () => `${currentBranchPath}/tags`,
    repoCurrentBranchRssFeed: () => `${currentBranchPath}/commits.xml`,
    homepageButtons: reposConfig.repos[repoName].defaultTemplateConfiguration?.homepageButtons || []
  }
js_templates/index.ts:16
Before
15
16
17
18
19
20
21
22
23
24
25
26
27
          return (
            m('div', {class: "m-2 card bezel-gray flex-grow-1", style: "flex-basis: 20rem;"},
              m('div', {class: "card-header"},
                m('a', {class: "card-title fs-5", href: `${data.reposPath}/${slugify(repo.name)}/branches/${repo.defaultBranch}`}, repo.name)
              ),
              m('div', {class: "card-body"},
                repo.description ? m('p', {class: "card-text"}, repo.description) : null
              ),
              m('div', {class: "card-footer"}, [
                m('a', {
                  href: `${data.reposPath}/${slugify(repo.name)}/branches/${repo.defaultBranch}`,
                  class: "ms-0 me-2 my-2 btn btn-primary text-white"
                }, 'Go to site'),
                m('button', {
After
15
16
17
18
19
20
21
22
23
24
25
26
27
          return (
            m('div', {class: "m-2 card bezel-gray flex-grow-1", style: "flex-basis: 20rem;"},
              m('div', {class: "card-header"},
                m('a', {class: "card-title fs-5", href: `${data.reposPath}/${slugify(repo.name)}/branch/${repo.defaultBranch}`}, repo.name)
              ),
              m('div', {class: "card-body"},
                repo.description ? m('p', {class: "card-text"}, repo.description) : null
              ),
              m('div', {class: "card-footer"}, [
                m('a', {
                  href: `${data.reposPath}/${slugify(repo.name)}/branch/${repo.defaultBranch}`,
                  class: "ms-0 me-2 my-2 btn btn-primary text-white"
                }, 'Go to site'),
                m('button', {
main.ts:19
Before
18
19
20

21
22
import commitsJsTemplate from './js_templates/commits.ts'
import indexJsTemplate from './js_templates/index.ts'
import branchesJsTemplate from './js_templates/branches.ts'
⁣
import rawJsTemplate from './js_templates/raw.ts'
import feedJsTemplate from './js_templates/feed.ts'
After
18
19
20
21
22
23
import commitsJsTemplate from './js_templates/commits.ts'
import indexJsTemplate from './js_templates/index.ts'
import branchesJsTemplate from './js_templates/branches.ts'
import tagsJsTemplate from './js_templates/tags.ts'
import rawJsTemplate from './js_templates/raw.ts'
import feedJsTemplate from './js_templates/feed.ts'
main.ts:328
Before
After