From e1e7853c34340a0279b351e3e62dd6250ece2e26 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sat, 25 May 2024 03:58:11 +0100 Subject: [PATCH] Update search widget --- docs/configuration.md | 108 ++++++++++++++------ docs/images/search-widget-bangs-preview.png | Bin 0 -> 5677 bytes docs/images/search-widget-preview.png | Bin 39677 -> 4940 bytes internal/assets/static/main.css | 96 +++++++++++++++++ internal/assets/static/main.js | 98 ++++++++++++++++++ internal/assets/templates/search.html | 36 ++++--- internal/widget/search.go | 52 ++++++++-- 7 files changed, 334 insertions(+), 56 deletions(-) create mode 100644 docs/images/search-widget-bangs-preview.png diff --git a/docs/configuration.md b/docs/configuration.md index 8fac540..6caedd7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -11,6 +11,7 @@ - [Videos](#videos) - [Hacker News](#hacker-news) - [Reddit](#reddit) + - [Search](#search-widget) - [Weather](#weather) - [Monitor](#monitor) - [Releases](#releases) @@ -22,7 +23,6 @@ - [Twitch Channels](#twitch-channels) - [Twitch Top Games](#twitch-top-games) - [iframe](#iframe) - - [Search](#search) ## Intro Configuration is done via a single YAML file and a server restart is required in order for any changes to take effect. Trying to start the server with an invalid config file will result in an error. @@ -642,6 +642,80 @@ Can be used to specify an additional sort which will be applied on top of the al The `engagement` sort tries to place the posts with the most points and comments on top, also prioritizing recent over old posts. +### Search Widget +Display a search bar that can be used to search for specific terms on various search engines. + +Example: + +```yaml +- type: search + search-engine: duckduckgo + bangs: + - title: YouTube + shortcut: "!yt" + url: https://www.youtube.com/results?search_query={QUERY} +``` + +Preview: + +![](images/search-widget-preview.png) + +#### Keyboard shortcuts +| Keys | Action | Condition | +| ---- | ------ | --------- | +| S | Focus the search bar | Not already focused on another input field | +| Enter | Perform search in the same tab | Search input is focused and not empty | +| Ctrl + Enter | Perform search in a new tab | Search input is focused and not empty | +| Escape | Leave focus | Search input is focused | + +#### Properties +| Name | Type | Required | Default | +| ---- | ---- | -------- | ------- | +| search-engine | string | no | duckduckgo | +| bangs | array | no | | + +##### `search-engine` +Either a value from the table below or a URL to a custom search engine. Use `{QUERY}` to indicate where the query value gets placed. + +| Name | URL | +| ---- | --- | +| duckduckgo | `https://duckduckgo.com/?q={QUERY}` | +| google | `https://www.google.com/search?q={QUERY}` | + +##### `bangs` +What now? [Bangs](https://duckduckgo.com/bangs). They're shortcuts that allow you to use the same search box for many different sites. Assuming you have it configured, if for example you start your search input with `!yt` you'd be able to perform a search on YouTube: + +![](images/search-widget-bangs-preview.png) + +##### Properties for each bang +| Name | Type | Required | +| ---- | ---- | -------- | +| title | string | no | +| shortcut | string | yes | +| url | string | yes | + +###### `title` +Optional title that will appear on the right side of the search bar when the query starts with the associated shortcut. + +###### `shortcut` +Any value you wish to use as the shortcut for the search engine. It does not have to start with `!`. + +> [!IMPORTANT] +> +> In YAML some characters have special meaning when placed in the beginning of a value. If your shortcut starts with `!` (and potentially some other special characters) you'll have to wrap the value in quotes: +> ```yaml +> shortcut: "!yt" +>``` + +###### `url` +The URL of the search engine. Use `{QUERY}` to indicate where the query value gets placed. Examples: + +```yaml +url: https://www.reddit.com/search?q={QUERY} +url: https://store.steampowered.com/search/?term={QUERY} +url: https://www.amazon.com/s?k={QUERY} +``` + ### Weather Display weather information for a specific location. The data is provided by https://open-meteo.com/. @@ -1189,35 +1263,3 @@ The source of the iframe. ##### `height` The height of the iframe. The minimum allowed height is 50. - -### Search -Display a search bar that can be used to search for specific terms on various search engines. - -Example: - -```yaml -- type: search - search-url: https://www.google.com/search?q= - query: This is a default search -``` - -Preview: - -![](images/search-widget-preview.png) - -#### Properties -| Name | Type | Required | Default | -| ---- | ---- | -------- | ------- | -| search-url | string | no | https://duckduckgo.com/?q= | -| query | string | no | | - -##### `search-url` -The URL to use for the search. The query will be appended to the end of the URL. Some common examples: -- Google: `https://www.google.com/search?q=` -- DuckDuckGo: `https://duckduckgo.com/?q=` -- Bing: `https://www.bing.com/search?q=` -- Perplexity AI: `https://perplexity.ai/search?q=` -- ChatGPT (requires ChatGPT Plus subscription): `https://chatgpt.com/?model=gpt-4o&oai-dm=1&q=` - -##### `query` -The default query to show in the search bar. If left blank the search bar will be empty. diff --git a/docs/images/search-widget-bangs-preview.png b/docs/images/search-widget-bangs-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..9490690eddbebc52f18fe4001e7cacb22649a09f GIT binary patch literal 5677 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 BRXYFx literal 39677 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 .search-icon { + animation: searchIconHover 2.9s forwards; +} + +@keyframes searchIconHover { + 0%, 39% { translate: 0 0; } + 20% { scale: 1.3; } + 40% { scale: 1; } + 50% { translate: -30% 30%; } + 70% { translate: 30% -30%; } + 90% { translate: -30% -30%; } + 100% { translate: 0 0; } +} + +.search { + transition: border-color .2s; + position: relative; +} + +.search:hover { + border-color: var(--color-text-subdue); +} + +.search:focus-within { + border-color: var(--color-primary); +} + +.search-input { + border: 0; + background: none; + width: 100%; + height: 6rem; + font: inherit; + outline: none; +} + +.search-input::placeholder { + color: var(--color-text-base-muted); + opacity: 1; +} + +.search-bangs { display: none; } + +.search-bang { + border-radius: calc(var(--border-radius) * 2); + background: var(--color-widget-background-highlight); + padding: 0.3rem 1rem; + flex-shrink: 0; + font-size: var(--font-size-h5); + animation: searchBangsEntrance .3s cubic-bezier(0.25, 1, 0.5, 1) backwards; +} + +@keyframes searchBangsEntrance { + 0% { + opacity: 0; + transform: translateX(-10px); + } +} + +.search-bang:empty { + display: none; +} + .forum-post-list-item { display: flex; gap: 1.2rem; diff --git a/internal/assets/static/main.js b/internal/assets/static/main.js index 146f781..3e10b96 100644 --- a/internal/assets/static/main.js +++ b/internal/assets/static/main.js @@ -107,6 +107,103 @@ function updateRelativeTimeForElements(elements) } } +function setupSearchboxes() { + const searchWidgets = document.getElementsByClassName("search"); + + if (searchWidgets.length == 0) { + return; + } + + for (let i = 0; i < searchWidgets.length; i++) { + const widget = searchWidgets[i]; + const defaultSearchUrl = widget.dataset.defaultSearchUrl; + const inputElement = widget.getElementsByClassName("search-input")[0]; + const bangElement = widget.getElementsByClassName("search-bang")[0]; + const bangs = widget.querySelectorAll(".search-bangs > input"); + const bangsMap = {}; + const kbdElement = widget.getElementsByTagName("kbd")[0]; + let currentBang = null; + + for (let j = 0; j < bangs.length; j++) { + const bang = bangs[j]; + bangsMap[bang.dataset.shortcut] = bang; + } + + const handleKeyDown = (event) => { + if (event.key == "Escape") { + inputElement.blur(); + return; + } + + if (event.key == "Enter") { + const input = inputElement.value.trim(); + let query; + let searchUrlTemplate; + + if (currentBang != null) { + query = input.slice(currentBang.dataset.shortcut.length + 1); + searchUrlTemplate = currentBang.dataset.url; + } else { + query = input; + searchUrlTemplate = defaultSearchUrl; + } + + if (query.length == 0) { + return; + } + + const url = searchUrlTemplate.replace("!QUERY!", encodeURIComponent(query)); + + if (event.ctrlKey) { + window.open(url, '_blank').focus(); + } else { + window.location.href = url; + } + + return; + } + }; + + const changeCurrentBang = (bang) => { + currentBang = bang; + bangElement.textContent = bang != null ? bang.dataset.title : ""; + } + + const handleInput = (event) => { + const value = event.target.value.trimStart(); + const words = value.split(" "); + + if (words.length >= 2 && words[0] in bangsMap) { + changeCurrentBang(bangsMap[words[0]]); + return; + } + + changeCurrentBang(null); + }; + + inputElement.addEventListener("focus", () => { + document.addEventListener("keydown", handleKeyDown); + document.addEventListener("input", handleInput); + }); + inputElement.addEventListener("blur", () => { + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("input", handleInput); + }); + + document.addEventListener("keydown", (event) => { + if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return; + if (event.key != "s") return; + + inputElement.focus(); + event.preventDefault(); + }); + + kbdElement.addEventListener("mousedown", () => { + requestAnimationFrame(() => inputElement.focus()); + }); + } +} + function setupDynamicRelativeTime() { const elements = document.querySelectorAll("[data-dynamic-relative-time]"); const updateInterval = 60 * 1000; @@ -454,6 +551,7 @@ async function setupPage() { try { setupClocks() setupCarousels(); + setupSearchboxes(); setupCollapsibleLists(); setupCollapsibleGrids(); setupDynamicRelativeTime(); diff --git a/internal/assets/templates/search.html b/internal/assets/templates/search.html index 211ed0c..a0dfaeb 100644 --- a/internal/assets/templates/search.html +++ b/internal/assets/templates/search.html @@ -1,18 +1,24 @@ {{ template "widget-base.html" . }} - + +{{ define "widget-content-classes" }}widget-content-frameless{{ end }} + {{ define "widget-content" }} -
-
- - -
-
+ {{ end }} diff --git a/internal/widget/search.go b/internal/widget/search.go index 1786ee1..9cfd64e 100644 --- a/internal/widget/search.go +++ b/internal/widget/search.go @@ -1,30 +1,66 @@ package widget import ( + "fmt" "html/template" + "strings" "github.com/glanceapp/glance/internal/assets" ) +type SearchBang struct { + Title string + Shortcut string + URL string +} + type Search struct { - widgetBase `yaml:",inline"` - SearchURL string `yaml:"search-url"` - Query string `yaml:"query"` + widgetBase `yaml:",inline"` + cachedHTML template.HTML `yaml:"-"` + SearchEngine string `yaml:"search-engine"` + Bangs []SearchBang `yaml:"bangs"` +} + +func convertSearchUrl(url string) string { + // Go's template is being stubborn and continues to escape the curlies in the + // URL regardless of what the type of the variable is so this is my way around it + return strings.ReplaceAll(url, "{QUERY}", "!QUERY!") +} + +var searchEngines = map[string]string{ + "duckduckgo": "https://duckduckgo.com/?q={QUERY}", + "google": "https://www.google.com/search?q={QUERY}", } func (widget *Search) Initialize() error { widget.withTitle("Search").withError(nil) - if widget.SearchURL == "" { - // set to the duckduckgo search engine - widget.SearchURL = "https://duckduckgo.com/?q=" + if widget.SearchEngine == "" { + widget.SearchEngine = "duckduckgo" } - // if no query is provided, leave an empty string + if url, ok := searchEngines[widget.SearchEngine]; ok { + widget.SearchEngine = url + } + widget.SearchEngine = convertSearchUrl(widget.SearchEngine) + + for i := range widget.Bangs { + if widget.Bangs[i].Shortcut == "" { + return fmt.Errorf("Search bang %d has no shortcut", i+1) + } + + if widget.Bangs[i].URL == "" { + return fmt.Errorf("Search bang %d has no URL", i+1) + } + + widget.Bangs[i].URL = convertSearchUrl(widget.Bangs[i].URL) + } + + widget.cachedHTML = widget.render(widget, assets.SearchTemplate) return nil } func (widget *Search) Render() template.HTML { - return widget.render(widget, assets.SearchTemplate) + return widget.cachedHTML }