feat: add vehicle customization, Arabic search normalization, and smooth movement interpolation

This commit is contained in:
Hamza-Ayed
2026-09-19 12:34:25 +03:00
parent 43ad2e0ad4
commit 55830beee8
16 changed files with 2295 additions and 393 deletions
@@ -0,0 +1,51 @@
class ArabicSearchNormalizer {
ArabicSearchNormalizer._();
static final RegExp _diacriticsRegExp = RegExp(
r'[\u064B-\u065F\u0670\u0640]', // Harakat, Tanween, Shadda, Sukoon, Tatweel
);
/// Normalizes Arabic text for flexible, fuzzy searching:
/// - Strips diacritics / tashkeel
/// - Normalizes all Alef forms (أ, إ, آ, ٱ) -> ا
/// - Normalizes Alef Maksura (ى) -> ي
/// - Normalizes Teh Marbuta (ة) -> ه
/// - Normalizes Hamza on Waw (ؤ) -> و and Hamza on Yaa (ئ) -> ي
/// - Lowercases English characters and trims spaces
static String normalize(String text) {
if (text.isEmpty) return '';
String cleaned = text
.replaceAll(_diacriticsRegExp, '')
.replaceAll(RegExp(r'[أإآٱ]'), 'ا')
.replaceAll('ى', 'ي')
.replaceAll('ة', 'ه')
.replaceAll('ؤ', 'و')
.replaceAll('ئ', 'ي')
.toLowerCase()
.trim();
// Collapse multiple whitespaces into a single space
cleaned = cleaned.replaceAll(RegExp(r'\s+'), ' ');
return cleaned;
}
/// Checks if [target] contains [query], normalizing both in Arabic and English.
static bool matches(String target, String query) {
final normTarget = normalize(target);
final normQuery = normalize(query);
if (normQuery.isEmpty) return true;
if (normTarget.isEmpty) return false;
if (normTarget.contains(normQuery)) return true;
// Split words in query and verify each token is present
final queryWords = normQuery.split(' ').where((w) => w.isNotEmpty);
if (queryWords.length > 1) {
return queryWords.every((word) => normTarget.contains(word));
}
return false;
}
}