Tucker McKnight <tucker@pangolin.lan> | Sun Feb 01 2026
Changes file.ts to mithril template Also fix a small problem with files.ts where it was showing the "view raw file" link on a directory's page.
1
1
import m from 'mithril'
import render from 'mithril-node-render'
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
return `
<div class="row mt-3 mb-1">
<div class="col">
<p>Files snapshot from <span class="font-monospace">${data.fileInfo.branchName}</span></p>
</div>
</div>
<div class="row my-1">
<div class="col">
<h3>
<span class="bezel-gray px-1"><a href="${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files">./</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).map((part) => slugify(part)).join('/')}.html">${dir}/</a></span>`
}
}).join('')
}
</h3>
</div>
</div>
<div class="row">
<div class="col">
<p><a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/raw/${data.fileInfo.file.split('.').map(filePart => slugify(filePart)).join('.')}">View raw file</a></p>
</div>
</div>
${isDirectory(data.fileInfo.file, data.fileInfo.repoName, data.fileInfo.branchName) ?
`<div class="row">
<div class="col">
<ul class="list-group">
${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>📁</span>` : ''}
<a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => {
return pathPart.split('.').map((subPart) => {
return slugify(subPart)
}).join('.')
}).join('/')}.html">${getRelativePath(data.fileInfo.file, dir.name)}</a>
</li>`
}).join('')}
</ul>
</div>
</div>`
:
`
${data.fileInfo.file.endsWith(".md")
? `
<div class="row">
<div class="col">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="showRenderedContent" checked />
<label class="form-check-label" for="showRenderedContent">Show rendered markdown</label>
</div>
</div>
</div>
<div class="row rendered-content py-4">
<div class="col">
${await renderContentIfAvailable(await getFileContents(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
), data.fileInfo.branchName)}
</div>
</div>
`
: ''
}
<div class="row code-content ${data.fileInfo.file.endsWith('.md') ? 'd-none' : ''}">
<div class="col">
<div class="row py-2">
<div class="col-auto">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="showLastTouch">
<label class="form-check-label" for="showLastTouch">Show last line change</label>
</div>
</div>
</div>
<div class="row">
<div class="col-auto p-0">
<code style="white-space: pre;"><pre class="language-text">${lineNumbers(await getFileContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file)).map((lineNumber) => {
return lineNumber
}).join('\n')}</pre></code>
</div>
<div id="annotations" class="col-auto d-none p-0">
<code style="white-space: pre;"><pre class="language-text">${
(await getFileLastTouchInfo(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
)).map(
(annotation) => {
return `<a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
}
).join('\n')
}</pre></code>
</div>
<div class="col overflow-scroll p-0">
<code>
${
highlightCode(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
return render([
m('div', {class: "row mt-3 mb-1"},
m('div', {class: "col"},
m('p', [
'Files snapshot from ',
m('span', {class: "font-monospace"}, data.fileInfo.branchName)
])
)
),
m('div', {class: "row my-1"},
m('div', {class: "col"},
m('h3', [
m('span', {class: "bezel-gray px-1"},
m('a', {href: `${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files`}, './')
),
data.fileInfo.file.split('/').map((dir, index, arr) => {
if (index === arr.length - 1) {
return m('span', {class: "px-2"}, dir)
}
else {
return m('span', {class: "bezel-gray px-1"}, [
m('a', {
href: `${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files/${arr.slice(0, index + 1).map((part) => slugify(part)).join('/')}.html`}, dir)
])
}
})
])
)
),
(isDirectory(data.fileInfo.file, data.fileInfo.repoName, data.fileInfo.branchName) ?
m('div', {class: "row"},
m('div', {class: "col"},
m('ul', {class: "list-group"},
topLevelFilesOnly(getDirectoryContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file), data.fileInfo.file + '/').map((dir) => {
return m('li', {class: 'list-group-item'}, [
dir.isDirectory ? m('span', m.trust('📁')) : null,
m('a', {
href: `${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => {
return pathPart.split('.').map((subPart) => {
return slugify(subPart)
}).join('.')
}).join('/')}.html`},
getRelativePath(data.fileInfo.file, dir.name)
)
])
})
)
)
)
: [
m('div', {class: "row"},
m('div', {class: "col"},
m('p', m('a', {
href: `${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/raw/${data.fileInfo.file.split('.').map(filePart => slugify(filePart)).join('.')}`}, 'View raw file'))
)
),
(data.fileInfo.file.endsWith(".md") ?
[
m('div', {class: "row"},
m('div', {class: "col"},
m('div', {class: "form-check form-switch"}, [
m('input', {
class: "form-check-input",
type: "checkbox",
role: "switch",
id: "showRenderedContent",
checked: true
}),
m('label', {
class: "form-check-label",
for: "showRenderedContent"
}, 'Show rendered markdown')
])
)
),
m('div', {class: "row rendered-content py-4"},
m('div', {class: "col"},
m.trust(await renderContentIfAvailable(await getFileContents(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
), data.fileInfo.branchName)
))
)
]
: null),
m('div', {class: `row code-content ${data.fileInfo.file.endsWith('.md') ? 'd-none' : ''}`},
m('div', {class: "col"}, [
m('div', {class: "row py-2"},
m('div', {class: "col-auto"},
m('div', {class: "form-check form-switch"}, [
m('input', {class: "form-check-input", type: "checkbox", role: "switch", id: "showLastTouch"}),
m('label', {class: "form-check-label", for: "showLastTouch"}, 'Show last line change'),
])
)
),
m('div', {class: "row"}, [
m('div', {class: "col-auto p-0"},
m('code', {style: "white-space: pre;"},
m('pre', {class: "language-text"},
lineNumbers(await getFileContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file)).map((lineNumber) => {
return lineNumber
}).join('\n')
)
)
),
m('div', {id: "annotations", class: "col-auto d-none p-0"},
m('code', {style: "white-space: pre;"}, [
m('pre', {class: "language-text"}, m.trust(
(await getFileLastTouchInfo(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
)).map((annotation) => {
return `<a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
}).join('\n')))
])
),
m('div', {class: "col overflow-scroll p-0"},
m('code', m.trust(highlightCode(121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
)
}
</code>
</div>
</div>
</div>
</div>
`}
<script type="text/javascript">
const toggleLastTouch = (event) => {
const isOn = event.target.checked
const annotations = document.getElementById("annotations")
if (isOn) {
annotations.classList.remove("d-none")
} else {
annotations.classList.add("d-none")
}
}
document.getElementById("showLastTouch")?.addEventListener('click', toggleLastTouch)
</script>
`121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
)))
)
])
])
),
m('script', {type: "text/javascript"},
`
const toggleLastTouch = (event) => {
const isOn = event.target.checked
const annotations = document.getElementById("annotations")
if (isOn) {
annotations.classList.remove("d-none")
} else {
annotations.classList.add("d-none")
}
}
document.getElementById("showLastTouch")?.addEventListener('click', toggleLastTouch)
`
)
]
)
])24 25
return m('li', [
file.isDirectory ? '<span>📁</span>' : null,24 25
return m('li', {class: 'list-group-item'}, [
file.isDirectory ? m.trust('<span>📁</span>') : null,