feat(app): show and write a word's senses with their grammar

A word with senses (as a dictionary gives them) shows them when its
answer shows: numbered when there are several, each with its label, its
translations and its grammar codes, small and slanted, their meaning on
hover. A word usually written in kana shows its reading from the start:
its kanji are rarely seen, so there is nothing to hide. The composer
writes the senses' translations, adds and removes senses, and keeps the
codes as they came.

The codes' short forms and meanings are the dictionary's own English
(JMdict's entities), kept as data beside the cards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-07 21:38:41 +02:00
commit 4c47ec06c1
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 611 additions and 34 deletions

View file

@ -208,6 +208,41 @@ void main() {
expect(saved.explanation, isNull);
});
testWidgets("a word's senses are written as such, their grammar kept, its "
'meanings taken from them', (tester) async {
await cards.create(
'japanese',
CardContent(
type: CardType.vocabulary,
front: paragraph('食べる'),
back: RichContent.empty(),
meanings: const ['to eat', 'to live on'],
senses: const [
VocabularySense(glosses: ['to eat'], pos: ['v1']),
VocabularySense(glosses: ['to live on']),
],
),
);
await showStudyset(tester);
await open(tester, '食べる');
expect(find.text('Ichidan'), findsOneWidget);
await tester.tap(find.byTooltip('Remove sense').first);
await settle(tester);
await tester.enterText(
find.widgetWithText(TextField, 'to live on'),
'to make a living',
);
await tester.tap(find.text('New flashcard'));
await settle(tester);
final saved = (await cards.cardsOf('japanese')).first.content;
expect(saved.senses, const [
VocabularySense(glosses: ['to make a living']),
]);
expect(saved.meanings, ['to make a living']);
});
testWidgets('New flashcard opens the new card, also while the changed one '
'it leaves is being saved', (tester) async {
await cards.create('japanese', card('猫', 'cat'));

View file

@ -419,6 +419,34 @@ void main() {
expect(find.text('to eat'), findsOneWidget);
});
testWidgets('a word with senses shows them numbered with their grammar; '
'one usually written in kana shows its reading from the start', (
tester,
) async {
await cards.create(
'japanese',
CardContent(
type: CardType.vocabulary,
front: paragraph('蛙'),
back: RichContent.empty(),
reading: 'かえる',
senses: const [
VocabularySense(glosses: ['frog'], pos: ['n'], misc: ['uk']),
VocabularySense(glosses: ['rain frog'], label: 'zoology'),
],
),
);
await study(tester);
expect(find.text('かえる'), findsOneWidget);
await tester.tap(find.text('Show answer'));
await tester.pumpAndSettle();
expect(find.text('1.'), findsOneWidget);
expect(find.text('frog'), findsOneWidget);
expect(find.text('(zoology)'), findsOneWidget);
expect(find.byTooltip('usually written using kana alone'), findsOneWidget);
});
testWidgets('the session settings narrow the cards studied to the tags '
'chosen', (tester) async {
final verb = await tags.create('verb');