Skip to content

feat(ui): add a public UI API for mods - #1224

Merged
Deftu merged 26 commits into
v1from
refactor/ui-hardening
Oct 21, 2026
Merged

Deftu merged 26 commits into
v1from
refactor/ui-hardening

Conversation

@Deftu

@Deftu Deftu commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Mods have been reaching into internal for pages, themes, components, search and keybinds, and 1.4.4 already broke PolyPlus because of it. This gives them a public API under api.ui.v1 instead, all of it behind @ExperimentalOneConfigUiApi for now.

  • Pages go through a registry, so the sidebar, header and search placeholder come from one place and are translatable
  • ShellState is replaced by a ShellController
  • Internal symbols that mods use are kept as they are, and the mixin targets still link
  • The .api dumps drop nested .internal packages and @ApiStatus.Internal members. The config and HUD members mods use stay checked.
  • Version is now 1.5.0

Comment on lines +132 to +134
LaunchedEffect(id) {
LOGGER.warn("OneConfig page {} is not registered, opening the mods page instead", id)
if (shell.navigation.currentRoute == PageRoute(id)) shell.navigation.navigate(ModsGraph)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 [P2] Remove unavailable pages from history before falling back

When a mod unregisters a visited page, this fallback pushes the unavailable PageRoute onto the back stack. Pressing Back then reopens that missing page, whose effect navigates to Mods again and pushes the same invalid entry back onto history. This repeats indefinitely, making earlier pages unreachable through Back. Remove or replace the unavailable history entry instead of performing ordinary navigation.


@JvmStatic
val isRecording get() = escapeHandler != null
val isRecording get() = escapeHandler != null || KeybindManager.isInputCaptured()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 [P2] Honor external input capture in the Escape path

When a custom control registers an active capture through KeybindManager.addInputCapture, isRecording becomes true, but consumeEscape() still returns false unless the internal escape handler is installed. ComposeScreen.dispatchKeyPressed() consequently reaches the unconditional Escape-close handler in both OneConfig screens before dispatching to Compose. Pressing Escape to cancel a custom recording therefore closes the entire menu. The Escape path needs to honor external capture and allow the control to handle the event.

@Deftu

Deftu commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both review points:

  • Unregistered pages (377b47e): when a visited page is unregistered, it's now removed from the back/forward history, and if it's the current page it's replaced with the Mods page instead of pushing a new entry. Back no longer loops.
  • Escape with external input capture (e97c91b): when a capture registered through KeybindManager.addInputCapture is active, Escape now goes to Compose so the control can handle it, instead of closing the menu.

@Deftu
Deftu requested a review from lunaynx October 21, 2026 11:46
@Deftu
Deftu merged commit 05fd0d8 into v1 Oct 21, 2026
24 checks passed
@Deftu
Deftu deleted the refactor/ui-hardening branch October 21, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants