diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | frontend/index.html | 12 | ||||
-rw-r--r-- | frontend/main.ts | 83 | ||||
-rw-r--r-- | makefile | 9 |
4 files changed, 106 insertions, 0 deletions
@@ -1,2 +1,4 @@ .stack-work bangs.json +/frontend/bundle.js* + diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..9a4c5d4 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <script type="module" src="./bundle.js"></script> + <title>Laufente</title> + </head> + <body> + <noscript>requires javascript</noscript> + </body> +</html> diff --git a/frontend/main.ts b/frontend/main.ts new file mode 100644 index 0000000..7f17fdd --- /dev/null +++ b/frontend/main.ts @@ -0,0 +1,83 @@ +/// <reference lib="dom" /> + +interface Bangs { [key: string]: string } +let bangs: Bangs = {} +let status_el: HTMLElement + +globalThis.addEventListener("hashchange", () => process_url()) +globalThis.addEventListener("load", async () => { + const bangs_res = await fetch("/bangs.json") + if (!bangs_res.ok) document.writeln("error: could not download bangs.json") + bangs = await bangs_res.json() + process_url() +}) + +function setup_page(engine?: string) { + const section = document.createElement("section") + + if (engine) { + const heading = document.createElement("h1") + heading.textContent = engine + + const input = document.createElement("input") + input.type = "input" + input.addEventListener("keydown", ev => { + if (ev.code == "Enter") process_query(engine, input.value ?? "") + }) + + const submit = document.createElement("button") + submit.textContent = "Search" + submit.addEventListener("click", () => { + process_query(engine, input.value ?? "") + }) + + const info = document.createElement("p") + info.textContent = `To install this as the default search engine, select "Add <name>" in the context-menu of the URL-bar.` + + section.append(heading, input, submit, info) + } else { + const info = document.createElement("p") + info.textContent = `todo` + section.append(info) + } + + status_el = document.createElement("p") + section.append(status_el) + document.body.append(section) +} + + +function status(text: string, color?: string) { + status_el.textContent = text + if (color) status_el.style.color = color +} + +function process_url() { + if (document.location.hash.length != 0) { + const input = document.location.hash.substring(1) + const [default_engine, query_encoded] = input.split("#") + if (!query_encoded) return setup_page(default_engine) + const query = decodeURIComponent(query_encoded) + process_query(default_engine, query) + } else { + setup_page() + } +} + +function process_query(default_engine: string, query: string) { + const bang_prefix = "!" + let url = "" + if (query.startsWith(bang_prefix)) { + const [engine, ...query_parts] = query.substring(bang_prefix.length).split(" ") + url = search_url(engine, query_parts.join(" ")) + } else { + url = search_url(default_engine, query) + } + + status(`Forwarding to ${url}`, "green") + setTimeout(() => document.location.href = url, 0) +} + +function search_url(engine: string, query: string) { + return bangs[engine].replace("{{{s}}}", encodeURIComponent(query).replaceAll("%20", "+")) +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..003ee5e --- /dev/null +++ b/makefile @@ -0,0 +1,9 @@ + +ESFLAGS = --target=esnext --sourcemap --format=esm + +all: frontend/bundle.js +watch: + esbuild frontend/main.ts --bundle --outfile=frontend/bundle.js $(ESFLAGS) --watch + +frontend/bundle.js: $(shell find frontend -name '*.ts') + esbuild frontend/main.ts --bundle --outfile=frontend/bundle.js $(ESFLAGS) |