diff options
Diffstat (limited to 'cmd/mbf/internal/pkgserver/ui')
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/index.html | 58 | ||||
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/index.ts | 331 | ||||
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/style.css | 21 | ||||
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/tsconfig.json | 8 | ||||
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/ui.go | 9 | ||||
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/ui_full.go | 21 | ||||
| -rw-r--r-- | cmd/mbf/internal/pkgserver/ui/ui_stub.go | 7 |
7 files changed, 0 insertions, 455 deletions
diff --git a/cmd/mbf/internal/pkgserver/ui/index.html b/cmd/mbf/internal/pkgserver/ui/index.html deleted file mode 100644 index 6a5d72b4..00000000 --- a/cmd/mbf/internal/pkgserver/ui/index.html +++ /dev/null @@ -1,58 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" href="style.css"> - <link rel="icon" href="https://hakurei.app/favicon.ico"/> - <title>Rosa OS Packages</title> - <script src="index.js"></script> -</head> -<body> -<h1>Rosa OS Packages</h1> -<div class="top-controls" id="top-controls-regular"> - <p>Showing entries <span id="entry-counter"></span>.</p> - <span id="search-bar"> - <label for="search">Search: </label> - <input type="text" name="search" id="search"/> - <button onclick="doSearch()">Find</button> - <label for="include-desc">Include descriptions: </label> - <input type="checkbox" name="include-desc" id="include-desc" checked/> - </span> - <div><label for="count">Entries per page: </label><select name="count" id="count"> - <option value="10">10</option> - <option value="20">20</option> - <option value="30">30</option> - <option value="50">50</option> - </select></div> - <div><label for="sort">Sort by: </label><select name="sort" id="sort"> - <option value="0">Definition (ascending)</option> - <option value="1">Definition (descending)</option> - <option value="2">Name (ascending)</option> - <option value="3">Name (descending)</option> - <option value="4">Size (ascending)</option> - <option value="5">Size (descending)</option> - </select></div> -</div> -<div class="top-controls" id="search-top-controls" hidden> - <p>Showing search results <span id="search-entry-counter"></span> for query "<span id="search-query"></span>".</p> - <button onclick="exitSearch()">Back</button> - <div><label for="search-count">Entries per page: </label><select name="search-count" id="search-count"> - <option value="10">10</option> - <option value="20">20</option> - <option value="30">30</option> - <option value="50">50</option> - </select></div> - <p>Sorted by best match</p> -</div> -<div class="page-controls"><a href="javascript:prevPage()">« Previous</a> <input type="text" class="page-number" value="1"/> <a href="javascript:nextPage()">Next »</a></div> -<table id="pkg-list"> - <tr><td>Loading...</td></tr> -</table> -<div class="page-controls"><a href="javascript:prevPage()">« Previous</a> <input type="text" class="page-number" value="1"/> <a href="javascript:nextPage()">Next »</a></div> -<footer> - <p>©<a href="https://hakurei.app/">Hakurei</a> (<span id="hakurei-version">unknown</span>). Licensed under the MIT license.</p> -</footer> -<script>main();</script> -</body> -</html> diff --git a/cmd/mbf/internal/pkgserver/ui/index.ts b/cmd/mbf/internal/pkgserver/ui/index.ts deleted file mode 100644 index 0784b81f..00000000 --- a/cmd/mbf/internal/pkgserver/ui/index.ts +++ /dev/null @@ -1,331 +0,0 @@ -interface PackageIndexEntry { - name: string - size?: number - description?: string - website?: string - version?: string - report?: boolean -} - -function entryToHTML(entry: PackageIndexEntry | SearchResult): HTMLTableRowElement { - let v = entry.version != null ? `<span>${escapeHtml(entry.version)}</span>` : "" - let s = entry.size != null && entry.size > 0 ? `<p>Size: ${toByteSizeString(entry.size)} (${entry.size})</p>` : "" - let n: string - let d: string - if ('name_matches' in entry) { - n = `<h2>${nameMatches(entry as SearchResult)} ${v}</h2>` - } else { - n = `<h2>${escapeHtml(entry.name)} ${v}</h2>` - } - if ('desc_matches' in entry && STATE.getIncludeDescriptions()) { - d = descMatches(entry as SearchResult) - } else { - d = (entry as PackageIndexEntry).description != null ? `<p>${escapeHtml((entry as PackageIndexEntry).description)}</p>` : "" - } - let w = entry.website != null ? `<a href="${encodeURI(entry.website)}">Website</a>` : "" - let r = entry.report ? `Log (<a href=\"${encodeURI('/api/v1/status/' + entry.name)}\">View</a> | <a href=\"${encodeURI('/status/' + entry.name)}\">Download</a>)` : "" - let row = <HTMLTableRowElement>(document.createElement('tr')) - row.innerHTML = `<td> - ${n} - ${d} - ${s} - ${w} - ${r} - </td>` - return row -} - -function nameMatches(sr: SearchResult): string { - return markMatches(sr.name, sr.name_matches) -} - -function descMatches(sr: SearchResult): string { - return markMatches(sr.description!, sr.desc_matches) -} - -function markMatches(str: string, indices: [number, number][]): string { - if (indices == null) { - return str - } - let out: string = "" - let j = 0 - for (let i = 0; i < str.length; i++) { - if (j < indices.length) { - if (i === indices[j][0]) { - out += `<mark>${escapeHtmlChar(str[i])}` - continue - } - if (i === indices[j][1]) { - out += `</mark>${escapeHtmlChar(str[i])}` - j++ - continue - } - } - out += escapeHtmlChar(str[i]) - } - if (indices[j] !== undefined) { - out += "</mark>" - } - return out -} - -function toByteSizeString(bytes: number): string { - if (bytes == null) return `unspecified` - if (bytes < 1024) return `${bytes}B` - if (bytes < Math.pow(1024, 2)) return `${(bytes / 1024).toFixed(2)}kiB` - if (bytes < Math.pow(1024, 3)) return `${(bytes / Math.pow(1024, 2)).toFixed(2)}MiB` - if (bytes < Math.pow(1024, 4)) return `${(bytes / Math.pow(1024, 3)).toFixed(2)}GiB` - if (bytes < Math.pow(1024, 5)) return `${(bytes / Math.pow(1024, 4)).toFixed(2)}TiB` - return "not only is it big, it's large" -} - -const API_VERSION = 1 -const ENDPOINT = `/api/v${API_VERSION}` - -interface InfoPayload { - count?: number - hakurei_version?: string -} - -async function infoRequest(): Promise<InfoPayload> { - const res = await fetch(`${ENDPOINT}/info`) - const payload = await res.json() - return payload as InfoPayload -} - -interface GetPayload { - values?: PackageIndexEntry[] -} - -enum SortOrders { - DeclarationAscending, - DeclarationDescending, - NameAscending, - NameDescending -} - -async function getRequest(limit: number, index: number, sort: SortOrders): Promise<GetPayload> { - const res = await fetch(`${ENDPOINT}/get?limit=${limit}&index=${index}&sort=${sort.valueOf()}`) - const payload = await res.json() - return payload as GetPayload -} - -interface SearchResult extends PackageIndexEntry { - name_matches: [number, number][] - desc_matches: [number, number][] - score: number -} - -interface SearchPayload { - count?: number - values?: SearchResult[] -} - -async function searchRequest(limit: number, index: number, search: string, desc: boolean): Promise<SearchPayload> { - const res = await fetch(`${ENDPOINT}/search?limit=${limit}&index=${index}&search=${encodeURIComponent(search)}&desc=${desc}`) - if (!res.ok) { - exitSearch() - alert("invalid search query!") - return Promise.reject(res.statusText) - } - const payload = await res.json() - return payload as SearchPayload -} - -class State { - entriesPerPage: number = 10 - entryIndex: number = 0 - maxTotal: number = 0 - maxEntries: number = 0 - sort: SortOrders = SortOrders.DeclarationAscending - search: boolean = false - - getEntriesPerPage(): number { - return this.entriesPerPage - } - - setEntriesPerPage(entriesPerPage: number) { - this.entriesPerPage = entriesPerPage - this.setEntryIndex(Math.floor(this.getEntryIndex() / entriesPerPage) * entriesPerPage) - } - - getEntryIndex(): number { - return this.entryIndex - } - - setEntryIndex(entryIndex: number) { - this.entryIndex = entryIndex - this.updatePage() - this.updateRange() - this.updateListings() - } - - getMaxTotal(): number { - return this.maxTotal - } - - setMaxTotal(max: number) { - this.maxTotal = max - } - - getSortOrder(): SortOrders { - return this.sort - } - - setSortOrder(sortOrder: SortOrders) { - this.sort = sortOrder - this.setEntryIndex(0) - } - - updatePage() { - let page = Math.ceil(((this.getEntryIndex() + this.getEntriesPerPage()) - 1) / this.getEntriesPerPage()) - for (let e of document.getElementsByClassName("page-number")) { - (e as HTMLInputElement).value = String(page) - } - } - - updateRange() { - let max = Math.min(this.getEntryIndex() + this.getEntriesPerPage(), this.getMaxTotal()) - document.getElementById("entry-counter")!.textContent = `${this.getEntryIndex() + 1}-${max} of ${this.getMaxTotal()}` - if (this.search) { - document.getElementById("search-entry-counter")!.textContent = `${this.getEntryIndex() + 1}-${max} of ${this.maxTotal}/${this.maxEntries}` - document.getElementById("search-query")!.innerHTML = `<code>${escapeHtml(this.getSearchQuery())}</code>` - } - } - - getSearchQuery(): string { - let queryString = document.getElementById("search")!; - return (queryString as HTMLInputElement).value - } - - getIncludeDescriptions(): boolean { - let includeDesc = document.getElementById("include-desc")!; - return (includeDesc as HTMLInputElement).checked - } - - updateListings() { - if (this.search) { - searchRequest(this.getEntriesPerPage(), this.getEntryIndex(), this.getSearchQuery(), this.getIncludeDescriptions()) - .then(res => { - let table = document.getElementById("pkg-list")! - table.innerHTML = '' - for (let row of res.values!) { - table.appendChild(entryToHTML(row)) - } - STATE.maxTotal = res.count! - STATE.updateRange() - if(res.count! < 1) { - exitSearch() - alert("no results found!") - } - }) - } else { - getRequest(this.getEntriesPerPage(), this.getEntryIndex(), this.getSortOrder()) - .then(res => { - let table = document.getElementById("pkg-list")! - table.innerHTML = '' - for (let row of res.values!) { - table.appendChild(entryToHTML(row)) - } - }) - } - } -} - -let STATE: State - - -function lastPageIndex(): number { - return Math.floor(STATE.getMaxTotal() / STATE.getEntriesPerPage()) * STATE.getEntriesPerPage() -} - -function setPage(page: number) { - STATE.setEntryIndex(Math.max(0, Math.min(STATE.getEntriesPerPage() * (page - 1), lastPageIndex()))) -} - - -function escapeHtml(str?: string): string { - let out: string = '' - if (str == undefined) return "" - for (let i = 0; i < str.length; i++) { - out += escapeHtmlChar(str[i]) - } - return out -} - -function escapeHtmlChar(char: string): string { - if (char.length != 1) return char - switch (char[0]) { - case '&': - return "&" - case '<': - return "<" - case '>': - return ">" - case '"': - return """ - case "'": - return "'" - default: - return char - } -} - -function firstPage() { - STATE.setEntryIndex(0) -} - -function prevPage() { - let index = STATE.getEntryIndex() - STATE.setEntryIndex(Math.max(0, index - STATE.getEntriesPerPage())) -} - -function lastPage() { - STATE.setEntryIndex(lastPageIndex()) -} - -function nextPage() { - let index = STATE.getEntryIndex() - STATE.setEntryIndex(Math.min(lastPageIndex(), index + STATE.getEntriesPerPage())) -} - -function doSearch() { - document.getElementById("top-controls-regular")!.toggleAttribute("hidden"); - document.getElementById("search-top-controls")!.toggleAttribute("hidden"); - STATE.search = true; - STATE.setEntryIndex(0); -} - -function exitSearch() { - document.getElementById("top-controls-regular")!.toggleAttribute("hidden"); - document.getElementById("search-top-controls")!.toggleAttribute("hidden"); - STATE.search = false; - STATE.setMaxTotal(STATE.maxEntries) - STATE.setEntryIndex(0) -} - -function main() { - STATE = new State() - infoRequest() - .then(res => { - STATE.maxEntries = res.count! - STATE.setMaxTotal(STATE.maxEntries) - document.getElementById("hakurei-version")!.textContent = res.hakurei_version! - STATE.updateRange() - STATE.updateListings() - }) - for (let e of document.getElementsByClassName("page-number")) { - e.addEventListener("change", (_) => { - setPage(parseInt((e as HTMLInputElement).value)) - }) - } - document.getElementById("count")?.addEventListener("change", (event) => { - STATE.setEntriesPerPage(parseInt((event.target as HTMLSelectElement).value)) - }) - document.getElementById("sort")?.addEventListener("change", (event) => { - STATE.setSortOrder(parseInt((event.target as HTMLSelectElement).value)) - }) - document.getElementById("search")?.addEventListener("keyup", (event) => { - if (event.key === 'Enter') doSearch() - }) -}
\ No newline at end of file diff --git a/cmd/mbf/internal/pkgserver/ui/style.css b/cmd/mbf/internal/pkgserver/ui/style.css deleted file mode 100644 index b4f281ac..00000000 --- a/cmd/mbf/internal/pkgserver/ui/style.css +++ /dev/null @@ -1,21 +0,0 @@ -.page-number { - width: 2em; - text-align: center; -} -.page-number { - width: 2em; - text-align: center; -} - -@media (prefers-color-scheme: dark) { - html { - background-color: #2c2c2c; - color: ghostwhite; - } -} -@media (prefers-color-scheme: light) { - html { - background-color: #d3d3d3; - color: black; - } -}
\ No newline at end of file diff --git a/cmd/mbf/internal/pkgserver/ui/tsconfig.json b/cmd/mbf/internal/pkgserver/ui/tsconfig.json deleted file mode 100644 index 24df4936..00000000 --- a/cmd/mbf/internal/pkgserver/ui/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2024", - "strict": true, - "alwaysStrict": true, - "outDir": "static" - } -}
\ No newline at end of file diff --git a/cmd/mbf/internal/pkgserver/ui/ui.go b/cmd/mbf/internal/pkgserver/ui/ui.go deleted file mode 100644 index 3fc0d89c..00000000 --- a/cmd/mbf/internal/pkgserver/ui/ui.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package ui holds the static web UI. -package ui - -import "net/http" - -// Register arranges for mux to serve the embedded frontend. -func Register(mux *http.ServeMux) { - mux.Handle("GET /", http.FileServer(http.FS(static))) -} diff --git a/cmd/mbf/internal/pkgserver/ui/ui_full.go b/cmd/mbf/internal/pkgserver/ui/ui_full.go deleted file mode 100644 index 564787dc..00000000 --- a/cmd/mbf/internal/pkgserver/ui/ui_full.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build frontend - -package ui - -import ( - "embed" - "io/fs" -) - -//go:generate tsc -//go:generate cp index.html style.css static -//go:embed static -var _static embed.FS - -var static = func() fs.FS { - if f, err := fs.Sub(_static, "static"); err != nil { - panic(err) - } else { - return f - } -}() diff --git a/cmd/mbf/internal/pkgserver/ui/ui_stub.go b/cmd/mbf/internal/pkgserver/ui/ui_stub.go deleted file mode 100644 index daf010f8..00000000 --- a/cmd/mbf/internal/pkgserver/ui/ui_stub.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !frontend - -package ui - -import "testing/fstest" - -var static fstest.MapFS |
