mirror of
https://github.com/Lissy93/dashy.git
synced 2024-12-25 01:42:00 +03:00
🔀 Merge pull request #224 from Lissy93/FEATURE/multi-search
[FEATURE] Multi-Search with Custom Bangs Fixes #206
This commit is contained in:
commit
fd3c043d86
3
.github/CHANGELOG.md
vendored
3
.github/CHANGELOG.md
vendored
@ -1,5 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## ✨ 1.7.6 - Adds Multi-Search Support with Bangs [PR #224](https://github.com/Lissy93/dashy/pull/224)
|
||||
- Adds option for user to add custom search bangs, in order to specify search engine/ target app. Re: #206
|
||||
|
||||
## 🎨 1.7.5 - Improved Language Detection & UI [PR #223](https://github.com/Lissy93/dashy/pull/223)
|
||||
- Makes the auto language detection algo smarter
|
||||
- Improves responsiveness for the language selector form
|
||||
|
@ -141,6 +141,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
**`searchEngine`** | `string` | _Optional_ | Set the key name for your search engine. Can also use a custom engine by setting this property to `custom`. Currently supported: `duckduckgo`, `google`, `whoogle`, `qwant`, `startpage`, `searx-bar` and `searx-info`. Defaults to `duckduckgo`
|
||||
**`customSearchEngine`** | `string` | _Optional_ | You can also use a custom search engine, or your own self-hosted instance. This requires `searchEngine: custom` to be set. Then add the URL of your service, with GET query string included here
|
||||
**`openingMethod`** | `string` | _Optional_ | Set your preferred opening method for search results: `newtab`, `sametab`, `workspace`. Defaults to `newtab`
|
||||
**`searchBangs`** | `object` | _Optional_ | A key-value-pair set of custom search _bangs_ for redirecting query to a specific app or search engine. The key of each should be the bang you will type (typically starting with `/`, `!` or `:`), and value is the destination, either as a search engine key (e.g. `reddit`) or a URL with search parameters (e.g. `https://en.wikipedia.org/w/?search=`)
|
||||
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
@ -50,13 +50,13 @@ In the above example, pressing <kbd>2</kbd> will launch Bookstack. Or hitting <k
|
||||
## Web Search
|
||||
It's possible to search the web directly from Dashy, which might be useful if you're using Dashy as your start page. This can be done by typing your query as normal, and then pressing <kbd>⏎</kbd>. Web search options are configured under `appConfig.webSearch`.
|
||||
|
||||
#### Setting Search Engine
|
||||
### Setting Search Engine
|
||||
Set your default search engine using the `webSearch.searchEngine` property. This defaults to DuckDuckGo. Search engine must be referenced by their key, the following providers are supported:
|
||||
- [`duckduckgo`](https://duckduckgo.com), [`google`](https://google.com), [`whoogle`](https://whoogle.sdf.org), [`qwant`](https://www.qwant.com), [`startpage`](https://www.startpage.com), [`searx-bar`](https://searx.bar), [`searx-info`](https://searx.info)
|
||||
- [`searx-tiekoetter`](https://searx.tiekoetter.com), [`searx-bissisoft`](https://searx.bissisoft.com), [`ecosia`](https://www.ecosia.org), [`metager`](https://metager.org/meta), [`swisscows`](https://swisscows.com), [`mojeek`](https://www.mojeek.com)
|
||||
- [`wikipedia`](https://en.wikipedia.org), [`wolframalpha`](https://www.wolframalpha.com), [`stackoverflow`](https://stackoverflow.com), [`github`](https://github.com), [`reddit`](https://www.reddit.com), [`youtube`](https://youtube.com), [`bbc`](https://www.bbc.co.uk)
|
||||
|
||||
#### Using Custom Search Engine
|
||||
### Using Custom Search Engine
|
||||
You can also use a custom search engine, that isn't included in the above list (like a self-hosted instance of [Whoogle](https://github.com/benbusby/whoogle-search) or [Searx](https://searx.github.io/searx/)). Set `searchEngine: custom`, and then specify the URL (plus query params) to you're search engine under `customSearchEngine`.
|
||||
|
||||
For example:
|
||||
@ -67,10 +67,34 @@ appConfig:
|
||||
customSearchEngine: 'https://searx.local/search?q='
|
||||
```
|
||||
|
||||
#### Setting Opening Method
|
||||
### Setting Opening Method
|
||||
In a similar way to opening apps, you can specify where you would like search results to be opened. This is done under the `openingMethod` attribute, and can be set to either `newtab`, `sametab` or `workspace`. By default results are opened in a new tab.
|
||||
|
||||
#### Disabling Web Search
|
||||
### Using Bangs
|
||||
An insanely useful feature of DDG is [Bangs](https://duckduckgo.com/bang), where you type a specific character combination at the start of your search query, and it will be redirected the that website, such as '!w Docker' will display the Docker wikipedia page. Dashy has a similar feature, enabling you to define your own custom bangs to redirect search results to a specific app, website or search engine.
|
||||
|
||||
This is done under the `searchBangs` property, with a list of key value pairs. The key is what you will type, and the value is the destination, either as an identifier or a URL with query parameters.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
appConfig:
|
||||
webSearch:
|
||||
searchEngine: 'duckduckgo'
|
||||
openingMethod: 'newtab'
|
||||
searchBangs:
|
||||
/r: reddit
|
||||
/w: wikipedia
|
||||
/s: https://whoogle.local/search?q=
|
||||
/a: https://www.amazon.co.uk/s?k=
|
||||
':wolf': wolframalpha
|
||||
':so': stackoverflow
|
||||
':git': github
|
||||
```
|
||||
|
||||
Note that bangs begging with `!` or `:` must be surrounded them in quotes
|
||||
|
||||
### Disabling Web Search
|
||||
Web search can be disabled, by setting `disableWebSearch`, for example:
|
||||
|
||||
```yaml
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Dashy",
|
||||
"version": "1.7.5",
|
||||
"version": "1.7.6",
|
||||
"license": "MIT",
|
||||
"main": "server",
|
||||
"scripts": {
|
||||
|
@ -9,7 +9,7 @@
|
||||
:placeholder="$t('search.search-placeholder')"
|
||||
v-on:input="userIsTypingSomething"
|
||||
@keydown.esc="clearFilterInput" />
|
||||
<p v-if="webSearchEnabled && input.length > 0" class="web-search-note">
|
||||
<p v-if="(!searchPrefs.disableWebSearch) && input.length > 0" class="web-search-note">
|
||||
{{ $t('search.enter-to-search-web') }}
|
||||
</p>
|
||||
</div>
|
||||
@ -25,7 +25,13 @@ import router from '@/router';
|
||||
import ArrowKeyNavigation from '@/utils/ArrowKeyNavigation';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { getCustomKeyShortcuts } from '@/utils/ConfigHelpers';
|
||||
import { searchEngineUrls, defaultSearchEngine, defaultSearchOpeningMethod } from '@/utils/defaults';
|
||||
import { getSearchEngineFromBang, findUrlForSearchEngine, stripBangs } from '@/utils/Search';
|
||||
import {
|
||||
searchEngineUrls,
|
||||
defaultSearchEngine,
|
||||
defaultSearchOpeningMethod,
|
||||
searchBangs as defaultSearchBangs,
|
||||
} from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
name: 'FilterTile',
|
||||
@ -41,37 +47,39 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
webSearchEnabled() {
|
||||
const { appConfig } = this.config;
|
||||
if (appConfig && appConfig.webSearch) {
|
||||
return !appConfig.webSearch.disableWebSearch;
|
||||
}
|
||||
return true;
|
||||
searchPrefs() {
|
||||
return this.config.appConfig.webSearch || {};
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('keydown', (event) => {
|
||||
window.addEventListener('keydown', this.handleKeyPress);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('keydown', this.handleKeyPress);
|
||||
},
|
||||
methods: {
|
||||
/* Call correct function dependending on which key is pressed */
|
||||
handleKeyPress(event) {
|
||||
const currentElem = document.activeElement.id;
|
||||
const { key, keyCode } = event;
|
||||
/* If a modal is open, then do nothing */
|
||||
const notAlreadySearching = currentElem !== 'filter-tiles';
|
||||
// If a modal is open, then do nothing
|
||||
if (!this.active) return;
|
||||
if (/^[a-zA-Z]$/.test(key) && currentElem !== 'filter-tiles') {
|
||||
/* Letter key pressed - start searching */
|
||||
if (/^[/:!a-zA-Z]$/.test(key) && notAlreadySearching) {
|
||||
// Letter or bang key pressed - start searching
|
||||
if (this.$refs.filter) this.$refs.filter.focus();
|
||||
this.userIsTypingSomething();
|
||||
} else if (/^[0-9]$/.test(key)) {
|
||||
/* Number key pressed, check if user has a custom binding */
|
||||
// Number key pressed, check if user has a custom binding
|
||||
this.handleHotKey(key);
|
||||
} else if (keyCode >= 37 && keyCode <= 40) {
|
||||
/* Arrow key pressed - start navigation */
|
||||
// Arrow key pressed - start navigation
|
||||
this.akn.arrowNavigation(keyCode);
|
||||
} else if (keyCode === 27) {
|
||||
/* Esc key pressed - reset form */
|
||||
// Esc key pressed - reset form
|
||||
this.clearFilterInput();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
/* Emmits users's search term up to parent */
|
||||
userIsTypingSomething() {
|
||||
this.$emit('user-is-searchin', this.input);
|
||||
@ -83,6 +91,7 @@ export default {
|
||||
document.activeElement.blur(); // Remove focus
|
||||
this.akn.resetIndex(); // Reset current element index
|
||||
},
|
||||
/* If configured, launch specific app when hotkey pressed */
|
||||
handleHotKey(key) {
|
||||
const usersHotKeys = this.getCustomKeyShortcuts();
|
||||
usersHotKeys.forEach((hotkey) => {
|
||||
@ -91,6 +100,7 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
/* Launch search results, with users desired opening method */
|
||||
launchWebSearch(url, method) {
|
||||
switch (method) {
|
||||
case 'newtab':
|
||||
@ -107,22 +117,24 @@ export default {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
},
|
||||
|
||||
/* Launch web search, to correct search engine, passing in users query */
|
||||
searchSubmitted() {
|
||||
// Get search preferences from appConfig
|
||||
const { appConfig } = this.config;
|
||||
const searchPrefs = appConfig.webSearch || {};
|
||||
if (this.webSearchEnabled) { // Only proceed if user hasn't disabled web search
|
||||
const { searchPrefs } = this;
|
||||
if (!searchPrefs.disableWebSearch) { // Only proceed if user hasn't disabled web search
|
||||
const bangList = { ...defaultSearchBangs, ...(searchPrefs.searchBangs || {}) };
|
||||
const openingMethod = searchPrefs.openingMethod || defaultSearchOpeningMethod;
|
||||
// Get search engine, and make URL
|
||||
const searchBang = getSearchEngineFromBang(this.input, bangList);
|
||||
const searchEngine = searchPrefs.searchEngine || defaultSearchEngine;
|
||||
let searchUrl = searchEngineUrls[searchEngine];
|
||||
if (!searchUrl) ErrorHandler(`Search engine not found - ${searchEngine}`);
|
||||
if (searchEngine === 'custom' && searchPrefs.customSearchEngine) {
|
||||
searchUrl = searchPrefs.customSearchEngine;
|
||||
// Use either search bang, or preffered search engine
|
||||
const desiredSearchEngine = searchBang || searchEngine;
|
||||
let searchUrl = findUrlForSearchEngine(desiredSearchEngine, searchEngineUrls);
|
||||
if (searchUrl) { // Append search query to URL, and launch
|
||||
searchUrl += encodeURIComponent(stripBangs(this.input, bangList));
|
||||
this.launchWebSearch(searchUrl, openingMethod);
|
||||
this.clearFilterInput();
|
||||
}
|
||||
// Append users encoded query onto search URL, and launch
|
||||
searchUrl += encodeURIComponent(this.input);
|
||||
this.launchWebSearch(searchUrl, openingMethod);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -260,6 +260,17 @@
|
||||
],
|
||||
"default": "newtab",
|
||||
"description": "Set where you would like search results to open to"
|
||||
},
|
||||
"searchBangs": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"examples": [
|
||||
{
|
||||
"/r": "reddit",
|
||||
"!w": "https://whoogle.local/search?q="
|
||||
}
|
||||
],
|
||||
"description": "A KV-pair of custom search bangs. The key should be the shortcut to type, and the value is the search engine, specified either by key or full URL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* Dashy: Licensed under MIT, (C) Alicia Sykes 2021 <https://aliciasykes.com> */
|
||||
|
||||
/* Tile filtering utility */
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
|
||||
/**
|
||||
* Extracts the site name from domain
|
||||
@ -35,7 +36,7 @@ const filterHelper = (compareStr, searchStr) => {
|
||||
* @param {string} searchTerm The users search term
|
||||
* @returns A filtered array of tiles
|
||||
*/
|
||||
const search = (allTiles, searchTerm) => {
|
||||
export const searchTiles = (allTiles, searchTerm) => {
|
||||
if (!allTiles) return []; // If no data, then skip
|
||||
return allTiles.filter((tile) => {
|
||||
const {
|
||||
@ -49,4 +50,30 @@ const search = (allTiles, searchTerm) => {
|
||||
});
|
||||
};
|
||||
|
||||
export default search;
|
||||
/* From a list of search bangs, return the URL associated with it */
|
||||
export const getSearchEngineFromBang = (searchQuery, bangList) => {
|
||||
const bangNames = Object.keys(bangList);
|
||||
const foundBang = bangNames.find((bang) => searchQuery.includes(bang));
|
||||
return bangList[foundBang];
|
||||
};
|
||||
|
||||
/* For a given search engine key, return the corresponding URL, or throw error */
|
||||
export const findUrlForSearchEngine = (searchEngine, availableSearchEngines) => {
|
||||
// If missing search engine, report error return false
|
||||
if (!searchEngine) { ErrorHandler('No search engine specified'); return undefined; }
|
||||
// If search engine is already a URL, then return it
|
||||
if ((/(http|https):\/\/[^]*/).test(searchEngine)) return searchEngine;
|
||||
// If search engine was found successfully, return the URL
|
||||
if (availableSearchEngines[searchEngine]) return availableSearchEngines[searchEngine];
|
||||
// Otherwise, there's been an error, log it and return false
|
||||
ErrorHandler(`Specified Search Engine was not Found: '${searchEngine}'`);
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/* Removes all known bangs from a search query */
|
||||
export const stripBangs = (searchQuery, bangList) => {
|
||||
const bangNames = Object.keys(bangList || {});
|
||||
let q = searchQuery;
|
||||
bangNames.forEach((bang) => { q = q.replace(bang, ''); });
|
||||
return q.trim();
|
||||
};
|
||||
|
@ -185,6 +185,17 @@ module.exports = {
|
||||
},
|
||||
defaultSearchEngine: 'duckduckgo',
|
||||
defaultSearchOpeningMethod: 'newtab',
|
||||
searchBangs: {
|
||||
'/b': 'bbc',
|
||||
'/d': 'duckduckgo',
|
||||
'/g': 'google',
|
||||
'/r': 'reddit',
|
||||
'/w': 'wikipedia',
|
||||
'/y': 'youtube',
|
||||
'/gh': 'github',
|
||||
'/so': 'stackoverflow',
|
||||
'/wa': 'wolframalpha',
|
||||
},
|
||||
/* Available built-in colors for the theme builder */
|
||||
swatches: [
|
||||
['#eb5cad', '#985ceb', '#5346f3', '#5c90eb'],
|
||||
|
@ -46,7 +46,7 @@
|
||||
|
||||
import SettingsContainer from '@/components/Settings/SettingsContainer.vue';
|
||||
import Section from '@/components/LinkItems/Section.vue';
|
||||
import SearchUtil from '@/utils/Search';
|
||||
import { searchTiles } from '@/utils/Search';
|
||||
import Defaults, { localStorageKeys, iconCdns } from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
@ -115,7 +115,7 @@ export default {
|
||||
},
|
||||
/* Returns only the tiles that match the users search query */
|
||||
filterTiles(allTiles, searchTerm) {
|
||||
return SearchUtil(allTiles, searchTerm);
|
||||
return searchTiles(allTiles, searchTerm);
|
||||
},
|
||||
/* Returns optional section display preferences if available */
|
||||
getDisplayData(section) {
|
||||
|
@ -54,7 +54,7 @@ import MinimalSection from '@/components/MinimalView/MinimalSection.vue';
|
||||
import MinimalHeading from '@/components/MinimalView/MinimalHeading.vue';
|
||||
import MinimalSearch from '@/components/MinimalView/MinimalSearch.vue';
|
||||
import { GetTheme, ApplyLocalTheme, ApplyCustomVariables } from '@/utils/ThemeHelper';
|
||||
import SearchUtil from '@/utils/Search';
|
||||
import { searchTiles } from '@/utils/Search';
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import ConfigLauncher from '@/components/Settings/ConfigLauncher';
|
||||
|
||||
@ -123,7 +123,7 @@ export default {
|
||||
/* Returns only the tiles that match the users search query */
|
||||
filterTiles(allTiles) {
|
||||
if (!allTiles) return [];
|
||||
return SearchUtil(allTiles, this.searchValue);
|
||||
return searchTiles(allTiles, this.searchValue);
|
||||
},
|
||||
/* Update data when modal is open (so that key bindings can be disabled) */
|
||||
updateModalVisibility(modalState) {
|
||||
|
Loading…
Reference in New Issue
Block a user