feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
import 'package:horatio_app/bloc/script_import/script_import_cubit.dart';
|
|
|
|
|
import 'package:horatio_app/bloc/srs_review/srs_review_cubit.dart';
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
import 'package:horatio_app/database/daos/annotation_dao.dart';
|
2026-03-29 21:11:43 +02:00
|
|
|
import 'package:horatio_app/database/daos/recording_dao.dart';
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
import 'package:horatio_app/router.dart';
|
2026-03-29 21:11:43 +02:00
|
|
|
import 'package:horatio_app/services/audio_playback_service.dart';
|
|
|
|
|
import 'package:horatio_app/services/recording_service.dart';
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
import 'package:horatio_app/services/script_repository.dart';
|
|
|
|
|
import 'package:horatio_core/horatio_core.dart';
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
import 'package:mocktail/mocktail.dart';
|
|
|
|
|
|
|
|
|
|
class _MockAnnotationDao extends Mock implements AnnotationDao {}
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
class _MockRecordingDao extends Mock implements RecordingDao {}
|
|
|
|
|
|
|
|
|
|
class _MockRecordingService extends Mock implements RecordingService {}
|
|
|
|
|
|
|
|
|
|
class _MockAudioPlaybackService extends Mock implements AudioPlaybackService {}
|
|
|
|
|
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
Widget _wrapRouter() {
|
|
|
|
|
final repository = ScriptRepository();
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
final mockDao = _MockAnnotationDao();
|
2026-03-29 21:11:43 +02:00
|
|
|
final mockRecordingDao = _MockRecordingDao();
|
|
|
|
|
final mockRecordingService = _MockRecordingService();
|
|
|
|
|
final mockPlaybackService = _MockAudioPlaybackService();
|
|
|
|
|
when(
|
|
|
|
|
() => mockDao.watchMarksForScript(any()),
|
|
|
|
|
).thenAnswer((_) => Stream.value([]));
|
|
|
|
|
when(
|
|
|
|
|
() => mockDao.watchNotesForScript(any()),
|
|
|
|
|
).thenAnswer((_) => Stream.value([]));
|
|
|
|
|
when(
|
|
|
|
|
() => mockDao.watchSnapshotsForScript(any()),
|
|
|
|
|
).thenAnswer((_) => Stream.value([]));
|
|
|
|
|
when(
|
|
|
|
|
() => mockRecordingDao.watchRecordingsForScript(any()),
|
|
|
|
|
).thenAnswer((_) => Stream.value([]));
|
|
|
|
|
|
2026-03-29 21:46:28 +02:00
|
|
|
when(mockRecordingService.hasPermission).thenAnswer((_) async => true);
|
2026-03-29 21:11:43 +02:00
|
|
|
when(
|
|
|
|
|
() => mockRecordingService.startRecording(any()),
|
|
|
|
|
).thenAnswer((_) async {});
|
2026-03-29 21:46:28 +02:00
|
|
|
when(mockRecordingService.stopRecording).thenAnswer((_) async => null);
|
2026-03-29 21:11:43 +02:00
|
|
|
|
|
|
|
|
when(() => mockPlaybackService.play(any())).thenAnswer((_) async {});
|
2026-03-29 21:46:28 +02:00
|
|
|
when(mockPlaybackService.stop).thenAnswer((_) async {});
|
|
|
|
|
when(
|
|
|
|
|
() => mockPlaybackService.status,
|
|
|
|
|
).thenAnswer((_) => const Stream.empty());
|
|
|
|
|
when(
|
|
|
|
|
() => mockPlaybackService.position,
|
|
|
|
|
).thenAnswer((_) => const Stream.empty());
|
2026-03-29 21:11:43 +02:00
|
|
|
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
return MultiRepositoryProvider(
|
|
|
|
|
providers: [
|
|
|
|
|
RepositoryProvider<ScriptRepository>(create: (_) => repository),
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
RepositoryProvider<AnnotationDao>.value(value: mockDao),
|
2026-03-29 21:11:43 +02:00
|
|
|
RepositoryProvider<RecordingDao>.value(value: mockRecordingDao),
|
|
|
|
|
RepositoryProvider<RecordingService>.value(value: mockRecordingService),
|
|
|
|
|
RepositoryProvider<AudioPlaybackService>.value(
|
|
|
|
|
value: mockPlaybackService,
|
|
|
|
|
),
|
|
|
|
|
RepositoryProvider<String>.value(value: '/tmp/test_recordings'),
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
],
|
|
|
|
|
child: MultiBlocProvider(
|
|
|
|
|
providers: [
|
|
|
|
|
BlocProvider<ScriptImportCubit>(
|
|
|
|
|
create: (_) => ScriptImportCubit(repository: repository),
|
|
|
|
|
),
|
|
|
|
|
BlocProvider<SrsReviewCubit>(create: (_) => SrsReviewCubit()),
|
|
|
|
|
],
|
|
|
|
|
child: MaterialApp.router(routerConfig: appRouter),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
group('Router with valid extras', () {
|
|
|
|
|
testWidgets('import route shows ImportScreen', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
appRouter.go(RoutePaths.import_);
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('Import Script'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testWidgets('role-selection route with Script extra', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
const role = Role(name: 'Hero');
|
|
|
|
|
const script = Script(
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
id: 'router-valid-id',
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
title: 'Valid',
|
|
|
|
|
roles: [role],
|
|
|
|
|
scenes: [
|
|
|
|
|
Scene(
|
|
|
|
|
lines: [
|
|
|
|
|
ScriptLine(
|
|
|
|
|
text: 'Line.',
|
|
|
|
|
role: role,
|
|
|
|
|
sceneIndex: 0,
|
|
|
|
|
lineIndex: 0,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
unawaited(appRouter.push(RoutePaths.roleSelection, extra: script));
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('Choose Your Role'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testWidgets('schedule route with map extra', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
const role = Role(name: 'Hero');
|
|
|
|
|
const script = Script(
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
id: 'router-play-id',
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
title: 'Play',
|
|
|
|
|
roles: [role],
|
|
|
|
|
scenes: [
|
|
|
|
|
Scene(
|
|
|
|
|
lines: [
|
2026-03-29 21:11:43 +02:00
|
|
|
ScriptLine(text: 'Hi.', role: role, sceneIndex: 0, lineIndex: 0),
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
unawaited(
|
|
|
|
|
appRouter.push(
|
|
|
|
|
RoutePaths.schedule,
|
|
|
|
|
extra: {'script': script, 'role': role},
|
|
|
|
|
),
|
|
|
|
|
);
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('Memorization Schedule'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testWidgets('rehearsal route with map extra', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
const role = Role(name: 'Hero');
|
|
|
|
|
const script = Script(
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
id: 'router-rehearse-id',
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
title: 'Rehearse',
|
|
|
|
|
roles: [role],
|
|
|
|
|
scenes: [
|
|
|
|
|
Scene(
|
|
|
|
|
lines: [
|
2026-03-29 21:11:43 +02:00
|
|
|
ScriptLine(text: 'A.', role: role, sceneIndex: 0, lineIndex: 0),
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
unawaited(
|
|
|
|
|
appRouter.push(
|
|
|
|
|
RoutePaths.rehearsal,
|
|
|
|
|
extra: {'script': script, 'role': role},
|
|
|
|
|
),
|
|
|
|
|
);
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('Rehearsing: Hero'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testWidgets('srs-review route with cards extra', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
final cards = [SrsCard(id: 'c1', cueText: 'Cue', answerText: 'Ans')];
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
|
|
|
|
|
unawaited(appRouter.push(RoutePaths.srsReview, extra: cards));
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
// SrsReviewScreen is visible.
|
|
|
|
|
expect(find.text('No review session active.'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testWidgets('error route shows 404', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
appRouter.go('/nonexistent-route');
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('Not Found'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
testWidgets('schedule route with wrong extra type falls back', (
|
|
|
|
|
tester,
|
|
|
|
|
) async {
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
// Push schedule with a non-Map extra → the builder returns SizedBox.
|
|
|
|
|
unawaited(appRouter.push(RoutePaths.schedule, extra: 'wrong'));
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
// Should not crash — shows SizedBox.shrink or redirects.
|
|
|
|
|
expect(tester.takeException(), isNull);
|
|
|
|
|
});
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
testWidgets('annotations route with Script extra shows editor', (
|
|
|
|
|
tester,
|
|
|
|
|
) async {
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
// Reset to home to clear any stale navigation stack.
|
|
|
|
|
appRouter.go(RoutePaths.home);
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
const role = Role(name: 'Hero');
|
|
|
|
|
const script = Script(
|
|
|
|
|
id: 'router-annotate-id',
|
|
|
|
|
title: 'Annotate Play',
|
|
|
|
|
roles: [role],
|
|
|
|
|
scenes: [
|
|
|
|
|
Scene(
|
|
|
|
|
lines: [
|
|
|
|
|
ScriptLine(
|
|
|
|
|
text: 'Line.',
|
|
|
|
|
role: role,
|
|
|
|
|
sceneIndex: 0,
|
|
|
|
|
lineIndex: 0,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
unawaited(appRouter.push(RoutePaths.annotations, extra: script));
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('Annotate: Annotate Play'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
testWidgets('annotations route with null extra redirects home', (
|
|
|
|
|
tester,
|
|
|
|
|
) async {
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
appRouter.go(RoutePaths.annotations);
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
// Redirected to home.
|
|
|
|
|
expect(find.text('Horatio'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
testWidgets('annotation-history route with Script extra shows history', (
|
|
|
|
|
tester,
|
|
|
|
|
) async {
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
const role = Role(name: 'Hero');
|
|
|
|
|
const script = Script(
|
|
|
|
|
id: 'router-history-id',
|
|
|
|
|
title: 'History Play',
|
|
|
|
|
roles: [role],
|
|
|
|
|
scenes: [
|
|
|
|
|
Scene(
|
|
|
|
|
lines: [
|
|
|
|
|
ScriptLine(
|
|
|
|
|
text: 'Line.',
|
|
|
|
|
role: role,
|
|
|
|
|
sceneIndex: 0,
|
|
|
|
|
lineIndex: 0,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
unawaited(appRouter.push(RoutePaths.annotationHistory, extra: script));
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
expect(find.text('History: History Play'), findsOneWidget);
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-29 21:11:43 +02:00
|
|
|
testWidgets('annotation-history route with null extra redirects home', (
|
|
|
|
|
tester,
|
|
|
|
|
) async {
|
feat: annotations subsystem — core models, drift DB, cubits, and UI
Add the complete annotations feature for marking and annotating script text:
Core models (horatio_core):
- TextMark, LineNote, AnnotationSnapshot, MarkType, NoteCategory
- Script.id field + UUID generation in text_parser
Database layer (horatio_app):
- Drift tables: text_marks, line_notes, annotation_snapshots
- AppDatabase with AnnotationDao (full CRUD + streams + bulk replace)
State management:
- AnnotationCubit: mark/note CRUD, line selection, editing context
- AnnotationHistoryCubit: snapshot save/restore with stream updates
UI components:
- MarkOverlay: colored span rendering for text marks
- NoteIndicator: per-line note count badge
- MarkTypePicker: 6-type ActionChip selector
- NoteEditorSheet: category dropdown + text field bottom sheet
- AnnotationEditorScreen: full editor with long-press marks + note editing
- AnnotationHistoryScreen: snapshot timeline with restore dialog
Wiring:
- main.dart: async DB init with path_provider
- app.dart: RepositoryProvider<AnnotationDao>
- router.dart: /annotations + /annotation-history routes
- role_selection_screen: Annotate Script option
- run.sh: app_codegen step + coverage filtering for generated code
352 tests (105 core + 247 app), 100% branch coverage, zero dead code.
2026-03-29 17:59:26 +02:00
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
appRouter.go(RoutePaths.annotationHistory);
|
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
|
|
|
|
|
|
// Redirected to home.
|
|
|
|
|
expect(find.text('Horatio'), findsOneWidget);
|
|
|
|
|
});
|
2026-03-29 21:46:28 +02:00
|
|
|
|
|
|
|
|
testWidgets('demo route shows DemoAnnotationEditorScreen', (tester) async {
|
|
|
|
|
// DemoAnnotationEditorScreen creates a real in-memory Drift DB.
|
|
|
|
|
// All Drift async timers (seeding, stream delivery, disposal cleanup)
|
|
|
|
|
// must fire in real time via runAsync to avoid pending fake-async timers.
|
|
|
|
|
await tester.runAsync(() async {
|
|
|
|
|
await tester.pumpWidget(_wrapRouter());
|
|
|
|
|
await tester.pump();
|
|
|
|
|
|
|
|
|
|
appRouter.go(RoutePaths.demo);
|
|
|
|
|
// Process the navigation frame.
|
|
|
|
|
await tester.pump();
|
|
|
|
|
await tester.pump();
|
|
|
|
|
|
|
|
|
|
// Wait for seeding to complete in real time.
|
|
|
|
|
await Future<void>.delayed(const Duration(seconds: 2));
|
|
|
|
|
await tester.pump();
|
|
|
|
|
// Allow Drift initial stream deliveries.
|
|
|
|
|
await Future<void>.delayed(const Duration(milliseconds: 500));
|
|
|
|
|
await tester.pump();
|
|
|
|
|
|
|
|
|
|
expect(find.textContaining('Hamlet', findRichText: true), findsWidgets);
|
|
|
|
|
|
|
|
|
|
// Replace entire widget tree to force DemoAnnotationEditorScreen
|
|
|
|
|
// disposal inside runAsync so Drift's markAsClosed timers fire in
|
|
|
|
|
// real time rather than as pending fake-async timers.
|
|
|
|
|
await tester.pumpWidget(const SizedBox.shrink());
|
|
|
|
|
await Future<void>.delayed(const Duration(milliseconds: 500));
|
|
|
|
|
});
|
|
|
|
|
});
|
feat(horatio): add Horatio actor script memorization app
Two-package monorepo:
- horatio_core: pure Dart package (parser, SRS, planner)
- horatio_app: Flutter UI (Bloc/Cubit, GoRouter, TTS)
Features:
- Script import (txt, docx, pdf) with drag-and-drop
- Four script format parsers (colon, bracketed, parenthetical, screenplay)
- SM-2 spaced repetition for line memorization
- Rehearsal mode with TTS and line comparison
- 5 bundled public domain scripts
Quality:
- 83 core tests + 160 app tests, both 100% branch coverage
- Strict analysis (130+ lint rules, fatal-infos)
- Dead code detection script (dead_code.sh)
- run.sh pipeline: analyze, test, dead-code, run, web
- Pre-commit hook for horatio test coverage
2026-03-29 14:44:57 +02:00
|
|
|
});
|
|
|
|
|
}
|