feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets

This commit is contained in:
2026-10-09 15:26:58 +05:00
parent 7f91366201
commit 0b12e4583b
244 changed files with 611461 additions and 1617 deletions
+110
View File
@@ -0,0 +1,110 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show rootBundle;
import '../models/tradition_model.dart';
class TraditionsRepository {
static final TraditionsRepository _instance = TraditionsRepository._internal();
factory TraditionsRepository() => _instance;
TraditionsRepository._internal();
List<TraditionCanon> _traditions = const [];
bool _loaded = false;
List<TraditionCanon> get traditions => List.unmodifiable(_traditions);
bool get isLoaded => _loaded;
static const Map<String, String> filterToCanonId = {
'ᚱ Руны': 'runes',
'᚛ Огам': 'ogham',
'✡️ Каббала': 'kabbalah',
'⚗️ Алхимия': 'alchemy',
'🎴 Таро': 'tarot',
'🪶 Ленорман': 'lenormand',
'☯️ И Цзин': 'iching',
'⌛ Геомантия': 'geomancy',
'♈ Гороскоп': 'western_zodiac',
'🐉 Китайский зодиак': 'zodiac',
'🌌 Космология': 'cosmology',
'📅 Дни недели': 'days_of_week',
'🗓️ Месяцы года': 'months_of_year',
'🔢 Нумерология': 'numerology',
};
TraditionCanon? byId(String id) {
for (final t in _traditions) {
if (t.id == id) return t;
}
return null;
}
/// Finds all stations across traditions that relate to this word (either as primary station or in cluster)
List<({TraditionCanon tradition, TraditionStation station, bool isPrimary})> findMatchesForWord(String word, {int? wordId}) {
final cleanWord = word.trim().toLowerCase();
final results = <({TraditionCanon tradition, TraditionStation station, bool isPrimary})>[];
for (final tradition in _traditions) {
for (final station in tradition.stations) {
if (station.primary != null &&
(station.primary!.word.trim().toLowerCase() == cleanWord ||
(wordId != null && station.primary!.id == wordId))) {
results.add((tradition: tradition, station: station, isPrimary: true));
} else if (station.cluster.any((cw) =>
cw.word.trim().toLowerCase() == cleanWord ||
(wordId != null && cw.id == wordId))) {
results.add((tradition: tradition, station: station, isPrimary: false));
}
}
}
return results;
}
Future<void> init() async {
if (_loaded && _traditions.isNotEmpty) return;
Map<String, dynamic>? decoded;
final cwd = Directory.current.path;
final candidates = <String>{
'assets/data/traditions_canon.json',
'../assets/data/traditions_canon.json',
'$cwd/assets/data/traditions_canon.json',
'$cwd/../assets/data/traditions_canon.json',
};
for (final path in candidates) {
try {
final file = File(path);
if (file.existsSync()) {
decoded = json.decode(file.readAsStringSync()) as Map<String, dynamic>;
break;
}
} catch (e) {
debugPrint('traditions_canon.json skip $path: $e');
}
}
if (decoded == null) {
try {
final jsonStr = await rootBundle.loadString('assets/data/traditions_canon.json');
decoded = json.decode(jsonStr) as Map<String, dynamic>;
} catch (e) {
debugPrint('Could not load traditions_canon.json: $e');
}
}
if (decoded == null) {
_traditions = const [];
_loaded = true;
return;
}
_traditions = decoded.values
.whereType<Map<String, dynamic>>()
.map(TraditionCanon.fromMap)
.toList();
_loaded = true;
}
}