Add a temporary commonPage.ts to eventually replace htmlPage.ts

a2866c2861af9a99acf22c0e600f1a1067d4495d

Tucker McKnight <tmcknight@instructure.com> | Wed Feb 11 2026

Add a temporary commonPage.ts to eventually replace htmlPage.ts

This new commonPage.ts should include everything that overlaps between
what was previously in js_templates/index.ts and
js_templates/common/htmlPage.ts.

The goal is to have *one* htmlPage.ts that is used on all pages.

For now, js_templates/index.ts uses this new commonPage.ts.

commonPage.ts should be updated to allow other content from htmlPage.ts
to be inserted into parts of the page.
js_templates/common/commonPage.ts:0
Before
0
After
0
import m from 'mithril'
import render from 'mithril-node-render'

export default async (rootPath: string, pageContent: Function) => {
  return render([
    m.trust('<!doctype html>'),
    m('html', {lang: "en"}, [
      m('head', [
        m('meta', {charset: "utf-8"}),
        m('meta', {name: "viewport", content: "width=device-width, initial-scale=1"}),
        // CUSTOMIZEABLE TITLE
        m('title', 'Repositories'),
        m('link', {rel: "stylesheet", href: "/css/design-board.css"}),
        // ADDITIONAL SCRIPT CONTENT HERE
        m('script', {src: `${rootPath}frontend/top.js`}),
        // ADDITIONAL LINK CONTENT FOR PRISM THEME HERE
      ]),
      m('body', [
        m('div', {class: "container-fluid container-lg"}, [
          m('div', {class: "row"}, [
            m('div', {class: "col"}, [
              // INSERT NAVBAR CONTENT HERE
            ]),
            m('div', {class: "col-auto pt-2"}, [
              m('div', {class: "dropdown"}, [
                m('button', {class: "dropdown-toggle btn btn-bg", id: "dark-mode-switch", type: "button", 'data-bs-toggle': "dropdown", 'aria-expanded': 'false'},
                  m('span', m.trust('&#xFE0F;'))
                ),
                m('ul', {class: "dropdown-menu"}, [
                  m('li',
                    m('button', {class: "btn shadow-none", 'data-theme-pref': "light", onclick: "toggleDarkMode(this)"}, [
                      m('span', {class: "me-1"}, m.trust('&#x1F31E;')),
                      'Light'
                    ])
                  ),
                  m('li',
                    m('button', {class: "btn shadow-none", 'data-theme-pref': "dark", onclick: "toggleDarkMode(this)"}, [
                      m('span', {class: "me-1"}, m.trust('&#x1F319;')),
                      'Dark'
                    ])
                  ),
                  m('li',
                    m('button', {class: "btn shadow-none", 'data-theme-pref': "auto", onclick: "toggleDarkMode(this)"}, [
                      m('span', {class: "me-1"}, m.trust('&#x1F5A5;&#xFE0F;')),
                      'Match OS'
                    ])
                  )
                ])
              ])
            ])
          ]),
          await pageContent,
        ]),
        m('script', {
          src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js",
          integrity: "sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI",
          crossorigin: "anonymous"
        }),
        m('script', {src: `${rootPath}frontend/main-frontend.bundle.js`})
      ])
    ])
  ])
}
js_templates/index.ts:1
Before
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
    return render([
      m.trust('<!doctype html>'),
      m('html', {lang: "en"}, [
        m('head', [
          m('meta', {charset:"utf-8"}),
          m('meta', {name: "viewport", content: "width=device-width, initial-scale=1"}),
          m('title', 'Repositories'),
          m('link', {rel: "stylesheet", href: "/css/design-board.css"}),
          m('script', {src: `${data.reposPath}/frontend/top.js`}),
        ]),
        m('body', [
          m('div', {class: "container"}, [
            m('div', {class: "row d-flex justify-content-end"}, [
              m('div', {class: "col-auto pt-2"}, [
                m('div', {class: "dropdown"}, [
                    class: "dropdown-toggle btn btn-bg",
                    id: "dark-mode-switch",
                    type: "button",
                    'data-bs-toggle': "dropdown",
                    'aria-expanded': "false"
                  }, m('span', m.trust('&#xFE0F;'))),
                  m('ul', {class: "dropdown-menu"}, [
                    m('li',
                      m('button', {class: "btn shadow-none", 'data-theme-pref': "light", onclick: "toggleDarkMode(this)"},
                        m('span', {class: "me-1"}, m.trust('&#x1F31E;')),
                        'Light'
                      )
                    ),
                    m('li',
                      m('button', {class: "btn shadow-none", 'data-theme-pref': "dark", onclick: "toggleDarkMode(this)"},
                        m('span', {class: "me-1"}, m.trust('&#x1F319;')),
                        'Dark'
                      )
                    ),
                    m('li',
                      m('button', {class: "btn shadow-none", 'data-theme-pref': "auto", onclick: "toggleDarkMode(this)"},
                        m('span', {class: "me-1"}, m.trust('&#x1F5A5;&#xFE0F;')),
                        'Match OS'
                      )
                    )
                  ])
              ])
            ]),
            m('div', {class: "row my-3"},
              m('div', {class: "col"},
                m('h1', data.reposConfig.defaultTemplateConfiguration?.allRepositoriesPageTitle || "All Repositories")
              )
            ),
            m('div', {class: "row"},
              m('div', {class: "col d-flex flex-wrap"},
                data.repos.map((repo) => {
                  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', {
                          class: "mx-1 my-2 btn btn-outline-secondary shadow-none dropdown-toggle clone-popover-btn",
                          'data-copy-text': repo.cloneUrl
                        }, 'Clone'),
                        (data.reposConfig.repos[repo.name].defaultTemplateConfiguration?.homepageButtons || []).map((button) => {
                          return m('a', {
                            class: "mx-1 my-2 btn btn-outline-secondary shadow-none",
                            href: button.url,
                            target: button.newTab ? '_blank' : '_self'
                          }, [button.text, button.newTab ? [' ', m('span', m.trust('&#x29C9;'))] : null])
                        })
                      ])
                    )
                  )
                })
          ])
        ]),
        m('script', {
          src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js",
          integrity: "sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI",
          crossorigin: "anonymous"
        }),
        m('script', {src: `${data.reposPath}/frontend/main-frontend.bundle.js`}),
      ])
After
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
import commonPage from './common/commonPage.ts'
    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"},
        m('div', {class: "col d-flex flex-wrap"},
          data.repos.map((repo) => {
            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'),
                    class: "mx-1 my-2 btn btn-outline-secondary shadow-none dropdown-toggle clone-popover-btn",
                    'data-copy-text': repo.cloneUrl
                  }, 'Clone'),
                  (data.reposConfig.repos[repo.name].defaultTemplateConfiguration?.homepageButtons || []).map((button) => {
                    return m('a', {
                      class: "mx-1 my-2 btn btn-outline-secondary shadow-none",
                      href: button.url,
                      target: button.newTab ? '_blank' : '_self'
                    }, [button.text, button.newTab ? [' ', m('span', m.trust('&#x29C9;'))] : null])
                  })
          })
        )
      )

    return commonPage(`${data.reposPath}/`, pageContent)