feat(version): version x.y.z propre et cohérente partout + bump script
CI / lint (push) Failing after 21s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 27s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
CI / lint (push) Failing after 21s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 27s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Corrige l'incoherence d'affichage (page principale 0.0.0-dev vs a propos v2.0.0-dev) et unifie la gestion de version sur une seule source de verite. Backend: - version.py: get_version() retourne toujours le tag x.y.z propre du dernier tag (plus de 0.0.0-dev / -N-gHASH dans l'UI). Ajout get_git_describe() et get_git_commit() pour le detail. - main.py: /api/health expose git_describe + git_commit (nouveaux champs). Frontend: - modale A propos (populateAbout): version + commit lus depuis /api/health, suppression du hardcode v2.0.0-dev dans index.html. - section config A propos (loadAbout): lit health.version, plus de state. APP_VERSION obsolete. - badge header: fetch /api/health (fallback neutre '...'). - state.js: suppression de APP_VERSION (1.5.0) devenu mort; imports nettoyes. Script: - scripts/bump_version.sh: incrementation SemVer selon les commits (breaking->major, feat->minor, sinon patch), creer/pousser le tag vX.Y.Z. Tests: 507 passed, frontend validators OK.
This commit is contained in:
+6
-2
@@ -287,9 +287,11 @@ class ReloadResponse(BaseModel):
|
|||||||
class HealthResponse(BaseModel):
|
class HealthResponse(BaseModel):
|
||||||
"""Application health status."""
|
"""Application health status."""
|
||||||
status: str = Field(description="Health status ('ok' or 'error')")
|
status: str = Field(description="Health status ('ok' or 'error')")
|
||||||
version: str = Field(description="Application version")
|
version: str = Field(description="Application version (x.y.z — latest release tag)")
|
||||||
vaults: int = Field(description="Number of configured vaults")
|
vaults: int = Field(description="Number of configured vaults")
|
||||||
total_files: int = Field(description="Total indexed files across all vaults")
|
total_files: int = Field(description="Total indexed files across all vaults")
|
||||||
|
git_describe: str = Field(default="", description="Full git describe string (commits beyond tag), empty if no git")
|
||||||
|
git_commit: str = Field(default="", description="Short HEAD commit hash, empty if no git")
|
||||||
|
|
||||||
|
|
||||||
class DirectoryCreateRequest(BaseModel):
|
class DirectoryCreateRequest(BaseModel):
|
||||||
@@ -645,7 +647,7 @@ async def lifespan(app: FastAPI):
|
|||||||
_search_executor = None
|
_search_executor = None
|
||||||
|
|
||||||
|
|
||||||
from backend.version import get_version
|
from backend.version import get_version, get_git_describe, get_git_commit
|
||||||
|
|
||||||
app = FastAPI(title="ObsiGate", version=get_version(), lifespan=lifespan)
|
app = FastAPI(title="ObsiGate", version=get_version(), lifespan=lifespan)
|
||||||
|
|
||||||
@@ -1029,6 +1031,8 @@ async def api_health():
|
|||||||
"version": app.version,
|
"version": app.version,
|
||||||
"vaults": len(index),
|
"vaults": len(index),
|
||||||
"total_files": total_files,
|
"total_files": total_files,
|
||||||
|
"git_describe": get_git_describe(),
|
||||||
|
"git_commit": get_git_commit(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+64
-28
@@ -1,13 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
Version management for ObsiGate.
|
Version management for ObsiGate.
|
||||||
|
|
||||||
Uses git describe to generate a SemVer-compatible version string.
|
The canonical version is the numeric SemVer of the LATEST release tag
|
||||||
Format: MAJOR.MINOR.PATCH[-commits-since-tag-gHASH]
|
(MAJOR.MINOR.PATCH). get_version() always returns a clean "x.y.z" string so
|
||||||
|
the UI never shows "-dev", "0.0.0-dev" or a "-N-gHASH" suffix — the same
|
||||||
|
number appears in the header badge, the About modal and the API health.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
v1.7.0 (exact tag)
|
tag v2.0.0 -> "2.0.0"
|
||||||
v1.7.0-3-gabc1234 (3 commits after v1.7.0)
|
HEAD 31 commits after -> "2.0.0" (release version, no dev clutter)
|
||||||
v1.7.0-dev (fallback when Git unavailable)
|
no git / no VERSION -> "0.0.0"
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -15,13 +17,14 @@ import subprocess
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
_ROOT = Path(__file__).resolve().parent.parent # ObsiGate repo root
|
_ROOT = Path(__file__).resolve().parent.parent # ObsiGate repo root
|
||||||
|
_VERSION_FILE = Path(__file__).resolve().parent / "VERSION"
|
||||||
|
|
||||||
|
|
||||||
def get_version() -> str:
|
def _run_git(args: list[str]) -> str:
|
||||||
"""Get the current version from Git tags."""
|
"""Run a git command in the repo root; return stdout (stripped) or ''."""
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["git", "describe", "--tags", "--dirty=-dirty"],
|
["git", *args],
|
||||||
cwd=str(_ROOT),
|
cwd=str(_ROOT),
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
@@ -29,35 +32,68 @@ def get_version() -> str:
|
|||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
tag = result.stdout.strip()
|
return result.stdout.strip()
|
||||||
# git describe returns v1.7.0 or v1.7.0-3-gabc1234
|
|
||||||
# Remove leading 'v' if present
|
|
||||||
tag = tag.removeprefix("v")
|
|
||||||
return tag
|
|
||||||
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
||||||
pass
|
pass
|
||||||
|
return ""
|
||||||
|
|
||||||
# Fallback: try reading from a VERSION file next to this module
|
|
||||||
version_file = Path(__file__).parent / "VERSION"
|
|
||||||
if version_file.exists():
|
|
||||||
return version_file.read_text().strip()
|
|
||||||
|
|
||||||
return "0.0.0-dev"
|
def _clean_base(raw: str) -> str:
|
||||||
|
"""Reduce a version string to its numeric MAJOR.MINOR.PATCH base.
|
||||||
|
|
||||||
|
Handles "v2.0.0", "2.0.0-31-gabc1234", "2.0.0-dev", "0.0.0-dev".
|
||||||
|
Returns "" if no numeric triplet can be parsed.
|
||||||
|
"""
|
||||||
|
s = (raw or "").strip().lstrip("vV")
|
||||||
|
base = s.split("-")[0] # strip -N-gHASH / -dev / -dirty suffixes
|
||||||
|
parts = base.split(".")
|
||||||
|
nums = []
|
||||||
|
for p in parts[:3]:
|
||||||
|
if not p.isdigit():
|
||||||
|
break
|
||||||
|
nums.append(str(int(p)))
|
||||||
|
if len(nums) != 3:
|
||||||
|
return ""
|
||||||
|
return ".".join(nums)
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_describe() -> str:
|
||||||
|
"""Full `git describe` string (e.g. "2.0.0-31-gabc1234") or '' if no git."""
|
||||||
|
return _run_git(["describe", "--tags", "--dirty=-dirty"]).lstrip("v")
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_commit() -> str:
|
||||||
|
"""Short HEAD commit hash (e.g. "abc1234") or '' if unavailable."""
|
||||||
|
return _run_git(["rev-parse", "--short", "HEAD"])
|
||||||
|
|
||||||
|
|
||||||
|
def get_version() -> str:
|
||||||
|
"""Return the clean release version x.y.z (latest tag) — never a -suffix.
|
||||||
|
|
||||||
|
Priority: latest git tag -> backend/VERSION file -> "0.0.0".
|
||||||
|
"""
|
||||||
|
# 1) Latest tag from git (works even with commits beyond the tag)
|
||||||
|
tag = _run_git(["describe", "--tags", "--abbrev=0"])
|
||||||
|
base = _clean_base(tag)
|
||||||
|
if base:
|
||||||
|
return base
|
||||||
|
|
||||||
|
# 2) backend/VERSION file (baked at build time by build.sh / CI / Docker)
|
||||||
|
if _VERSION_FILE.exists():
|
||||||
|
base = _clean_base(_VERSION_FILE.read_text(encoding="utf-8"))
|
||||||
|
if base:
|
||||||
|
return base
|
||||||
|
|
||||||
|
# 3) Nothing available
|
||||||
|
return "0.0.0"
|
||||||
|
|
||||||
|
|
||||||
def get_version_tuple() -> tuple[int, int, int]:
|
def get_version_tuple() -> tuple[int, int, int]:
|
||||||
"""Return (major, minor, patch) tuple for programmatic use."""
|
"""Return (major, minor, patch) tuple for programmatic use."""
|
||||||
raw = get_version()
|
raw = get_version()
|
||||||
# Strip -suffix for parsing
|
base = _clean_base(raw) or "0.0.0"
|
||||||
base = raw.split("-")[0]
|
major, minor, patch = (int(p) for p in base.split("."))
|
||||||
try:
|
return (major, minor, patch)
|
||||||
parts = base.split(".")
|
|
||||||
major = int(parts[0]) if len(parts) > 0 else 0
|
|
||||||
minor = int(parts[1]) if len(parts) > 1 else 0
|
|
||||||
patch = int(parts[2]) if len(parts) > 2 else 0
|
|
||||||
return (major, minor, patch)
|
|
||||||
except (ValueError, IndexError):
|
|
||||||
return (0, 0, 0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+1
-1
@@ -4950,7 +4950,7 @@ curl -X POST https://votre-serveur.com/webhook \
|
|||||||
</div>
|
</div>
|
||||||
<h1 class="about-title" data-i18n="header.logo">ObsiGate</h1>
|
<h1 class="about-title" data-i18n="header.logo">ObsiGate</h1>
|
||||||
<p class="about-tagline">Vos notes Obsidian,<br>simplement accessibles.</p>
|
<p class="about-tagline">Vos notes Obsidian,<br>simplement accessibles.</p>
|
||||||
<span class="about-version">v2.0.0-dev</span>
|
<span class="about-version">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="about-body">
|
<div class="about-body">
|
||||||
<div class="about-stats">
|
<div class="about-stats">
|
||||||
|
|||||||
+1
-2
@@ -5,7 +5,6 @@
|
|||||||
import * as UI from './ui.js';
|
import * as UI from './ui.js';
|
||||||
import * as Utils from './utils.js';
|
import * as Utils from './utils.js';
|
||||||
import { initI18n, t } from './i18n.js';
|
import { initI18n, t } from './i18n.js';
|
||||||
import { state } from './state.js';
|
|
||||||
import { initAIFab } from './ai-fab.js';
|
import { initAIFab } from './ai-fab.js';
|
||||||
|
|
||||||
// Wire up AI toolbar toast (avoids circular import in utils.js)
|
// Wire up AI toolbar toast (avoids circular import in utils.js)
|
||||||
@@ -117,7 +116,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
// Show version in header — always visible, fallback before the async fetch
|
// Show version in header — always visible, fallback before the async fetch
|
||||||
const versionBadge = document.getElementById('version-badge');
|
const versionBadge = document.getElementById('version-badge');
|
||||||
if (versionBadge) {
|
if (versionBadge) {
|
||||||
versionBadge.textContent = window.__OBSIGATE_VERSION || state.APP_VERSION || 'dev';
|
versionBadge.textContent = window.__OBSIGATE_VERSION || '…';
|
||||||
}
|
}
|
||||||
fetch('/api/health')
|
fetch('/api/health')
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
|
|||||||
@@ -923,8 +923,7 @@ function loadAbout() {
|
|||||||
title: t("about.section_app"),
|
title: t("about.section_app"),
|
||||||
rows: [
|
rows: [
|
||||||
[t("about.name"), "ObsiGate"],
|
[t("about.name"), "ObsiGate"],
|
||||||
[t("about.version"), state.APP_VERSION],
|
[t("about.version"), health.version || "—"],
|
||||||
[t("about.api_version"), health.version || "—"],
|
|
||||||
[t("about.status"), health.status || "—"],
|
[t("about.status"), health.status || "—"],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -1653,7 +1652,7 @@ function initAboutModal() {
|
|||||||
document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && overlay.classList.contains('active')) overlay.classList.remove('active'); });
|
document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && overlay.classList.contains('active')) overlay.classList.remove('active'); });
|
||||||
|
|
||||||
function populateAbout() {
|
function populateAbout() {
|
||||||
// Live stats from health endpoint
|
// Live stats + version from health endpoint (single source of truth)
|
||||||
fetch('/api/health')
|
fetch('/api/health')
|
||||||
.then(function(r) { return r.json(); })
|
.then(function(r) { return r.json(); })
|
||||||
.then(function(d) {
|
.then(function(d) {
|
||||||
@@ -1661,6 +1660,11 @@ function initAboutModal() {
|
|||||||
var vEl = document.getElementById('about-stat-vaults');
|
var vEl = document.getElementById('about-stat-vaults');
|
||||||
if (fEl) fEl.textContent = d.total_files || '—';
|
if (fEl) fEl.textContent = d.total_files || '—';
|
||||||
if (vEl) vEl.textContent = d.vaults || '—';
|
if (vEl) vEl.textContent = d.vaults || '—';
|
||||||
|
// Clean x.y.z version (not hardcoded) + git detail
|
||||||
|
var versionEl = document.getElementById('about-version');
|
||||||
|
if (versionEl && d.version) versionEl.textContent = d.version;
|
||||||
|
var commitEl = document.getElementById('about-commit');
|
||||||
|
if (commitEl) commitEl.textContent = d.git_describe || d.git_commit || '—';
|
||||||
})
|
})
|
||||||
.catch(function() {});
|
.catch(function() {});
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
Use state.xxx to read/write any value. ES module imports are read-only,
|
Use state.xxx to read/write any value. ES module imports are read-only,
|
||||||
so we export a single mutable object instead of individual let bindings. */
|
so we export a single mutable object instead of individual let bindings. */
|
||||||
export const state = {
|
export const state = {
|
||||||
APP_VERSION: "1.5.0",
|
|
||||||
|
|
||||||
// Core navigation
|
// Core navigation
|
||||||
currentVault: null,
|
currentVault: null,
|
||||||
currentPath: null,
|
currentPath: null,
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# ObsiGate — version bump helper (SemVer MAJOR.MINOR.PATCH)
|
||||||
|
#
|
||||||
|
# The canonical version is the latest release tag. This tool inspects the
|
||||||
|
# commits since that tag and decides the next version from their type
|
||||||
|
# (Conventional Commits), then creates and pushes the new tag:
|
||||||
|
#
|
||||||
|
# breaking change / `!:` / "BREAKING CHANGE" -> MAJOR bump (x.0.0)
|
||||||
|
# feat / feature -> MINOR bump (x.y.0)
|
||||||
|
# anything else (fix, perf, refactor, ...) -> PATCH bump (x.y.z)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/bump_version.sh [--major|--minor|--patch] [--dry-run] [--push]
|
||||||
|
#
|
||||||
|
# --dry-run : only print the computed next version, do not tag.
|
||||||
|
# --push : also `git push origin` the new tag (default: local only).
|
||||||
|
# --major|--minor|--patch : force the increment, overriding commit detection.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# scripts/bump_version.sh --dry-run # preview next version
|
||||||
|
# scripts/bump_version.sh # tag locally per commits
|
||||||
|
# scripts/bump_version.sh --patch --push # force patch + push
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(git rev-parse --show-toplevel)" # run from repo root regardless of cwd
|
||||||
|
BRANCH="$(git branch --show-current)"
|
||||||
|
|
||||||
|
# ── Parse args ────────────────────────────────────────────────────────────
|
||||||
|
FORCE=""; DRY_RUN=0; DO_PUSH=0
|
||||||
|
for a in "$@"; do
|
||||||
|
case "$a" in
|
||||||
|
--major) FORCE=major ;;
|
||||||
|
--minor) FORCE=minor ;;
|
||||||
|
--patch) FORCE=patch ;;
|
||||||
|
--dry-run) DRY_RUN=1 ;;
|
||||||
|
--push) DO_PUSH=1 ;;
|
||||||
|
*) echo "✗ Argument inconnu: $a"; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Current version = latest tag base ─────────────────────────────────────
|
||||||
|
CURRENT="$(git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0")"
|
||||||
|
CURRENT="${CURRENT#v}"
|
||||||
|
IFS='.' read -r MAJ MIN PAT <<< "$CURRENT"
|
||||||
|
MAJ="${MAJ:-0}"; MIN="${MIN:-0}"; PAT="${PAT:-0}"
|
||||||
|
|
||||||
|
echo "Version actuelle : $CURRENT (branche: $BRANCH)"
|
||||||
|
|
||||||
|
# ── Detect bump type from commits since last tag ──────────────────────────
|
||||||
|
if [ -z "$FORCE" ]; then
|
||||||
|
# Use HEAD~1..HEAD when no tag exists yet (all commits "since" nothing).
|
||||||
|
RANGE="${CURRENT#0.0.0}" # if 0.0.0 fallback there may be no tag at all
|
||||||
|
if git rev-parse "v${CURRENT}" >/dev/null 2>&1; then
|
||||||
|
RANGE="v${CURRENT}..HEAD"
|
||||||
|
else
|
||||||
|
RANGE="HEAD~10..HEAD"
|
||||||
|
echo "⚠ Aucun tag v${CURRENT} trouvé — analyse des 10 derniers commits."
|
||||||
|
fi
|
||||||
|
LOG="$(git log --format='%s%n%b' "$RANGE" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
BUMP="patch" # default
|
||||||
|
if echo "$LOG" | grep -qE '^[a-zA-Z]+\([^)]*\)!:|\!:\s|BREAKING CHANGE:'; then
|
||||||
|
BUMP="major"
|
||||||
|
elif echo "$LOG" | grep -qiE '^(feat|feature)(\(|:|\s)'; then
|
||||||
|
BUMP="minor"
|
||||||
|
fi
|
||||||
|
# If nothing meaningful in range, keep patch
|
||||||
|
echo "Détection (${RANGE}) : changement ${BUMP}"
|
||||||
|
else
|
||||||
|
BUMP="$FORCE"
|
||||||
|
echo "Incrément forcé : ${BUMP}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Compute next version ──────────────────────────────────────────────────
|
||||||
|
case "$BUMP" in
|
||||||
|
major) MAJ=$((MAJ + 1)); MIN=0; PAT=0 ;;
|
||||||
|
minor) MIN=$((MIN + 1)); PAT=0 ;;
|
||||||
|
*) PAT=$((PAT + 1)) ;;
|
||||||
|
esac
|
||||||
|
NEXT="${MAJ}.${MIN}.${PAT}"
|
||||||
|
|
||||||
|
# ── Guard: tag already exists? ────────────────────────────────────────────
|
||||||
|
if git rev-parse "v${NEXT}" >/dev/null 2>&1; then
|
||||||
|
echo "✗ Le tag v${NEXT} existe déjà. Corrigez l'incrément ou retirez le tag."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
echo "── DRY RUN ─────────────────────────────"
|
||||||
|
echo " Prochain tag : v${NEXT}"
|
||||||
|
echo " (aucun tag créé)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Create tag ────────────────────────────────────────────────────────────
|
||||||
|
git tag -a "v${NEXT}" -m "Release v${NEXT} (${BUMP} bump depuis v${CURRENT})"
|
||||||
|
echo "✓ Tag créé localement : v${NEXT}"
|
||||||
|
|
||||||
|
if [ "$DO_PUSH" = "1" ]; then
|
||||||
|
git push origin "v${NEXT}"
|
||||||
|
echo "✓ Tag poussé : v${NEXT}"
|
||||||
|
else
|
||||||
|
echo "ℹ Tag local uniquement. Poussez avec: git push origin v${NEXT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Done."
|
||||||
@@ -28,7 +28,7 @@ function testEscapeHtml() {
|
|||||||
// ── Test state object structure ────────────────────────────────────────────
|
// ── Test state object structure ────────────────────────────────────────────
|
||||||
// Replicate state.js structure for testing
|
// Replicate state.js structure for testing
|
||||||
const expectedStateKeys = [
|
const expectedStateKeys = [
|
||||||
'APP_VERSION', 'currentVault', 'currentPath', 'allVaults', 'selectedContextVault',
|
'currentVault', 'currentPath', 'allVaults', 'selectedContextVault',
|
||||||
'searchTimeout', 'searchAbortController', 'advancedSearchOffset', 'advancedSearchTotal',
|
'searchTimeout', 'searchAbortController', 'advancedSearchOffset', 'advancedSearchTotal',
|
||||||
'advancedSearchSort', 'advancedSearchLastQuery', 'suggestAbortController',
|
'advancedSearchSort', 'advancedSearchLastQuery', 'suggestAbortController',
|
||||||
'dropdownActiveIndex', 'dropdownItems', 'currentSearchId', 'selectedTags',
|
'dropdownActiveIndex', 'dropdownItems', 'currentSearchId', 'selectedTags',
|
||||||
@@ -43,7 +43,7 @@ const expectedStateKeys = [
|
|||||||
|
|
||||||
function testStateKeys() {
|
function testStateKeys() {
|
||||||
const state = {
|
const state = {
|
||||||
APP_VERSION: "1.5.0", currentVault: null, currentPath: null, allVaults: [],
|
currentVault: null, currentPath: null, allVaults: [],
|
||||||
selectedContextVault: "all", searchTimeout: null, searchAbortController: null,
|
selectedContextVault: "all", searchTimeout: null, searchAbortController: null,
|
||||||
advancedSearchOffset: 0, advancedSearchTotal: 0, advancedSearchSort: "relevance",
|
advancedSearchOffset: 0, advancedSearchTotal: 0, advancedSearchSort: "relevance",
|
||||||
advancedSearchLastQuery: "", suggestAbortController: null, dropdownActiveIndex: -1,
|
advancedSearchLastQuery: "", suggestAbortController: null, dropdownActiveIndex: -1,
|
||||||
|
|||||||
Reference in New Issue
Block a user