Skip to content

feat(mods): add toggle functionality to core mods - #1165

Open
Tektonikal wants to merge 4 commits into
Polyfrost:v1from
Tektonikal:mod-enabler
Open

Tektonikal wants to merge 4 commits into
Polyfrost:v1from
Tektonikal:mod-enabler

Conversation

@Tektonikal

Copy link
Copy Markdown
Contributor

Description

Adds a switch to mod cards in the OneConfig screen, as well as in the mod configs themselves. Has a few issues on 1.8.9, but outside of that seems to work flawlessly

Checklist

  • I made a clear description of what was changed
  • I stated why these changes were necessary
  • I updated documentation or said what needs to be updated
  • I made sure these changes are backwards compatible
  • This pull request is for one feature/bug fix

@Tektonikal
Tektonikal marked this pull request as ready for review October 7, 2026 03:22
@lunaynx

lunaynx commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

I know this is something people want, but I feel like this will cause more problems than it solves. Mods update, and suddenly the toggle silently only disables some of the mod, or none at all, while pretending it was disabled.

But either way, if this gets added, Polyfrost mods should expose a global master switch themselves rather than us mixing into our own mods. We should expose an API that OneConfig-native mods, and maybe even third party mods that prefer a different configuration library, can use to provide this.

@Tektonikal

Copy link
Copy Markdown
Contributor Author

Ideally this is only temporary, until there is either an API adopted by mods or until we've got a way to enable/disable mods at runtime

@awruff

awruff commented Oct 9, 2026

Copy link
Copy Markdown
Member

I dont think we should mixin into oneconfig mods, we could very easily update them for a more standard api

Comment on lines +123 to +125
val (field, owner) = resolve(type, path) ?: continue
originals += path to field.get(owner)
field.set(owner, convert(value, field.type))

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.

🟠 [P1] Keep temporary masks out of compatibility snapshots

For field-masked configs such as Blur, CompatSnapshots.onProfileSaving() captures these substituted values before the later ModGates listener restores the originals. A local reproduction exported useGradient=true as false; switching profiles also overwrote the destination profile's setting because the restored original was interpreted as an external change. Preserve the original values during compatibility snapshot capture, rather than only blocking the mod's own save method.

Comment on lines +403 to +409
ConfigManager.addProfileChangeListener(object : ConfigManager.ProfileChangeListener {
override fun onProfileSaving(profile: String) {
present.forEach { it.mask?.remove() }
}

override fun onProfileChanged(newProfile: String) {
present.forEach { if (!ModToggles.isEnabled(it.id)) it.mask?.apply() }

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] Restore disabled masks after exporting a profile

Exporting a profile invokes onProfileSaving() without subsequently invoking onProfileChanged(). Consequently, exporting while a masked mod is disabled removes its mask and reactivates its features while its switch still reports off. This was reproduced with ScrollTweaks. Ensure save-only operations restore the mask even when no profile switch follows.

val option = ((entry as? ConfigListEntry.Item)?.node as? SettingNode.Leaf)?.prop
val lock = locked && (option == null || option !== lockExempt)
Box(Modifier.alpha(if (lock) 0.45f else 1f).blockInteraction(lock)) { ConfigListRow(entry) }
}

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] Apply the disabled-options lock to global search

After disabling a masked mod, its options remain editable through global search because SearchResultsScreen calls SettingEntryRow() directly, bypassing this wrapper. Those controls still write through prop.set(...), potentially reactivating features while disabled; ConfigMask.remove() then discards the edits by restoring the pre-disable values. Apply the lock to global-search results too, or enforce it at the shared row/property layer.

Gate("appleskin"),
Gate("detailabreconst"),
Gate("status-effect-bars"),
Gate("waveycapes"),

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] Only register the WaveyCapes gate where its hook exists

This gate is registered on every supported Minecraft version, but its only hook, Mixin_Toggle_WaveyCapes, is compiled and added only for versions >=26.1. On supported 1.21.x builds there is no mask or callback either, so switching WaveyCapes off only changes the card's appearance and leaves the mod running. Restrict registration to supported versions or supply the older-version hook (preferred).

Comment on lines +430 to +432
val toggleRevision = ModToggles.revision
val toggle = remember(mod, toggleRevision) { mod.toggle }
var enabled by remember(toggle, toggleRevision) { mutableStateOf(toggle?.isEnabled() ?: true) }

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] Observe property-backed toggle changes on mod cards

Changing an automatically discovered enabled property does not increment ModToggles.revision, and the card never subscribes to that property's callbacks. An already composed card therefore retains its old switch value and appearance—for example, when global search displays both the card and its Enabled option and the user changes the latter. Observe property changes so the card reflects the current value rather than only its last local click.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants