import 'package:flutter/material.dart'; import '../app/ai_local.dart' show AiStore; import '../app/app_controller.dart'; import '../l10n/tr.dart'; import '../services/model_store.dart'; import 'confirm.dart'; import 'setting_tile.dart'; import 'theme.dart'; /// 260: 받은 AI 자막 모델 (Whisper · NLLB) - 차지한 크기 · [지우기]. 다시 쓰면 그때 다시 받는다 class AiSubtitleModels extends StatefulWidget { final AppController c; const AiSubtitleModels({super.key, required this.c}); @override State createState() => _AiSubtitleModelsState(); } class _AiSubtitleModelsState extends State { /// 모델 id → 차지한 바이트 (0 이면 받지 않음) Map? _used; /// 다 받은 모델 (나머지 중 크기가 있으면 받다 만 조각) final _installed = {}; String? _error; ModelStore get _store => widget.c.services.models; @override void initState() { super.initState(); _refresh(); } Future _refresh() async { final used = {for (final m in [...whisperModels, nllbModel]) m.id: await _store.usedBytes(m)}; _installed ..clear() ..addAll([for (final m in [...whisperModels, nllbModel]) if (await _store.isInstalled(m)) m.id]); if (mounted) setState(() => _used = used); } Future _remove(ModelSpec m) async { final ok = await confirmAction(context, title: trf('{0} 을(를) 지울까요?', [m.label]), body: tr('다시 쓰려면 다시 받아야 합니다.'), ok: tr('지우기')); if (!ok) return; try { await _store.remove(m); _error = null; } catch (e) { _error = trf('지우지 못했습니다: {0}', [e]); } await _refresh(); } @override Widget build(BuildContext context) { final used = _used; if (used == null) return const SizedBox(); return ListenableBuilder( listenable: widget.c, builder: (context, _) { final inUse = widget.c.aiModelsInUse; return Column(mainAxisSize: MainAxisSize.min, children: [ for (final m in [...whisperModels, nllbModel]) SettingTile( dense: true, leading: Icon(used[m.id]! > 0 ? Icons.check_circle : Icons.cloud_download_outlined, color: used[m.id]! > 0 ? JjColors.success : JjColors.textDim, size: 20), title: Text(m.label), subtitle: Text(used[m.id]! > 0 ? trf(_installed.contains(m.id) ? '받음 · {0}' : '받다 만 조각 · {0}', [AiStore.sizeText(used[m.id]!)]) : trf('받지 않음 (처음 쓸 때 내려받기 {0})', [m.sizeLabel])), trailing: used[m.id]! > 0 ? inUse ? Tooltip( message: tr('AI 자막 · 번역 작업이 끝난 뒤 지울 수 있습니다'), child: TextButton(onPressed: null, child: Text(tr('지우기'))), ) : TextButton(onPressed: () => _remove(m), child: Text(tr('지우기'))) : null, ), if (_error != null) Padding( padding: const EdgeInsets.fromLTRB(16, 4, 16, 0), child: Text(_error!, style: TextStyle(fontSize: 12, color: JjColors.danger)), ), ]); }, ); } }