Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 132 additions & 8 deletions .openhands/memory/2026-11-13.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ engine (`4896b69`).
physically impossible without a game-side anchor (the stripped `libminecraftpe.so` gives no
entity-list/level anchor, so 52 ENGINE_HOOK modules stay badged and 60 SERVER_DECIDED cannot be
done by any client code).
- **UI pack port**: `app/src/main/java/org/chimeramc/client/ui/compose/caramel/` — theme, glass
(real Haze 1.3.1 blur), buttons, controls, labels, loader icons, dropdowns. Compiles; 3761 tests
green; `check_ui_hygiene.py` clean.
- **UI pack port**: `app/src/main/java/org/chimeramc/client/ui/compose/theme/` — theme, glass
(real Haze 1.3.1 blur). Compiles; tests green; `check_ui_hygiene.py` clean. (An earlier note
pointed at `ui/compose/caramel/`; that package was an unused duplicate and was later deleted.)
- **Build/verify that worked here**: `./gradlew :app:assembleDebug` (3m35s) compiles the NDK code
(C++ is only checked by the NDK build; `Signature.cpp` is not host-compiled). APK ships
`libsweetloader.so` (158 `Java_org_chimeramc_client` exports), `libcaramel_universe.so` (8),
Expand Down Expand Up @@ -198,11 +198,11 @@ engine (`4896b69`).
fixtures (changelog payload, publish-order list), not assertions on this file.
- **UI pack verdict (`Lynxifyy/CaramelClient-UI-pack`)**: it is a Compose **Desktop** app
(`kotlin("jvm")` + `compose.desktop.currentOs`, `Window`/`application`, `TargetFormat.Msi/Dmg/Deb`)
plus a **GLFW/OpenGL3 Dear ImGui** demo — not an Android library. Its `components/` layer is
**already ported** into `ui/compose/caramel/` (`CaramelButtons`/`Controls`/`Dropdowns`/`Labels`/
`LoaderIcons`, `GlowGlass`, `GlowTheme`), and the port improved it (touch `collectIsPressedAsState`
instead of desktop `collectIsHoveredAsState`; explicit imports instead of `layout.*` wildcards;
doc comments explaining *why*). Remaining hazards for a naive port: whole-window `HazeState`
plus a **GLFW/OpenGL3 Dear ImGui** demo — not an Android library. Its token layer is **ported**
into `ui/compose/theme/` (`GlowPalette`/`GlowTheme`/`GlowGlass`) and `sweetloader/src/pl/menu/`
(`MenuTheme`/`MenuWidgets`); its Desktop `components/` files are not imported anywhere. (A
parallel `ui/compose/caramel/` package existed but was dead code — same token values, zero
importers — and has been deleted.) Remaining hazards for a naive port: whole-window `HazeState`
blur model (per-frame full-window blur is a phone-GPU killer), 63 hardcoded `Text("...")` literals
with no `strings.xml`, `(size / 2).dp` truncating `Int` division in `launcher/Tabs.kt`, no Gradle
wrapper, no tests, Kotlin 2.0.21/Compose 1.7.3 vs the client's 2.3.0/BOM 2026.01.01. The pack's
Expand Down Expand Up @@ -233,3 +233,127 @@ Two gates earned their keep by catching real misses in my own batch:
sources, where "Item 1" is coincidental English. The gate already excluded the `libHttpClient`
submodule for the same reason; the cache needed the same exclusion (and it is gitignored, so CI
never sees it — **this failure only reproduces locally after running the host tests**).

## One Caramel UI, the native ImGui menu, present-frame effects, and FPS honesty

Shipped as **CaramelClient PR #183** + **SweetLoader PR #38** (the parent pins the submodule
commit, so the two must merge in order: SweetLoader first).

### The old mod-menu UI is gone (as asked: "delete the UI completely")
- Deleted `ui/compose/modmenu/` (+ `ModMenuStateTest.kt`), `overlay/ModMenuOverlay.java`, the whole
radial wheel (`RadialMenuOverlay`/`RadialMenuView`/`RadialWheelMaterial`/`Wheel*`/`WheelHoldPolicy`),
their 24 tests, and the layouts (`overlay_mod_menu.xml`, `overlay_radial_menu.xml`, the
`item_mod_menu_*` / `vip_*_section` files). ~21.9k lines removed.
- `ModMenuNavLayoutTest` (kept) pins that `overlay_mod_menu.xml` and `overlay_radial_menu.xml` stay
**absent** and that `MenuRender.cpp` keeps ONE source of truth for the tab labels+icons.
- The only mod menu now is Dear ImGui inside the game's GL context:
`PreloaderMenu.java` ↔ `sweetloader/src/pl/menu/MenuJni.cpp`, with `InGameMenuController` building
the catalogue JSON and forwarding touch/back/events.
- `PointerCursorRenderer` javadoc referenced the deleted `RadialMenuView` — fixed, else the
file-size/UI-hygiene gates and a stale-doc reader both suffer.

### The UI pack port, both halves
- Compose launcher: the live theme is `ui/compose/theme/` (`GlowPalette.java` + `GlowTheme.kt` +
`GlowGlass.kt`). The parallel `ui/compose/caramel/Caramel*` package that earlier notes called the
port was **never imported by anything** — a duplicate of the same token values — and was deleted in
the follow-up cleanup. Do not reintroduce a second `Caramel*` component layer.
- Native in-game: `pl/menu/MenuTheme.{hpp,cpp}` + `MenuWidgets.{hpp,cpp}` are the pack's
`imgui/src/CaramelTheme.*` + `CaramelWidgets.*` ported token-for-token. **Do NOT wire the pack's
GLFW backend** (Android has no windowing stack) — only `imgui_impl_opengl3` is compiled.
- Dear ImGui **1.91.5 is vendored at `sweetloader/third_party/imgui/`** (not FetchContent: the
configure step must not need the network). `check_pinned_deps.py` only looks at `GIT_REPOSITORY`
pins, so vendored code just needs a NOTICE row — added `ocornut/imgui 1.91.5 MIT`.

### FPS honesty (the "fake frames" fix)
- `FrameRateMeter` is the **launcher's** `Choreographer` vsync cadence, NOT the game rate — its doc
now says so (this is what `HudReadoutLayoutTest` asserts).
- `isExact()` added to `FrameTickSource` (default `false`, fail-closed) and threaded all the way up
`GameBackend` → `GameBridge.isFrameRateExact()` → `FpsMod.nativeIsFrameRateExact()`. `FpsReadout`
labels the source; `ModStatsFormatter.fps(v, approximate)` prefixes `~`; `DebugScreenText` prints
"FPS: ~N (launcher refresh, not the game's)" / "(approximate; present hook absent)".

### Frame generation + motion blur (both NEW modules)
- `core.mods.render.PresentFrameCatalog` (2 modules × 4 options, keys unique) + `PresentFramePolicy`
(JVM-testable) + `pl/render/PresentFrameEffect.cpp` on the **`eglSwapBuffers` detour** seam;
`PresentFrameMath.hpp` is the pure blend + budget clamp.
- Honesty contract, pinned by `PresentFrameModulesTest`: neither description may claim a frame-rate
gain, both are off when the present hook is absent regardless of their switches, blur budget clamped.
- Registered as `ModIds.FRAME_GENERATION` (new) and existing `MOTION_BLUR`, both in the VIP group.

### Verify
- Unit 3516/0/0 + `lintDebug` green; `assembleDebug` green (the only thing that compiles the NDK C++).
- `sweetloader/tests/cosmetics/run_tests.sh` green incl. new `PresentFrameMathTest` + `MenuFilterTest`.
- CI: SweetLoader PR #38 fully green (host, rust, repo-integrity, **arm64-v8a native build**);
CaramelClient PR #183 `Android CI` + `Build Debug APK` green.
- GH007 push rejection again → `git rebase origin/main --exec 'git commit --amend --no-edit --reset-author'`
after setting the repo-local noreply identity (already in MEMORY.md).

### Follow-up cleanup: the last dead UI, and docs that lied about it
Pushed as `f8482be` on the same PR branch (`feat/native-imgui-menu-present-frame-effects`, PR #183);
all CI checks green afterwards.
- **Found by a zero-reference sweep** (`\bClassName\b` across main+test+res, minus the file itself):
`ui/compose/caramel/Caramel*.kt` (7 files) and `overlay/ModMenuSwitch.java` had **no importers at
all**. The `caramel` package was a duplicate of the live `ui/compose/theme/` token values, so it
was deleted — do not recreate a second `Caramel*` component layer. Earlier memory claimed it was
the live port; that was wrong and is corrected in MEMORY.md.
- The launch quick dock still carried a "Radial Menu" item wired to `openModsFromHome()`; removed
end-to-end (layout item, `ic_wheel`, `RadialHeadTurn` + test, `radial_wheel_hold_to_open` pref,
21 strings × 10 locales, 180 priority-JSON entries, 20 translation-baseline rows).
- **Docs that described the deleted surfaces as live** were the bigger find: `ui-pack-review.md`
claimed the pack's Desktop `components/` were "already ported"; `ACCESSIBILITY.md` and
`ARCHITECTURE.md` cited `ModMenuSwitch` for navigation. The real navigation is Dear ImGui's own
(`ImGuiConfigFlags_NavEnableKeyboard` in `pl/menu/MenuHost.cpp`). `UI_CHECKLIST.md` sections were
renumbered (1 native menu, 2 row rules, 3 nav, 4-10) and its skin section no longer claims an
in-game cosmetics preview — the native Cosmetics tab says the preview is launcher-side.
- Verify: 3516 unit tests 0 failures, `lintDebug` + `assembleDebug` green, hygiene/ABI/release/
submodule gates green. Note the full `lintDebug testDebugUnitTest assembleDebug` run takes ~4-5
min and its log can contain NUL bytes — read it with `tr -d '\000' | grep -a`.

### Correction: `f8482be` overclaimed, and the real sweep (commit `6fcb73b`)
The note above says `f8482be` "deleted the last dead UI". It did not: `ic_wheel.xml`,
`RadialHeadTurn.java` + its test, and a much larger orphan cluster were still in the tree and
still compiled. `f8482be`'s message was wrong; `6fcb73b` is the commit that actually removed them.

**Lesson: a commit message that says "deleted X" is not evidence X is gone — verify against the
tree (`git ls-files | grep X`), not the message.** The same trap applies to my own summaries.

What `6fcb73b` removed (each verified with a zero-reference sweep over main+test+res, minus the
file itself):
- Mod-menu/VIP leftovers: `ControllerProfileEditor`, `PackChangerPanel`, `HudSafeAreaView`,
`RenderDebugOverlay`, `MenuBindRelay`, `VipKeyLayout`, `VipTheme`, `ModCategory` (+test),
`KeyboardIllustrationView`, `ModToggleAnim`, `RadialHeadTurn` (+test).
- The whole retired 3D splash scene: `SplashSceneView`, `SplashIsland` (+test), `SplashWorld`
(+test), `SplashParticles` (+test), `WordmarkShatter`, `ShatterPlan`, `OreBlockLoaderView`,
`OreLoaderLayout`, `OreCrackSprites` (+`SplashLoaderTest`). **`OreCrackSprites` looked live
because `SplashTimeline.crackStage()` referenced it — but `crackStage()` itself had no caller.**
Chase the chain to a real call site before declaring a class live; "referenced by another dead
method" is not live.
- Unreachable launcher screens: `CompatibilityProbe`, `QuickActionsAdapter`, `SettingsAdapter`,
`GlideImageGetter`, `GameVersionSelectDialog` + the `gameversionselect` package, and the 7
layouts they were the last users of (`item_version*`, `dialog_game_version_select`,
`dialog_rename_entry`, `item_quick_action`, `item_settings_container`).

**`LayoutResourceTest.noLayoutIsUnreferenced` catches orphaned layouts** — deleting the last Java
user of a layout fails this test, so a UI deletion is not done until that test is re-run. It is the
fastest signal that a deletion left a layout behind.

False positives I nearly deleted (they ARE live — keep them): `CaramelBadge` (CosmeticsPanel +
VoicePanel), `ModIconHelper` (ModNotificationManager), `OverlayResizeHandles`
(BaseOverlayButton). The `bg_vip_*` drawables stay too — the pause menu borrows `bg_vip_surface`
and `bg_vip_icon_button`. The `ModMenu*` palette/settings/metrics classes stay — `ModMenuTheme`
and `InbuiltModManager` still read them.

Also updated so the tooling matches the tree: `scripts/hygiene/findviewbyid-baseline.txt`
(retired dead entries), `app/lint-baseline.xml` (9 stale issues for deleted files),
`FileSizeBudgetTest` doc comment, `AGENTS.md` entry, and the `splash-screen.md` /
`one-mod-menu-...-were-merged.md` / `in-game-pack-changer.md` sections.

Verify: 3460 unit tests 0 failures, `:app:lintDebug` green, `check_ui_hygiene.py`,
`validate_release_content.py`, `verify_berryapi_abi.py` green. **`:app:lintDebug` needs the network**
— it resolves the AGP plugin, so `--offline` fails with "Plugin com.android.application not found".
`compile`/`test`/`assemble` are fine offline.

**GH007 push rejection** recurred on this container; the fix is the repo-local noreply identity
already in MEMORY.md plus `git rebase origin/main --exec 'git commit --amend --no-edit --reset-author'`.
`$GithubLynxifyy` is NOT exported in this container (empty); only `$GITHUB_TOKEN` (40 chars) is set
and it *can* read the API (`/actions/runs`) — it 403s on push.
54 changes: 46 additions & 8 deletions .openhands/memory/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,30 @@ The repo was called **GlowberryClient** before; the package is `org.chimeramc.cl

### UI
- The UI pack (`Lynxifyy/CaramelClient-UI-pack`) is a Compose **Desktop** app plus a GLFW Dear ImGui
demo, not an Android library. Its `components/` layer is **already ported** to
`ui/compose/caramel/` (`CaramelButtons`/`Controls`/`Dropdowns`/`Labels`/`LoaderIcons`, `GlowGlass`,
`GlowTheme`), improved where it diverged (touch `collectIsPressedAsState` not desktop
`collectIsHoveredAsState`; explicit imports not `layout.*`; docs explaining *why*). Remaining
hazards for a naive port: the whole-window `HazeState` blur model, 63 hardcoded `Text("...")`
literals with no `strings.xml`, `(size / 2).dp` truncating `Int` division, no wrapper, no tests,
Kotlin 2.0.21/Compose 1.7.3 vs the client's 2.3.0/BOM 2026.01.01. The pack's preview apple is a
*different* asset from the launcher art. Full review: `docs/agents/ui-pack-review.md`.
demo, not an Android library. **Its tokens are the single design reference**, carried by three live
sources that must agree: the shared theme `ui/compose/theme/` (`GlowPalette.java` + `GlowTheme.kt` +
`GlowGlass.kt`, used by account/connect/replay), the pack component port `ui/compose/caramel/`
(`CaramelTheme.kt` + `CaramelControls/Labels/Glass.kt`, used by the launch screen; `CaramelColors` =
the pack's `theme/Theme.kt` value for value incl. amber/greens the shared scale lacks), and the
in-game menu `sweetloader/src/pl/menu/MenuTheme.*` + `MenuWidgets.*` (token for token).
`CaramelPaletteParityTest` pins the overlap equal to `UiTokens`/`GlowPalette` and the ImGui twin
equal to the Compose port. (An earlier `caramel/Caramel*` layer was dead code and deleted; it is
live again because the launch screen imports it — check for importers before calling it dead.)
Never wire the GLFW backend. PR #183 + SweetLoader #38.
Hazards (Haze blur model, hardcoded `Text` literals, `(size / 2).dp`, version skew, and a preview
apple that is not the launcher art) are in `docs/agents/ui-pack-review.md`.
- **The mod menu is the native ImGui one; the Compose/View mod menu and the radial wheel are deleted**
(`ui/compose/modmenu/`, `ModMenuOverlay`, `RadialMenu*`/`Wheel*`, their layouts). `PreloaderMenu`
(Java) ↔ `pl/menu/MenuJni.cpp` is the only bridge; `ModMenuNavLayoutTest` fails if a deleted layout
comes back or the native tab bar loses its single labels+icons source.
- **Dear ImGui 1.91.5 is vendored at `sweetloader/third_party/imgui/`** (the configure step must not
need the network). `check_pinned_deps.py` only inspects `GIT_REPOSITORY` pins, so a vendored dep
just needs a NOTICE row.
- **Frame generation + motion blur are present-frame effects, not frame-rate gains.** Both live in
`core.mods.render.PresentFrameCatalog`/`PresentFramePolicy` and run on the `eglSwapBuffers` detour
(`pl/render/PresentFrameEffect.cpp`); they blend the previous presented frame in, present no extra
frame, and are badged while the present hook is absent. `PresentFrameModulesTest` forbids any
frame-rate claim in their copy.
- **Animated values that cross zero belong on `offset`, never `padding`.** `Modifier.padding` throws
`IllegalArgumentException: Padding must be non-negative`; a drift of `(value - 0.5f) * 6f` is
negative on the first frame, so `LaunchChangelogCard` threw on **every** launch.
Expand Down Expand Up @@ -110,3 +126,25 @@ The repo was called **GlowberryClient** before; the package is `org.chimeramc.cl
(diagnose with `actionlint`, not the API); and `set -o pipefail` + `cmd | grep -q` exits 74
(SIGPIPE) even on a match — dump to a file and grep the file. The Rust tree is rustfmt-normalized;
keep it so.

### Hygiene gates (the four that fail a local change)
- **`FileSizeBudgetTest`** (`scripts/hygiene/file-sizes-baseline.txt`, threshold 1500 lines): a listed
file may only **shrink**. `InbuiltModuleProvider.java` was at its 1765 baseline; the fix is to
**split**, not to edit the baseline. It was split into `ModConfigSchema.java` (config keys +
`ConfigEntry` list + `RuntimeConfigSchema` builders, 1175 lines) and the provider (748). The
provider keeps thin `static schemaTypeFor`/`configDescriptionRes` delegators so
`InbuiltModuleProviderTypeTest` (same package) still compiles; the moved members are package-private
and the provider calls them as `ModConfigSchema.x(...)`.
- **`ModuleDescriptionsTest`**: every `inbuilt_mod_*_desc` in `values/strings.xml` must be ≤ **80**
chars (target 60) and one sentence. A long "weighted by how fast the view is moving" tail is the
usual miss — keep it in the `*_scope_note` instead.
- **`TranslationCoverageTest` / `check_ui_hygiene.py` translation baseline**: a new string must be
translated in all 10 locales (`es fr hi id ja pt ru tr vi zh-rCN`) or marked `translatable="false"`.
Add a `scripts/translations_*.json` batch, register it in `BATCHES` in
`scripts/apply_translations.py`, then run the applier (it is idempotent and refuses a dropped `%s`).
After applying, `check_ui_hygiene.py` may report baseline entries that "no longer exist" — retire
them with `python3 scripts/check_ui_hygiene.py --update` (that also refreshes the findViewById
baseline, which *shrinks* when a `findViewById` is replaced by a ViewBinding read).
- **`NoSilentCatchTest`**: a `catch` that discards the throwable fails. Use
`AppLog.warnOnce(tag, key, message, e)`, even for an expected `UnsatisfiedLinkError` fail-closed path.
- Baseline format note: the baseline files live under **`scripts/hygiene/`**, not `scripts/`.
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ file is missing from the index.
- [Select Hit touch ordering](docs/agents/select-hit-touch-ordering.md)
- [Proximity voice chat (core.voice + VoiceChatOverlay)](docs/agents/proximity-voice-chat.md)
- [Voice relay server (server/voice-relay, Go) тАФ internet voice](docs/agents/voice-relay-server-internet-voice.md)
- [In-game pack changer (core.content.InGamePackChanger + PackChangerPanel)](docs/agents/in-game-pack-changer.md)
- [In-game pack changer (core.content.InGamePackChanger)](docs/agents/in-game-pack-changer.md)
- [One Mod Menu (U4): the touch screen and the VIP screen were merged](docs/agents/one-mod-menu-the-touch-screen-and-the-vip-screen-were-merged.md)
- [Mod Menu focus highlight (the "50% white overlay")](docs/agents/mod-menu-focus-highlight.md)
- [Mod Menu keybinds (keyboard + controller) and the in-game capture](docs/agents/mod-menu-keybinds-and-the-in-game-capture.md)
Expand Down Expand Up @@ -107,3 +107,4 @@ file is missing from the index.
- [World reconstruction — RE groundwork and the remaining wall](docs/agents/world-reconstruction-re-groundwork.md)
- [MB shader loader version support (mtbinloader2 / libmtbinloader2.so)](docs/agents/mb-shader-loader-version-support.md)
- [BerryAPI cosmetic C ABI (sweetloader mirror + launcher surface)](docs/agents/berryapi-cosmetic-c-abi.md)
- [One UI: the Caramel UI pack, the native ImGui menu, and FPS honesty](docs/agents/one-ui-the-caramel-ui-pack-the-native-imgui-menu-and-fps-honesty.md)
Loading
Loading