Add ui_components classes for rows and columns

2ff4b18e9af34e83c4e72205946abc59999da280

Tucker McKnight <tucker@pangolin.lan> | Tue May 26 2026

Add ui_components classes for rows and columns

Started using these classes on the index.ts and repo.ts pages.
It's a wrapper for creating an m('div') object for bootstrap
rows and columns.
js_templates/index.ts:1
Before
0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import m from 'mithril'
⁣
import render from 'mithril-node-render'

export default (_reposConfig: any, eleventyConfig: any, data: any) => {
  const slugify = eleventyConfig.getFilter("slugify")

  const pageContent = m('div', {class: "container"}, [
    m('div', {class: "row my-3"},
      m('div', {class: "col"},
        m('h1', data.reposConfig.defaultTemplateConfiguration?.allRepositoriesPageTitle || "All Repositories")
      )
    ),
    m('div', {class: "row d-flex flex-wrap"},
      data.repos.map((repo) => {
        return (
          m('div', {class: "col", style: "flex-basis: 50%; max-width: 50%;"},
            m('div', {class: "my-2 card bezel-gray flex-grow-1"},
              m('div', {class: "card-header"},
                m('a', {class: "card-title fs-5", href: `${data.reposPath}/${slugify(repo.name)}/branches/${repo.defaultBranch}`}, repo.name)
After
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import m from 'mithril'
import { Row, Col } from './ui_components/bootstrap.ts'
import render from 'mithril-node-render'

export default (_reposConfig: any, eleventyConfig: any, data: any) => {
  const slugify = eleventyConfig.getFilter("slugify")

  const pageContent = m('div', {class: "container"}, [
    Row("my-3", 
      Col.c(
        m('h1', data.reposConfig.defaultTemplateConfiguration?.allRepositoriesPageTitle || "All Repositories")
      )
    ),
    Row("d-flex flex-wrap",
      data.repos.map((repo) => {
        return (
          Col.c('', {style: "flex-basis: 50%; max-width: 50%;"},
            m('div', {class: "my-2 card bezel-gray flex-grow-1"},
              m('div', {class: "card-header"},
                m('a', {class: "card-title fs-5", href: `${data.reposPath}/${slugify(repo.name)}/branches/${repo.defaultBranch}`}, repo.name)
js_templates/repo.ts:1
Before
0

1
2
import m from 'mithril'
⁣
import { type Repository } from '../src/dataTypes.ts'
import { NavHelper } from './helpers/nav.ts'
import htmlPage from './common/htmlPage.ts'
After
0
1
2
3
import m from 'mithril'
import { Row, Col } from './ui_components/bootstrap.ts'
import { type Repository } from '../src/dataTypes.ts'
import { NavHelper } from './helpers/nav.ts'
import htmlPage from './common/htmlPage.ts'
js_templates/repo.ts:49
Before
48
49
50
51
52
53
54
55
56
57
  const readmeContent = await renderContentIfAvailable(await getReadMe(repo.name, branch.name), branch.name)

  const pageContent = [
    m('div', {class: "row"}, [
      m('div', {class: "col"}, [
        m('div', {class: "px-4 pt-3 bezel-header"}, [
          m('div', {class: "row"}, [
            m('div', {class: "col-12 col-lg-6"}, [
              m('h1', {class: "display-3 text-white"},
                m('em', repo.name)
              ),
After
48
49
50
51
52
53
54
55
56
57
  const readmeContent = await renderContentIfAvailable(await getReadMe(repo.name, branch.name), branch.name)

  const pageContent = [
    Row([
      Col.c([
        m('div', {class: "px-4 pt-3 bezel-header"}, [
          Row([
            Col.c12('col-lg-6', [
              m('h1', {class: "display-3 text-white"},
                m('em', repo.name)
              ),
js_templates/repo.ts:61
Before
60
61
62
63
64
65
66
67
                ? m('p', {class: "text-white fs-4 fw-light"}, repo.description)
                : null
            ]),
            m('div', {class: "col-12 col-lg-6 d-flex flex-column justify-content-around"}, [
              m('div', {class: "row flex-grow-1 align-items-stretch language-percent-row"}, [
                m('div', {class: "col-12 d-flex align-items-stretch"}, [
                  m('div', {class: "language-cols d-flex flex-grow-1 flex-nowrap"}, topLanguagePercentages.map((percentTuple) => {
                    return m('div', {class: 'language-col flex-grow-1 overflow-hidden'}, [
                      m('div', {class: "language-name text-light font-monospace"}, percentTuple[0]),
After
60
61
62
63
64
65
66
67
                ? m('p', {class: "text-white fs-4 fw-light"}, repo.description)
                : null
            ]),
            Col.c12('col-lg-6 d-flex flex-column justify-content-around', [
              Row('flex-grow-1 align-items-stretch language-percent-row', [
                Col.c12('d-flex align-items-stretch', [
                  m('div', {class: "language-cols d-flex flex-grow-1 flex-nowrap"}, topLanguagePercentages.map((percentTuple) => {
                    return m('div', {class: 'language-col flex-grow-1 overflow-hidden'}, [
                      m('div', {class: "language-name text-light font-monospace"}, percentTuple[0]),
js_templates/repo.ts:75
Before
74
75
76
77
78
79
80
                  )])),
                ])
              ]),
              m('div', {class: "row align-items-center"}, [
                m('div', {class: "col-12"}, [
                  m('div', {class: "header-button-container"}, [
                    m('button', {class: "btn btn-info btn-lg dropdown-toggle clone-popover-btn"}, 'Clone'),
                    nav.homepageButtons.map((buttonConfig) => {
After
74
75
76
77
78
79
80
                  )])),
                ])
              ]),
              Row('align-items-center', [
                Col.c12([
                  m('div', {class: "header-button-container"}, [
                    m('button', {class: "btn btn-info btn-lg dropdown-toggle clone-popover-btn"}, 'Clone'),
                    nav.homepageButtons.map((buttonConfig) => {
js_templates/repo.ts:92
Before
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
            ])
          ]),
          m('noscript',
            m('div', {class: "row mt-2"},
              m('div', {class: "col"},
                m('p', {class: "font-monospace text-white"},
                  `Clone URL: ${repo.cloneUrl}`
                )
              )
            )
          ),
          m('div', {class: "row mt-2"}, [
            m('div', {class: "col"}, [
              m('p', {class: "font-monospace text-white mb-2 d-inline-block"}, [
                m('a', {class: "btn btn-outline-info badge shadow-none me-2", href: nav.repoCurrentBranchRssFeed()}, [
                  m('img', {src: `${nav.rootPath()}frontend/img/rss-icon.svg`, style: "width: 11px; margin-right: 5px;"}),
After
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
            ])
          ]),
          m('noscript',
            Row('mt-2',
              Col.c(
                m('p', {class: "font-monospace text-white"},
                  `Clone URL: ${repo.cloneUrl}`
                )
              )
            )
          ),
          Row('mt-2', [
            Col.c([
              m('p', {class: "font-monospace text-white mb-2 d-inline-block"}, [
                m('a', {class: "btn btn-outline-info badge shadow-none me-2", href: nav.repoCurrentBranchRssFeed()}, [
                  m('img', {src: `${nav.rootPath()}frontend/img/rss-icon.svg`, style: "width: 11px; margin-right: 5px;"}),
js_templates/repo.ts:117
Before
116
117
118
119
120
121
122
        ])
      ])
    ]),
    m('div', {class: "row my-4 mx-1"},
      m('div', {class: "col readme"}, m.trust(readmeContent))
    )
  ]
After
116
117
118
119
120
121
122
        ])
      ])
    ]),
    Row('my-4 mx-1',
      Col.c('readme', m.trust(readmeContent))
    )
  ]
js_templates/ui_components/bootstrap.ts:0
Before




⁣
⁣
⁣
⁣
⁣
⁣
After
-1
0
1
2
3
4
import Row from './bootstrap/row.ts'
import Col from './bootstrap/col.ts'

export {
  Row,
  Col,
}
js_templates/ui_components/bootstrap/row.ts:0
Before












⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
After
-1
0
1
2
3
4
5
6
7
8
9
10
11
12
import m from 'mithril'

export default (classesOrContent: any, optionalContent?: any)  => {
  let classes = ''
  let content = classesOrContent

  if (optionalContent !== undefined) {
    classes = classesOrContent
    content = optionalContent
  }

  return m('div', {
    class: 'row ' + classes
  }, content)
}