MediaWiki:Gadget-CustomSearchCommands.js
MediaWiki interface page
More actions
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
//See: https://github.com/Yellow-Dog-Man/resonite-wiki/issues/33
mw.messages.set({
'citizen-command-palette-type-component-search': 'Component Search',
'citizen-command-palette-type-protoflux-search': 'ProtoFlux Search'
});
// https://github.com/StarCitizenTools/mediawiki-skins-Citizen/blob/main/resources/skins.citizen.commandPalette/types.js for types
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function createFuzzySearchCommand(config) {
const { id, triggers, description, namespace, namespaceLabel, fuzziness } = config;
// Cache for storing search results to avoid duplicate requests
const searchCache = new Map();
const checkCache = function (key) {
if (searchCache.has(key))
return searchCache.get(key);
return [];
}
const debouncedSearch = debounce((subQuery, resolve, reject) => {
const api = new mw.Api();
const cacheKey = `${namespace}:${subQuery}`;
const fuzzyQuery = `${subQuery}~${fuzziness}`;
// Check cache first
const cachedResults = checkCache(cacheKey);
if (cachedResults.length > 0)
{
resolve(cachedResults);
return;
}
api.get({
action: 'query',
list: 'search',
srsearch: fuzzyQuery,
srnamespace: namespace,
srlimit: 10,
srprop: 'snippet|titlesnippet',
format: 'json'
}).then(response => {
if (!response.query || !response.query.search || response.query.search.length === 0) {
const results = [{
id: `${id}-no-results`,
label: `No ${namespaceLabel} found for "${subQuery}"`,
description: 'Try a different search term',
type: id
}];
resolve(results);
return;
}
const results = response.query.search.map(result => {
let resultDescription = namespaceLabel;
if (result.snippet) {
// Remove all HTML tags from snippet response
resultDescription = result.snippet.replace(/<\/?[^>]+(>|$)/g, "");
}
return {
id: `${id}-${result.pageid}`,
label: result.title,
description: resultDescription,
url: mw.util.getUrl(result.title),
highlightQuery: true,
type: id,
source: 'search'
};
});
// Cache results
searchCache.set(cacheKey, results);
// Limit cache size to prevent memory issues
// FIFO
if (searchCache.size > 50) {
const firstKey = searchCache.keys().next().value;
searchCache.delete(firstKey);
}
resolve(results);
}).catch(error => {
console.error(`[${namespaceLabel}Search] API error:`, error);
reject([{
id: `${id}-error`,
label: 'Search error',
description: 'Could not complete search',
type: id
}]);
});
}, 300); // 300ms debounce delay
return {
id: id,
triggers: triggers,
description: description,
getResults(subQuery) {
if (!subQuery || subQuery.trim().length === 0) {
return Promise.resolve([{
id: `${id}-empty`,
label: `Type to search ${namespaceLabel}...`,
description: `Fuzzy match search for ${namespaceLabel}`,
type: id
}]);
}
// Return a promise that will be resolved by the debounced function
return new Promise((resolve, reject) => {
debouncedSearch(subQuery, resolve, reject);
});
},
onResultSelect(item) {
if (item.url) {
return { action: 'navigate', payload: item.url };
}
return { action: 'none' };
}
};
}
const NS_COMPONENT = 3000;
const NS_PROTOFLUX = 3002;
// Create the ProtoFlux search command
const protofluxSearchCommand = createFuzzySearchCommand({
id: 'protoflux-search',
triggers: ['/pf:', '/protoflux:'],
description: 'Fuzzy search ProtoFlux Nodes',
namespace: NS_PROTOFLUX,
namespaceLabel: 'ProtoFlux Nodes',
fuzziness: 2
});
// Create the Component search command
const componentSearchCommand = createFuzzySearchCommand({
id: 'component-search',
triggers: ['/comp:', '/component:'],
description: 'Fuzzy search Components',
namespace: NS_COMPONENT,
namespaceLabel: 'Components',
fuzziness: 2
});
// Register both commands
mw.loader.using('skins.citizen.commandPalette').then(() => {
mw.hook('skins.citizen.commandPalette.registerCommand').add((registrationData) => {
if (!registrationData || !registrationData.registerCommand)
return;
registrationData.registerCommand(protofluxSearchCommand);
registrationData.registerCommand(componentSearchCommand);
});
});