summaryrefslogtreecommitdiff
path: root/web/js/search.js
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-09 11:11:49 +0300
committerPaul Buetow <paul@buetow.org>2026-05-09 11:11:49 +0300
commit8c32cd117abe79f4ee6cff3a950ffc35c95faeff (patch)
tree5b63a4789351da4f2d9c1cd2e7007f67d43b3371 /web/js/search.js
parent59c8870061465142306e7a6e5d3fcb344686cfe9 (diff)
Refine media browsing and set covers
Diffstat (limited to 'web/js/search.js')
-rw-r--r--web/js/search.js48
1 files changed, 43 insertions, 5 deletions
diff --git a/web/js/search.js b/web/js/search.js
index 4e78b55..31c3004 100644
--- a/web/js/search.js
+++ b/web/js/search.js
@@ -6,10 +6,12 @@ export function initSearch({ onChange, input, clearBtn }) {
_onChange = onChange;
if (!input) return;
input.addEventListener('keydown', (e) => {
- if (e.key === 'Escape') {
- input.value = '';
+ if (e.key === 'Escape' || e.key === 'Enter') {
+ e.preventDefault();
+ e.stopPropagation();
input.blur();
- trigger('');
+ hideSearch();
+ document.dispatchEvent(new CustomEvent('search:navigate-results'));
}
});
input.addEventListener('input', () => {
@@ -29,6 +31,7 @@ export function initSearch({ onChange, input, clearBtn }) {
}
export function focusSearch() {
+ showSearch();
const input = document.getElementById('search-input');
if (input) {
input.focus();
@@ -41,6 +44,15 @@ export function trigger(query) {
_onChange(query);
}
+export function showSearch() {
+ document.getElementById('search-overlay')?.classList.remove('hidden');
+}
+
+export function hideSearch() {
+ document.getElementById('search-overlay')?.classList.add('hidden');
+ hideSearchHelp();
+}
+
/**
* Parse a raw search string into structured filters and free-text search.
*
@@ -50,6 +62,7 @@ export function trigger(query) {
* tag:a,b – comma-separated tag names
* like:1 – only favorited items
* type:video – media type (video | audio)
+ * set:yoga – select a set by id or name
* sort:name – sort order (name | date | duration | play_count | random)
* minsize:10 – minimum file size in MB
* maxsize:500 – maximum file size in MB
@@ -62,6 +75,7 @@ export function parseQuery(raw) {
favorites: false,
tags: '',
sort: '',
+ set: '',
minDuration: '',
maxDuration: '',
minFileSize: '',
@@ -69,8 +83,7 @@ export function parseQuery(raw) {
search: '',
};
const tokens = [];
- // Split by whitespace while respecting double-quoted strings.
- const parts = raw.match(/(".*?"|\S+)/g) || [];
+ const parts = tokenize(raw);
for (const part of parts) {
const m = part.match(/^([a-zA-Z_]+):(.+)$/);
if (m) {
@@ -95,6 +108,9 @@ export function parseQuery(raw) {
case 'type':
filters.type = value;
break;
+ case 'set':
+ filters.set = value;
+ break;
case 'sort':
filters.sort = value;
break;
@@ -116,6 +132,28 @@ export function parseQuery(raw) {
return filters;
}
+function tokenize(raw) {
+ const parts = [];
+ let current = '';
+ let inQuote = false;
+ for (const ch of raw || '') {
+ if (ch === '"') {
+ inQuote = !inQuote;
+ continue;
+ }
+ if (/\s/.test(ch) && !inQuote) {
+ if (current) {
+ parts.push(current);
+ current = '';
+ }
+ continue;
+ }
+ current += ch;
+ }
+ if (current) parts.push(current);
+ return parts;
+}
+
export function showSearchHelp() {
const el = document.getElementById('search-help');
if (!el) return;