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
+127
View File
@@ -0,0 +1,127 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
test('Markdown parser extracts all 5 blocks without loss or substring collisions', () {
const input = '''```markdown
## 1. ЯДРО / ФОРМУЛА ЗАКОНА:
(1-2 чеканных предложения)
Огонь — это первородная сингулярность трансформации материи в чистую радиацию духа.
## 2. ОРАКУЛ (МОНОЛИТНОЕ ЭССЕ):
(5-8 абзацев)
Праиндоевропейский корень *egni- фиксирует не субстанцию, а событие горения.
В шумерском мифе бог Гибил нисходит в тигель трансформации.
В алхимии огонь выступает универсальным растворителем формы ради освобождения скрытой субстанции.
## 3. ТЕНЕВАЯ ЛОВУШКА И ИСКАЖЕНИЕ:
(Теневая ловушка принципа)
Теневая ловушка принципа — испепеление контекста, мания тотального контроля через разрушение.
## 4. ЭКЗИСТЕНЦИАЛЬНЫЙ ВОПРОС:
(Острый коан)
Что именно в твоей структуре ты отказываешься отдать пламени обновления?
## 5. РИТУАЛЬНЫЙ ФИЗИЧЕСКИЙ ЖЕСТ:
(Конкретное микродействие)
Взять сухую щепку, разломить её надвое и бросить в открытый огонь, не оборачиваясь.
Метрика: 4120 символов
```''';
Map<String, String> parseRevisionBlocks(String text) {
final blocks = <String, String>{};
if (text.trim().isEmpty) return blocks;
var src = text.trim();
if (src.startsWith('```')) {
src = src.replaceFirst(RegExp(r'^```[a-zA-Z]*\n?'), '');
if (src.endsWith('```')) {
src = src.substring(0, src.length - 3).trim();
}
}
final lines = src.replaceAll('\r\n', '\n').split('\n');
String? detectSlot(String line) {
final stripped = line.trim();
if (stripped.isEmpty || stripped.length > 95) return null;
final isHeaderFormat = stripped.startsWith('#') ||
stripped.startsWith('*') ||
stripped.startsWith('[') ||
RegExp(r'^(?:\d+[\.\)]|[IVX]+[\.\)])').hasMatch(stripped) ||
(stripped.length < 70 && (stripped.endsWith(':') || stripped == stripped.toUpperCase())) ||
RegExp(r'\b(?:ЯДРО|ОРАКУЛ|ТЕНЬ|ВОПРОС|ЖЕСТ|КОАН)\b', caseSensitive: false).hasMatch(stripped);
if (!isHeaderFormat) return null;
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ЯДР[ОАЕУ]|ФОРМУЛ[А-Яа-я]*|ОНТОЛОГИ[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
return 'ЯДРО';
}
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ВОПРОС[А-Яа-я]*|КОАН[А-Яа-я]*|РЕФЛЕКСИ[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
return 'ВОПРОС';
}
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ТЕН[ЬЯЕИЮ]|ТЕНЕВ[А-Яа-я]*|ЛОВУШК[А-Яа-я]*|ИСКАЖЕН[А-Яа-я]*|СЛЕП[А-Яа-я]*\s+ЗОН[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
return 'ТЕНЬ';
}
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ЖЕСТ[А-Яа-я]*|МИКРОДЕЙСТВИ[А-Яа-я]*|ДЕЙСТВИ[ЕЯ]|ПРАКТИК[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
return 'ЖЕСТ';
}
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ОРАКУЛ[А-Яа-я]*|СТАТЬ[ЯЕИЮ]|ЭССЕ|ТОЛКОВАНИ[А-Яа-я]*|ОБОГАЩЕНИ[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
return 'ОРАКУЛ';
}
return null;
}
final headers = <({int lineIdx, String slot})>[];
for (int i = 0; i < lines.length; i++) {
final slot = detectSlot(lines[i]);
if (slot != null) {
headers.add((lineIdx: i, slot: slot));
}
}
for (int i = 0; i < headers.length; i++) {
final startLineIdx = headers[i].lineIdx + 1;
final slot = headers[i].slot;
final endLineIdx = (i + 1 < headers.length) ? headers[i + 1].lineIdx : lines.length;
var content = lines.sublist(startLineIdx, endLineIdx).join('\n').trim();
content = content.replaceFirst(
RegExp(r'^\s*\([^)]*(?:предложен|абзац|коан|действи|формул|ловушк|стать)[^)]*\)\s*\n?', caseSensitive: false),
'',
).trim();
content = content.replaceAll(
RegExp(r'(?:\n|\r\n)\s*(?:Метрика|Количество символов|Символов|Длина текста)[:—–\s][\s\S]*$', caseSensitive: false),
'',
).trim();
if (content.isNotEmpty) {
if (!blocks.containsKey(slot) || content.length > blocks[slot]!.length) {
blocks[slot] = content;
}
}
}
return blocks;
}
final result = parseRevisionBlocks(input);
expect(result.length, 5);
expect(result.containsKey('ЯДРО'), true);
expect(result.containsKey('ОРАКУЛ'), true);
expect(result.containsKey('ТЕНЬ'), true);
expect(result.containsKey('ВОПРОС'), true);
expect(result.containsKey('ЖЕСТ'), true);
expect(result['ЯДРО']!.startsWith('Огонь — это первородная'), true);
expect(result['ОРАКУЛ']!.contains('Праиндоевропейский корень'), true);
expect(result['ТЕНЬ']!.contains('Теневая ловушка принципа'), true);
expect(result['ВОПРОС']!.contains('Что именно в твоей структуре'), true);
expect(result['ЖЕСТ']!.contains('Взять сухую щепку'), true);
expect(result['ЖЕСТ']!.contains('Метрика'), false);
});
}