Skip to content
This repository was archived by the owner on Apr 23, 2026. It is now read-only.

2.10.3 — Fonts, dictionaries, and gentler exits

Choose a tag to compare

@ShiroiKuma0 ShiroiKuma0 released this 22 Apr 09:08

This project has moved. Development continues at ShiroiKuma0/shiroikumanojisho under the name 白い熊の辞書 (Shiroikuma no Jisho). This repository is archived and preserved for historical release tags referenced from the linked project's release history. It will not receive further updates.

v2.10.3 — Fonts, dictionaries, and gentler exits

Bringing custom fonts into the reader end-to-end, a dictionary prewarm option to cut cold-search latency, better behavior for the half-screen dictionary popup on back, a one-tap exit overlay for the full-screen dictionary, and a small polish fix to the reader-to-dictionary-to-reader transition.

📚 Reader

Custom fonts in the appearance dialog

The Reader appearance and Translation appearance dialogs (bottom toolbar, serif/sans-serif font fields) now actually let you import and apply custom TTF/OTF fonts. Previously the font_download icon would pick a file, copy it to app-docs, and register it with Flutter's FontLoader — which does nothing for the reader content, since that's rendered in a WebView. The CSS setting referenced a font family the WebView had never heard of, and the stack silently fell through to Noto Serif JP. The predefined-font picker was a hardcoded list of the six bundled TTU presets, with no in-dialog way to pick a font imported through TTU's own settings.

  • New UserFontsStore singleton owns a directory of imported font files and a Hive index of entries. On import, the font's OpenType name table is parsed for the canonical family name (Typographic Family Name → Font Family Name → Full Font Name), so imports end up with the same display name whether you go through this dialog or TTU's own add-font dialog. WOFF/WOFF2 aren't supported (zlib and Brotli compressed respectively; decompression isn't worth the bundle weight for a rare path) and fall back to the filename stem.
  • A small loopback HTTP server streams imported fonts to the reader WebView with proper Content-Type and CORS headers. Injection is a tiny <style id="user-fonts"> listing one @font-face rule per imported font, wired at all four WebView load hooks plus the settings-apply path so a newly imported font becomes usable the moment you hit Apply, without reloading the book. Mirrors how TTU itself serves its own imported fonts (cache API + service worker at /userfonts/*).
  • The font picker merges the bundled TTU presets with your imports in one list, labelling each row bundled / custom.

Back press closes the half-screen dictionary popup

Tapping a word, reading the definition, and pressing system back (or edge-swiping back on gesture-nav devices) used to surface the "exit book?" confirmation dialog instead of dismissing the popup — forcing a two-dialog round-trip on the most common reading interaction. Now back just closes the popup and keeps you in the book.

Handles the hardware-key case (popup still live when back arrives) and the edge-swipe race where the popup's inner Dismissible fires at 5% of the swipe and clears the notifiers before Android delivers the back event: a short dismiss-timestamp window in onWillPop treats a back event within 250 ms of an auto-dismiss as the tail of the same swipe and swallows it. A later back press falls through to the normal exit flow, so deliberate exits still work.

No more visible shake after closing the full-screen dictionary

The reader's onSearch used to toggle SystemUiMode between edgeToEdge (before the dictionary push) and immersiveSticky (after the pop). The round-trip caused a visible reflow when the dictionary closed: the mode change fires an async platform-channel call that updates window insets, which triggers MediaQuery to rebuild, which reflows the WebView. On screen this looked like the book scrolling two lines further in for a frame, then snapping back. The dictionary's search bar renders fine with the system nav present, so the toggle was just churn — gone now.

📖 Dictionary

One-tap exit overlay on the full-screen dictionary page

The full-screen RecursiveDictionaryPage (shown when auto-full-screen is enabled, or when the dictionary is opened from the app launcher) wraps its UI in a FloatingSearchBar that has multi-stage back handling — first press clears the query, second collapses the bar, third pops the route — so exiting from a populated search state required two swipes, or three from the freshly-opened state. Several attempts to route the back event around FSB's handling all hit dead ends: the priority race against FSB's own inner WillPopScope, WidgetsBindingObserver.didPopRoute never firing, Navigator.maybePop triggering the wrong stage, synthetic hardware-back KeyEvent injection producing no observable effect.

Rather than keep fighting FSB for priority of events it's designed to own, the page now has a second always-visible way to exit: a vertically-centered right-edge overlay button, drawn over the FSB via a Stack in the page's body. 48×48, yellow border on black fill, yellow arrow icon, rounded corners. Its onTap does exactly what FSB's top-left back-arrow does — pops the route (or shuts down the app for the launcher killOnPop case) — with no FSB involvement.

Index prewarm for faster cold searches

New setting (app settings → dictionary) controls when the in-memory term index is built:

  • Never — index builds lazily on first search (previous behaviour; accepts first-search latency).
  • On book open (default) — prewarm fires when a book opens, so the first dictionary lookup in that session is instant.
  • On app launch — prewarm fires at startup. Adds 15–20 s to the initial app launch on large indexes but guarantees no cold-search latency from that point on.

The worker checks the build state before starting, so repeated triggers (navigating between books, returning from background) are cheap.

Also: a per-language indexing overlay now shows progress and language when the index is being built, so you know why the first search on a new language feels slow instead of wondering if the app's stuck.

🎨 TTU

Themed add-font dialog + TTF/OTF name autofill

TTU's own font-add dialog (reached from Font Family Group 1/2 → computer icon) rendered with a white-on-yellow mix against the rest of the app's yellow-on-black theme, and its workflow required manually typing the font family name before the Add button enabled. Both fixed:

  • Dialog now inherits the app's yellow-on-black palette via CSS overrides for the Tailwind classes TTU uses inside its modal container (.bg-white, .hover:bg-white, .text-gray-700, etc.) plus the native file-upload button. TTU's runtime theme switcher for book content still wins when you pick a reader theme.
  • New JavaScript hook listens for change events on file inputs, reads the ArrayBuffer, parses the OpenType name table, and fills the canonical family name into the sibling text input, dispatching an input event so Svelte's bound state updates and the Add button enables. Windows UCS-2, Unicode, and Mac Roman platforms handled. Only fills when the field is blank, so manual typing isn't clobbered.

🐞 Known issues

  • After using the full-screen dictionary and closing it (via either back arrow), the system back gesture to exit the book still needs two swipes. The first swipe is consumed somewhere in Android's native layer before it reaches Flutter — confirmed via HardwareKeyboard listeners not seeing the event along with didPopRoute and WillPopScope missing it. Iterations trying to defuse it (IME dismissal, WebView focus clearing, route-level observer, synthetic key injection) didn't resolve the issue. The one-tap exit overlay mitigates the dictionary-exit half of the old two-press flow; the book-exit half after dictionary use remains a known quirk for now.

📦 Commits

  • 6e69ebb7 — Dictionary: prewarm mode + per-language indexing overlay
  • bee9afd9 — TTU: theme add-font dialog + auto-fill font name from TTF/OTF
  • 98aa10cb — Reader: import custom fonts from appearance dialog
  • 76e68c23 — Reader: back press closes the half-screen dictionary popup
  • 1431d20c — Dictionary: right-edge exit overlay for one-tap close
  • 920d0e75 — Release 2.10.3