Compose Multiplatform Patterns
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
Intercept back gestures and run Predictive Back animations using the NavigationEvent (androidx.navigationevent) library in Compose Android.
$ npx skills add rosuH/EasyWatermark --skill navigation-event -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install rosuH/EasyWatermark navigation-event --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/navigation-event .claude/skills/navigation-event && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "navigation-event" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-event into .claude/skills/navigation-event/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "navigation-event", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-eventType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add rosuH/EasyWatermark --skill navigation-event -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install rosuH/EasyWatermark navigation-event --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/navigation-event .agents/skills/navigation-event && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "navigation-event" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-event into .agents/skills/navigation-event/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "navigation-event", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add rosuH/EasyWatermark --skill navigation-event -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install rosuH/EasyWatermark navigation-event --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/navigation-event .cursor/skills/navigation-event && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "navigation-event" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-event into .cursor/skills/navigation-event/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "navigation-event", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/rosuH/EasyWatermark.git --path skills/navigation-event--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add rosuH/EasyWatermark --skill navigation-event -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install rosuH/EasyWatermark navigation-event --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/navigation-event .gemini/skills/navigation-event && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "navigation-event" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-event into .gemini/skills/navigation-event/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "navigation-event", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install rosuH/EasyWatermark navigation-eventInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add rosuH/EasyWatermark --skill navigation-event -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/navigation-event .github/skills/navigation-event && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "navigation-event" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-event into .github/skills/navigation-event/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "navigation-event", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add rosuH/EasyWatermark --skill navigation-event -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install rosuH/EasyWatermark navigation-event --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/navigation-event .opencode/skills/navigation-event && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "navigation-event" agent skill from https://github.com/rosuH/EasyWatermark/tree/master/skills/navigation-event into .opencode/skills/navigation-event/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "navigation-event", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
navigation-eventIntercept back gestures and run Predictive Back animations using the NavigationEvent (androidx.navigationevent) library in Compose Android.
Navigation Event is an agent skill from rosuH/EasyWatermark. Intercept back gestures and run Predictive Back animations using the NavigationEvent (androidx.navigationevent) library in Compose Android. Handles Activity setup, parent-child dispatcher scoping in ViewPagers or tabs, Compose NavigationBackHandler, and migration from legacy BackHandler on SDK 36+.
Its SKILL.md is about 3.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 9 other files, including reference files (for example `references/android/guide/navigation/navigation-event/dispatcher.md`, `references/android/guide/navigation/navigation-event/handle-back.md` and `references/android/guide/navigation/navigation-event/index.md`).
It sits in Mobile. It works with Android. The repository describes itself as: 🔒 🖼 Securely, easily add a watermark to your sensitive photos. 安全、简单地为你的敏感照片添加水印,防止被人泄露、利用. The licence is MIT.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 61223db. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Navigation Event loads about 3.3k tokens when it runs, and up to ~7.5k if it reads all its reference files. Until then it costs about 81 tokens; SKILL.md has 1,033 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from rosuH/EasyWatermark at commit 61223db, republished under its MIT licence (© rosuH). 1,033 words, ~3,276 tokens.
.claude/skills/navigation-event/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.36 or higher in build.gradle.kts.ComponentActivity automatically implements NavigationEventDispatcherOwner out-of-the-box. You must use the built-in navigationEventDispatcher without creating anonymous delegate owners or overriding member properties.Dialog, ModalBottomSheet, ComponentDialog) automatically provide a NavigationEventDispatcherOwner. You don't need manual CompositionLocalProvider propagation for dialogs.ViewPagers, tabbed interfaces, or nested navigation containers in Compose, use rememberNavigationEventDispatcherOwner() to create a child owner linked to the parent. Disabling the owner (enabled = false) automatically cascades to disable all child handlers.NavigationEventState and handlers is strictly enforced. Never bind the same NavigationEventState to multiple active NavigationBackHandler instances (IllegalArgumentException).To complete this step, you MUST ensure the following:
compileSdk is lower than 36, set it to 36 or higher in build.gradle.kts.ViewPagers, tabbed interfaces, or nested navigation hosts that require back gesture interception or parent-child dispatcher linking.OnBackPressedCallback, BackHandler, onBackPresser) to NavigationEvent and NavigationBackHandler.NavigationEvent.To complete this step, you MUST ensure the following:
To complete this step, you MUST ensure the following:
ComponentActivity or ComponentDialog owner resolution.To complete this step, you MUST ensure the following:
[!WARNING] Warning: Compose APIs perform teardown automatically. When using Compose APIs such as
NavigationBackHandlerandrememberNavigationEventDispatcherOwner(), handler removal and dispatcher disposal occur automatically when the composable leaves the composition.
You MUST perform explicit manual cleanup only when managing custom dispatchers or non-Compose handlers:
remove() on active handlers during teardown.isEnabled = false to temporarily disable navigation subtrees.dispose() on dispatcher instances when hosting components are destroyed. Disposing a parent dispatcher automatically cascades to all child dispatchers.ComponentActivity implements NavigationEventDispatcherOwner automatically
out-of-the-box. Don't override navigationEventDispatcher or wrap it in an
anonymous delegate owner.
Why this is RIGHT : Compose apps use ComponentActivity as the host.
LocalNavigationEventDispatcherOwner.current automatically resolves the
Activity's built-in dispatcher.
// RIGHT
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationContent()
}
}
}<br />
Why this is WRONG : Implementing NavigationEventDispatcherOwner directly on
MainActivity and overriding navigationEventDispatcher with a new instance
shadows the library's extension property, causing a recursive infinite loop
crash on launch (StackOverflowError). Creating redundant anonymous delegate
owners (object : NavigationEventDispatcherOwner) is unnecessary.
// WRONG
class MainActivity : ComponentActivity(), NavigationEventDispatcherOwner {
override val navigationEventDispatcher = NavigationEventDispatcher() // Shadow loop crash
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationContent()
}
}
}<br />
Floating windows (Compose Dialog, ModalBottomSheet, and any window backed by
ComponentDialog) automatically provide a NavigationEventDispatcherOwner.
Don't manually re-provide LocalNavigationEventDispatcherOwner using
CompositionLocalProvider inside dialogs.
Why this is RIGHT : ComponentDialog handles navigation dispatchers
automatically. Compose Dialog components resolve their dispatcher owner
out-of-the-box without manual propagation.
// RIGHT
@Composable
fun MyDialog(onDismiss: () -> Unit) {
Dialog(onDismissRequest = onDismiss) {
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
state = navigationState,
onBackCompleted = onDismiss
)
}
}<br />
Why this is WRONG : Wrapping dialog content in a manual
CompositionLocalProvider creates redundant boilerplate and obscures the
automatic dispatcher resolution provided by ComponentDialog.
// WRONG
@Composable
fun MyDialog(onDismiss: () -> Unit) {
val dispatcherOwner = LocalNavigationEventDispatcherOwner.current!!
Dialog(onDismissRequest = onDismiss) {
// Redundant: ComponentDialog provides NavigationEventDispatcherOwner automatically
CompositionLocalProvider( LocalNavigationEventDispatcherOwner provides dispatcherOwner) {
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
state = navigationState,
onBackCompleted = onDismiss
)
}
}
}<br />
ViewPagers and nested navigation)When managing nested UI hierarchies such as ViewPagers, tabbed
interfaces, or custom navigation containers in Compose, use
rememberNavigationEventDispatcherOwner() to create a child owner linked to the
composition hierarchy. Setting enabled = false on the child owner
automatically disables its dispatcher and all registered child handlers.
Why this is RIGHT : Using rememberNavigationEventDispatcherOwner(enabled = isSelected) creates a scoped child dispatcher linked to the parent from
LocalNavigationEventDispatcherOwner.current. Providing it using
CompositionLocalProvider ensures non-visible tabs or pages automatically stop
intercepting back gestures without leaking handlers.
// RIGHT: Scoping child navigation in a ViewPager or Tab interface
@Composable
fun TabPage(isSelected: Boolean) {
val childOwner = rememberNavigationEventDispatcherOwner(enabled = isSelected)
CompositionLocalProvider(LocalNavigationEventDispatcherOwner provides childOwner) {
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
state = navigationState,
onBackCompleted = { /* Handle page back navigation */ }
)
// Page content
}
}<br />
Why this is WRONG : Creating unlinked standalone dispatchers, instantiating
raw dispatchers without remembering them across recompositions, or attempting to
use non-existent methods like .addChild() breaks hierarchy routing and leaves
child handlers active even when the page is inactive.
// WRONG
@Composable
fun TabPage(isSelected: Boolean) {
val parentDispatcher = LocalNavigationEventDispatcherOwner.current?.navigationEventDispatcher
val childDispatcher = NavigationEventDispatcher() // Unlinked and not remembered across recompositions
// WRONG: Method does not exist
parentDispatcher?.addChild(childDispatcher)
}<br />
You must not bind the same NavigationEventState to multiple active
NavigationBackHandler instances, as this throws an IllegalArgumentException
at runtime. To handle conditional workflows (such as checking for unsaved
changes versus navigating back immediately), you must register a single unified
handler and branch logic inside onBackCompleted.
Why this is RIGHT : Using a single NavigationBackHandler with internal
branching logic inside onBackCompleted maintains a strict 1:1 mapping between
NavigationEventState and the handler, preventing state collisions.
// RIGHT
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
state = navigationState,
isBackEnabled = true,
onBackCompleted = {
if (hasUnsavedChanges) {
showDiscardDialog()
} else {
onNavigateUp()
}
}
)<br />
Why this is WRONG : Attaching multiple NavigationBackHandler composables to
the same navigationState instance attempts to bind duplicate handlers to a
single state object, which throws an IllegalArgumentException at runtime.
// WRONG
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
state = navigationState,
isBackEnabled = hasUnsavedChanges,
onBackCompleted = { /* Discard changes */ }
)
NavigationBackHandler(
state = navigationState,
isBackEnabled = !hasUnsavedChanges,
onBackCompleted = { /* Navigate up */ }
)<br />
For Compose Android targets:
36 or higher? (If compile SDK is lower than 36, set it to 36 or higher in build.gradle.kts).android:enableOnBackInvokedCallback NOT explicitly set to "false" in AndroidManifest.xml? (On API 36+, it defaults to "true"; on API 33--35, ensure it is set to "true").ComponentActivity dispatcher owner without redundant anonymous delegate wrapping?ComponentDialog dispatcher resolution without redundant CompositionLocalProvider wrapping?rememberNavigationEventDispatcherOwner() when managing nested hierarchies?NavigationBackHandler to avoid duplicate registration (IllegalArgumentException)?BackHandler usages migrated to NavigationBackHandler with predictive progress support?© rosuH, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 4 other files (references) in skills/navigation-event of rosuH/EasyWatermark.
Open the folder on GitHubat commit 61223db
We found 3 copies of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in rosuH/EasyWatermark, which our catalogue first saw on October 7, 2026.
Navigation Event next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Navigation Event this skillrosuH/EasyWatermark | 1.9k | 1 repos | ~3.3k | Automated safety check: Pass | MIT | |
| Compose Multiplatform Patternsmonta-app/ocpp-emulator | 180 | 5 repos | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Phone HarnessShawnPana/phone-harness | 3.2k | — | ~4.3k | Automated safety check: Pass | MIT | |
| Stylesarindamxd/camerax-android | 132 | 4 repos | ~2.3k | Automated safety check: Pass | Apache-2.0 | |
| Verified Emailarindamxd/camerax-android | 132 | 4 repos | ~4.7k | Automated safety check: Pass | Apache-2.0 | |
| Argent Metro Debuggerbbplayer-app/BBPlayer | 1.1k | — | ~3.4k | Automated safety check: Pass | MIT |
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
ShawnPana/phone-harness
Control the user's phone — an iPhone through the Mac's iPhone Mirroring window, an Android over adb, or a rented cloud Android: open apps, tap, type, swipe, read the screen.
arindamxd/camerax-android
A skill your agent uses to integrate the Jetpack Compose Styles API into an Android project.
arindamxd/camerax-android
Provides a complete workflow for implementing verified email retrieval on Android Credential Manager API.
bbplayer-app/BBPlayer
Debug a JS runtime via CDP using argent debugger tools. An agent skill from bbplayer-app/BBPlayer.
afkT/DevUtils
统一 Android XML 资源命名:layout、drawable、mipmap、color、values、id 的前缀与 snakecase 规则;把颜色/圆角/描边/状态编码进文件名(bg、border、textcolor…selector)。
rosuH/EasyWatermark
A skill your agent uses to push frequently-changing Jetpack Compose state reads (scroll position, animation values, drag offsets) out of the Composition phase and down into Layout or Draw using…
rosuH/EasyWatermark
A skill your agent uses to diagnose Jetpack Compose stability problems by enabling and reading the Compose Compiler Reports (classes.txt, composables.txt, composables.csv, module.json).
rosuH/EasyWatermark
A skill your agent uses to generate and measure Jetpack Compose Baseline Profiles end-to-end with the AGP 8.2+ Baseline Profile Generator module and the Macrobenchmark harness.
rosuH/EasyWatermark
A skill your agent uses to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T.
rosuH/EasyWatermark
Analyzes Android codebases to implement ML Kit GenAI Prompt API.
rosuH/EasyWatermark
A skill your agent uses to fix unstable Jetpack Compose types once a stability diagnosis has identified them.
Works with
Categories
Intercept back gestures and run Predictive Back animations using the NavigationEvent (androidx.navigationevent) library in Compose Android. Navigation Event is an agent skill from rosuH/EasyWatermark.navigationevent) library in Compose Android.
Navigation Event fits situations like: mobile work in your project.
Run `npx skills add rosuH/EasyWatermark --skill navigation-event -a claude-code`. Or copy the skill folder (skills/navigation-event in rosuH/EasyWatermark) into .claude/skills/navigation-event in your project. Claude Code loads it when a task matches its description.
Run `npx skills add rosuH/EasyWatermark --skill navigation-event -a codex`. Or copy the skill folder (skills/navigation-event in rosuH/EasyWatermark) into .agents/skills/navigation-event in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add rosuH/EasyWatermark --skill navigation-event -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/navigation-event, .gemini/skills/navigation-event, .github/skills/navigation-event and .opencode/skills/navigation-event in your project.
SKILL.md names no scripts, command-line tools or credentials: Navigation Event is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Navigation Event is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.3k tokens (SKILL.md is roughly 13k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 4.2k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Navigation Event: Compose Multiplatform Patterns (monta-app/ocpp-emulator, 180 stars), Phone Harness (ShawnPana/phone-harness, 3.2k stars), Styles (arindamxd/camerax-android, 132 stars) and Verified Email (arindamxd/camerax-android, 132 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
rosuH (a GitHub user) maintains it in rosuH/EasyWatermark, which has 1,894 GitHub stars. The repository holds 28 skills in this directory. The repository was last updated on October 6, 2026.
Source: rosuH/EasyWatermark on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.