halkyon/halkyon_learn_flutter/lib/main.dart

53 lines
2.3 KiB
Dart
Raw Normal View History

import 'dart:io';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_learn_flutter/src/app/learn_frame.dart';
import 'package:halkyon_learn_flutter/src/app/learn_sessions.dart';
import 'package:halkyon_learn_flutter/src/core/bloc/app_bloc_observer.dart';
import 'package:halkyon_learn_flutter/src/core/country/country_names.dart';
import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies.dart';
import 'package:halkyon_learn_flutter/src/core/device_settings/widget/device_settings_scope.dart';
import 'package:halkyon_learn_flutter/src/core/theme/cjk_fonts.dart';
feat(dictionary): Japanese cut into words and looked up on the device Once the person agrees, the device holds MeCab's IPADIC and the instance's JMdict and cuts lines as Jisho.org does, so their text never leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build hook compiles it; its tokens are grouped by Ve's rules, written anew in Dart, with に kept a word of its own. Each word is found as it stands or as a form of a word, else by its dictionary form, else as the longest words it holds from token to token; longer words over token boundaries are offered as before. The curator packs Ubuntu's compiled IPADIC beside the dictionary files (curate ipadic, and every update run). The server exports its JMdict from its tables, so the device finds the same keys and keeps the same versions, and serves both at /device-dictionaries/, named in dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path. The app checks each file against its hash, unpacks IPADIC, builds a SQLite database from the export on an isolate, and asks the instance for newer files every 30 days, downloading only what changed. The Watch screen offers the download once per device with its size (34 MB, some 150 MB once set up); Settings downloads and removes them. Without them, or should cutting on the device fail, lines go to the instance as before. MeCab's, IPADIC's and Ve's licences join the licence list. Every build now needs a C++ compiler; mecab_for_dart holds code_assets at 1.x. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 21:55:00 +02:00
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/mecab_license.dart';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
import 'package:halkyon_learn_flutter/src/feature/geo_map/data/cldr_license.dart';
import 'package:halkyon_learn_flutter/src/feature/kanji/data/kanjivg_license.dart';
import 'package:halkyon_ui/halkyon_ui.dart';
import 'package:material_ui/material_ui.dart';
import 'package:path/path.dart' as p;
import 'package:serverpod_flutter/serverpod_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = const AppBlocObserver();
registerFontLicenses();
registerCjkFontLicenses();
registerKanjiVgLicense();
registerFlagIconsLicense();
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
registerCldrLicense();
feat(dictionary): Japanese cut into words and looked up on the device Once the person agrees, the device holds MeCab's IPADIC and the instance's JMdict and cuts lines as Jisho.org does, so their text never leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build hook compiles it; its tokens are grouped by Ve's rules, written anew in Dart, with に kept a word of its own. Each word is found as it stands or as a form of a word, else by its dictionary form, else as the longest words it holds from token to token; longer words over token boundaries are offered as before. The curator packs Ubuntu's compiled IPADIC beside the dictionary files (curate ipadic, and every update run). The server exports its JMdict from its tables, so the device finds the same keys and keeps the same versions, and serves both at /device-dictionaries/, named in dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path. The app checks each file against its hash, unpacks IPADIC, builds a SQLite database from the export on an isolate, and asks the instance for newer files every 30 days, downloading only what changed. The Watch screen offers the download once per device with its size (34 MB, some 150 MB once set up); Settings downloads and removes them. Without them, or should cutting on the device fail, lines go to the instance as before. MeCab's, IPADIC's and Ve's licences join the licence list. Every build now needs a C++ compiler; mecab_for_dart holds code_assets at 1.x. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 21:55:00 +02:00
registerMecabLicenses();
// The instance the build names (`--dart-define=SERVER_URL=…`, or
// `assets/config.json`): the first offered, and the one installs from
// before the choice go on with.
final builtIn = Instance.parse(await getServerUrl())!;
final device = await DeviceParts.create(builtIn: builtIn);
final instances = InstanceChoice(
FileInstanceStore(File(p.join(device.support, 'instances.json'))),
builtIn: builtIn,
forget: device.storage.forget,
);
await instances.load(usedBefore: device.storage.usedBefore());
runApp(
InstanceGate(
frame: learnFrame,
choice: instances,
// Only an instance of Halkyon Learn: one of another Halkyon app's
// answers too, but holds nothing of this app's.
checker: ServerpodInstanceChecker(app: 'learn'),
countryNames: AssetCountryNames(),
open: (instance) => Dependencies.create(instance, device),
readLook: () async => (await device.deviceSettingsStore.read()).look,
app: const LearnSessions(),
),
);
}