aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-07-26 16:27:16 +0200
committermetamuffin <metamuffin@disroot.org>2023-07-26 16:27:16 +0200
commitf35043b395adf9ffa06b71000dfba6f560d44cb7 (patch)
treef95ecf9785b86da750064510038b5159f36ce2c5 /frontend
parent0ae2d031aa2bf13f9e0ba90b945e79cd614cfd7e (diff)
downloadfastbangs-f35043b395adf9ffa06b71000dfba6f560d44cb7.tar
fastbangs-f35043b395adf9ffa06b71000dfba6f560d44cb7.tar.bz2
fastbangs-f35043b395adf9ffa06b71000dfba6f560d44cb7.tar.zst
add a bunch of ui for the search page and style
Diffstat (limited to 'frontend')
-rw-r--r--frontend/index.html3
-rw-r--r--frontend/main.ts94
-rw-r--r--frontend/query.ts42
-rw-r--r--frontend/style.css2
-rw-r--r--frontend/style.sass59
-rw-r--r--frontend/ui.ts92
6 files changed, 206 insertions, 86 deletions
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 @@
<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>
+ <link rel="stylesheet" href="./style.css" />
+ <title>Projectname</title>
</head>
<body>
<noscript>requires javascript</noscript>
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 @@
/// <reference lib="dom" />
+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")) {
- // <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 <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)
- }
- 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<Bangs>;
+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")) {
+ // <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 <name>" 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}`)
+}