vomebook commited on
Commit
a134179
·
1 Parent(s): eeb6f43

Upload 2 files

Browse files
Files changed (2) hide show
  1. static/app.js +22 -5
  2. static/index.html +2 -2
static/app.js CHANGED
@@ -16,7 +16,7 @@ const STATE = {
16
  selectedSources: [],
17
  minSize: null,
18
  maxSize: null,
19
- exact: false,
20
  fulltext: false,
21
  searchPaths: true,
22
  fulltextManifest: null,
@@ -53,6 +53,18 @@ function tokenize(text) {
53
  return Array.from(new Set(String(text || "").toLowerCase().match(/[a-z0-9]+|[\u4e00-\u9fff\u3400-\u4dbf]+/g) || []));
54
  }
55
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  function formatSize(bytes) {
57
  if (!bytes) return "";
58
  if (bytes < 1024) return bytes + " B";
@@ -353,10 +365,11 @@ function syncUrl(replace = true) {
353
  if (STATE.minSize !== null) params.set("min_size", String(STATE.minSize));
354
  if (STATE.maxSize !== null) params.set("max_size", String(STATE.maxSize));
355
  if (DOM.sortSelect.value !== "relevance") params.set("sort", DOM.sortSelect.value);
356
- if (STATE.exact) params.set("exact", "1");
357
  if (STATE.fulltext) params.set("fulltext", "1");
358
  if (!STATE.searchPaths) params.set("search_paths", "0");
359
  if (!STATE.historyEnabled) params.set("history", "0");
 
360
  const basePath = STATE.source ? `/${encodeURIComponent(STATE.source)}` : "/";
361
  const nextUrl = params.toString() ? `${basePath}?${params.toString()}` : basePath;
362
  if (replace) history.replaceState(null, "", nextUrl);
@@ -370,7 +383,7 @@ function loadUrlState() {
370
  STATE.folderSelections = params.getAll("folder");
371
  STATE.minSize = params.get("min_size") ? parseInt(params.get("min_size"), 10) : null;
372
  STATE.maxSize = params.get("max_size") ? parseInt(params.get("max_size"), 10) : null;
373
- STATE.exact = params.get("exact") === "1";
374
  STATE.fulltext = params.get("fulltext") === "1";
375
  STATE.searchPaths = params.get("search_paths") !== "0";
376
  STATE.historyEnabled = params.get("history") !== "0";
@@ -381,6 +394,7 @@ function loadUrlState() {
381
  DOM.searchPathsToggle.checked = STATE.searchPaths;
382
  DOM.historyToggle.checked = STATE.historyEnabled;
383
  DOM.sortSelect.value = sort;
 
384
  if (STATE.minSize !== null) DOM.filterMinSize.value = STATE.minSize;
385
  if (STATE.maxSize !== null) DOM.filterMaxSize.value = STATE.maxSize;
386
  }
@@ -425,7 +439,7 @@ async function fulltextSearch() {
425
  if (STATE.exact) {
426
  matchedIds = Object.keys(payload.docs).filter((docId) => {
427
  const snippet = (payload.snippets[docId] || "") + " " + (payload.docs[docId]?.display_name || "");
428
- return snippet.toLowerCase().includes(query.toLowerCase());
429
  });
430
  } else {
431
  for (const token of queryTokens) {
@@ -1013,15 +1027,18 @@ function attachEvents() {
1013
  STATE.rightSidebarOpen = !STATE.rightSidebarOpen;
1014
  if (STATE.isMobile && STATE.rightSidebarOpen) STATE.leftSidebarOpen = false;
1015
  updateSidebarVisibility();
 
1016
  });
1017
  DOM.closeFiltersBtn.addEventListener("click", () => {
1018
  STATE.rightSidebarOpen = false;
1019
  updateSidebarVisibility();
 
1020
  });
1021
  DOM.overlay.addEventListener("click", () => {
1022
  STATE.leftSidebarOpen = false;
1023
  STATE.rightSidebarOpen = false;
1024
  updateSidebarVisibility();
 
1025
  });
1026
  DOM.themeBtn.addEventListener("click", () => {
1027
  STATE.isDark = !STATE.isDark;
@@ -1305,8 +1322,8 @@ async function init() {
1305
  applyTheme();
1306
  STATE.isMobile = window.innerWidth <= 768;
1307
  syncRoute();
1308
- loadUrlState();
1309
  applyMobileMode();
 
1310
  STATE.sources = await API.getSources();
1311
  STATE.sourceMap = Object.fromEntries(STATE.sources.map((source) => [source.slug, source]));
1312
  await renderSourcesFilter();
 
16
  selectedSources: [],
17
  minSize: null,
18
  maxSize: null,
19
+ exact: true,
20
  fulltext: false,
21
  searchPaths: true,
22
  fulltextManifest: null,
 
53
  return Array.from(new Set(String(text || "").toLowerCase().match(/[a-z0-9]+|[\u4e00-\u9fff\u3400-\u4dbf]+/g) || []));
54
  }
55
 
56
+ function wildcardPatternToRegExp(pattern) {
57
+ const escaped = String(pattern || "").replace(/[.+^${}()|[\]\\]/g, "\\$&");
58
+ return new RegExp(escaped.replace(/\*/g, ".*").replace(/\?/g, "."), "i");
59
+ }
60
+
61
+ function matchesExactQuery(text, query) {
62
+ const value = String(text || "");
63
+ const pattern = String(query || "");
64
+ if (pattern.includes("*") || pattern.includes("?")) return wildcardPatternToRegExp(pattern).test(value);
65
+ return value.toLowerCase().includes(pattern.toLowerCase());
66
+ }
67
+
68
  function formatSize(bytes) {
69
  if (!bytes) return "";
70
  if (bytes < 1024) return bytes + " B";
 
365
  if (STATE.minSize !== null) params.set("min_size", String(STATE.minSize));
366
  if (STATE.maxSize !== null) params.set("max_size", String(STATE.maxSize));
367
  if (DOM.sortSelect.value !== "relevance") params.set("sort", DOM.sortSelect.value);
368
+ if (!STATE.exact) params.set("exact", "0");
369
  if (STATE.fulltext) params.set("fulltext", "1");
370
  if (!STATE.searchPaths) params.set("search_paths", "0");
371
  if (!STATE.historyEnabled) params.set("history", "0");
372
+ if (STATE.rightSidebarOpen) params.set("filters", "1");
373
  const basePath = STATE.source ? `/${encodeURIComponent(STATE.source)}` : "/";
374
  const nextUrl = params.toString() ? `${basePath}?${params.toString()}` : basePath;
375
  if (replace) history.replaceState(null, "", nextUrl);
 
383
  STATE.folderSelections = params.getAll("folder");
384
  STATE.minSize = params.get("min_size") ? parseInt(params.get("min_size"), 10) : null;
385
  STATE.maxSize = params.get("max_size") ? parseInt(params.get("max_size"), 10) : null;
386
+ STATE.exact = params.get("exact") !== "0";
387
  STATE.fulltext = params.get("fulltext") === "1";
388
  STATE.searchPaths = params.get("search_paths") !== "0";
389
  STATE.historyEnabled = params.get("history") !== "0";
 
394
  DOM.searchPathsToggle.checked = STATE.searchPaths;
395
  DOM.historyToggle.checked = STATE.historyEnabled;
396
  DOM.sortSelect.value = sort;
397
+ STATE.rightSidebarOpen = params.get("filters") === "1";
398
  if (STATE.minSize !== null) DOM.filterMinSize.value = STATE.minSize;
399
  if (STATE.maxSize !== null) DOM.filterMaxSize.value = STATE.maxSize;
400
  }
 
439
  if (STATE.exact) {
440
  matchedIds = Object.keys(payload.docs).filter((docId) => {
441
  const snippet = (payload.snippets[docId] || "") + " " + (payload.docs[docId]?.display_name || "");
442
+ return matchesExactQuery(snippet, query);
443
  });
444
  } else {
445
  for (const token of queryTokens) {
 
1027
  STATE.rightSidebarOpen = !STATE.rightSidebarOpen;
1028
  if (STATE.isMobile && STATE.rightSidebarOpen) STATE.leftSidebarOpen = false;
1029
  updateSidebarVisibility();
1030
+ syncUrl();
1031
  });
1032
  DOM.closeFiltersBtn.addEventListener("click", () => {
1033
  STATE.rightSidebarOpen = false;
1034
  updateSidebarVisibility();
1035
+ syncUrl();
1036
  });
1037
  DOM.overlay.addEventListener("click", () => {
1038
  STATE.leftSidebarOpen = false;
1039
  STATE.rightSidebarOpen = false;
1040
  updateSidebarVisibility();
1041
+ syncUrl();
1042
  });
1043
  DOM.themeBtn.addEventListener("click", () => {
1044
  STATE.isDark = !STATE.isDark;
 
1322
  applyTheme();
1323
  STATE.isMobile = window.innerWidth <= 768;
1324
  syncRoute();
 
1325
  applyMobileMode();
1326
+ loadUrlState();
1327
  STATE.sources = await API.getSources();
1328
  STATE.sourceMap = Object.fromEntries(STATE.sources.map((source) => [source.slug, source]));
1329
  await renderSourcesFilter();
static/index.html CHANGED
@@ -108,8 +108,8 @@
108
  </div>
109
  <div class="filter-section" id="exact-search-section">
110
  <label class="toggle-row">
111
- <span class="toggle-label">精准搜索</span>
112
- <input type="checkbox" id="exact-search-toggle">
113
  <span class="toggle-switch"></span>
114
  </label>
115
  </div>
 
108
  </div>
109
  <div class="filter-section" id="exact-search-section">
110
  <label class="toggle-row">
111
+ <span class="toggle-label">精准搜索(支持通配符*和?)</span>
112
+ <input type="checkbox" id="exact-search-toggle" checked>
113
  <span class="toggle-switch"></span>
114
  </label>
115
  </div>