first deployment of kikomiko with url photos

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
shayan khoshrou
2026-10-19 19:10:57 +02:00
co-authored by Cursor
parent 8d98304137
commit ae3ed7c7b7
159 changed files with 59509 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
/** Shared client-side catalog search (catalog.dedupe2.json). */
let catalogPromise = null;
export function loadSearchCatalog() {
if (!catalogPromise) {
catalogPromise = fetch("/data/catalog.dedupe2.json")
.then((r) => {
if (!r.ok) throw new Error("catalog fetch failed");
return r.json();
})
.then((data) => data.products || []);
}
return catalogPromise;
}
export function displayName(p) {
const variants = p.colorVariants;
let title = p.name;
if (variants && variants.length >= 2) {
const shade = variants.find((v) => v.slug === p.slug)?.shade;
if (shade) {
const trimmed = p.name.trim();
if (trimmed.endsWith(shade)) title = trimmed.slice(0, -shade.length).trim();
}
}
const fa = p.categoryFa || null;
if (fa && !title.includes(fa)) return `\u2067${fa} · \u2066${title}\u2069\u2069`;
return title;
}
export function searchCatalogProducts(products, query, limit) {
const needle = String(query || "").trim().toLowerCase();
if (!needle) return [];
const hits = products.filter((p) => {
const hay = `${p.name} ${p.category || ""} ${p.categoryFa || ""}`.toLowerCase();
return hay.includes(needle);
});
return limit != null ? hits.slice(0, limit) : hits;
}
export function formatSearchPrice(price) {
const amount = Number.isFinite(price) ? Math.round(price) : 0;
return `${new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 0 }).format(amount)} تومان`;
}
export function escapeSearchHtml(s) {
return String(s)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;");
}