Skip to content

initial font library implementation#19

Merged
teidesu merged 10 commits into
mainfrom
editor-custom-fonts
Jun 20, 2026
Merged

initial font library implementation#19
teidesu merged 10 commits into
mainfrom
editor-custom-fonts

Conversation

@polina4096

@polina4096 polina4096 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Added a dedicated Fonts settings page to import, add, remove, drag-to-reorder, and hide/show font families (with an eye toggle).
    • Updated app font selection to support Default/System and imported family choices, with editor text and app rendering following the active selection.
  • Bug Fixes / Improvements
    • Improved font roster updates so changes apply immediately and stay in sync across editor UI and theming.
    • Reworked appearance settings to route font configuration through the new Fonts page.
  • Documentation
    • Refreshed in-app font labels and instructions, removing the old single custom-font option and adding roster-based guidance.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR transforms font management from a single-pack system to a multi-family architecture. FontHelper now stores per-family directories under inu_fonts/ with manifest persistence and roster ordering via index.json. A new FontsSettingsActivity provides UI for reordering fonts, toggling visibility, importing new fonts via document picker, and selecting the app font from system or imported families. Java-layer integration with AndroidUtilities and PaintTypeface enables dynamic editor roster entries and typeface resolution through the refactored model.

Changes

Multi-Family Font Management

Layer / File(s) Summary
Configuration & Preferences
src/kotlin/InuConfig.kt, src/kotlin/InuHooks.kt
Adds ACTIVE_FONT_ID preference to store selected imported font family identifier; updates FONT_MODE documentation to clarify mode 2 uses imported families via this setting; updates font-mode conditional to use FontModeItem.CUSTOM enum instead of numeric comparison.
FontHelper: model, storage, and APIs
src/kotlin/helpers/font/FontHelper.kt
Refactors from single-pack JSON to multi-family subdirectories: introduces Family class for per-family metadata and typeface resolution, replaces flat faces list with families map, adds index.json for roster order and hidden-token persistence, implements legacy migration, adds token helpers, and rewires import/removal/resolution logic to support family bucketing and roster persistence.
Java framework integration
patches/feature/custom-font.patch
Adds AndroidUtilities.inu_getTypeface(String, boolean) overload consulting FontHelper; applies default-font hooks to TextPaints and Theme; refactors PaintTypeface to build roster from FontHelper.buildEditorRoster(...), exposes public LazyTypeface and new constructor, and adds generation-counter guarding for stale async rebuilds.
Settings navigation & search
src/kotlin/ui/settings/AppearanceSettingsActivity.kt, src/kotlin/SearchRegistry.kt
Replaces inline font-mode picker with sub-page button (BUTTON_FONTS) that opens FontsSettingsActivity; updates SearchRegistry to include the new page for search and deep-link discovery.
FontsSettingsActivity, UI components, & resources
src/kotlin/ui/settings/FontsSettingsActivity.kt, src/res/values/strings_inu.xml, src/res/values-ja/strings_inu.xml, src/res/values-ru/strings_inu.xml, src/res/values-zh-rCN/strings_inu.xml, scripts/config.ts
Introduces FontsSettingsActivity with reorderable roster (drag handles per row), app-font mode selection, document picker for importing fonts, eye-toggle for visibility control, and FontRow component rendering font names in their own typefaces. Includes string resources in English and four locales for labels and action descriptions; adds eye/eye-off icon configuration for UI rendering.
Documentation
FEATURES.md
Updates font feature description to detail new multi-family management: add/remove/reorder fonts, select default app font from system or user TTF/OTF/TTC, and use fonts in media editor text tool.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • teidesu

Poem

🐰 A multi-family font forest grows,
Rosters reordered with a drag and a scroll,
Typefaces dance through fridges of code,
Kotlin maps families, Java ties the load,
Settings bloom bright in the app's UI glow. 🌿✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary objective of the pull request: implementing an initial font library system with comprehensive font management capabilities.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch editor-custom-fonts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
patches/feature/custom-font.patch (1)

23-30: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Rename the new stock overload to inu_getTypeface.

This adds a fresh overload on a stock class without the required inu_ prefix, which will make future rebases harder than necessary. Rename the overload and its new call sites while the surface is still small.

As per coding guidelines, "If you must add fields/methods to stock classes, prefix them with inu_ (including overloads, e.g., inu_addTab)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patches/feature/custom-font.patch` around lines 23 - 30, The new public
overload getTypeface(String assetPath, boolean inu_forPaint) must be renamed to
inu_getTypeface to follow the inu_ prefix rule; update the implementation and
all new call sites accordingly (change the method declaration name and any calls
that pass inu_forPaint), keep the same parameter names and behavior around
typefaceCache, the inu_override lookup via
desu.inugram.helpers.font.FontHelper.onGetTypeface, and the early return logic
intact so only the identifier changes; ensure the original getTypeface(String)
continues to delegate to inu_getTypeface(assetPath, false).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/kotlin/helpers/font/FontHelper.kt`:
- Around line 367-429: importFromUris mutates shared collections (families,
rosterTokens, hiddenTokens) off the globalQueue and must instead build new state
locally and publish it under synchronization or on the main thread; change
importFromUris to collect files, create targetDir/faces, and assemble a local
map/list of changes (e.g. newFamilies: Map<id,Family>, updatedFamilyDirs,
addedRosterTokens: List<String>, addedFaces count) without touching the shared
families/rosterTokens/hiddenTokens, then after all file I/O complete apply those
changes inside a synchronized block or by posting a single update to the main
thread (e.g. runOnUiThread or a single lock around families) where you call
readFamily(targetDir) and assign families[targetId] = it, mutate
rosterTokens/hiddenTokens, call saveRoster() and any
rosterTokens.add("font:$targetId") only from that synchronized/main-thread
publish step; reference functions/vars: importFromUris, families, rosterTokens,
hiddenTokens, readFamily, writeManifest, saveRoster, newId so the code that
renames/copies files remains the same but shared collections are only mutated in
one atomic, synchronized publish.

In `@src/kotlin/InuConfig.kt`:
- Around line 505-507: ACTIVE_FONT_ID is currently created as an exportable
StringItem causing backups to carry a local font-family id; change it to be
non-exportable and add a normalization step when loading font settings: update
the ACTIVE_FONT_ID declaration (StringItem("active_font_id", "")) to use the API
to mark it non-exportable (e.g., pass/export flag or call setExportable(false)
on ACTIVE_FONT_ID) and in the font-loading path that respects FONT_MODE (the
code that reads/uses ACTIVE_FONT_ID when FONT_MODE == 2) validate the stored id
against available font families and fallback to "" (or force FONT_MODE != 2) if
the id is missing.

In `@src/kotlin/ui/settings/FontsSettingsActivity.kt`:
- Around line 144-149: The option added in FontsSettingsActivity via opts.add
currently appears for every roster token but only works correctly for imported
families; update the code that adds the app-font target (the opts.add block that
sets InuConfig.FONT_MODE, InuConfig.ACTIVE_FONT_ID, calls
listView.adapter.update(true) and showRestartBulletin()) to only be created for
tokens the main picker can represent — e.g., check the token value or type
before adding (only allow tokens that start with "font:" or pass whatever
isFontToken(token) predicate your model uses); alternatively, if you prefer
keeping the option for built-ins, extend showAppFontPicker()/its model to
include built-in families so the picker can display/select them consistently.

---

Outside diff comments:
In `@patches/feature/custom-font.patch`:
- Around line 23-30: The new public overload getTypeface(String assetPath,
boolean inu_forPaint) must be renamed to inu_getTypeface to follow the inu_
prefix rule; update the implementation and all new call sites accordingly
(change the method declaration name and any calls that pass inu_forPaint), keep
the same parameter names and behavior around typefaceCache, the inu_override
lookup via desu.inugram.helpers.font.FontHelper.onGetTypeface, and the early
return logic intact so only the identifier changes; ensure the original
getTypeface(String) continues to delegate to inu_getTypeface(assetPath, false).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb20b5cd-9c17-4324-9763-75663d07fbc6

📥 Commits

Reviewing files that changed from the base of the PR and between e494b66 and b07fe89.

📒 Files selected for processing (8)
  • FEATURES.md
  • patches/feature/custom-font.patch
  • src/kotlin/InuConfig.kt
  • src/kotlin/SearchRegistry.kt
  • src/kotlin/helpers/font/FontHelper.kt
  • src/kotlin/ui/settings/AppearanceSettingsActivity.kt
  • src/kotlin/ui/settings/FontsSettingsActivity.kt
  • src/res/values/strings_inu.xml

Comment thread src/kotlin/helpers/font/FontHelper.kt Outdated
Comment thread src/kotlin/InuConfig.kt Outdated
Comment thread src/kotlin/ui/settings/FontsSettingsActivity.kt Outdated
@teidesu teidesu force-pushed the editor-custom-fonts branch from b07fe89 to b8814ad Compare June 3, 2026 23:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/kotlin/helpers/font/FontHelper.kt (1)

136-154: ⚖️ Poor tradeoff

Hardcoded user-facing face labels should go through LocaleController.

faceLabel returns user-visible strings ("Variable", "Thin"…"Black", "Italic", "$w Italic") shown in the tap menu. These bypass the project's string-resource path.

As per coding guidelines, "Access strings via LocaleController.getString(R.string.InuXxx) in Kotlin/Java code".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/kotlin/helpers/font/FontHelper.kt` around lines 136 - 154, Replace
hardcoded user-facing labels in faceLabel(Face) with localized strings via
LocaleController.getString(...) using appropriate R.string entries (e.g.,
R.string.font_variable, R.string.font_thin, font_extralight, font_light,
font_regular, font_medium, font_semibold, font_bold, font_extrabold, font_black,
font_italic, font_italic_combined). Update the logic in faceLabel to map the
conditions on f.variable, f.weight, and f.italic to those resource IDs and
return LocaleController.getString(resourceId) (or a formatted localized string
for combined weight+italic) instead of returning literal strings like
"Variable", "Thin", "$w Italic".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/kotlin/helpers/font/FontHelper.kt`:
- Around line 136-154: Replace hardcoded user-facing labels in faceLabel(Face)
with localized strings via LocaleController.getString(...) using appropriate
R.string entries (e.g., R.string.font_variable, R.string.font_thin,
font_extralight, font_light, font_regular, font_medium, font_semibold,
font_bold, font_extrabold, font_black, font_italic, font_italic_combined).
Update the logic in faceLabel to map the conditions on f.variable, f.weight, and
f.italic to those resource IDs and return LocaleController.getString(resourceId)
(or a formatted localized string for combined weight+italic) instead of
returning literal strings like "Variable", "Thin", "$w Italic".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a11c7c61-423c-4c3e-b871-437f15518ce3

📥 Commits

Reviewing files that changed from the base of the PR and between b07fe89 and b8814ad.

📒 Files selected for processing (8)
  • FEATURES.md
  • patches/feature/custom-font.patch
  • src/kotlin/InuConfig.kt
  • src/kotlin/SearchRegistry.kt
  • src/kotlin/helpers/font/FontHelper.kt
  • src/kotlin/ui/settings/AppearanceSettingsActivity.kt
  • src/kotlin/ui/settings/FontsSettingsActivity.kt
  • src/res/values/strings_inu.xml
✅ Files skipped from review due to trivial changes (2)
  • src/res/values/strings_inu.xml
  • FEATURES.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/kotlin/InuConfig.kt
  • src/kotlin/SearchRegistry.kt
  • src/kotlin/ui/settings/AppearanceSettingsActivity.kt
  • src/kotlin/ui/settings/FontsSettingsActivity.kt

@polina4096 polina4096 force-pushed the editor-custom-fonts branch from b8814ad to f7cba01 Compare June 14, 2026 13:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@patches/feature/custom-font.patch`:
- Line 161: The static field inu_loadGeneration needs to be declared as volatile
to ensure thread-safe visibility across the UI thread and theme queue thread.
Currently, inu_loadGeneration is accessed from multiple threads (read on the
theme queue thread at line 168, written by inu_invalidate() on the UI thread,
and read again on the UI thread at line 180) without synchronization, which
could lead to stale values being used. Add the volatile keyword to the
inu_loadGeneration field declaration to guarantee that all threads see the most
up-to-date value and prevent stale async builds from committing after the
generation has been incremented.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 63152aad-da7d-4146-aeae-96735c132150

📥 Commits

Reviewing files that changed from the base of the PR and between b8814ad and f7cba01.

📒 Files selected for processing (13)
  • FEATURES.md
  • patches/feature/custom-font.patch
  • src/kotlin/InuConfig.kt
  • src/kotlin/InuHooks.kt
  • src/kotlin/SearchRegistry.kt
  • src/kotlin/helpers/NotificationsHelper.kt
  • src/kotlin/helpers/font/FontHelper.kt
  • src/kotlin/ui/settings/AppearanceSettingsActivity.kt
  • src/kotlin/ui/settings/FontsSettingsActivity.kt
  • src/res/values-ja/strings_inu.xml
  • src/res/values-ru/strings_inu.xml
  • src/res/values-zh-rCN/strings_inu.xml
  • src/res/values/strings_inu.xml
✅ Files skipped from review due to trivial changes (4)
  • src/res/values-zh-rCN/strings_inu.xml
  • src/kotlin/helpers/NotificationsHelper.kt
  • src/kotlin/SearchRegistry.kt
  • FEATURES.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/kotlin/InuConfig.kt
  • src/res/values/strings_inu.xml
  • src/kotlin/ui/settings/AppearanceSettingsActivity.kt
  • src/kotlin/helpers/font/FontHelper.kt

Comment thread patches/feature/custom-font.patch Outdated

private static List<PaintTypeface> typefaces;
public static boolean loadingTypefaces;
+ private static int inu_loadGeneration = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make inu_loadGeneration volatile for cross-thread visibility.

inu_loadGeneration is read on the theme queue thread (line 168), written on the UI thread (inu_invalidate()), and read again on the UI thread for comparison (line 180). Without volatile or synchronization, visibility is not guaranteed, which could allow a stale async build to commit after inu_invalidate() has incremented the generation.

🔒 Proposed fix
-    private static int inu_loadGeneration = 0;
+    private static volatile int inu_loadGeneration = 0;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
+ private static int inu_loadGeneration = 0;
private static volatile int inu_loadGeneration = 0;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patches/feature/custom-font.patch` at line 161, The static field
inu_loadGeneration needs to be declared as volatile to ensure thread-safe
visibility across the UI thread and theme queue thread. Currently,
inu_loadGeneration is accessed from multiple threads (read on the theme queue
thread at line 168, written by inu_invalidate() on the UI thread, and read again
on the UI thread at line 180) without synchronization, which could lead to stale
values being used. Add the volatile keyword to the inu_loadGeneration field
declaration to guarantee that all threads see the most up-to-date value and
prevent stale async builds from committing after the generation has been
incremented.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
patches/feature/custom-font.patch (1)

145-152: ⚠️ Potential issue | 🔴 Critical

Verify that getName() handles nameKey=null for imported fonts.

The new constructor (lines 98–106) sets nameKey = null while getName() at line 149 returns LocaleController.getString(nameKey) without checking for null. This means calling getName() on imported fonts created with this constructor will pass null to LocaleController.getString(), likely causing a null-pointer exception or returning null/empty string. Add a null check or fallback to the name field.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patches/feature/custom-font.patch` around lines 145 - 152, The getName()
method does not check if nameKey is null before calling
LocaleController.getString(nameKey), which will fail when importing fonts using
the new PaintTypeface constructor (that sets nameKey to null). Update the
getName() method to first check if nameKey is null, and if so, return the name
field directly as a fallback; otherwise, proceed with the existing
LocaleController.getString(nameKey) call to support both localized and imported
font names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@patches/feature/custom-font.patch`:
- Around line 145-152: The getName() method does not check if nameKey is null
before calling LocaleController.getString(nameKey), which will fail when
importing fonts using the new PaintTypeface constructor (that sets nameKey to
null). Update the getName() method to first check if nameKey is null, and if so,
return the name field directly as a fallback; otherwise, proceed with the
existing LocaleController.getString(nameKey) call to support both localized and
imported font names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 268656f6-8137-4a68-a52f-12cfbfb31eaa

📥 Commits

Reviewing files that changed from the base of the PR and between f7cba01 and 4737d6f.

📒 Files selected for processing (2)
  • patches/feature/custom-font.patch
  • scripts/config.ts

@polina4096 polina4096 force-pushed the editor-custom-fonts branch 2 times, most recently from 578e592 to 7a0067f Compare June 14, 2026 13:59
@teidesu teidesu force-pushed the editor-custom-fonts branch from 7a0067f to 33d5129 Compare June 19, 2026 11:47
@teidesu teidesu force-pushed the editor-custom-fonts branch from 33d5129 to cab985b Compare June 19, 2026 12:34
@teidesu teidesu merged commit 0d9c1be into main Jun 20, 2026
2 checks passed
@teidesu teidesu deleted the editor-custom-fonts branch June 20, 2026 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants