- Flutter app (Android/iOS): home, explore, albums, search, player with equalizer, mini player, sign up/sign in with email codes, profile, encrypted offline downloads, About page - server/: one-command deploy of self-hosted Supabase behind HestiaCP/nginx (or Caddy), migrations, backups, email templates - supabase/migrations: schema, row-level security, profiles - tools/: song scanner and importer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
40 lines
1.3 KiB
Dart
40 lines
1.3 KiB
Dart
/// Badaga has no fixed Latin spelling ("Madhuvae" / "Madhuve", "Hethai" / "Hettai" / "Hethe").
|
|
/// Both the query and the searchable text are folded to a common key before matching.
|
|
/// Tamil-script text passes through unchanged (only lower-cased Latin is folded).
|
|
String foldForSearch(String input) {
|
|
var s = input.toLowerCase().replaceAll(RegExp(r"[^a-z0-9-ಀ- ]"), ' ');
|
|
|
|
const digraphs = {
|
|
'ae': 'e',
|
|
'ee': 'i',
|
|
'oo': 'u',
|
|
'aa': 'a',
|
|
'th': 't',
|
|
'dh': 'd',
|
|
'bh': 'b',
|
|
'ph': 'p',
|
|
'kh': 'k',
|
|
'gh': 'g',
|
|
'sh': 's',
|
|
'zh': 'l',
|
|
'w': 'v',
|
|
};
|
|
digraphs.forEach((from, to) => s = s.replaceAll(from, to));
|
|
|
|
// Word-final "ai" and "e" are written interchangeably (Hethai / Hethe).
|
|
s = s.replaceAllMapped(RegExp(r'ai\b'), (_) => 'e');
|
|
|
|
// Collapse doubled letters: "hettai" -> "hetai", "madhuvve" -> "madhuve".
|
|
s = s.replaceAllMapped(RegExp(r'([a-z])\1+'), (m) => m[1]!);
|
|
|
|
return s.replaceAll(RegExp(r'\s+'), ' ').trim();
|
|
}
|
|
|
|
/// True when every word of [query] appears in at least one of [fields].
|
|
bool matchesSearch(String query, Iterable<String?> fields) {
|
|
final words = foldForSearch(query).split(' ').where((w) => w.isNotEmpty).toList();
|
|
if (words.isEmpty) return false;
|
|
final haystack = fields.whereType<String>().map(foldForSearch).join(' ');
|
|
return words.every(haystack.contains);
|
|
}
|