Files
999/lib/screens/word_of_the_day_screen.dart

1862 lines
68 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../models/syncretic_session_model.dart';
import '../models/history_entry_model.dart';
import '../models/word_model.dart';
import '../models/word_of_day_model.dart';
import '../services/ai_oracle_service.dart';
import '../services/day_numerology.dart';
import '../services/settings_repository.dart';
import '../services/word_of_the_day_service.dart';
import '../services/words_repository.dart';
import '../widgets/divination_instruction_modal.dart';
import '../widgets/gradient_star_badge.dart';
import 'symbol_detail_screen.dart';
class WordOfTheDayScreen extends StatefulWidget {
final DateTime? initialDate;
const WordOfTheDayScreen({super.key, this.initialDate});
@override
State<WordOfTheDayScreen> createState() => _WordOfTheDayScreenState();
}
class _WordOfTheDayScreenState extends State<WordOfTheDayScreen>
with SingleTickerProviderStateMixin {
final _service = WordOfTheDayService();
final _wordsRepo = WordsRepository();
final _aiService = AiOracleService();
final _settingsRepo = SettingsRepository();
late DateTime _selectedDate;
late TextEditingController _diaryController;
late TabController _tabController;
bool _isLoadingAi = false;
String? _aiAdvice;
bool _isDiarySaved = false;
@override
void initState() {
super.initState();
_selectedDate = widget.initialDate ?? DateTime.now();
_tabController = TabController(length: 3, vsync: this);
final rec = _service.getRecord(_selectedDate);
_diaryController = TextEditingController(text: rec.diaryNote);
}
@override
void dispose() {
_diaryController.dispose();
_tabController.dispose();
super.dispose();
}
void _changeDate(DateTime newDate) {
setState(() {
_selectedDate = newDate;
final rec = _service.getRecord(_selectedDate);
_diaryController.text = rec.diaryNote;
_aiAdvice = null;
_isDiarySaved = false;
});
}
void _previousDay() {
HapticFeedback.selectionClick();
_changeDate(_selectedDate.subtract(const Duration(days: 1)));
}
void _nextDay() {
HapticFeedback.selectionClick();
_changeDate(_selectedDate.add(const Duration(days: 1)));
}
void _jumpToToday() {
HapticFeedback.mediumImpact();
_changeDate(DateTime.now());
}
Future<void> _pickDate() async {
final picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2020),
lastDate: DateTime(2035),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: const ColorScheme.dark(
primary: Color(0xFFFFD54F),
onPrimary: Color(0xFF151829),
surface: Color(0xFF151829),
onSurface: Color(0xFFE8EAF6),
),
),
child: child!,
);
},
);
if (picked != null) {
_changeDate(picked);
}
}
String _formatDateTitle(DateTime d) {
final now = DateTime.now();
final isToday = d.year == now.year && d.month == now.month && d.day == now.day;
final isYesterday = d.year == now.year &&
d.month == now.month &&
d.day == now.day - 1;
final isTomorrow = d.year == now.year &&
d.month == now.month &&
d.day == now.day + 1;
const months = [
'января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'
];
final dateStr = '${d.day} ${months[d.month - 1]} ${d.year}';
if (isToday) return 'СЕГОДНЯ • $dateStr';
if (isYesterday) return 'ВЧЕРА • $dateStr';
if (isTomorrow) return 'ЗАВТРА • $dateStr';
return dateStr;
}
DayCode _dayCode() => DayNumerology.ofDate(_selectedDate);
ReadingSignItem _dayCodeSign() {
final code = _dayCode();
final linked = code.word.isEmpty ? null : _wordsRepo.getWordByName(code.word);
return ReadingSignItem(
title: 'Код суток: ${code.value}',
subtitle: code.title,
symbol: '${code.value}',
lore: code.clause,
linkedWord: linked,
meta: {
'type': 'day_numerology',
'number': code.value,
'formula': code.formula,
},
);
}
void _openDayCodeWord() {
final linked = _wordsRepo.getWordByName(_dayCode().word);
if (linked == null) return;
HapticFeedback.selectionClick();
Navigator.push(
context,
MaterialPageRoute(builder: (_) => SymbolDetailScreen(word: linked)),
);
}
Widget _buildDayCodeCard() {
final code = _dayCode();
return Material(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(16),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: _openDayCodeWord,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.35)),
),
child: Row(
children: [
Container(
width: 48,
height: 48,
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0xFF2A2412),
borderRadius: BorderRadius.circular(12),
),
child: Text(
'${code.value}',
style: const TextStyle(
color: Color(0xFFFFD54F),
fontSize: 20,
fontWeight: FontWeight.w900,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'КОД СУТОК',
style: TextStyle(
color: Color(0xFFFFD54F),
fontSize: 10,
fontWeight: FontWeight.w800,
letterSpacing: 0.8,
),
),
const SizedBox(height: 2),
Text(
code.word.isEmpty ? code.title : '«${code.word}» · ${code.title}',
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 13, fontWeight: FontWeight.w700),
),
const SizedBox(height: 2),
Text(
'день ${code.reducedDay} · месяц ${code.reducedMonth} · год ${code.reducedYear}',
style: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
),
],
),
),
const Icon(Icons.chevron_right_rounded, color: Color(0xFFFFD54F)),
],
),
),
),
);
}
Future<void> _revealWord({String phase = 'main'}) async {
HapticFeedback.heavyImpact();
await _service.revealWord(_selectedDate, phase: phase);
setState(() {});
}
Future<void> _toggleStep(bool completed, {bool isDayStep = false}) async {
HapticFeedback.mediumImpact();
await _service.toggleStepCompleted(_selectedDate, completed, isDayStep: isDayStep);
setState(() {});
}
Future<void> _saveDiary() async {
HapticFeedback.mediumImpact();
await _service.saveDiaryNote(_selectedDate, _diaryController.text);
setState(() {
_isDiarySaved = true;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Row(
children: [
GradientStar(size: 16),
SizedBox(width: 8),
Text('Синхронистическая заметка сохранена в летопись'),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
);
}
}
Future<void> _toggleMode(bool isTriptych) async {
HapticFeedback.selectionClick();
await _service.setRecordTriptychMode(_selectedDate, isTriptych);
await _service.setGlobalTriptychMode(isTriptych);
setState(() {});
}
Future<void> _weaveIntoSyncreticSessionSingle(WordItem word) async {
HapticFeedback.mediumImpact();
final reading = SyncreticReading(
id: 'sod_${DateTime.now().millisecondsSinceEpoch}',
systemId: 'word_of_day',
systemName: 'Слово Дня',
systemIcon: '☀️',
timestamp: DateTime.now(),
layoutTitle: 'Архетип Дня • ${_formatDateTitle(_selectedDate)}',
inputMethod: 'calendar',
items: [
ReadingSignItem(
title: word.word.toUpperCase(),
subtitle: word.category.isNotEmpty ? word.category : 'Слово Дня',
symbol: word.emoji.isNotEmpty ? word.emoji : '☀️',
lore: word.oracle.isNotEmpty ? word.oracle : word.cleanDescription,
linkedWord: word,
meta: {
'dateKey': WordOfTheDayService.formatDateKey(_selectedDate),
'core': word.core,
'asks': word.asks,
'step': word.step,
},
),
_dayCodeSign(),
],
);
await _wordsRepo.addReadingToSyncreticSession(reading);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const GradientStar(size: 18),
const SizedBox(width: 8),
Expanded(
child: Text('«${word.word.toUpperCase()}» вплетено в Сессию Дня!'),
),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
);
}
}
Future<void> _weaveTriptychIntoSyncreticSession(
WordItem morning,
WordItem day,
WordItem evening,
) async {
HapticFeedback.mediumImpact();
final reading = SyncreticReading(
id: 'trip_${DateTime.now().millisecondsSinceEpoch}',
systemId: 'word_of_day',
systemName: 'Триптих Дня',
systemIcon: '⏳',
timestamp: DateTime.now(),
layoutTitle: 'Триптих Суток (Утро • День • Вечер) • ${_formatDateTitle(_selectedDate)}',
inputMethod: 'calendar_triptych',
items: [
ReadingSignItem(
title: '🌅 УТРО: ${morning.word.toUpperCase()}',
subtitle: morning.category,
symbol: morning.emoji.isNotEmpty ? morning.emoji : '🌅',
lore: morning.core.isNotEmpty ? morning.core : morning.oracle,
linkedWord: morning,
),
ReadingSignItem(
title: '⚡ ДЕНЬ: ${day.word.toUpperCase()}',
subtitle: day.category,
symbol: day.emoji.isNotEmpty ? day.emoji : '⚡',
lore: day.step.isNotEmpty ? day.step : day.oracle,
linkedWord: day,
),
ReadingSignItem(
title: '🌙 ВЕЧЕР: ${evening.word.toUpperCase()}',
subtitle: evening.category,
symbol: evening.emoji.isNotEmpty ? evening.emoji : '🌙',
lore: evening.shadow.isNotEmpty ? evening.shadow : evening.oracle,
linkedWord: evening,
),
],
);
await _wordsRepo.addReadingToSyncreticSession(reading);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Row(
children: [
GradientStar(size: 18),
SizedBox(width: 8),
Expanded(
child: Text('Триптих дня (Утро, День, Вечер) вплетён в Сессию!'),
),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
);
}
}
Future<void> _consultOracleSingle(WordItem word) async {
setState(() => _isLoadingAi = true);
HapticFeedback.mediumImpact();
try {
final promptQuestion =
'Слово Дня: «${word.word.toUpperCase()}». ${_dayCode().clause} Напутствие на день: как прожить этот архетип осознанно, не упав в тень?';
final advice = await _aiService.interpret(
word: word,
question: promptQuestion,
positionTitle: 'Камертон дня',
);
if (mounted) {
setState(() {
_aiAdvice = advice;
_isLoadingAi = false;
});
}
} catch (e) {
if (mounted) {
setState(() => _isLoadingAi = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ошибка связи с Оракулом: $e')),
);
}
}
}
Future<void> _consultOracleTriptych(
WordItem morning,
WordItem day,
WordItem evening,
) async {
setState(() => _isLoadingAi = true);
HapticFeedback.mediumImpact();
try {
final promptQuestion =
'Триптих Дня: 🌅 Утро — «${morning.word.toUpperCase()}» (Архетип настройки); '
'⚡ День — «${day.word.toUpperCase()}» (Действие и вызов); '
'🌙 Вечер — «${evening.word.toUpperCase()}» (Тень и интеграция). '
'${_dayCode().clause} Дай синтез сюжета дня: как утреннее намерение воплотить в дневном поступке и осмыслить вечером?';
final advice = await _aiService.interpretSession(
words: [
SessionWord.fromWordItem(morning, positionTitle: 'Утро (Камертон)'),
SessionWord.fromWordItem(day, positionTitle: 'День (Действие)'),
SessionWord.fromWordItem(evening, positionTitle: 'Вечер (Интеграция)'),
],
question: promptQuestion,
);
if (mounted) {
setState(() {
_aiAdvice = advice;
_isLoadingAi = false;
});
}
} catch (e) {
if (mounted) {
setState(() => _isLoadingAi = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ошибка связи с Оракулом: $e')),
);
}
}
}
void _shareSingle(WordItem word, WordOfDayRecord rec) {
HapticFeedback.lightImpact();
final buf = StringBuffer();
buf.writeln('☀️ СЛОВО ДНЯ • ${_formatDateTitle(_selectedDate)}');
buf.writeln('🔢 Код суток: ${_dayCode().formula} · ${_dayCode().word}');
buf.writeln('Символ: ${word.emoji} ${word.word.toUpperCase()} (#${word.sessionNumber})');
if (word.category.isNotEmpty) buf.writeln('Категория: ${word.category}');
if (word.core.isNotEmpty) buf.writeln('\n🏛️ Архетип:\n${word.core}');
if (word.oracle.isNotEmpty) buf.writeln('\n🔮 Оракул:\n${word.oracle}');
if (word.asks.isNotEmpty) buf.writeln('\n🧘 Коан для ума:\n«${word.asks}»');
if (word.step.isNotEmpty) {
buf.writeln('\n⚡ Практикум дня:\n${word.step}');
buf.writeln('Статус: ${rec.isStepCompleted ? "Выполнено ✓" : "В процессе"}');
}
if (word.shadow.isNotEmpty) buf.writeln('\n🌑 Теневая ловушка:\n${word.shadow}');
if (rec.diaryNote.isNotEmpty) {
buf.writeln('\n🪶 Дневник синхроний:\n${rec.diaryNote}');
}
Clipboard.setData(ClipboardData(text: buf.toString()));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Row(
children: [
GradientStar(size: 16),
SizedBox(width: 8),
Text('Карточка Слова Дня скопирована в буфер обмена'),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
);
}
void _shareTriptych(
WordItem morning,
WordItem day,
WordItem evening,
WordOfDayRecord rec,
) {
HapticFeedback.lightImpact();
final buf = StringBuffer();
buf.writeln('⏳ ТРИПТИХ ДНЯ • ${_formatDateTitle(_selectedDate)}');
buf.writeln('🔢 Код суток: ${_dayCode().formula} · ${_dayCode().word}');
buf.writeln('\n🌅 УТРО — ${morning.emoji} ${morning.word.toUpperCase()} (#${morning.sessionNumber})');
if (morning.core.isNotEmpty) buf.writeln(' Архетип: ${morning.core}');
if (morning.asks.isNotEmpty) buf.writeln(' Коан: «${morning.asks}»');
buf.writeln('\n⚡ ДЕНЬ — ${day.emoji} ${day.word.toUpperCase()} (#${day.sessionNumber})');
if (day.step.isNotEmpty) buf.writeln(' Вызов дня: ${day.step}');
buf.writeln(' Статус: ${rec.isDayStepCompleted ? "Выполнено ✓" : "В процессе"}');
buf.writeln('\n🌙 ВЕЧЕР — ${evening.emoji} ${evening.word.toUpperCase()} (#${evening.sessionNumber})');
if (evening.shadow.isNotEmpty) buf.writeln(' Тень: ${evening.shadow}');
if (rec.diaryNote.isNotEmpty) {
buf.writeln(' 🪶 Дневник синхроний: ${rec.diaryNote}');
}
Clipboard.setData(ClipboardData(text: buf.toString()));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Row(
children: [
GradientStar(size: 16),
SizedBox(width: 8),
Text('Триптих дня скопирован в буфер обмена'),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
);
}
@override
Widget build(BuildContext context) {
final record = _service.getRecord(_selectedDate);
final isTriptych = record.isTriptychMode;
final streak = _service.calculateStreak();
final isToday = WordOfTheDayService.formatDateKey(_selectedDate) ==
WordOfTheDayService.formatDateKey(DateTime.now());
final singleWord = _service.getWordForDate(_selectedDate, phase: 'main');
final triptych = _service.getTriptychWordsForDate(_selectedDate);
return Scaffold(
backgroundColor: const Color(0xFF0C0E1A),
appBar: AppBar(
title: Row(
children: [
const GradientStar(size: 22),
const SizedBox(width: 8),
Text(
isTriptych ? 'Триптих Дня' : 'Слово Дня',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
],
),
actions: [
IconButton(
tooltip: 'Выбрать дату',
icon: const Icon(Icons.calendar_month_rounded, color: Color(0xFFFFD54F)),
onPressed: _pickDate,
),
IconButton(
tooltip: 'Поделиться',
icon: const Icon(Icons.share_rounded, color: Color(0xFFFFD54F)),
onPressed: () {
if (isTriptych) {
_shareTriptych(triptych.morning, triptych.day, triptych.evening, record);
} else {
_shareSingle(singleWord, record);
}
},
),
IconButton(
tooltip: 'Инструкция',
icon: const Icon(Icons.info_outline_rounded, color: Color(0xFF8C93B8)),
onPressed: () => DivinationInstructionModal.show(context, 'word_of_day'),
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Date Navigation Header
_buildDateBar(streak, isToday),
const SizedBox(height: 12),
_buildDayCodeCard(),
const SizedBox(height: 12),
// Mode Selector: 1 Word vs 3 Words Triptych
_buildModeSelector(isTriptych),
const SizedBox(height: 16),
// Main Content: Single Word Mode OR Triptych Mode
if (!isTriptych) ...[
_buildSingleWordBody(singleWord, record),
] else ...[
_buildTriptychBody(triptych.morning, triptych.day, triptych.evening, record),
],
if (_isLoadingAi) ...[
const SizedBox(height: 16),
const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(color: Color(0xFFFFD54F)),
),
),
] else if (_aiAdvice != null) ...[
const SizedBox(height: 16),
_buildAiAdviceCard(),
],
],
),
),
);
}
Widget _buildDateBar(int streak, bool isToday) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF232742)),
),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.chevron_left_rounded, color: Color(0xFFFFD54F)),
onPressed: _previousDay,
tooltip: 'Предыдущий день',
),
Expanded(
child: InkWell(
onTap: _pickDate,
borderRadius: BorderRadius.circular(8),
child: Column(
children: [
Text(
_formatDateTitle(_selectedDate),
textAlign: TextAlign.center,
style: const TextStyle(
color: Color(0xFFFFF9C4),
fontSize: 13,
fontWeight: FontWeight.w900,
letterSpacing: 0.5,
),
),
if (!isToday)
GestureDetector(
onTap: _jumpToToday,
child: const Padding(
padding: EdgeInsets.only(top: 2),
child: Text(
'Вернуться к сегодня',
style: TextStyle(
color: Color(0xFF80DEEA),
fontSize: 11,
decoration: TextDecoration.underline,
),
),
),
),
],
),
),
),
IconButton(
icon: const Icon(Icons.chevron_right_rounded, color: Color(0xFFFFD54F)),
onPressed: _nextDay,
tooltip: 'Следующий день',
),
if (streak > 0)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF332014),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFFFB74D).withValues(alpha: 0.5)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text('🔥', style: TextStyle(fontSize: 13)),
const SizedBox(width: 4),
Text(
'$streak',
style: const TextStyle(
color: Color(0xFFFFB74D),
fontSize: 11.5,
fontWeight: FontWeight.w900,
),
),
],
),
),
],
),
);
}
Widget _buildModeSelector(bool isTriptych) {
return Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF2A2D4A)),
),
child: Row(
children: [
Expanded(
child: InkWell(
onTap: () => _toggleMode(false),
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: !isTriptych ? const Color(0xFF2C2248) : Colors.transparent,
borderRadius: BorderRadius.circular(10),
border: !isTriptych
? Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.6))
: null,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('☀️', style: TextStyle(fontSize: !isTriptych ? 14 : 12)),
const SizedBox(width: 6),
Text(
'Единый Камертон (1)',
style: TextStyle(
color: !isTriptych ? const Color(0xFFFFF9C4) : const Color(0xFF8C93B8),
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
),
),
),
const SizedBox(width: 4),
Expanded(
child: InkWell(
onTap: () => _toggleMode(true),
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: isTriptych ? const Color(0xFF2C2248) : Colors.transparent,
borderRadius: BorderRadius.circular(10),
border: isTriptych
? Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.6))
: null,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('⏳', style: TextStyle(fontSize: isTriptych ? 14 : 12)),
const SizedBox(width: 6),
Text(
'Триптих Суток (3)',
style: TextStyle(
color: isTriptych ? const Color(0xFFFFF9C4) : const Color(0xFF8C93B8),
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
),
),
),
],
),
);
}
// ==================== 1-WORD MODE ====================
Widget _buildSingleWordBody(WordItem word, WordOfDayRecord record) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (!record.isRevealed)
_buildUnrevealedSeal(
title: 'ПЕЧАТЬ ДНЯ',
desc:
'Время сегодня звучит уникальным архетипом. Слово дня задаёт главный камертон событий, мыслей и внутренних озарений.',
buttonText: 'РАСПЕЧАТАТЬ СЛОВО ДНЯ',
onTap: () => _revealWord(phase: 'main'),
)
else
_buildRevealedTalisman(word),
if (record.isRevealed) ...[
const SizedBox(height: 20),
Container(
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF2A2D4A)),
),
child: TabBar(
controller: _tabController,
indicatorColor: const Color(0xFFFFD54F),
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
labelColor: const Color(0xFFFFD54F),
unselectedLabelColor: const Color(0xFF8C93B8),
labelStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
tabs: const [
Tab(icon: Icon(Icons.wb_twilight_rounded, size: 18), text: '1. Настройка'),
Tab(icon: Icon(Icons.flash_on_rounded, size: 18), text: '2. Практикум'),
Tab(icon: Icon(Icons.nights_stay_rounded, size: 18), text: '3. Синхронии'),
],
),
),
const SizedBox(height: 14),
AnimatedBuilder(
animation: _tabController,
builder: (context, _) {
switch (_tabController.index) {
case 0:
return _buildMorningFocusTab(word);
case 1:
return _buildDailyPracticeTab(word, record);
case 2:
return _buildEveningDiaryTab(record);
default:
return const SizedBox.shrink();
}
},
),
const SizedBox(height: 20),
_buildActionRowSingle(word),
],
],
);
}
// ==================== 3-WORDS TRIPTYCH MODE ====================
Widget _buildTriptychBody(
WordItem morning,
WordItem day,
WordItem evening,
WordOfDayRecord record,
) {
final areAllRevealed = record.areAllTriptychRevealed;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Triptych Header Banner
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: const Color(0xFF1E1733),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.3)),
),
child: Row(
children: [
const Icon(Icons.auto_awesome_motion_rounded, size: 18, color: Color(0xFFFFD54F)),
const SizedBox(width: 8),
const Expanded(
child: Text(
'Сюжет суток: Утренний камертон ➔ Дневное действие ➔ Вечерняя интеграция',
style: TextStyle(color: Color(0xFFFFF9C4), fontSize: 11.5, height: 1.3),
),
),
if (!areAllRevealed)
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
foregroundColor: const Color(0xFF80DEEA),
),
onPressed: () => _revealWord(phase: 'all'),
child: const Text('Открыть все', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 11)),
),
],
),
),
const SizedBox(height: 14),
// Phase 1: Morning
_buildPhaseCard(
phaseTitle: '1. УТРО • НАСТРОЙКА И АРХЕТИП',
phaseIcon: '🌅',
accentColor: const Color(0xFFFFD54F),
word: morning,
isRevealed: record.isMorningRevealed,
onReveal: () => _revealWord(phase: 'morning'),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
morning.core.isNotEmpty ? morning.core : morning.description,
style: const TextStyle(color: Color(0xFFECEFF1), fontSize: 13, height: 1.4),
),
if (morning.asks.isNotEmpty) ...[
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFF261D3A),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFBA68C8).withValues(alpha: 0.4)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.self_improvement_rounded, size: 16, color: Color(0xFFBA68C8)),
const SizedBox(width: 8),
Expanded(
child: Text(
'Коан утра: «${morning.asks}»',
style: const TextStyle(
color: Color(0xFFFFF9C4),
fontSize: 12,
fontStyle: FontStyle.italic,
),
),
),
],
),
),
],
],
),
),
const SizedBox(height: 14),
// Phase 2: Day
_buildPhaseCard(
phaseTitle: '2. ДЕНЬ • ВЫЗОВ И ДЕЙСТВИЕ',
phaseIcon: '⚡',
accentColor: const Color(0xFF81C784),
word: day,
isRevealed: record.isDayRevealed,
onReveal: () => _revealWord(phase: 'day'),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
day.step.isNotEmpty
? day.step
: 'Воплотите импульс слова в конкретном материальном действии или осознанном поступке.',
style: const TextStyle(color: Color(0xFFECEFF1), fontSize: 13, height: 1.4),
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: record.isDayStepCompleted
? const Color(0xFF2E7D32)
: const Color(0xFF232A3E),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
icon: Icon(
record.isDayStepCompleted
? Icons.check_circle_rounded
: Icons.radio_button_unchecked_rounded,
size: 18,
color: record.isDayStepCompleted
? const Color(0xFFFFF9C4)
: const Color(0xFF81C784),
),
label: Text(
record.isDayStepCompleted ? 'Дневной шаг выполнен ✓' : 'Отметить дневной шаг как выполненный',
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
onPressed: () => _toggleStep(!record.isDayStepCompleted, isDayStep: true),
),
),
],
),
),
const SizedBox(height: 14),
// Phase 3: Evening
_buildPhaseCard(
phaseTitle: '3. ВЕЧЕР • ТЕНЬ И ИНТЕГРАЦИЯ',
phaseIcon: '🌙',
accentColor: const Color(0xFF80DEEA),
word: evening,
isRevealed: record.isEveningRevealed,
onReveal: () => _revealWord(phase: 'evening'),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (evening.shadow.isNotEmpty) ...[
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFF26191D),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFFF7043).withValues(alpha: 0.4)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.warning_amber_rounded, size: 16, color: Color(0xFFFF7043)),
const SizedBox(width: 8),
Expanded(
child: Text(
'Теневой капкан вечера: ${evening.shadow}',
style: const TextStyle(color: Color(0xFFFFCCBC), fontSize: 12),
),
),
],
),
),
const SizedBox(height: 12),
],
_buildEveningDiaryTab(record),
],
),
),
const SizedBox(height: 20),
// Action Buttons for Triptych
_buildActionRowTriptych(morning, day, evening),
],
);
}
Widget _buildPhaseCard({
required String phaseTitle,
required String phaseIcon,
required Color accentColor,
required WordItem word,
required bool isRevealed,
required VoidCallback onReveal,
required Widget content,
}) {
return Container(
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: isRevealed
? accentColor.withValues(alpha: 0.6)
: const Color(0xFF2A2D4A),
width: isRevealed ? 1.5 : 1.0,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Bar
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: accentColor.withValues(alpha: 0.08),
borderRadius: const BorderRadius.vertical(top: Radius.circular(17)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(phaseIcon, style: const TextStyle(fontSize: 16)),
const SizedBox(width: 8),
Text(
phaseTitle,
style: TextStyle(
color: accentColor,
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 0.8,
),
),
],
),
if (isRevealed)
TextButton.icon(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
foregroundColor: const Color(0xFF80DEEA),
padding: EdgeInsets.zero,
),
icon: const Icon(Icons.arrow_forward, size: 14),
label: const Text('Корень', style: TextStyle(fontSize: 11)),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => SymbolDetailScreen(word: word)),
);
},
),
],
),
),
Padding(
padding: const EdgeInsets.all(14),
child: !isRevealed
? Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
foregroundColor: accentColor,
side: BorderSide(color: accentColor.withValues(alpha: 0.6)),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
icon: const Icon(Icons.lock_open_rounded, size: 16),
label: Text('Распечатать ($phaseTitle)'.split('•').first.trim(),
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12)),
onPressed: onReveal,
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
word.emoji.isNotEmpty ? word.emoji : '✨',
style: const TextStyle(fontSize: 26),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
word.word.toUpperCase(),
style: const TextStyle(
color: Color(0xFFFFF9C4),
fontSize: 18,
fontWeight: FontWeight.w900,
letterSpacing: 1.0,
),
),
Text(
'#${word.formattedSessionNumber} • ${word.category} • ${word.localizedOperator}',
style: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
),
],
),
),
],
),
const SizedBox(height: 12),
content,
],
),
),
],
),
);
}
// ==================== SHARED WIDGETS ====================
Widget _buildUnrevealedSeal({
required String title,
required String desc,
required String buttonText,
required VoidCallback onTap,
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 36, horizontal: 20),
decoration: BoxDecoration(
gradient: const RadialGradient(
center: Alignment(0, -0.2),
radius: 1.2,
colors: [
Color(0xFF2E2452),
Color(0xFF14172B),
Color(0xFF0C0E1A),
],
),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.5), width: 1.5),
boxShadow: [
BoxShadow(
color: const Color(0xFFFFD54F).withValues(alpha: 0.15),
blurRadius: 20,
spreadRadius: 2,
),
],
),
child: Column(
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Color(0xFFFFD54F), Color(0xFFFF8F00)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: const Color(0xFFFFD54F).withValues(alpha: 0.4),
blurRadius: 16,
spreadRadius: 2,
),
],
),
child: const Icon(
Icons.auto_awesome_rounded,
color: Color(0xFF0C0E1A),
size: 40,
),
),
const SizedBox(height: 18),
Text(
title,
style: const TextStyle(
color: Color(0xFFFFF9C4),
fontSize: 18,
fontWeight: FontWeight.w900,
letterSpacing: 2.0,
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
desc,
textAlign: TextAlign.center,
style: const TextStyle(color: Color(0xFF8C93B8), fontSize: 13, height: 1.4),
),
),
const SizedBox(height: 22),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFFFD54F),
foregroundColor: const Color(0xFF0C0E1A),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 13),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
elevation: 6,
),
icon: const Icon(Icons.lock_open_rounded, size: 20),
label: Text(
buttonText,
style: const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 0.8, fontSize: 13),
),
onPressed: onTap,
),
],
),
);
}
Widget _buildRevealedTalisman(WordItem word) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF241B42), Color(0xFF14172B)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.6), width: 1.5),
boxShadow: [
BoxShadow(
color: const Color(0xFFFFD54F).withValues(alpha: 0.12),
blurRadius: 18,
spreadRadius: 2,
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF382959),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
),
child: Text(
'ЗНАК #${word.sessionNumber.toString().padLeft(3, '0')}',
style: const TextStyle(
color: Color(0xFFFFD54F),
fontSize: 11,
fontWeight: FontWeight.w900,
),
),
),
if (word.category.isNotEmpty)
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF1E243D),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFF80DEEA).withValues(alpha: 0.3)),
),
child: Text(
word.category,
style: const TextStyle(
color: Color(0xFF80DEEA),
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 16),
if (word.emoji.isNotEmpty)
Text(word.emoji, style: const TextStyle(fontSize: 48)),
const SizedBox(height: 6),
Text(
word.word.toUpperCase(),
textAlign: TextAlign.center,
style: const TextStyle(
color: Color(0xFFFFF9C4),
fontSize: 30,
fontWeight: FontWeight.w900,
letterSpacing: 2.0,
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildPill(word.partOfSpeech, const Color(0xFFB388FF)),
const SizedBox(width: 8),
_buildPill(word.localizedOperator, const Color(0xFF81C784)),
const SizedBox(width: 8),
_buildPill(word.localizedValence, const Color(0xFFFFB74D)),
],
),
const SizedBox(height: 12),
TextButton.icon(
style: TextButton.styleFrom(
foregroundColor: const Color(0xFF80DEEA),
visualDensity: VisualDensity.compact,
),
icon: const Icon(Icons.menu_book_rounded, size: 16),
label: const Text('Открыть в полном справочнике корней', style: TextStyle(fontSize: 12)),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => SymbolDetailScreen(word: word)),
);
},
),
],
),
);
}
Widget _buildPill(String text, Color color) {
if (text.isEmpty) return const SizedBox.shrink();
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withValues(alpha: 0.4)),
),
child: Text(
text,
style: TextStyle(color: color, fontSize: 10, fontWeight: FontWeight.bold),
),
);
}
Widget _buildMorningFocusTab(WordItem word) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildInfoCard(
title: 'АРХЕТИП И СУТЬ',
icon: Icons.hub_rounded,
accentColor: const Color(0xFFFFD54F),
content: word.core.isNotEmpty ? word.core : word.description,
),
const SizedBox(height: 12),
if (word.oracle.isNotEmpty) ...[
_buildInfoCard(
title: 'ПОСЛАНИЕ ОРАКУЛА ДНЯ',
icon: Icons.auto_awesome_rounded,
accentColor: const Color(0xFF80DEEA),
content: word.oracle,
),
const SizedBox(height: 12),
],
if (word.asks.isNotEmpty) ...[
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF252044), Color(0xFF181B30)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFBA68C8).withValues(alpha: 0.6)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.self_improvement_rounded, size: 18, color: Color(0xFFBA68C8)),
SizedBox(width: 8),
Text(
'КОАН ДЛЯ УМА (ВОПРОС ДНЯ)',
style: TextStyle(
color: Color(0xFFBA68C8),
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.0,
),
),
],
),
const SizedBox(height: 10),
Text(
'«${word.asks}»',
style: const TextStyle(
color: Color(0xFFFFF9C4),
fontSize: 14.5,
fontStyle: FontStyle.italic,
height: 1.45,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
const Text(
'Держите этот вопрос в фокусе внимания во время ключевых решений дня.',
style: TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
),
],
),
),
],
],
);
}
Widget _buildDailyPracticeTab(WordItem word, WordOfDayRecord rec) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: rec.isStepCompleted
? const Color(0xFF81C784)
: const Color(0xFFFFD54F).withValues(alpha: 0.5),
width: rec.isStepCompleted ? 1.5 : 1.0,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Row(
children: [
Icon(Icons.directions_run_rounded, size: 18, color: Color(0xFFFFD54F)),
SizedBox(width: 8),
Text(
'ВЫЗОВ И ПРАКТИКУМ ДНЯ',
style: TextStyle(
color: Color(0xFFFFD54F),
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.0,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: rec.isStepCompleted
? const Color(0xFF1B3D23)
: const Color(0xFF262A44),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: rec.isStepCompleted
? const Color(0xFF81C784)
: const Color(0xFF8C93B8).withValues(alpha: 0.4),
),
),
child: Text(
rec.isStepCompleted ? 'ВЫПОЛНЕНО ✓' : 'К ВЫПОЛНЕНИЮ',
style: TextStyle(
color: rec.isStepCompleted
? const Color(0xFF81C784)
: const Color(0xFF8C93B8),
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 12),
Text(
word.step.isNotEmpty
? word.step
: 'Воплотите смысл слова в конкретном материальном поступке или осознанном отказе от автоматизма.',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
height: 1.45,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: rec.isStepCompleted
? const Color(0xFF2E7D32)
: const Color(0xFF2A2D4A),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
icon: Icon(
rec.isStepCompleted ? Icons.check_circle_rounded : Icons.radio_button_unchecked_rounded,
color: rec.isStepCompleted ? const Color(0xFFFFF9C4) : const Color(0xFFFFD54F),
),
label: Text(
rec.isStepCompleted
? 'Практика выполнена (Синхрония закреплена)'
: 'Отметить шаг как выполненный',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
),
onPressed: () => _toggleStep(!rec.isStepCompleted),
),
),
],
),
),
const SizedBox(height: 14),
if (word.shadow.isNotEmpty) ...[
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF1E1724),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFFF7043).withValues(alpha: 0.5)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.warning_amber_rounded, size: 18, color: Color(0xFFFF7043)),
SizedBox(width: 8),
Text(
'ТЕНЕВАЯ ЛОВУШКА ДНЯ',
style: TextStyle(
color: Color(0xFFFF7043),
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.0,
),
),
],
),
const SizedBox(height: 8),
Text(
word.shadow,
style: const TextStyle(
color: Color(0xFFFFCCBC),
fontSize: 13,
height: 1.4,
),
),
],
),
),
],
],
);
}
Widget _buildEveningDiaryTab(WordOfDayRecord rec) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFF2A2D4A)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.edit_note_rounded, size: 20, color: Color(0xFF80DEEA)),
SizedBox(width: 8),
Text(
'ДНЕВНИК СИНХРОНИЙ И НАБЛЮДЕНИЙ',
style: TextStyle(
color: Color(0xFF80DEEA),
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.0,
),
),
],
),
const SizedBox(height: 8),
const Text(
'Зафиксируйте знаки, сны, диалоги или события, через которые слово дня проявилось в вашей жизни:',
style: TextStyle(color: Color(0xFF8C93B8), fontSize: 12.5, height: 1.35),
),
const SizedBox(height: 12),
TextField(
controller: _diaryController,
maxLines: 4,
style: const TextStyle(color: Colors.white, fontSize: 13.5),
decoration: InputDecoration(
hintText: 'Сегодня я заметил(а), что слово проявилось в...',
hintStyle: const TextStyle(color: Color(0xFF636A8A), fontSize: 12.5),
filled: true,
fillColor: const Color(0xFF1B1E33),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF2C3252)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF2C3252)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF80DEEA)),
),
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (_isDiarySaved)
const Row(
children: [
Icon(Icons.check_circle_rounded, size: 16, color: Color(0xFF81C784)),
SizedBox(width: 6),
Text('Сохранено', style: TextStyle(color: Color(0xFF81C784), fontSize: 12)),
],
)
else
const SizedBox.shrink(),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF80DEEA),
foregroundColor: const Color(0xFF0C0E1A),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
icon: const Icon(Icons.save_rounded, size: 16),
label: const Text('Сохранить заметку', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12)),
onPressed: _saveDiary,
),
],
),
],
),
);
}
Widget _buildActionRowSingle(WordItem word) {
final persona = _settingsRepo.persona;
return Row(
children: [
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF262042),
foregroundColor: const Color(0xFFFFF9C4),
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
),
),
icon: const Icon(Icons.psychology_rounded, size: 18, color: Color(0xFFFFD54F)),
label: Text(
'Оракул (${persona.title.split(' ').first})',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
onPressed: () => _consultOracleSingle(word),
),
),
const SizedBox(width: 10),
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1E283D),
foregroundColor: const Color(0xFF80DEEA),
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: const Color(0xFF80DEEA).withValues(alpha: 0.4)),
),
),
icon: const Icon(Icons.merge_rounded, size: 18),
label: const Text(
'В Сессию Дня',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
onPressed: () => _weaveIntoSyncreticSessionSingle(word),
),
),
],
);
}
Widget _buildActionRowTriptych(WordItem morning, WordItem day, WordItem evening) {
final persona = _settingsRepo.persona;
return Row(
children: [
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF262042),
foregroundColor: const Color(0xFFFFF9C4),
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
),
),
icon: const Icon(Icons.psychology_rounded, size: 18, color: Color(0xFFFFD54F)),
label: Text(
'Синтез Триады (${persona.title.split(' ').first})',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
onPressed: () => _consultOracleTriptych(morning, day, evening),
),
),
const SizedBox(width: 10),
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1E283D),
foregroundColor: const Color(0xFF80DEEA),
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: const Color(0xFF80DEEA).withValues(alpha: 0.4)),
),
),
icon: const Icon(Icons.merge_rounded, size: 18),
label: const Text(
'Вплести Триаду',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
onPressed: () => _weaveTriptychIntoSyncreticSession(morning, day, evening),
),
),
],
);
}
Widget _buildAiAdviceCard() {
final persona = _settingsRepo.persona;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF1A1C30),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.5)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const GradientStar(size: 16),
const SizedBox(width: 8),
Expanded(
child: Text(
'НАСТАВЛЕНИЕ: ${persona.title.toUpperCase()}',
style: const TextStyle(
color: Color(0xFFFFD54F),
fontSize: 11,
fontWeight: FontWeight.w900,
),
),
),
],
),
const SizedBox(height: 10),
Text(
_aiAdvice!,
style: const TextStyle(
color: Color(0xFFECEFF1),
fontSize: 13,
height: 1.45,
),
),
],
),
);
}
Widget _buildInfoCard({
required String title,
required IconData icon,
required Color accentColor,
required String content,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF2A2D4A)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, size: 16, color: accentColor),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
color: accentColor,
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.0,
),
),
],
),
const SizedBox(height: 10),
Text(
content,
style: const TextStyle(
color: Color(0xFFECEFF1),
fontSize: 13.5,
height: 1.4,
),
),
],
),
);
}
}