From f35043b395adf9ffa06b71000dfba6f560d44cb7 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 26 Jul 2023 16:27:16 +0200 Subject: add a bunch of ui for the search page and style --- frontend/index.html | 3 +- frontend/main.ts | 94 +++++++---------------------------------------------- frontend/query.ts | 42 ++++++++++++++++++++++++ frontend/style.css | 2 -- frontend/style.sass | 59 +++++++++++++++++++++++++++++++++ frontend/ui.ts | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+), 86 deletions(-) create mode 100644 frontend/query.ts delete mode 100644 frontend/style.css create mode 100644 frontend/style.sass create mode 100644 frontend/ui.ts (limited to 'frontend') diff --git a/frontend/index.html b/frontend/index.html index 9a4c5d4..f2c481f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,8 @@ - Laufente + + Projectname diff --git a/frontend/main.ts b/frontend/main.ts index 71d9702..c9cd654 100644 --- a/frontend/main.ts +++ b/frontend/main.ts @@ -1,97 +1,25 @@ /// +import { load_bangs, process_query } from "./query.ts"; +import { add_page_content } from "./ui.ts" -interface Bangs { [key: string]: string } -let bangs: Bangs = {} -let status_el: HTMLElement globalThis.addEventListener("hashchange", () => process_url()) -globalThis.addEventListener("load", async () => { - status_el = document.createElement("p") - document.body.append(status_el) - - const bangs_res = await fetch("/bangs.json") - if (!bangs_res.ok) document.writeln("error: could not download bangs.json") - bangs = await bangs_res.json() +globalThis.addEventListener("load", () => { + load_bangs() // not awaiting so we can continue loading process_url() }) -function setup_page(engine?: string) { - document.getElementById("content")?.remove() - const section = document.createElement("section") - section.id = "content" - - if (engine) { - if (document.getElementById("search-link")) { - // is already present, we need reload because browser wont notice otherwise - window.location.reload() - } - const link = document.createElement("link") - link.rel = "search" - link.id = "search-link" - link.type = "application/opensearchdescription+xml" - link.href = `/search.xml?default=${encodeURIComponent(engine)}` - link.title = `Laufente (default engine: ${engine})` - document.head.append(link) - - 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 " 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) - } - 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.replaceAll("+"," ")) - 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(" ")) + if (query_encoded) { + const query = decodeURIComponent(query_encoded.replaceAll("+", " ")) + process_query(default_engine, query) + } else { + return add_page_content(default_engine) + } } else { - url = search_url(default_engine, query) + add_page_content() } - - 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/frontend/query.ts b/frontend/query.ts new file mode 100644 index 0000000..c0cc9ea --- /dev/null +++ b/frontend/query.ts @@ -0,0 +1,42 @@ +import { status } from "./ui.ts" + +// TODO embed this information into bangs.js +const ENGINE_NAMES: {[key:string]:string} = { + "ddg": "DuckDuckGo", + "qw": "Qwant", + "qwl": "Qwant Lite", + "g": "Google", +} + +interface Bangs { [key: string]: { url: string, name: string } } +export let bangs: Promise; +export function load_bangs() { + status("info", "Loading bangs...") + bangs = new Promise(r => { + (async () => { + const bangs_res = await fetch("/bangs.json") + if (!bangs_res.ok) return status("error", "could not download bangs.json") + const k: {[key:string]:string} = await bangs_res.json() + status("info", "Bangs loaded.") + r(Object.fromEntries(Object.entries(k).map(([key,url]) => [key, { url, name: ENGINE_NAMES[key] ?? key }]))) + })() + }) +} + +export async 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 = await search_url(engine, query_parts.join(" ")) + } else { + url = await search_url(default_engine, query) + } + + status("success", `Forwarding to ${url}`) + setTimeout(() => document.location.href = url, 0) +} + +async function search_url(engine: string, query: string) { + return (await bangs)[engine].url.replace("{{{s}}}", encodeURIComponent(query).replaceAll("%20", "+")) +} diff --git a/frontend/style.css b/frontend/style.css deleted file mode 100644 index 139597f..0000000 --- a/frontend/style.css +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/frontend/style.sass b/frontend/style.sass new file mode 100644 index 0000000..e61799a --- /dev/null +++ b/frontend/style.sass @@ -0,0 +1,59 @@ +$ac-dark: rgb(52, 35, 50) +$ac-light: rgb(255, 116, 227) + +body + background-color: $ac-dark + margin: 0px + +p, h1, h2, h3, h4, h5, h6, input, button + color: white + +section.info + margin: 5em + +.status + background-color: black + padding: 1em + margin: 0px + text-align: center + box-shadow: 0px 0px 10px 0px black + + &.level-error + color: #ff0000 + + &.level-success + color: #00ff00 + + +section.search + width: 100vw + height: 50vh + display: flex; + flex-direction: column + place-content: center; + align-items: center; + + font-size: large + font-weight: 500 + + input, button + border-radius: 0.5em + padding: 0.5em + + input + width: 25em + border: 0.15em solid $ac-light + background-color: rgba(0, 0, 0, 0.3) + outline: none + + &:focus + transition: 0.1s ease-out + background-color: rgba(0, 0, 0, 0.5) + border-color: lightgray !important + + button + margin-left: 1em + background-color: $ac-light + backdrop-filter: blur(5px) + border: 0 + cursor: pointer diff --git a/frontend/ui.ts b/frontend/ui.ts new file mode 100644 index 0000000..4096204 --- /dev/null +++ b/frontend/ui.ts @@ -0,0 +1,92 @@ +import { bangs, process_query } from "./query.ts"; + +export function add_page_content(engine?: string) { + document.getElementById("content")?.remove() + if (engine) { + document.body.append(create_search_bar(engine), create_search_info()) + } else { + document.body.append(create_start_page()) + } +} + +function create_search_bar(engine: string) { + const section = document.createElement("section") + section.classList.add("search") + + if (document.getElementById("search-link")) { + // is already present, we need reload because browser wont notice otherwise + window.location.reload() + } + const link = document.createElement("link") + link.rel = "search" + link.id = "search-link" + link.type = "application/opensearchdescription+xml" + link.href = `/search.xml?default=${encodeURIComponent(engine)}` + link.title = `Projectname (default engine: ${engine})` + document.head.append(link) + + const heading = document.createElement("h1") + heading.textContent = engine + bangs.then(bangs => heading.textContent = bangs[engine].name) + + 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 search = document.createElement("div") + search.classList.add("bar") + search.append(input, submit) + + section.append(heading, search) + return section +} + +function create_search_info() { + const section = document.createElement("section") + section.classList.add("info") + + const heading = document.createElement("h1") + heading.textContent = "Heading" + + const info = document.createElement("p") + info.textContent = `To install this as the default search engine, select "Add " in the context-menu of the URL-bar.` + + section.append(heading, info) + return section +} + + +function create_start_page() { + const section = document.createElement("section") + section.id = "content" + + const heading = document.createElement("h1") + heading.textContent = "Projectname" + + const info = document.createElement("p") + info.textContent = "This is where one would write what this application does..." + + section.append(heading, info) + return section +} + + + +let status_el: HTMLElement +export function status(level: "error" | "info" | "success", text: string) { + if (!status_el) { + status_el = document.createElement("p") + document.body.append(status_el) + } + status_el.textContent = text + status_el.classList.value = "" + status_el.classList.add("status", `level-${level}`) +} -- cgit v1.2.3-70-g09d2