Tauri Config Ipc
ifer47/markeron
Extend MarkerOn settings, persisted config, or Tauri IPC commands.
How this app stays usable from a TV remote (Android TV / Google TV).
$ npx skills add ventic/ventic --skill tv-remote-ui -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install ventic/ventic tv-remote-ui --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/ventic/ventic.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/tv-remote-ui .claude/skills/tv-remote-ui && 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 "tv-remote-ui" agent skill from https://github.com/ventic/ventic/tree/main/.claude/skills/tv-remote-ui into .claude/skills/tv-remote-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tv-remote-ui", 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/ventic/ventic/tree/main/.claude/skills/tv-remote-uiType 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 ventic/ventic --skill tv-remote-ui -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install ventic/ventic tv-remote-ui --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ventic/ventic.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/tv-remote-ui .agents/skills/tv-remote-ui && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "tv-remote-ui" agent skill from https://github.com/ventic/ventic/tree/main/.claude/skills/tv-remote-ui into .agents/skills/tv-remote-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tv-remote-ui", 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 ventic/ventic --skill tv-remote-ui -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install ventic/ventic tv-remote-ui --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ventic/ventic.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/tv-remote-ui .cursor/skills/tv-remote-ui && 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 "tv-remote-ui" agent skill from https://github.com/ventic/ventic/tree/main/.claude/skills/tv-remote-ui into .cursor/skills/tv-remote-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tv-remote-ui", 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/ventic/ventic.git --path .claude/skills/tv-remote-ui--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 ventic/ventic --skill tv-remote-ui -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install ventic/ventic tv-remote-ui --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ventic/ventic.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/tv-remote-ui .gemini/skills/tv-remote-ui && 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 "tv-remote-ui" agent skill from https://github.com/ventic/ventic/tree/main/.claude/skills/tv-remote-ui into .gemini/skills/tv-remote-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tv-remote-ui", 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 ventic/ventic tv-remote-uiInstalls 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 ventic/ventic --skill tv-remote-ui -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/ventic/ventic.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/tv-remote-ui .github/skills/tv-remote-ui && 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 "tv-remote-ui" agent skill from https://github.com/ventic/ventic/tree/main/.claude/skills/tv-remote-ui into .github/skills/tv-remote-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tv-remote-ui", 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 ventic/ventic --skill tv-remote-ui -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install ventic/ventic tv-remote-ui --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ventic/ventic.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/tv-remote-ui .opencode/skills/tv-remote-ui && 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 "tv-remote-ui" agent skill from https://github.com/ventic/ventic/tree/main/.claude/skills/tv-remote-ui into .opencode/skills/tv-remote-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tv-remote-ui", 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.
tv-remote-uiHow this app stays usable from a TV remote (Android TV / Google TV).
Tv Remote UI is an agent skill from ventic/ventic. How this app stays usable from a TV remote (Android TV / Google TV). Read before adding or changing any interactive UI — a new page, dialog, control bar, list, card, or overlay — and before touching plugins/dpad.client.ts, utils/dpad.ts, or the Android manifest/activity. Triggers on "remote", "d-pad", "dpad", "Android TV", "smart TV", "leanback", "focus", "keyboard navigation", "arrow keys", "back button".
Its SKILL.md is about 4.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Frontend & Design, covering Accessibility. It works with Android, Tauri, Nuxt and Vue.js. The repository describes itself as: A media library and BitTorrent player for desktop and Android - phone, tablet and TV. Streams torrents into a real embedded mpv window. Built with Nuxt and Tauri. Ships with no… The licence is MIT.
7 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 393d796. 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.
Shell commands in SKILL.md call:
bunFrom 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.
Tv Remote UI loads about 4.2k tokens when it runs. Until then it costs about 106 tokens; SKILL.md has 2,582 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 ventic/ventic at commit 393d796, republished under its MIT licence (© ventic). 2,582 words, ~4,177 tokens.
.claude/skills/tv-remote-ui/SKILL.md (or your agent's skills folder).Ventic runs on desktop and on Android TV, where the only input is a d-pad: up, down, left, right, OK, back. No pointer, no hover, no text entry worth having. Anything you add has to be reachable and legible with those six keys.
app/plugins/dpad.client.ts is the whole mechanism, and it is generic — new
pages get remote support for free as long as they follow the rules below.
pickDirection() in app/utils/dpad.ts. Sideways drift is measured
between the boxes' edges, not their centres — boxes that face each other
at all count as level, or a 44px nav link never reaches the 361px poster
beside it. Anything level like that beats everything that isn't, however much
closer: a run along the toolbar has to reach Downloads 264px away rather than
fall into the grid 74px below. Drift costs double only among equals, which is
what keeps a grid walking straight down its column.clipped() first: each edge pulled inside every scroller it sits in,
collapsing onto the edge it went past rather than being dropped. Without it a
poster three rows above the fold still measured level with the toolbar — right
off the search box landed on it instead of on Settings, and up out of the
drawer landed on one 350px above the screen, after which every further press
was measured from somewhere nobody could see. Collapsing and not dropping is
what keeps a horizontal row walking: its next card is off the right edge by
design, and sat on that edge it is still the nearest thing that way. It is
handed a real DOMRect, whose edges are prototype getters — destructure it,
never spread it, or every edge is NaN and the page answers no arrow at all.scroller(), which nudge shares). A title
page's synopsis is prose, so nothing above the Play row is a target and up
escaped straight to the toolbar — leaving the title, poster and overview
unreachable for the rest of the visit, there being nothing to move to that
would bring them back.heading holds
where each scroller is going and step() eases it there on a frame loop,
because the platform's animation cannot be retargeted: scrollTo with a
smooth behaviour cancels whatever is running and eases in from a standstill,
so a press landing mid-scroll stopped the page dead and started again —
measured on the set at eight presses 120ms apart, the page crawled at 5px a
frame for three seconds and then covered 2000px in half of one, once the
presses stopped. Easing toward a moving target has no standstill in it, and
costs nothing extra: the same per-frame durations as the platform's, at 60fps.
The loop stops when it arrives, and lets go the moment a wheel or a finger
moves the scroller instead.scrollIntoView
is no better — its nearest is measured from where the scroll has got to, so
it asks for the whole remaining distance plus a row and each press of a
burst aims further than the last. show() works the destination out itself
from the resting geometry; move() measures candidates the same way, because
mid-flight every row below the fold clips onto the same edge and only document
order tells them apart, which walked the grid backwards; and focusFirst()
likewise, or it lands on a row already on its way off the screen. A heading is
clamped to where the scroller can stop, and re-clamped every frame — an
unreachable one is answered with for ever, and every later press is then
judged against a page that does not exist. nudge steps on from where the
scroller is heading, and keeps focus unless the element it was on really did
scroll out of view.document in the bubble phase and bails on
e.defaultPrevented, so any component that already handles arrows keeps them:
Vuetify sliders, lists, selects, and the player's seek keys.[role="slider"] (whose
value is left/right, and which otherwise changes as you try to leave), up/down
off the first/last item of a .v-list (which otherwise wraps, so the drawer
cycles for ever instead of letting go), and left/right off the ends of a
.v-slide-group — chip groups and tabs wrap the same way, which is why the
category chips circled instead of reaching the genre filter beside them.tabindex="-1" opts out. Vuetify's lists rove the tabindex across
their items and park -2 on the ones that aren't current — every drawer link
carries it, and they are all real targets. A wrapper that merely contains
other targets (the .v-list div is focusable itself) is not one.nudge()),
then focus is dropped so the next press lands on the freshly revealed row.
This is what makes lazily-mounted content (v-lazy rows) reachable at all.<a>/<button> itself.
Vuetify's list items and chips handle Enter/Space too.window.__tvHold(), which fires a contextmenu on whatever
has focus — the same event a right-click and a finger held on a card produce,
so a card answers all three with one handler (MediaMenu). The press itself
is MainActivity's to hold back: the WebView clicks a focused link on the
key's way down (measured on the set: click 8ms after keydown, 600ms
before keyup), so an OK the WebView had seen would already have opened the
card. The first DOWN is kept, a repeat of it (about half a second in) is the
hold, and a release with no repeat replays the kept DOWN — the click lands on
release, as a tap's does. A hold nothing claims is swallowed, not clicked.back() in the plugin: close the top dialog, else let the page
claim Escape (the player's menus, then leaving playback), else router.back(),
else return false — at which point the app goes behind whatever is next
(backgroundApp(), the bridge's leave()). On Android it arrives through
tauri's back-button plugin event, which the plugin registers a listener
for at boot, and that registration is the whole fix for "BACK left the film
instead of closing the subtitle panel": tauri's own AppPlugin sits above
MainActivity on the back dispatcher and, with no listener, pops the
WebView's history itself whenever there is any — so the page was only ever
asked at the root. MainActivity's backToPage (which calls
window.__tvBack()) is now the fallback for a press that lands before the
page has loaded. Three things still have to be true for any of that to run.
handleBackNavigation must be
false — WryActivity.setWebView otherwise adds a callback of its own
that does nothing but webView.goBack(), and it wins, because the dispatcher
runs the last callback added and ours goes on in onCreate while wry's goes on
when the webview is created. That is a silent failure with a plausible face:
popping a history entry looks exactly like a page-level back until something is
open in front of it, and at the root canGoBack() is false, so the one case
that ever reached __tvBack was the one that backgrounds the app. And the
override has to sit on
OnBackPressedDispatcher, not on onKeyDown. An app targeting API 35+ gets
predictive back, where BACK arrives through OnBackInvokedDispatcher and
onKeyDown is never called at all; the manifest declares
enableOnBackInvokedCallback so 33 and 34 take that path too, and the
dispatcher is what both mechanisms feed. Catching the keycode instead is what
made Android 15 phones close the app out of dialogs and out of a film.canGoBack() is true, and it sits below the
activity in the view hierarchy — super.dispatchKeyEvent walks the views
before onBackPressedDispatcher is ever consulted. So turning wry's callback
off was only half the job: the key was still spent on a history pop, and
__tvBack ran only at the root, where there is no history to pop. Measured on
the box: BACK on a film with the subtitle panel open left the film and never
closed the panel, and no dialog anywhere could be closed with it.
MainActivity therefore takes KEYCODE_BACK in dispatchKeyEvent and hands
it to onBackPressedDispatcher itself. Predictive back never calls
dispatchKeyEvent, so that is the older path being routed to the same
callback, not a second rule.ProcessLifecycleOwner observer that ignores being added
twice), so the next launch built a fresh activity onto an event loop whose
webview was already gone — and aborted. moveTaskToBack(true) avoids all of
it; onDestroy kills the process when the activity is genuinely finishing, so
a real close is always followed by a cold start. bun run check:dpad holds
both ends of this.<html> carries .dpad, added on the first
arrow key and removed on the first mouse move. The ring lives in the
vuetify-final layer in assets/css/layers.css.Run bun run check:dpad after touching the geometry.
<a href> or a <button>. Not a div with
a click handler — that is unreachable by remote. A custom control that must
be a div needs tabindex="0", a role, and its own Enter/Space handling.@mouseenter needs a matching
@focus, hover: needs focus-visible: or group-focus-within:. A remote
never hovers, so hover-only affordances are invisible on a TV.tabindex="-1". Buttons layered on
top of a card (favourite, watched, ⋮) would otherwise swallow a d-pad moving
across the grid. The card is the target; the extras are for pointers.data-dpad-start; focus lands on the first thing inside it after a
navigation. Put the primary action (Play, the first card) early in the DOM.inert under a transparent
<button> — TvField, which every <v-text-field> in the app sits inside
(check:dpad holds that), and OK on that button is what focuses it. It has
to be a button: the WebView drops OK for an <input> it thinks can't be
edited, and Android only raises the keyboard for a focus that happens inside
a real press — nextTick after the click still counts, a timer seconds later
does not. router.afterEach leaves focus alone while a caret is in play, or
the debounced search navigation snatches it away mid-word.text-body-medium, targets no
smaller than ~40px, and never signal state with colour alone at that
distance..v-overlay--active, tooltips excepted). A hand-rolled overlay has to
either be one, or accept that the d-pad will walk out of it.gen/android/app/src/main/AndroidManifest.xml declares
android.software.leanback and android.hardware.touchscreen as
required="false" (one APK for phones and TVs), the LEANBACK_LAUNCHER
category, and android:banner — a TV launcher shows a blank tile without one
(res/drawable-xhdpi/tv_banner.png, 320×180).gen/android is committed, so edits there survive; regenerating the project
will clobber them, so re-apply the BACK callback and the
handleBackNavigation = false beside it, the OK forward and hold
(dispatchKeyEvent → window.__tvOk, window.__tvHold), the VenticScreen JS interface
(fullscreen, orientation, metered network, tv()), the wide viewport
settings, mediaPlaybackRequiresUserGesture = false, Downloads.kt with its
onResume/onPause/onDestroy hooks, the process kill in onDestroy, the
windowBackground in res/values*/themes.xml, and the manifest lines — the
leanback ones, enableOnBackInvokedCallback, the service and its permissions —
if that ever happens. bun run check:android-downloads fails loudly when the
download half is gone, and check:dpad when the BACK half is.lg: rule the desktop layout is built on, and at twice
the size anything wants to be across a room. plugins/tv.client.ts asks for a
1280 viewport when isTv() (UiModeManager, through the bridge — a TV's user
agent is a phone's), which needs useWideViewPort and
loadWithOverviewMode in MainActivity: the first lets the page ask for more
width than the screen has, the second scales it down to fit. With only the
first, the right quarter of the layout is off the side of the screen. It also
puts .tv on <html> for anything that follows from being a TV rather than
from a width.<input> behind a
Vuetify select — and it claims the key before the activity's onKeyDown runs,
so the forward has to sit in dispatchKeyEvent. window.__tvOk opens what it
can and answers false for everything else, and the key is passed on either
way. Vuetify opens a select on mousedown, not on click().<video>, not mpv (app/utils/htmlvideo.ts),
so the player's bars stack in CSS rather than being punched out of a native
window. A control added to MpvPlayer.vue works on both without extra work;
anything reaching for player_ipc directly does not.hover: none and means it, but the webview still fires a
pointerenter at whatever appears under its idea of where a pointer last was
— the middle of the screen. One phantom enter with no leave behind it pinned
the player's chrome open for a whole film, so anything that lets hover hold UI
up has to check it can hover at all (hoverable in MpvPlayer.vue).barTransport), not in the middle
of the picture where a thumb would want it: a remote arrives at the bar
anyway, and the centre cluster is up the longest of anything on screen.pickDirection is ~30 lines and the
polyfills are far larger than what a poster grid needs.© ventic, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/tv-remote-ui of ventic/ventic.
Open the folder on GitHubat commit 393d796
Tv Remote UI 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 |
|---|---|---|---|---|---|---|
| Tv Remote UI this skillventic/ventic | 174 | — | ~4.2k | Automated safety check: Pass | MIT | |
| Tauri Config Ipcifer47/markeron | 1.2k | — | ~1k | Automated safety check: Pass | MIT | |
| Rgsm Gui Debugmcthesw/game-save-manager | 1.1k | — | ~267 | Automated safety check: Pass | AGPL-3.0 | |
| Create Docsvictorgarciaesgi/nuxt-typed-router | 413 | 2 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Cross Platform Tauri UIifer47/markeron | 1.2k | — | ~2k | Automated safety check: Pass | MIT | |
| Worklog Designregisx001/Worklog | 258 | — | ~3.3k | Automated safety check: Pass | MIT |
ifer47/markeron
Extend MarkerOn settings, persisted config, or Tauri IPC commands.
mcthesw/game-save-manager
Debug the RGSM frontend against the real Rust backend in a normal browser, without Tauri IPC or WebView tooling.
victorgarciaesgi/nuxt-typed-router
Create complete documentation sites for projects. An agent skill from victorgarciaesgi/nuxt-typed-router.
ifer47/markeron
Cross-platform UI styling for MarkerOn Tauri app (macOS WKWebView vs Windows WebView2).
regisx001/Worklog
Design and UI skill for the Worklog desktop project manager.
drewnekota/cetus
A skill your agent uses when building any web page, HTML artifact, landing page, dashboard, slide deck, report, email, or UI component the user will look at.
Categories
How this app stays usable from a TV remote (Android TV / Google TV). Tv Remote UI is an agent skill from ventic/ventic. How this app stays usable from a TV remote (Android TV / Google TV).
Tv Remote UI fits situations like: keyboard navigation; tasks that involve Accessibility.
Run `npx skills add ventic/ventic --skill tv-remote-ui -a claude-code`. Or copy the skill folder (.claude/skills/tv-remote-ui in ventic/ventic) into .claude/skills/tv-remote-ui in your project. Claude Code loads it when a task matches its description.
Run `npx skills add ventic/ventic --skill tv-remote-ui -a codex`. Or copy the skill folder (.claude/skills/tv-remote-ui in ventic/ventic) into .agents/skills/tv-remote-ui 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 ventic/ventic --skill tv-remote-ui -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/tv-remote-ui, .gemini/skills/tv-remote-ui, .github/skills/tv-remote-ui and .opencode/skills/tv-remote-ui in your project.
Going by SKILL.md and its folder, Tv Remote UI needs the command-line tools its instructions call (bun).
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.
Tv Remote UI is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.2k tokens (SKILL.md is roughly 17k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Tv Remote UI: Tauri Config Ipc (ifer47/markeron, 1.2k stars), Rgsm Gui Debug (mcthesw/game-save-manager, 1.1k stars), Create Docs (victorgarciaesgi/nuxt-typed-router, 413 stars) and Cross Platform Tauri UI (ifer47/markeron, 1.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
ventic (a GitHub organization) maintains it in ventic/ventic, which has 174 GitHub stars. The repository was last updated on October 4, 2026.
Source: ventic/ventic on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.