diff --git a/lang/en/LC_MESSAGES/default.po b/lang/en/LC_MESSAGES/default.po index cae96aa..ee954aa 100644 --- a/lang/en/LC_MESSAGES/default.po +++ b/lang/en/LC_MESSAGES/default.po @@ -59,7 +59,7 @@ msgid "all_settings" msgstr "All settings" msgid "site_name" -msgstr "Ocásek" +msgstr "QGato" msgid "search" msgstr "Search" diff --git a/lang/pl/LC_MESSAGES/default.po b/lang/pl/LC_MESSAGES/default.po index a424815..99436a1 100644 --- a/lang/pl/LC_MESSAGES/default.po +++ b/lang/pl/LC_MESSAGES/default.po @@ -59,7 +59,7 @@ msgid "all_settings" msgstr "Wszystkie ustawienia" msgid "site_name" -msgstr "Ocásek" +msgstr "QGato" msgid "search" msgstr "Szukaj" diff --git a/open-search.go b/open-search.go index 8ae97e1..0cdb72a 100644 --- a/open-search.go +++ b/open-search.go @@ -25,9 +25,9 @@ func generateOpenSearchXML(config Config) { opensearch := OpenSearchDescription{ Xmlns: "http://a9.com/-/spec/opensearch/1.1/", - ShortName: "Warp", - Description: "Warp search engine", - Tags: "search, engine, warp", + ShortName: "QGato", + Description: "QGato search engine", + Tags: "search, spitfire, qgato", URLs: []URL{ { Type: "text/html", diff --git a/static/css/style-search.css b/static/css/style-search.css index adda3d2..27f516f 100644 --- a/static/css/style-search.css +++ b/static/css/style-search.css @@ -1,61 +1,358 @@ +/* Font Definitions */ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 300; + src: local(''), + url('/static/fonts/inter-v12-latin-300.woff2') format('woff2'), + url('/static/fonts/inter-v12-latin-300.woff') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + src: local(''), + url('/static/fonts/inter-v12-latin-regular.woff2') format('woff2'), + url('/static/fonts/inter-v12-latin-regular.woff') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + src: local(''), + url('/static/fonts/inter-v12-latin-700.woff2') format('woff2'), + url('/static/fonts/inter-v12-latin-700.woff') format('woff'); +} + +@font-face { + font-display: swap; + font-family: 'Material Icons Round'; + font-style: normal; + font-weight: 400; + src: url('/static/fonts/material-icons-round-v108-latin-regular.woff2') format('woff2'); +} + +/* General Styles */ +body, html { + margin: 0; + padding: 0; + background-color: var(--html-bg); + font-family: 'Inter', Arial, Helvetica, sans-serif; + font-size: 16px; + color: var(--text-color); +} + +body.menu-open { + overflow: hidden; +} + +.highlight { + color: var(--highlight); + font-weight: bold; + font-style: normal; +} + +.material-icons-round { + font-family: 'Material Icons Round' !important; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -webkit-font-feature-settings: 'liga'; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: 'liga'; +} + +#search-wrapper-ico { + background: none; + border: none; + color: var(--fg); + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + cursor: pointer; + font-size: 24px; +} + +#search-wrapper-ico:hover { + transition: color 0.3s ease; + color: var(--blue); +} + +.wrapper { + margin: 0 auto; + background: var(--search-bg-input); + border-radius: 22px; + position: relative; + width: 100%; + max-width: 600px; + overflow: visible; + border: 1px solid var(--search-bg-input-border); +} + +.wrapper input { + width: 100%; + padding: 10px 50px 10px 20px; + font-size: 16px; + color: var(--font-fg); + background-color: var(--search-bg-input); + background: none; + border: none; + outline: none; +} + +.wrapper input::placeholder { + color: var(--fg); +} + +/* Autocomplete Styles */ +.autocomplete { + text-align: left; + width: 100%; /* Ensure it matches the input width */ +} + +.wrapper.show .autocomplete { + display: block; +} + +.autocomplete ul { + margin: 0; + padding: 0; +} + +.autocomplete ul li { + list-style: none; + padding: 8px 12px; + cursor: pointer; + color: var(--font-fg); +} + +.autocomplete ul li.selected, +.autocomplete ul li:hover { + background: var(--search-select); +} + +.autocomplete ul li:last-child { + border-bottom-left-radius: 22px; + border-bottom-right-radius: 22px; +} + +/* Logo Styles */ +.logo-container { + margin: 0 auto 40px auto; + color: var(--font-fg); + text-align: center; +} + +.logo-container svg { + max-width: 300px; + height: auto; + vertical-align: middle; + margin: 0 auto; +} + +/* Centering Content */ .search-page-content { + max-width: 800px; + margin: 0 auto; + text-align: center; display: flex; flex-direction: column; - align-items: center; -} - -.search-page-content h1 { - text-align: center; - margin-bottom: 20px; -} - -#search-input { - width: 100%; - padding: 12px; - font-size: 16px; + justify-content: center; /* Center vertically */ + height: 100vh; /* Make it full viewport height */ } +/* Search Type Icons */ .search-type-icons { display: flex; justify-content: center; flex-wrap: wrap; - gap: 30px; margin-top: 30px; + gap: 20px; } .icon-button { display: flex; flex-direction: column; align-items: center; - background: none; - border: none; - cursor: pointer; text-align: center; } -.icon-button .material-icons-round { - font-size: 48px; - color: var(--sub-search-wrapper-ico); +.icon-button button { + background: none; + border: none; + color: var(--search-button); + cursor: pointer; + font-size: 24px; +} + +.icon-button button:hover { + color: var(--blue); + transition: color 0.3s ease; } .icon-button p { - margin-top: 8px; + margin-top: 5px; font-size: 14px; - color: var(--sub-search-wrapper-ico); + color: var(--font-fg); } -.icon-button:hover .material-icons-round { - transition: all .3s ease; +/* Menu Button */ +.settings-icon-link-search { + position: fixed; + top: 20px; + right: 20px; + background: none; + border: none; + color: var(--fg); + cursor: pointer; + font-size: 36px; + z-index: 999; +} + +.settings-icon-link-search:hover { + color: var(--highlight); +} + +/* Style for Close Button in Menu */ +.closebtn { + background: none; + border: none; + color: var(--fg); + cursor: pointer; + font-size: 36px; + position: absolute; + top: 20px; + right: 20px; +} + +.side-nav .closebtn:hover { + color: var(--highlight); + background: none; +} + +/* Side Navigation Menu */ +.side-nav { + height: 100%; + width: 0; + position: fixed; + top: 0; + right: 0; + background-color: var(--html-bg); + overflow-x: hidden; + transition: 0.5s; + z-index: 1000; + box-shadow: -2px 0 5px rgba(0,0,0,0.5); +} + +/* Menu Content */ +.side-nav a, +.side-nav button, +.side-nav select { + padding: 8px 32px; + text-decoration: none; + font-size: 18px; + color: var(--font-fg); + display: block; + transition: background-color 0.3s ease; + border: none; + background: none; + width: 100%; + text-align: left; +} + +.side-nav a:hover, +.side-nav button:hover, +.side-nav select:hover { + background-color: var(--search-select); color: var(--blue); + cursor: pointer; } -.icon-button:hover p { - transition: all .3s ease; - color: var(--blue); +/* Style for buttons in the menu */ +.side-nav button { + background: none; + border: none; + font-size: 18px; + font-family: inherit; + padding: 8px 32px; + width: 100%; + text-align: left; + color: var(--font-fg); } -.icon-button button:focus { +.side-nav .closebtn:hover { + color: var(--highlight); +} + +/* Overlay effect when menu is open */ +body.menu-open::before { + content: ''; + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.5); + z-index: 998; +} + +/* Responsive Design */ +@media only screen and (max-width: 450px) { + .wrapper { + width: 90%; + } + .logo-container svg { + width: 75%; + max-width: 90%; + min-width: 25%; + } +} + +/* Additional Global Styles */ +a { + text-decoration: none; + color: var(--link); +} + +a:hover { + text-decoration: underline; +} + +h1 { + font-size: 70px; + color: var(--font-fg); + font-family: 'Inter'; +} + +p { + color: var(--fg); + font-size: 14px; + line-height: 1.58; +} + +input, button { outline: none; -} \ No newline at end of file +} + +/* + Remove default focus outline and add custom focus style +.wrapper input[type="text"]:focus { + outline: none; + border: 1px solid var(--blue); + box-shadow: none; +} */ + + diff --git a/static/css/style.css b/static/css/style.css index 65d0346..34dacec 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1057,6 +1057,16 @@ p { color: #b0316e; } +.logo-container { + color: var(--font-fg); + line-height: 0; +} + +.logo-container svg { + vertical-align: middle; + margin: 0 auto; +} + .results-search-container { background-color: var(--search-bg); width: 100%; diff --git a/static/images/icon.svg b/static/images/icon.svg new file mode 100644 index 0000000..5ce7113 --- /dev/null +++ b/static/images/icon.svg @@ -0,0 +1,185 @@ + + diff --git a/static/images/icon2.svg b/static/images/icon2.svg new file mode 100644 index 0000000..902a961 --- /dev/null +++ b/static/images/icon2.svg @@ -0,0 +1,54 @@ + + \ No newline at end of file diff --git a/static/images/logo.svg b/static/images/logo.svg new file mode 100644 index 0000000..952d6ab --- /dev/null +++ b/static/images/logo.svg @@ -0,0 +1,94 @@ + + diff --git a/static/js/autocomplete.js b/static/js/autocomplete.js index 354f254..9ac63f3 100644 --- a/static/js/autocomplete.js +++ b/static/js/autocomplete.js @@ -1,180 +1,120 @@ -/** - * @source: ./script.js (originally from araa-search on Github) - * - * @licstart The following is the entire license notice for the - * JavaScript code in this page. - * - * Copyright (C) 2023 Extravi - * - * The JavaScript code in this page is free software: you can - * redistribute it and/or modify it under the terms of the GNU Affero - * General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * The code is distributed WITHOUT ANY WARRANTY; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * As additional permission under GNU Affero General Public License - * section 7, you may distribute non-source (e.g., minimized or compacted) - * forms of that code without the copy of the GNU Affero General Public - * License normally required by section 4, provided you include this - * license notice and a URL through which recipients can access the - * Corresponding Source. - * - * @licend The above is the entire license notice - * for the JavaScript code in this page. - */ +// Wait for the DOM to load +document.addEventListener('DOMContentLoaded', () => { + const searchInput = document.getElementById('search-input'); + const searchWrapper = document.querySelector('.wrapper'); + const resultsWrapper = document.querySelector('.autocomplete'); -// Removes the 'Apply Settings' button for Javascript users, -// since changing any of the elements causes the settings to apply -// automatically. -let resultsSave = document.querySelector(".results-save"); -if (resultsSave != null) { - resultsSave.style.display = "none"; -} + let suggestions = []; + let currentIndex = -1; // Keep track of the currently selected suggestion -const searchInput = document.getElementById('search-input'); -const searchWrapper = document.querySelectorAll('.wrapper, .wrapper-results')[0]; -const resultsWrapper = document.querySelector('.autocomplete'); -// const clearSearch = document.querySelector("#clearSearch"); - -async function getSuggestions(query) { - try { - const params = new URLSearchParams({ "q": query }).toString(); - const response = await fetch(`/suggestions?${params}`); - const data = await response.json(); - return data[1]; // Return only the array of suggestion strings - } catch (error) { - console.error(error); - } - } - -let currentIndex = -1; // Keep track of the currently selected suggestion - -// Handle click events on the type buttons -let results = []; -searchInput.addEventListener('input', async () => { - let input = searchInput.value; - if (input.length) { - results = await getSuggestions(input); - } - renderResults(results); - currentIndex = -1; // Reset index when we return new results -}); - -searchInput.addEventListener("focus", async () => { - let input = searchInput.value; - if (results.length === 0 && input.length != 0) { - results = await getSuggestions(input); - } - renderResults(results); -}) - -// clearSearch.style.visibility = "visible"; // Only show the clear search button for JS users. -// clearSearch.addEventListener("click", () => { -// searchInput.value = ""; -// searchInput.focus(); -// }) - -searchInput.addEventListener('keydown', (event) => { - if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') { - event.preventDefault(); // Prevent the default behavior, such as moving the cursor - - // Find the currently selected suggestion element - const selectedSuggestion = resultsWrapper.querySelector('.selected'); - if (selectedSuggestion) { - selectedSuggestion.classList.remove('selected'); // Deselect the currently selected suggestion + // Fetch suggestions from the server + async function getSuggestions(query) { + try { + const response = await fetch(`/suggestions?q=${encodeURIComponent(query)}`); + const data = await response.json(); + return data[1]; // Return only the array of suggestion strings + } catch (error) { + console.error('Error fetching suggestions:', error); + return []; + } } - // Increment current index when ArrowUp is pressed otherwise hen Tab OR ArrowDown decrement - if (event.key === 'ArrowUp') { - currentIndex--; - } else { - currentIndex++; + // Function to render results + function renderSuggestions(results) { + if (!results || !results.length) { + searchWrapper.classList.remove('show'); + resultsWrapper.innerHTML = ''; + return; + } + + let content = ''; + results.forEach((item, index) => { + content += `