Documentation
The guide
Everything Termux Launcher does and how to drive it, from the first swipe to the Linux display and the models on your phone.
The eight moves
- Hold a pane corner The corner tab opens; tap ? to see what every control does.
- Hold the apps row Choose your pinned apps.
- Swipe down on the apps row The app drawer; Home brings you back.
- Swipe up on the bottom border The keyboard comes up; swipe down to put it away.
- Swipe up on the space bar The command palette.
- Hold on terminal text Select and copy; in htop or nvim your finger becomes the mouse.
- Hold Ctrl + Alt Every key shows its shortcut.
- Hold a pane border, then drag Move between Terminal, Home and the Linux display.
Start here
Get started
Termux Launcher turns the Termux terminal emulator into your Android home screen. The shell underneath is still upstream Termux (pkg, the repositories and everything you already know keep working), but everything above it, from the dock to the keyboard to the multiplexer, is built for running your phone from a prompt.
svg: home-anatomy
title: The home screen
caption: Status bar, terminal, pinned apps, A-Z row, extra keys and the keyboard: every part of the screen, named.
Choose an edition
There are two editions, plus a legacy one that is deprecated.
| Edition | Android package | Notes |
|---|---|---|
| Termux edition (recommended) | com.termux |
Official Termux package ecosystem with timely package updates. Cannot be installed alongside official Termux. |
| Nix edition | com.termux.launcher.nix |
The full nixpkgs collection with declarative configs and rollbacks. Can be installed alongside official Termux. Releases carry nix- tags (nix-vX.Y.Z from 1.0.0, vX.Y.Z-nix before). arm64-v8a and x86_64. See Nix edition. |
| Demo edition Deprecated | io.vaj.tl |
Its hand-built package repo is no longer updated, so treat it as a demo. Migrate to the Nix edition. arm64-v8a only. |
Download and install
- Download the main APK for your edition from the project’s Releases.
- Optionally, add the matching Termux:API or Termux:Styling. Styling is rarely needed, since fonts and colours are handled in the app.
- Termux edition: Termux:API and Termux:Styling (plain tags).
- Nix edition: TLNix:API and TLNix:Styling (
nix-v*tags).
Keep the set matched. Mixing official add-ons, old forks or APKs signed with a different key breaks the install, because Android rejects shared-UID and signature mismatches. You only need the main APK to try the launcher.
First launch
- The app downloads and unpacks the Termux bootstrap.
- A Before you start card asks for the few permissions the launcher can use. Each row has its own Allow: Wallpaper (colour the launcher after your wallpaper), Weather (shown only while the weather widget or readout is on; its text reads “Shows the weather for a place you pick, or for where you are.”) and Linux display (only in builds that have it). The Weather row has a city search field under its text, the same search as Settings → Status bar → Weather → Location, credited “Search by Open-Meteo.” Pick a place and the row reads “Now:” followed by the place, with no permission asked. A small Use my location button clears the place and runs the Allow flow instead; once granted the row reads Allowed and the search stays available. Tap Continue when you are done. Everything stays changeable later in Settings.
- The tour offers itself: Take the tour or Not now.
The tour plays over the real home screen, one card per gesture, in this order:
- How you will use it: the usage mode (see Launcher mode below).
- Hold a corner of a pane, then tap ? for help on what you are touching.
- Hold the page border and drag to slide to the next place.
- Swipe up on the bottom border to raise the keyboard, down to put it away.
- Swipe down on the top border to open the status bar, up to fold it.
- Hold an empty spot in the apps row to pin apps.
- Swipe down on the apps row to open the app drawer (swipe right on a left rail, left on a right rail).
- Swipe up on the space bar for the command palette.
- After an update only: the offer of the new extra-keys row.
- Use Termux Launcher as your home screen.
- A closing card with tips: holding on the terminal, the Ctrl+Alt shortcut strip, the Appearance corner, minimal mode, dictation,
tlstoreextras with a Copy button for the commands, Linux GUI apps, and Read the docs.
Skip any card if you would rather explore. Replay the tour later from Settings → About & help → Replay tour.
After the tour, an On-device AI card, What runs on this phone, lists what this phone can run locally (voice typing, read aloud, an assistant that tidies dictation, and more) with the downloads each needs. Pick what you want and tap Download selected, keep Wi-Fi only on to wait for Wi-Fi, or tap Later. Reopen it any time from Settings → On-device AI → What runs on this phone. See On-device AI.
Launcher mode
Settings → Launcher mode, the first row under the search box, asks “How will you use it?”:
- Terminal: just the terminal.
- Terminal + Home: apps and widgets.
- Terminal + Home + Display: also run Linux desktop apps.
It is a preset, not a lock. Picking one sets the pinned apps row, the A-Z index, the app drawer, the widget page, the Linux display switch and whether the app stays in Recents. Each of these stays editable on its own page, and once one drifts from the preset the row reads Custom.
Picking Terminal while Termux Launcher is still your home app says so and offers Choose another home app or Keep.
Make it your Home app
Settings → Apps → Set as default launcher opens Android’s default home app screen. Pick Termux Launcher there. Switch back to another launcher at any time from Android’s own settings.
Your first hour
Install a Nerd Font. Prompts, TUIs and the setup below use Nerd Font icons, so do this first. Open the command palette (swipe up on the space bar) and run Terminal fonts, or search Settings for “fonts”, which leads to Theme & fonts → Terminal fonts. Install one from the picker. Details on Terminal fonts.
Shell setup. The store that ships inside the launcher sets up a themed shell whose colours follow your wallpaper. Termux and Demo editions, details on tlstore:
tlstore install fish-shell # the fish shell setup in one go
tlstore install # pick anything else, Claude Code included
Nix edition (com.termux.launcher.nix), after initialising the launcher flake (see Nix edition):
setup-toolkits
A config you already have is never replaced without showing you the change first, and every replaced file gets a timestamped .bak.
Shared storage. Run termux-setup-storage to reach your internal shared storage from the shell.
Where next. Home screen & apps covers the dock, the A-Z row and the drawer; Layout & full screen and Look & themes cover arranging and styling it.
Full details: Get started and Learn the launcher
Everyday
Home screen & apps
The home screen is three places, a dock, an A-Z row, a prompt that launches apps, and an app drawer. Widgets are on their own page: Widgets.
Places
The home screen is three places side by side: Widgets on the left, the Terminal in the middle (where it opens by default) and a Linux Display on the right.
- Move between places: hold the page border (the frame line on the top, bottom, left or right, not a corner) until you feel a tick, then drag sideways. A short drag springs back; a longer one, or a quick flick, lands on the neighbour.
- Jump straight there: the icon beside the clock names the place you are on, and the other two peek in from the status bar’s edges on the side they slide in from. Tap a peeking icon to go there.
- Keyboard: swipe up from the bottom border of any place to raise the keyboard, down to put it away. No hold needed.
- Status bar: swipe down from the top border to open the status bar, up to fold it. See Status bar.
Your session keeps running while you are away, and the terminal never resizes for the others, so nothing reflows in your shells when you move between places. Want every place to take the whole screen? See minimal mode.
The dock
- Pin apps: hold the empty space in the dock to pick your favourites, or manage the whole row from Settings → Apps → Pinned apps (“Choose and reorder dock apps.”). The same row sits under Settings → Apps → Dock.
- Reorder or unpin: hold a pinned icon for its Android shortcuts plus App info, Uninstall, Change app icon, Change dock icon and Unpin, or drag it along the dock to move it.
name: app-icon-menu
crop: 0.13 0.48 0.74 0.32
title: App actions
caption: Long-pressing a docked icon for its shortcuts, app info and icon options.
- Folders: drag one icon onto another to make a folder.
- Custom app icons: for every app, or for the pinned row only. See Look & themes for icon packs.
- Quick reply: when a pinned app has an unread notification, swipe up on its icon to answer right there. The Android keyboard opens with it; send, and you are back at the terminal. It uses the reply field on the app’s notification and needs notification access (see Notifications).
src: assets/docs/figures/quick-reply
formats: webm,mp4
size: 398x340
title: Quick reply
caption: Swiping up on a pinned app with an unread notification and replying without opening the app.
- Most-used apps page: an extra page after the pinned pages with your most-launched apps that are not pinned. Turn it on under Settings → Apps → Dock → Most-used apps page (off by default).
- Double-tap to lock: double-tap the A-Z row to turn the screen off. Choose the method under Settings → Apps → Double-tap to lock, Lock method: Off (the default), Shizuku (sends the power key, so your phone’s own screen-off animation plays and secure lock behaves normally) or Accessibility (the method most launchers use; the screen can flicker as it turns off).
Launching apps
The A-Z row. Tap a letter to see the apps under it; tap an icon to open it.
- Slide along the letters and the icons above filter as you go. Slide up to the icons without lifting your finger, over to the app you want, and let go to launch it.
- Apps are ranked by use, so your most-launched apps end up closest to the letters. Clear the learned ranking, without touching your pins, under Settings → Apps → App search → Reset usage ranking.
- More than one page of results? Hold your finger near the left or right edge and it scrolls.
name: app-row-scrub
crop: 0.13 0.66 0.74 0.20
title: A-Z app row
caption: Sliding across the alphabet row - the icons above filter as you go, and letting go over one launches it.
From the prompt. At an idle shell prompt, type the search prefix (% by default) and a query. The app row shows the results: Enter opens the first, the arrow keys move between them, or tap an icon. Change the prefix under Settings → Apps → App search → App search prefix.
name: app-launching
crop: 0.13 0.50 0.74 0.30
title: Launch from the prompt
caption: Typing "%" and a query at the prompt searches installed apps; Enter opens the first result.
Command palette. Press Ctrl+Alt+Shift+P or swipe up on the space bar. Every installed app is a row, with the same usage ranking. See Palette & shortcuts.
Bind an app to a key. Find the app in the command palette, hold its row, then press the key combination you want (on a hardware keyboard, focus the row and press Ctrl+Alt+Enter). The binding is written to ~/.termux/termux-launcher-bindings.conf and works at once. See Config files.
The app drawer
Swipe down on the pinned apps row for a full-screen app drawer. On a left rail swipe right, and on a right rail swipe left. The gesture is the Swipe down for app drawer switch under Settings → Apps. Settings → Apps → Drawer layout chooses the layout: Vertical, Horizontal pages or Categories. The same page has:
- Open keyboard automatically: start typing a search as soon as the drawer opens.
- Search keyboard: use the Android keyboard, with its suggestions and swipe typing, for the drawer’s search.
- Category sorting and Re-sort apps, below.
image: assets/docs/figures/drawer-vertical.webp
size: 720x794
title: Vertical
layout: trio
image: assets/docs/figures/drawer-pages.webp
size: 720x794
title: Horizontal pages
image: assets/docs/figures/drawer-categories.webp
size: 720x794
title: Categories
caption: The three drawer layouts under Settings → Apps → Drawer layout.
Categories. Established launchers sort apps with server-side lists. Termux Launcher has no server, so Category sorting (“Choose how apps are grouped.”) offers two ways in its Sort apps into categories dialog:
svg: category-sorting
title: How apps get sorted
caption: Two ways to the same result: a local Gemma model, or a prompt you paste into any AI chat.
- On this device: a local Gemma model sorts your apps, and nothing leaves your phone. Which model runs depends on your phone’s tier: E2B on Tier 2, E4B on Tier 3, and the option is unavailable on Tier 1 phones, which lack the memory. You can change the model per function in the Model Centre; see On-device AI.
- Copy a prompt for an AI chat: copies a prompt with your installed-app list. Paste it into any AI chat, then paste the answer back for the same result. This sends your app list to that service.
Installed new apps since the last sort? Re-sort apps shows when it last ran and how many new apps are not sorted yet, and sorts only those. Once six or more are waiting, the drawer itself offers a nudge to sort them.
Landscape
In landscape the dock becomes a vertical rail, on the left by default or on any edge you choose in Layout, and the drawer uses a denser grid. Both stay clear of display cutouts and the system navigation bar.
src: assets/showcase/features/landscape-launcher
title: Landscape launcher
caption: The side dock rail, terminal and dense app drawer adapting to the wider display.
shape: wide
layout: full
Full details: Home screen and apps
Everyday
Widgets
The Widgets page is a grid of home-screen widgets beside the terminal: Android widgets from your apps, and eleven built-in ones drawn in the launcher’s own style.
Turn it on and get there
The page is switched by Home widgets (“Show a widget page beside the terminal.”) under Settings → Apps, in the Home app section. It is on by default, and the Terminal launcher mode turns it off (see Get started).
Widgets is the place to the left of the terminal. To get there:
- Hold the page border (any side, not a corner) until you feel a tick, then drag sideways.
- Or tap the peeking house icon at the edge of the status bar.
- Or bind Go to Widgets to an extra key, a keyboard key or a chord.
The built-in keyboard goes away on Widgets, since nothing there takes typing, and comes back when you return to the terminal.
Add a widget
- Hold an empty spot on the grid. An empty page says “Long-press to add widgets”.
- Choose Add widget. The same menu has Edit widgets and Remove page.
- Pick a widget in the picker. The launcher’s own widgets come first, under Termux Launcher.
Placing on another page. Picker cards stay live even when the page on screen is full. Tapping one with no room says “No room on this page. Hold the widget to place it on another page.” Hold a card to pick the widget up, then rest it at the trailing edge of the page for a moment: the grid turns to the next page, or makes a new one past the last. Let go where you want it. Only a widget bigger than the whole grid is greyed out (“too big for this grid”).
Built-in widgets
Listed first in the picker under Termux Launcher:
- Analog clock and Digital clock
- Agenda and Calendar (a month view)
- Weather, Battery and System (device stats). Weather follows the place picked under Settings → Status bar → Weather → Location as soon as you return, with no tap needed
- Media (what is playing, with controls) and Notifications
- Tasks, a task list kept in
~/notes/tasks.md - Scratchpad, a note kept in
~/notes/scratch.md; a tap opens the file in your editor
A few details:
- The widgets that read your calendar, your notifications or what is playing ask for that access on the card itself. Notifications and Media use the same notification access as the status bar (see Notifications).
- Tasks and Scratchpad are plain Markdown files, so the shell sees exactly what the card shows.
- The clocks and the two file widgets take settings (time zones, the file path) from a cog that appears while you are editing the page.
Built-in widget style under Settings → Apps chooses how they are drawn: Tonal (soft cards with rounded corners) or Pane (squared cards with a hairline rim, like a terminal pane).
Move and resize
- Hold a widget, then drag it to move it, or drag its edges to resize it.
- Drop it on top of others and they slide aside into free space. With no room for them, the widget takes the nearest free spot instead.
- Swipe inside the grid to reach the page’s other pages.
The corner tab
Hold a corner of the Widgets page for its tab:
- Edit widgets (pencil): every widget on the page is outlined, and a tap on one picks it up to move or resize.
- Add page (+).
- Appearance, the minimal mode button and ? for help, as on every place.
Grid size
While you are editing, the corner tab reads the grid’s size as columns × rows. Tap it for Columns and Rows wheels; the widgets rearrange as you turn them. Tap the tick (Keep changes) or the cross (Discard changes).
The grid can also be sized in the Layout editor: on Home, a round handle on the corner of the first cell resizes the grid in whole cells, separately for portrait and landscape. See Layout & full screen.
The grid is capped at what the screen fits. Widgets that no longer fit a smaller grid move to free space or a new page rather than being dropped.
Full details: Widgets
Everyday
Status bar
The status bar is the launcher’s own bar: a clock, the row of sessions and windows, and readings for CPU, memory and weather. Its settings are under Settings → Status bar. Pinned notifications in the bar are on Notifications.
svg: status-bar
title: Folded and open
caption: Tap the bar to open it: the clock and date sit above the session row and the CPU, memory and weather readings.
Open and fold it
- Swipe down from the top border of the page to open the bar with your finger; swipe up to fold it back to its slim form. Let go past about a third of the way, or flick, and it finishes on its own.
- A drag across the bar itself folds and unfolds it too.
- A swipe that starts in Android’s own strip at the very top of the screen still pulls down the notification shade.
The top-border swipe works on every place, minimal mode included, while the bar stands along the top. With the bar on another edge or hidden, the top border only pages.
The status row
The row under the clock belongs to the place on screen. On the terminal:
- Sessions badge: the numbered badge on the left. Tap it to open the sessions panel. See Panes & sessions.
- Window chips: one per window. Tap one to switch to it, and swipe along the chips to scroll them. Tap
+to open a new window. - CPU, memory and weather chips: tap one for its detail card: per-core load and top processes, memory use, or the forecast with an Open-Meteo credit. CPU and memory describe the whole device, not only the shell.
- Mouse mode: a small mouse at the end of the readings means mouse mode is on. Tap it to turn mouse mode off. See Terminal features.
On the Display place the row lists the apps open on the display, and a tap brings one forward. On Widgets it holds only the readings and the weather.
Place icons. The icon beside the clock names the place you are on: a house for Home, a prompt for Terminal, a screen for Display. The other two peek in from the bar’s edges; tap one to go there.
What a window chip tells you
A chip shows one short item: the open file in an editor, the running process, or the directory of an idle shell. A mark in the icon’s place tells you what the window is doing:
- a ring while its foreground process is busy;
- a bell once it rang the terminal bell or asked for attention;
- a tick or a cross once a command finished while you were not looking.
A background window that rings gets a pulsing rim until you focus it.
Agent status. When a coding agent runs in a window, a small dot sits in front of the chip’s label: the accent colour while it is Working, a warm colour when it Needs you (a permission prompt or a question), and a muted colour when it is Idle. The Sessions drawer shows the same state word after each pane’s name. The launcher recognises common agents (Claude Code, Codex, opencode, Gemini, aider and others) from the screen; for exact states, launcherctl agent install-hooks writes Claude Code hooks that report them with launcherctl agent working|blocked|idle|clear.
Clock
- Face and alignment: open Appearance → Look, choose Custom, tap the status bar and tap Clock. Faces: Flip, LCD, Minimal, LED matrix, Tape and Slab; alignment Left, Center or Right. See Look & themes.
- 12-hour time (“Show AM and PM.”): Settings → Status bar → Clock. Off by default.
- In the open bar, tap the clock to open Android’s clock app, or the cog to open Settings.
Readings and weather
Under Settings → Status bar:
- CPU usage and Memory usage, under Indicators. Both off by default.
- Weather, on by default.
- Location: tap it to search for a city (“Search for a city”), or choose Use device location. While a city is set, the launcher asks for no location permission, and a change refetches the weather straight away. With device location, weather needs location permission.
- Fahrenheit (“Show °F instead of °C.”).
On any edge
The status bar does not have to sit at the top. In the Layout editor (Layout & full screen) drag it to any edge, separately for portrait and landscape:
- Bottom: it grows upward from the dock, clock at its foot and window chips along its upper edge.
- Left or right: a narrow column with the place badge, one chip per window and the readings stacked down it.
- Hidden: the clock, weather and window chips go with it and the content takes its room. This is how a full-screen layout is built.
Full details: Status bar
Everyday
Notifications
Android’s shade pulls you away from what you are doing. The launcher can pin the notifications you actually wait for (a code, a reply, a build result) in the status bar above the prompt, show dots on docked apps, and keep a history your scripts can read. The rest of the bar is on Status bar.
Nothing is pinned by default. You choose what qualifies by writing rules; with no rules, nothing pins and the clock keeps its full size.
Turn it on
Everything here lives on Settings → Notifications (“Dots, pinned alerts and history”). The same page is linked from Settings → Status bar → More and from Settings → Apps.
- Notification access opens Android’s notification-access screen. Without it the launcher cannot read notifications at all, so dots, pins, media, quick reply and history all need it.
- Pinned notifications (“Pin matching notifications to the top pane. Match an app package, keywords, or both.”) holds your rules.
svg: notification-rule
title: A pinned notification rule
caption: An app package, keywords, or both; matching notifications pin to the top pane.
Pinned notification rules
The add form has two fields and a switch, then Add rule:
- App package (e.g. com.whatsapp): matched exactly against the posting app, ignoring case.
com.whatsapp, notWhatsApp. Leave it blank for any app. - Keywords in title or text: a plain substring, ignoring case, not a pattern.
otpmatches “Your OTP is 481920”. It is tested against the title and the body. Leave it blank for any text. - Dismissing the pin also clears the notification: off by default. Off, swiping the pin away leaves the notification in the shade; on, the notification is cleared too.
At least one field must be filled; a rule with both blank would pin everything, so the form says “Enter an app package, keywords, or both”.
Each saved rule is a card with its own on/off switch, a Clears pill when it clears the notification, and Remove. A rule that is off never matches.
Some rules worth copying:
| Package | Keywords | What it catches |
|---|---|---|
| (blank) | otp |
One-time codes from any app |
com.whatsapp |
(blank) | Every WhatsApp notification |
com.google.android.gm |
invoice |
Only invoice mail |
| (blank) | build failed |
CI results from whichever app reports them |
- The first matching rule wins. Rules are tested in list order, so a narrow rule above a broad one takes precedence.
- Adding the same rule again replaces it. The same package and keywords move to the end of the list, take the new clear setting and come back switched on.
- The list holds 32 rules; past that the form says “Rule list is full”.
Pinned cards on screen
- Each card shows the sender in bold, then the message, with the sender’s avatar and the app’s badge, a tint from the app icon and the age (now, 4m, 2h, 1d). A conversation that folds several messages shows a count.
- Up to 8 matches are kept; past that the oldest is dropped. The bar shows two cards at once; swipe up or down on them to scroll the rest.
- Tap a card to open what the notification points at, as tapping it in the shade would.
- Swipe sideways to dismiss it. “Dismissed” with an undo shows for four seconds. A dismissed pin stays gone while that notification is active, even though the rule still matches; if the app posts it again, it can pin again.
- Hold a card for the whole message with Open, Dismiss and Mute this rule. Mute turns that rule’s switch off.
- TalkBack users get the same actions on each card.
The clock keeps its full face as cards arrive and scales down as one piece to the room left, never below its compact size. A media session shares the space only with a single card. In the open status bar the album art sits next to the play controls, the next button stays clear of the bar’s end so a near miss does not page, and tapping the art or title opens the playing app.
Where the rules live
Rules are stored as a JSON array in the app’s own preferences under essential_notification_rules, defaulting to []. Each entry is {"id":…, "package":…, "match":…, "clear":…, "enabled":…}; an entry without enabled counts as on. A malformed entry is dropped on load rather than breaking the list. There is no shell command or config file for rules yet; the settings page is the only way to edit them.
Notification dots
Settings → Notifications → On the home screen → Notification dots shows a dot on docked apps with an active notification. Off by default.
Notification history for the shell
Settings → Notifications → For the shell → Notification history records notifications into a database under ~/.launcherctl, where commands and agents in Termux can read them:
launcherctl notifications --app … --since 7d
- Nothing is recorded until you check an app in the page’s app list. Only checked apps are saved.
- Keep for: 7 days, 30 days, 90 days or 1 year.
- Mask one-time codes (“Codes are hidden before a notification is saved.”).
- Clear history deletes every saved notification; the checked apps stay checked.
Dots, pins and the widgets do not need history turned on.
Elsewhere
- Quick reply from the dock answers a notification without opening the app; see Home screen & apps.
- The Notifications and Media widgets use the same notification access; see Widgets.
- Programs in the terminal can post their own Android notifications; see Terminal features.
Full details: Notifications
Everyday
Layout & full screen
Layout is where things sit and how much of the screen they get: the status bar, the apps row, the A-Z index, the extra keys and the keyboard. The corner tab and the Layout editor set that; minimal mode and full screen take things away. For what things look like, see Look & themes.
The corner tab
Hold a corner of a pane for its tab. Tap away from it to put it away. What it holds depends on the place:
- Terminal, one pane: Appearance, the minimal mode button, the automatic tiling toggle, Open settings and ? for help on what you are touching.
- Terminal, a split pane: close, move, and maximise or restore that pane.
- Widgets: Edit widgets, Add page, Appearance, minimal mode and ? (see Widgets).
- Display: turn the display on or off, minimal mode, settings, Appearance and ?.
Open the Layout editor
| Layout is a tab of Appearance. Open Appearance from Settings → Appearance, from the corner tab, or from the terminal’s long-press menu, then pick Layout in the **Wallpaper | Look | Layout | Icon pack** pill at the top. |
Home, Terminal and Display share one layout, so whatever you arrange applies to all three. Portrait and landscape each have their own layout; the toggle in the editor switches which one you are arranging.
The Layout editor
The canvas, a picture of your screen, fills the editor. Under it is a sheet of fixed height. On a tablet in landscape the same controls sit in a side pane on the right instead, and the move control stops at the pane’s left edge. The other orientation is drawn in your device’s real shape, so a tablet is not shown as a phone outline.
- Row A: the Portrait/Landscape toggle, Style: Docked / Floating, and the eye-off Hidden elements button.
- Row B: Corners and Margin, or the keyboard’s own tools while the keyboard is selected.
- Undo and Done sit in the top bar.
Move a bar. Press the status bar, the apps row, the A-Z index or the extra keys anywhere and drag it to any of the four edges. Dropping it between two others on the same edge sets their order. Or tap a bar to select it: it gets an outline and a four-arrow Move button whose menu lists the edges and Hide.
Under the keyboard. The apps row, the A-Z index or the extra keys can drop into the slot under the keyboard. They then stay at the bottom of the screen and the keyboard opens above them. The status bar always stands over the keyboard.
Hide and restore. Drop a bar on the eye-off button to hide it; the button shows how many are hidden. Tap eye-off to see hidden elements as tiles with a restore arrow: tap one to put it back on the edge it left, or drag it onto the canvas.
The A-Z index rides the pinned apps row while they share an edge; on another edge it stands on its own. It is either on an edge or hidden.
The status bar can go on any edge. At the bottom it grows upward from the dock; on a side it becomes a column of place badge, window chips and readings. Hide it to give the content its room: that is how a full-screen layout is built. See Status bar.
Heights, by handle. Each is set per orientation by dragging on the canvas; a readout shows the value while you hold:
- the dock’s inner edge sets Dock height;
- the keyboard’s top edge sets Keyboard height;
- the bottom of the keys sets Bottom padding.
The keyboard. Select it and three Keyboard type chips appear (docked, floating, split), with Key radius under them in Row B, set per orientation. To turn the built-in keyboard off, drag it onto eye-off; its tile in the hidden row turns it back on. This one switch covers every place and both orientations, and is the same as Keyboard on/off in the command palette and on the extra-keys row. See Keyboard.
The widget grid (Home only): a round handle on the corner of the first cell resizes the grid in whole cells, per orientation. See Widgets.
Style. Docked joins the bars into one flush glass frame with the pane as a rounded insert. Floating gives each bar its own card. Corners and Margin apply in both styles.
On the canvas the apps row shows seven placeholder icons; the extra keys show your real keys. Every bar and handle is also reachable with TalkBack, with move, hide, show and resize actions.
Done keeps your changes and Undo steps back. Pressing Back with unsaved changes offers Keep editing or Discard, plus Save when a wallpaper change is pending.
Border gestures
- Hold the page border and drag sideways to move between places.
- Swipe up from the bottom border to open the keyboard, down to close it. This also turns the keyboard back on if you switched it off.
- Swipe down from the top border to open the status bar, up to fold it, while the bar is along the top.
Minimal mode
Minimal mode is a second saved layout. It starts with only the content showing: the status bar, the apps row, the A-Z index, the extra keys and the keyboard go away, and the terminal, widgets or display take the room. Open Layout while minimal mode is on to choose what it keeps.
Everything else works as in your normal layout: a split shows all its panes, and the status bar and the borders answer the same gestures.
- Turn it on or off: hold a corner and tap the four-outward-corners button; the same button, now pointing inward, turns it off. Nothing else does.
- It is one mode for the whole launcher, it survives restarts, and moving between places never turns it off.
- The keyboard goes away when you enter it. Raise it with a swipe up from the bottom border or a tap on the terminal; it goes down again when you move to another place.
Full screen
Settings → Terminal → Full screen (“Hide system bars while using the launcher”) hides Android’s own status and navigation bars.
Pane padding
Settings → Terminal → Extend edge colors (“Match pane padding to nearby terminal colors.”, on by default) fills the space between the text and a pane’s rounded border with the colour of the nearest edge, so a full-screen program reaches the border instead of stopping short of it.
Full details: Layout and full screen
Everyday
Look & themes
Look is what things look like: wallpaper, glass, colours, icons and terminal effects. It lives in two places: the Appearance surface and the Theme & fonts page. For where things sit, see Layout & full screen.
Appearance
| Open Appearance from Settings → Appearance, from a pane’s corner tab, or from the terminal’s long-press menu. It opens on the real home screen with four tabs in a pill at the top: **Wallpaper | Look | Layout | Icon pack**. It opens on Wallpaper, or on the tab you last used if you were there in the past 30 minutes. On a tablet (smallest width 600dp or more) held in landscape, the controls sit in a side pane on the right and the preview fits and centres to its left; the pill stays on top, tall pages scroll inside the pane, and the clock face popup opens inside it. In portrait the controls return to the bottom sheet. Phones are unchanged. |
Home, Terminal and Display share one look, so a change here applies to all three.
One Done, top right, applies everything from every tab at once: the Home wallpaper, the Lock wallpaper, the look and the layout. Pressing Back or Home with a wallpaper not yet applied offers Keep editing, Discard or Save.
Wallpaper
- Two cards, Lock screen and Home screen. The Lock card can be Same as Home.
- Choose photo picks an image, previewed before you apply it. Your last five photos stay on the tab for a quick switch. While another app set the current wallpaper, a small grey line under it reads “Set your wallpaper here to see fancier glass.” It goes away once you apply a wallpaper from Appearance.
- If another app set your lock screen wallpaper, the Lock card notices, and applying a Home wallpaper does not overwrite it.
Look
A Look slider with five stops: Clear, Mist, Tint, Solid and Custom. Clear is the most glassy.
At Custom, with nothing tapped, vertical sliders set Blur, Grain, Opacity, Tint, Margin and Corner radius for everything. Tap one element on the screen to tune it on its own; tap bare wallpaper to go back to all of them:
- Status bar: blur, grain, opacity and tint, plus a Clock button for the clock face (Flip, LCD, Minimal, LED matrix, Tape, Slab) and its alignment (Left, Center, Right).
- Terminal: the same four plus Contrast, with Cursor trail and Terminal effect pills.
- Dock: plus Size and Icons.
- Keyboard: plus Radius and Spacing, with a Keyboard theme button for its theme, colours and typeface (see Keyboard).
Undo steps back; Done applies.
Glass
Every glass surface bends the wallpaper at its edge as far as the chosen Look says, where the device can. There is no switch for it. It needs Android 13 or newer and a wallpaper set through the launcher’s own picker or launcherctl wallpaper set. Reduce idle activity, battery saver and Android’s reduced motion turn the extra motion off.
Cursor trail and terminal effect
- Cursor trail: Default, Motion blur, Railgun, Torpedo, Pixie dust or Comet.
- Terminal effect: None, CRT, CRT (green), CRT (amber) or TFT grid. It covers the status bar, dock and keyboard too, with the scanlines fixed to the screen.
Both are on Settings → Terminal, under Terminal display, and on the Terminal element in Look → Custom.
Icon pack
The Icon pack tab previews each pack on your real dock. The row of pack tiles starts with System; a tap applies a pack. The Pinned app icons only switch limits the pack to the dock.
Theme & fonts
Theme & fonts has no row in Settings. Reach it from Settings search (try “theme” or “fonts”) or the command palette’s Look and feel settings. It holds:
- App theme: System, Light or Dark.
- Wallpaper colors (“Match the launcher and terminal to the wallpaper.”, on by default) builds the launcher and terminal palette from your wallpaper. It overrides manual terminal colours: turn it off, or apply a Termux:Styling scheme, to use your own. Then the dock, status bar, drawer, keyboard and palette follow
~/.termux/colors.propertiestoo (that part needs Android 11 or newer). - Terminal contrast: Softer · pastel, Default · system or Harder · punchy, for the wallpaper palette. Greyed out while Wallpaper colors is off.
- Wallpaper parallax: the wallpaper pans a little as the places slide. On by default; needs a wallpaper set through the launcher’s picker.
- Wallpaper alignment: lines up the wallpaper behind the launcher with the one on your screen.
- Monochrome icons, Icon pack and Pinned-app icon pack, the same choices as the Icon pack tab.
- Surface style, which opens the Look tab, and Keyboard theme.
- Terminal fonts; see Fonts.
Tools that follow the terminal colours
On Theme & fonts, pick which command-line tools are recoloured whenever the palette changes: Starship, Helix, tmux, bat, Yazi, fzf, lazygit, Oh My Posh, Neovim, fish and herdr are built in. Turning one off puts its config back the way it was. You can add your own tool with a template; see Config files.
Battery: Reduce idle activity
Settings → App behavior → Reduce idle activity (“Lazy mode. Pause idle animations and update status less often.”, off by default) stops the launcher animating while you are only looking at it: the clock swaps its digits instead of folding them, a working window’s rim holds lit instead of breathing, the CPU, memory and weather readings sample far less often, and nothing repaints until something on screen changes.
Full details: Look and themes
Typing
Keyboard
The launcher ships a built-in port of Unexpected Keyboard by Jules Aguillon, a keyboard originally designed for programmers using Termux. Every key has up to eight extra characters on its corners, typed by swiping the key towards them. That puts Esc, Tab, Ctrl, arrows and all of shell punctuation on a normal-sized keyboard without extra rows. If you like it, support the upstream project; it is also a standalone keyboard app on Google Play and F-Droid.
The port is built into the app as a view: no separate keyboard to install, no Android input-method setup, and your system keyboard for other apps is untouched. It shows when you tap the terminal. Ctrl + Alt + K toggles it.
image: assets/docs/figures/home-still.webp
size: 480x1080
crop: 0 0.70 1 0.28
title: Built-in keyboard
caption: The built-in keyboard - corner symbols on every key, real Ctrl and Alt.
What it can do
- Corner swipes: swipe any key towards a corner for the symbol printed there. The small circle on a key gives its shifted character.
- Real modifiers: Ctrl and Alt are actual keys. Tap to latch for the next key, double-tap to lock.
- Ctrl key swipes: up-right (NE) opens Clipboard history, down-right (SE) switches to the numeric layer, down-left (SW) is Meta.
- Fn layer: swipe the Alt key up-left (NW) for Fn. Its layer puts F1 to F10 on the top row and F11 / F12 on Z / X; Esc / Tab on A / S; Home / Up / End on D / F / G; PgUp / Left / Down / Right on H / J / K / L; Insert / Delete on C / V; PgDn on B; Ctrl+C / Ctrl+D on N / M.
- Space bar gestures: swipe up opens the Command palette; up-left / up-right switch to the previous / next window; down-left / down-right switch to the previous / next session; slide left or right to move the cursor; swipe down to step through your keyboard layouts.
- Voice: swipe up on Enter to dictate. See Voice & speech.
- Keybind hints: hold Ctrl + Alt and the keys with bindings light up with a legend. To light only the ? key instead, turn off Settings → Terminal → Sessions and panes → Shortcut hints.
name: keybind-discovery
crop: 0.13 0.60 0.74 0.30
title: Keybind hints
caption: Holding Ctrl + Alt lights the bound keys and prints the chord map above them.
Layouts and extra keys
Settings → Keyboard → Layouts (“Languages, extra keys and custom layout”) holds:
- Layouts: choose the layouts the keyboard cycles through. A space-bar swipe down steps through them; the palette rows Cycle keyboard layout and Switch keyboard layout do the same.
- Keyboard extra keys: add optional keys (Copy, Paste, Select all, Undo, F11/F12, dead keys and more) to the keyboard’s own layout. This is not the extra-keys row under the terminal; for that, see Extra keys.
- Custom layout: the whole layout is one XML file at
~/.termux/keyboard/layout.xml, and it replaces the bundled layout completely, every key and every swipe slot. See Keyboard layout schema for the file format and thetool:key values.
Docked, floating or split
The keyboard has three shapes:
- Docked: the default, along the bottom of the screen.
- Floating: a smaller keyboard over the content instead of pushing it up. Move it by either top corner or by the pill in its handle row. Resize it from the bottom-left grip: dragging left makes it wider, dragging up makes the rows taller; the right and bottom edges stay put.
-
Split: two halves pushed against the screen edges with one straight gap between them, for two-thumb typing. The bundled layout parts q to t y to p, a to g h to l, and Shift z to v b to m Backspace. On the bottom row, Ctrl, Alt and a piece of space bar sit left; a shorter piece of space bar, the arrow key and Enter sit right. Both space pieces type a space.
To change shape:
- Swipe up on the Keyboard key of the extra-keys row to step through the three.
- Open Appearance → Layout and select the keyboard: three type chips plus Key radius, set per orientation.
- Run Keyboard type (one row per type) or Next keyboard type from the palette.
Settings → Keyboard → Size and position sets the sizes, separately for portrait and landscape: Floating keyboard width (percent of the screen width), Floating keyboard height (percent of the normal keyboard height) and Split keyboard gap (percent of the keyboard width between the two halves).
Hiding it or turning it off
- Hide for now: swipe down from the current page’s bottom border, or tap the Keyboard key. A hidden keyboard comes back the next time you tap the terminal.
- Keep it off: run Keyboard on/off from the palette, or in Appearance → Layout drag the keyboard onto the eye-off button (its tile in the hidden row switches it back on). A swipe up from the bottom border turns it back on.
- Use another keyboard: Settings → Keyboard → Input method → On-screen keyboard offers Built-in, Android or Off.
In Appearance → Layout you can also drop the extra-keys row, the apps row or the A-Z index into the slot under the keyboard. See Layout.
Hardware keyboards
Settings → Keyboard → Hardware keyboard has two switches:
- Hide on-screen keyboard: “While a hardware keyboard is connected.” Works for the built-in keyboard too.
- Android language shortcut: “Ctrl+Space switches Android keyboard languages. Off sends it to the terminal.” Off by default.
If the Android on-screen keyboard covers terminal content, try Settings → Terminal → Compatibility → Keyboard resize workaround; turn it off if it causes gaps or jumpy resizing.
Typing and feedback
Settings → Keyboard → Typing and feedback:
- Adaptive touch accuracy: “Adjust key detection to your typing.” Off by default. Reset learned taps forgets what it learned.
- Key preview: shows the pressed key above your finger.
- Key sound: uses the system touch-sound volume.
- Vibration: opens Settings → App behavior, where Allow vibration, Keyboard vibration and Launcher vibration live.
Looks
The keyboard’s material and colours are set with the rest of the home screen:
- Appearance → Look, choose Custom, then tap the keyboard: Blur, Grain, Opacity, Tint, Radius and Spacing, plus a Keyboard theme button. See Look.
- Height is not a Look control: drag the keyboard’s top edge in Appearance → Layout. The handle on the bottom of the keys sets the padding under the last row.
The Keyboard theme page (“Colors and typeface”) has a live preview and:
- One chip group for what you paint: Key fill, Border, Label, Corner labels, Bottom label, Background.
- 24 swatches. Tap a key, or drag across the keyboard, to paint it. Edit colors / Save colors change the swatches, with a hex editor; Reset to theme returns to the theme’s colours.
- A Font row: Choose font file… picks any TTF or OTF file from internal storage; Use system default goes back. This is separate from the terminal’s font (see Terminal fonts). With a custom font, the space bar’s icons keep the bundled symbols font.
The same two rows, Surface style and Keyboard theme, also sit in the “Keyboard look” section of the Theme & fonts page, which you reach through Settings search or the palette row Look and feel settings.
Full details: Keyboard
Typing
Extra keys
The extra-keys row is the configurable row of keys on the terminal page, separate from the full built-in Keyboard defined by ~/.termux/keyboard/layout.xml. You can edit it in a visual editor or straight in termux.properties.
What ships on the row
The Launcher default row has seven keys. Swipe up on a key for its second action:
- Keyboard: show or hide the keyboard; swipe up for Next keyboard type.
- Mouse: turn Mouse mode on or off.
- Widgets, Terminal, Display: go to that place. They are coloured primary, secondary and tertiary.
- New pane: swipe up for New window.
- Sessions: open the sessions browser; swipe up for New session.
Other launcher actions can be added from the editor’s search. App drawer (app.open_app_drawer, “Open the app drawer.”) opens the app drawer from the apps row’s current edge, and does nothing while Swipe down for app drawer is off in Settings → Apps. It is not on the shipped row. DRAWER still opens the sessions drawer.
The second page ships empty, so the row has one page until you add keys to another.
Keys that cannot act on the current place go faint and stop responding. On Display, the pane, window and session keys are dimmed; on Widgets, typing keys, modifiers, Paste and Scroll are dimmed.
In Appearance → Layout the row can be moved to any edge, dropped into the slot under the keyboard, or hidden on the eye-off button. See Layout.
Visual editor
Open Settings → Keyboard → Terminal extra keys (“Keys and swipe actions”), or run the extrakeys.edit launcher action. The editor writes the same configuration described further down, so you can start visually and hand-edit later. Next to it, Uppercase key labels (“Show ESC, TAB and other labels in capitals.”) is on by default.
svg: extra-key-anatomy
title: Anatomy of a key
caption: What the editor lets you set on each key, and how rows and pages fit together.
src: assets/showcase/features/extra-keys-editor
crop: 0 0 1 0.45
title: Extra keys editor
caption: Editing tap and swipe-up actions, choosing a glyph, then adding another page and row.
- Rows: Add row appends one below the last; a row’s drag handle reorders it, and holding a key drags it within or between rows. Removing a row asks for confirmation, since its keys go with it.
- Pages: swipe the row sideways to switch pages. A page with no keys stays hidden from the live row until it has something to show. The first page can’t be emptied out this way; with nothing on it, the whole row stays hidden instead.
- Editing a key: tap it to set what Tap and Swipe up do (a key, a built-in Extra Key command, or a
tool:action), its Label (blank shows the key name), Swipe-up label, and its colour. The Ω glyph picker offers a searchable catalogue of arrows, box drawing, blocks, shapes, Powerline separators and terminal marks for the label fields; only glyphs your device can draw are offered. - Save applies the row immediately; Discard asks first if you have unsaved edits.
Presets
The editor’s Presets section replaces everything on the current page in one tap, after a confirmation:
- Launcher default: the row the app ships with, described above.
- Classic Termux: upstream Termux’s row,
ESC,TAB,CTRL,ALT,-(swipe up for|),DOWN,UP. - Two rows: upstream Termux’s two-row layout,
ESC / - HOME UP END PGUPon top andTAB CTRL ALT LEFT DOWN RIGHT PGDNbelow. No launcher actions. - Clear page: empties the current page.
- Before the update: your previous row. When an update ships a new default row, a card offers Switch or Keep mine; choosing Switch saves the row you had under this preset first, so your old row is one preset away.
Editing the file
Configure the rows in ~/.termux/termux.properties directly, then apply changes with:
termux-reload-settings
Item schema
extra-keys is a matrix: the outer array contains rows and each inner array contains buttons.
extra-keys = [[ESC, TAB, CTRL, ALT, LEFT, DOWN, UP, RIGHT]]
A button can be a simple key name or an object:
| Field | Value | Meaning |
|---|---|---|
key |
One key/action string | Run one key, built-in Extra Key command, or tool: launcher action |
macro |
Space-separated key sequence | Send several classic terminal keys in order |
display |
Text or glyph | Override the label shown on the button |
popup |
Simple key or another item object | Secondary action selected by swiping upward |
An item must contain either key or macro, never both. popup supports the same key, macro, and display structure.
extra-keys = [[ \
{key: ESC, popup: {macro: "CTRL f d", display: "tmux exit"}}, \
{macro: "ALT j", display: "A-j", popup: {macro: "ALT g", display: "A-g"}}, \
{key: KEYBOARD, popup: PASTE} \
]]
Launcher action syntax
Use a key item, not macro, to run a registry action such as tool:app.command_palette, tool:workspace.picker, tool:workspace.save_prompt or tool:terminal.toggle_scratchpad. Unlike keyboard/layout.xml, Extra Keys can carry named action arguments after the second colon:
tool:<action-id>
tool:<action-id>:name=value,name=value
Multiplexer control row
This row combines pane creation, focus, layouts, floating panes, and window navigation. Swipe upward on buttons with a popup to run the secondary action.
extra-keys = [[ \
{key: "tool:pane.split_vertical", display: "⇳", popup: {key: "tool:pane.split_horizontal", display: "⇔"}}, \
{key: "tool:pane.focus_direction:direction=left", display: "←", popup: {key: "tool:pane.move_to_edge:edge=left", display: "⇤"}}, \
{key: "tool:pane.focus_direction:direction=down", display: "↓"}, \
{key: "tool:pane.focus_direction:direction=up", display: "↑", popup: {key: "tool:pane.next_layout", display: "⟳"}}, \
{key: "tool:pane.focus_direction:direction=right", display: "→", popup: {key: "tool:pane.move_to_edge:edge=right", display: "⇥"}}, \
{key: "tool:window.previous", display: "◧"}, \
{key: "tool:window.next", display: "◨"}, \
{key: "tool:pane.toggle_float", display: "◈", popup: {key: "tool:pane.equalize", display: "="}} \
]]
Pane and window actions are unavailable while Split-pane controls is off (Settings → Terminal → Sessions and panes); see Panes & sessions.
App and session shortcuts
Extra Keys can launch apps because they can supply the required query argument:
extra-keys = [[ \
{key: "tool:app.launch:query=com.whatsapp", display: "WA"}, \
{key: "tool:app.launch:query=YouTube", display: "YT"}, \
{key: "tool:session.new:name=build,failsafe=false", display: "+build"}, \
{key: "tool:session.browser", display: "sessions"}, \
{key: "tool:session.previous", display: "↰"}, \
{key: "tool:session.next", display: "↳"} \
]]
For values containing punctuation or spaces, prefer a package name or stable app ID. The Extra Key argument parser trims names and values and separates multiple arguments with commas; it does not provide a second quoting layer inside the tool: string.
Classic terminal and tmux macros
Use macro when you want to send ordinary terminal keystrokes instead of invoking a launcher action:
extra-keys = [[ \
{key: ESC, popup: {macro: "CTRL b d", display: "tmux detach"}}, \
{macro: "CTRL b c", display: "tmux +win"}, \
{macro: "CTRL b n", display: "tmux next"}, \
{macro: "CTRL c", display: "^C"}, \
{macro: "CTRL d", display: "^D"} \
]]
These macros target a shell program such as tmux. They are unrelated to the launcher’s in-app multiplexer actions such as pane.split_vertical and window.next.
Choosing the right surface
| Goal | Best configuration |
|---|---|
| Physical keyboard shortcut or multi-stroke chord | termux-launcher-bindings.conf |
| Modal leader keymap | termux-launcher-bindings.conf |
| Full embedded keyboard layout or swipe direction | keyboard/layout.xml |
| Visible button with a swipe-up secondary | extra-keys in termux.properties |
| Run an action requiring arguments from a touch button | extra-keys in termux.properties |
| Send a shell/tmux key sequence | Extra Keys macro, or binding-file send-key / send-text |
The older shortcut.create-session, shortcut.next-session, shortcut.previous-session, and shortcut.rename-session properties still exist, but the launcher binding file is the flexible path for new shortcuts, conditions, chords, and registry actions.
If a tool: Extra Key does nothing, confirm the action ID and argument names in Action reference, check whether split panes or a terminal selection is required, and inspect the app log. Tool failures are intentionally logged rather than shown as repeated toast messages.
Full details: Extra keys
Terminal
Terminal features
The terminal core is upstream Termux, with a lot built on top. The differences are in touch and mouse handling, links, the clipboard, graphics, effects and status from programs. Panes, windows, sessions and workspaces are on Panes & sessions.
Touch, copy and mouse mode
- Drag scrolls, always, with one or two fingers. Inside mouse-aware apps the drag becomes scroll-wheel events, so lists in
htop, lazygit or vim scroll naturally. - Tap sends a click when the running app tracks the mouse.
- Press and hold, then drag to hold the mouse button down; a small haptic marks the handoff. From there you can select text in vim, drag tmux-style splits, or resize TUI panes like a desktop mouse. A quick long-press without moving still opens ordinary text selection with the copy toolbar, and a drag before either haptic just scrolls.
- In a plain shell there is no mouse for an app to take, so the same hold always starts text selection.
- Modifiers ride along: Ctrl, Alt and Shift from the extra-keys row or a hardware keyboard are sent with a mouse click, xterm style. A latched Ctrl covers one click, so Ctrl+tap lets a TUI open its own links.
- Mouse mode turns every touch into the mouse, for a program that wants one without you holding first: a finger down clicks at that cell, held and dragged it drags, two fingers turn the wheel. Tap the Mouse key on the shipped extra-keys row, run Mouse mode from the palette, or bind
tool:mouse.toggleto any key. A small mouse icon at the end of the status bar shows it is on. - Two-finger flick on a split pane swaps it with the pane across the edge the fingers moved towards. See Panes & sessions.
- Pinch to zoom changes the focused pane’s font size, with jitter filtering so two-finger scrolling doesn’t zoom by accident. A new split or window inherits the size of the pane it came from; panes you have never zoomed keep following the global terminal size.
Find text
Ctrl + Alt + S searches the focused pane’s history and screen, case-insensitively, and jumps straight to a result.
Links
Tap a link and a Copy / Open strip appears with the full target, for OSC 8 hyperlinks and plain-text URLs alike. The link is read when your finger lifts, so a TUI that redraws while you tap does not move it.
- Over a program that tracks the mouse, the click still goes to the program and the strip follows.
- Hold or latch Shift for the tap to send nothing to the program; only the strip appears.
- Only
http,https,mailto,tel,sms,geo,ftpandftpslinks can be opened; anything else,filelinks included, can only be copied. - Plain-text URLs that a program wrapped by hand are joined back together within one pane, including an address wrapped inside a padded box such as herdr’s sidebar. A symbol printed after the host, like fish’s
⏎, is not taken as part of it.
Ctrl + Alt + U opens Quick select: keyboard labels on what is on screen, such as URLs, absolute and relative paths, path:line[:column] references and hashes. Pick a label to open a URL or copy anything else; hold Shift while picking a URL to copy it instead.
Clipboard
Swipe up-right (NE) on the keyboard’s Ctrl key to open Clipboard history in place of the keys. It lists what you copied in the launcher: a terminal selection, the keyboard’s copy and cut keys, a link or a hint you copied from a sheet, a yank in find mode, and anything a program copies with launcherctl clipboard copy or an OSC 52 escape. What you copy in other apps isn’t collected; the paste key still pastes whatever is on the phone’s clipboard.
Tap an item to paste it and put it back on the clipboard. Pin an item to keep it above the rest and across restarts. Recent items live in memory only, about thirty of them, and the panel keeps up to twenty pins; anything over 16 KB still reaches the clipboard but isn’t listed. Clear asks twice and never touches pins.
Settings → Terminal → Clipboard has two switches:
- Let programs read the clipboard: “Programs in the terminal can paste what you copied.”
- Clean up clipboard text: “Trim trailing spaces and single-line paste newlines.” On by default.
launcherctl clipboard copy works even when the launcher is not on screen; OSC 52 from a pane keeps the on-screen rule. Programs can also use kitty’s extended clipboard escape (OSC 5522, kitten clipboard), text only, under the same rules as OSC 52.
Images, GIFs and graphics
Three graphics protocols are supported out of the box:
- Sixel and iTerm2 inline images, so
img2sixel,chafaand friends work. - Kitty graphics protocol: PNG and raw pixel data, placements, z-index, and animation. Send an animated GIF through it and it keeps playing on the terminal’s own clock, even after the program that sent it exits.
src: assets/showcase/raw/fetch
size: 576x1296
crop: 0 0.06 1 0.34
title: Graphics in a pane
formats: mp4
caption: fastfetch drawing its logo through the kitty graphics protocol, over a wallpaper-themed prompt.
name: kew
crop: 0.13 0.28 0.74 0.34
title: Album art in the terminal
caption: kew, a terminal music player, drawing cover art through the same graphics protocol.
Tested clients: timg -pk, chafa -f kitty, and yazi’s image previews. kitty’s kitten is installable from tlstore: kitten icat works (it warns that it cannot create shared memory and falls back to sending the image in-band), kitten clipboard works, and kitten @ remote control does nothing.
Big and small text
A program can draw a word or a line at any size, not just one cell per character, with kitty’s text-sizing protocol (OSC 66). A heading can print twice as tall and twice as wide as body text, or a status glyph can shrink to a fraction of a cell. Selecting any part of an enlarged block selects and copies all of it.
If a pane gets too narrow for a block already on screen, it drops back to normal size on its own row and grows again once the pane is wide enough; rotating the phone keeps a block whole either way.
Cursor trail and effects
Settings → Terminal → Terminal display holds the visual extras. Cursor trail and Terminal effect also appear when you choose Custom in Appearance → Look and tap the terminal.
- Cursor trail: a streak when the cursor jumps. Styles: Default (kitty’s shearing trail), Motion blur (a soft lagging smear), Railgun (a thin white-hot beam with sparks, gone in a blink), Torpedo (a tapered body travelling nose first with a faint wake), Pixie dust (falling glitter) and Comet (a tapered streak with a glowing head). The palette’s Toggle cursor trail switches it on or off. It is off in power-save mode and under reduce-motion, and never drawn while you are scrolled back.
- Terminal effect: None, CRT, CRT (green), CRT (amber) or TFT grid, on Android 13 and later. It covers the whole home screen, scanlines stay locked to the screen, and only panes curve like a tube.
- Extend edge colors: “Match pane padding to nearby terminal colors.” On by default.
kitty users can set the trail in ~/.config/kitty/kitty.conf with cursor_trail, cursor_trail_decay, cursor_trail_start_threshold and cursor_trail_color; a custom_shaders line naming cursor-trail-default, cursor-trail-motion-blur, cursor-trail-railgun, cursor-trail-torpedo or cursor-trail-pixiedust overrides the Settings choice. See Config files.
Status from programs
- A window whose foreground program is actively using CPU shows a breathing rim; a background window that rings the bell gets a pulsing rim until you focus it. A silent but busy build can look active, and an idle TUI doesn’t count as work just because it is open.
- Every window pill picks up a small mark in the icon’s corner: a tick or a cross once a command finishes unseen, or a bell once the window rings.
- Agent status: a coding agent’s window chip shows a dot, and the sessions browser a word: Working, Needs you or Idle. Set it with
launcherctl agent working|blocked|idle|clear, or let the built-in screen rules spot known agents.launcherctl agent install-hookswires Claude Code’s hooks into~/.claude/settings.json. - A program with no terminal to write into can still post a real Android notification (
launcherctl notify, the same as anOSC 99request: named, replaceable, with an urgency level) or set a progress ring on its window’s pill (launcherctl progress, the same asOSC 9;4). OSC 99 buttons become notification actions and report back which one you pressed;launcherctl notify --closecloses a notification.
Small but useful
- Prompt jumping: jump between shell prompts from the palette. Works out of the box in fish; bash and zsh need one
sourceline (Config files). - Kitty keyboard protocol: modern TUIs get full key disambiguation, all five enhancement levels.
- Key inspector: a palette action that shows what any key press produces: the Android event, which keybind claimed it, and the bytes sent to the shell.
- Fonts, ligatures and gap-free box drawing have their own page: Terminal fonts. Graphics and protocol limits are covered in depth on Graphics, protocols and compatibility.
Full details: Touch, links and clipboard
Terminal
Panes & sessions
No tmux needed: the app is a multiplexer of its own. The hierarchy is sessions → windows → panes: a session is a fully separate workspace, a window is a tab inside it, and a pane is one shell. Everything below is reachable from the Command palette, keybinds, extra keys, or the space-bar swipes on the built-in keyboard. The shortcuts below assume Split-pane controls is on; a few change meaning when it is off (see “With split panes off” below).
svg: pane-splitting
title: Split, split again, swap
caption: A pane splits in two, the new pane splits again (dwindle), and a two-finger flick swaps neighbours. No tmux running.
Panes
- Split: side by side (
Ctrl+Alt+V) or top and bottom (Ctrl+Alt+H); New pane (Ctrl+Alt+Enter) splits along the longer side. The new shell starts in the source pane’s working directory and at its font size. - Move focus between panes with
Alt+Arrow. When there is no pane that way, the keys pass through to the shell. - Resize by dragging a divider: put a finger in the gap between two panes and drag. The split snaps to the cell grid when you lift, with a tick. From the keyboard,
Ctrl+Alt+Shift+Arrowresizes the focused pane; repeating it keeps resizing the same way. Equalize pane dividers in the palette resets a window’s split ratios to even. - Close the focused pane with
Ctrl+Alt+W. A window always keeps at least one tiled pane. - Rename the pane’s shell from the palette (Rename pane).
The pane’s corner tab
Hold a split pane’s corner for its tab:
- Close: close the pane.
- Move: drag the move icon onto another pane. Under
dwindle, the pane takes the half you drop it on. - Maximise: zoom the pane to fill the window. The tab stays out while maximised and offers Restore pane.
Swap with a neighbour without the tab: flick two fingers briskly across a split pane towards the edge you want. The pane swaps its shell with the pane across that edge and both slide; the layout shape and sizes never change, also under dwindle. In a T layout, flicking a lower column up swaps it with the full-width row. A slow drag does nothing, nor does a third finger, a lone, maximised or floating pane, selected text, or an edge with no neighbour. Pinch and two-finger scroll work as before, and the gesture puts the corner tab away if it was out.
A lone pane’s tab instead offers minimal mode, the automatic tiling switch, settings and help. Holding a corner shows the tab only; it does not resize.
Zoom without the tab: apply the stack layout (Ctrl+Alt+L cycles to it, or pick it by name in the palette) to maximise the focused pane while the rest stay alive behind it. It is temporary: saving a workspace stores the underlying pane tree, not the maximised view.
Floating panes
Ctrl+Alt+F (or Float / dock pane in the palette) detaches the focused tiled pane above the layout. Move it by its top-left corner, like every other frame on the home screen, and resize it from the bottom-right corner. Tap its pill for Close and Dock. Run the action again to dock it back into the tree. Positions and sizes survive app restarts, and workspace save and load record them too. The last tiled pane in a window can’t float.
Scratchpad (Ctrl+Alt+`, backtick) is a floating pane of its own: it summons a dedicated shell above whatever you are doing, and toggling it again hides it without closing it. The shell keeps running and follows you across windows and sessions, handy for a music player or a quick calculation.
Automatic tiling
Next pane layout (Ctrl+Alt+L or the palette) cycles a window through seven layouts: grid, dwindle, tall, fat, horizontal, vertical and stack. Applying one makes it the policy for that window, so later splits and closes re-tile the survivors to match. dwindle grows instead of rebuilding: a new pane halves the focused one along its longer side, and the dividers you have dragged stay put. Resizing or moving a pane by hand hands the window back to manual control (equalizing doesn’t); applying any layout again puts it back under management.
Two settings in Settings → Terminal → Sessions and panes set this up for every window:
- Automatic tiling: every new window starts under
dwindle. - Focus active pane: “Enlarge the active pane and shrink the others.” Works with whichever layout is in use.
Windows
A window is like a tmux window:
Ctrl+Alt+Copens a new one.Ctrl+Alt+[/Ctrl+Alt+]orCtrl+Alt+Left/Ctrl+Alt+Rightswitch to the previous or next window.Ctrl+Alt+1…9jump to a window by number.Ctrl+Alt+Rrenames the current window.Ctrl+Alt+Xcloses the current window and every pane in it.
Its pill sits in the status row: tap to switch, hold to rename, tap its own × to close. Pills label themselves after the open file in your editor, the running process, or the working directory, in that order.
Sessions
A session is a fully separate set of windows.
- Tap the chip at the left of the status row, or press
Ctrl+Alt+Shift+S(Toggle sessions), for a quick sessions panel with rename and close buttons on every row. - Sessions in the palette opens the full searchable session → window → pane tree. It searches working directories and cached process or title labels too.
Ctrl+Alt+Up/Ctrl+Alt+Downstep to the previous or next session, andCtrl+Alt+Shift+1…9jump to a session by number.Ctrl+Alt+Shift+Cstarts a new session,Ctrl+Alt+Shift+Rrenames the current one, andCtrl+Alt+Shift+Xcloses it.- Cloning a session starts a fresh shell at the selected pane’s directory; it doesn’t copy the running process, scrollback or layout.
Window pills and the sessions browser also show agent status for coding agents: Working, Needs you or Idle. See Terminal features.
With split panes off
Turn off Split-pane controls in Settings → Terminal → Sessions and panes to return to a single plain pane per session. Some shortcuts then change meaning: Ctrl+Alt+1…9 jump to a session, Ctrl+Alt+R renames the pane, Ctrl+Alt+C starts a new session, and Ctrl+Alt+Left / Ctrl+Alt+Right close and open the sessions browser.
Workspaces
Save workspace in the palette records the whole arrangement (sessions, windows, pane trees and their split ratios, floating panes and their bounds, focused panes, titles and working directories) to a named file, optionally with the foreground command in each pane too. Files live in ~/.termux/workspaces/ as JSON.
Load workspace offers Add to current to keep what is already open or Replace to remove it once the replacement terminals are ready. Recorded commands, if you saved them, are a separate confirmation, and each one starts again from the beginning in a fresh login shell; it is never resumed at its old point. Review a hand-edited workspace file before agreeing to run its commands.
Programs controlling panes
A program running in the terminal can open, switch and close panes on its own if you allow it: Let programs and agents control panes in Settings → Terminal → Sessions and panes. See Keybindings config to script the same actions yourself.
Full details: Panes, windows and sessions
Terminal
Palette & shortcuts
Every action the launcher knows, from splits, sessions and windows to appearance, the keyboard and launching Android apps, lives in one searchable list. Keybinds, keyboard gestures and the palette all run the same actions, so anything you can bind to a key you can also just type.
name: command-palette
crop: 0.13 0.24 0.74 0.32
title: Command palette
caption: Swipe up from the space bar, type "split", run it - the keycap strip shows the chord for what matched.
Opening it
Any of these:
- Swipe up from the space bar of the built-in keyboard.
- Ctrl + Alt + Shift + P on a hardware keyboard.
- Ctrl + Alt + Space, release, then P (two-stroke chord).
- Long press the terminal and pick Command palette from the action sheet.
Using it
- The palette opens with just a search box and a strip of four keycaps; your most used actions end up there over time.
- Type to filter. Matching is forgiving: titles, word starts, fuzzy letters, action ids (“split pane” finds Split pane vertically) and even keybinds (typing
ctrl+alt+vfinds whatever is bound to it) all work. - Press ↓ with nothing typed to browse the whole catalogue, grouped by category.
- Enter runs the focused action, Esc or a tap outside closes.
- If nothing matches, Enter runs what you typed in the shell instead, so a quick command doesn’t need a round trip to the keyboard.
Some rows want more from you:
- Rows marked
›open a small submenu of choices (like pane resize directions). - Rows marked
argsask you to type a value (rename a session, for example), then Enter applies it. - Destructive actions (like Kill focused pane) ask for confirmation first.
- Rows that can’t run right now stay visible but greyed out, with the reason (“no text selected”, “no active session”).
What’s inside
- Pane: new pane and splits, focus, resize, equalize, float and dock, layouts, rename, kill.
- Window and Session: create, close, switch, rename, Sessions (the full session browser), Toggle sessions, clone, save and load workspaces.
- Terminal: toggle the keyboard and the dock, scratchpad, font size, search scrollback, Quick select, prompt jumping, share transcript, edit the key row, reset.
- Keyboard: Keyboard type (one row per type), Next keyboard type, Show keyboard / Hide keyboard, Keyboard on/off, Clipboard history, Cycle keyboard layout and Switch keyboard layout (one row per layout), Mouse mode, Dictate.
- Places: Go to Widgets, Go to Terminal, Go to Display.
- Clipboard: copy selection, paste.
- Appearance: wallpaper, Toggle cursor trail, surface editor, Terminal fonts, and Install terminal font (an
argsrow that asks for the font id). - App: Open settings, Help, Look and feel settings, Key inspector.
- Apps: every installed Android app as a row, ranked by how often you launch things. This is separate from the
%app search in the terminal, but both use the same ranking. Hold an app row, then press a key combination, to bind that key to the app. - Sessions: every live session as a row; pick one to jump straight to it.
Handy defaults
A few worth remembering. The ones marked “splits on” need Split-pane controls on; the full set, and how to change them, is on the Keybindings config page.
| Keys | Action |
|---|---|
| Ctrl + Alt + Shift + P | Open the palette |
| Ctrl + Alt + Enter | New pane (splits on) |
| Ctrl + Alt + V / H | Split pane vertically / horizontally (splits on) |
| Alt + arrows | Move pane focus (splits on) |
| Ctrl + Alt + W | Kill focused pane (splits on) |
| Ctrl + Alt + Left / Right | Previous / next window (splits on) |
| Ctrl + Alt + Up / Down | Previous / next session |
| Ctrl + Alt + 1…9 | Window 1 to 9 (splits on); session 1 to 9 (splits off) |
| Ctrl + Alt + Shift + 1…9 | Session 1 to 9 |
| Ctrl + Alt + X | Close window (splits on) |
| Ctrl + Alt + R | Rename window (splits on); rename pane (splits off) |
| Ctrl + Alt + Shift + R | Rename session (splits on) |
| Ctrl + Alt + F | Float / dock the pane (splits on) |
| Ctrl + Alt + ` | Toggle scratchpad (splits on) |
| Ctrl + Alt + K | Toggle the keyboard |
| Ctrl + Alt + S | Search scrollback |
| Ctrl + Alt + U | Quick select |
Shortcut hints
Hold Ctrl + Alt on the built-in keyboard at any time, in or out of the palette, and the bound keys light up with a legend of what they do. To light only the ? key instead, turn off Settings → Terminal → Sessions and panes → Shortcut hints (“Show pane shortcuts while holding Ctrl+Alt. Off lights the ? key instead.”).
Changing bindings
Every shortcut can be remapped, and new keys bound to any palette action, in ~/.termux/termux-launcher-bindings.conf; see Keybindings config. Keyboard shortcuts lists every default.
Full details: Command palette and actions
Terminal
Fonts
Font handling is ported from kitty and then taken further. There are three ways in and they stack: an in-app picker for people who want a good font quickly, the classic ~/.termux/font.ttf for people who already have one, and ~/.termux/fonts.conf for people who want every knob.
Which file wins
Config is loaded in this order, and a later duplicate directive replaces an earlier one:
~/.config/kitty/kitty.conf: only its font directives are read (one level ofincludeis followed), so your kitty setup carries over.~/.termux/fonts.d/*.conf: drop-ins, in ascending filename order. The app writes exactly one of them,10-launcher.conf.~/.termux/fonts.conf: your own file, read last, so it wins every directive it mentions.
Anything those files leave unset comes from ~/.termux/font.ttf and ~/.termux/font-italic.ttf (what Termux:Styling writes), and after that from Android monospace.
So a hand-written fonts.conf beats the picker, the picker beats kitty.conf, and all of them beat Termux:Styling. If you set a font in the app and nothing changes on screen, a fonts.conf is overriding it.
The easy path: the picker
Terminal fonts is a font store: it downloads from the upstream release, checks the file against a SHA-256 pinned in the app, installs it under ~/.termux/fonts/, and writes the managed drop-in for you. The catalog ships inside the APK, so the list works offline. Open it with Terminal fonts in the Command palette (fonts.pick), by searching Settings for “fonts”, or from the Theme & fonts page (palette row Look and feel settings) under Terminal font → Terminal fonts.
Install terminal font (fonts.install) in the palette installs a family by id. Both actions also work from a keybind or an extra key.
image: assets/screenshots/terminal-fonts-picker.webp
title: Terminal font picker
caption: The managed Maple Mono setup, rendering controls and installed family cards.
The picker has four sections:
- Setup: the Active font card, showing the face in use and whether
~/.termux/fonts.d/10-launcher.confmanages it. - Tuning: Nerd Font icons routes
U+E000-U+F8FFandU+F0000-U+FFFFDto the bundled Symbols Nerd Font Mono (in the APK, not downloaded), so powerline, devicon, codicon and Material Design glyphs work with any family. Ligature policy offers the threedisable_ligaturesvalues: always shaped, un-fuse under the cursor, or off. Weight is awghtslider for variable families only; bold moves with it, so the family keeps its own contrast. - Families: fourteen curated families. Maple Mono carries the recommended star. Each row shows the download size, face count, whether it is variable and whether it ligates, plus a License button with the full notice and upstream link before anything is fetched.
- Manual control: Use font.ttf / Termux:Styling is the exit. It deletes
~/.termux/fonts.d/10-launcher.confand nothing else; yourfonts.confand the installed font files stay. Hand-edited config explains writing your ownfonts.conf.
| Family | Download | Faces | Notes |
|---|---|---|---|
| Maple Mono (recommended) | 373 KB | 4 | Variable wght 100-800, ligatures. The face the launcher’s text rendering is built around. |
| Intel One Mono | 494 KB | 4 | Designed with and for low-vision developers. |
| Hack | 601 KB | 4 | Four hand-tuned static faces, no ligatures. |
| Commit Mono | 707 KB | 4 | Deliberately neutral, smart kerning; alternates are opt-in font_features. |
| JetBrains Mono | 1.1 MB | 4 | Tall x-height, wide ligature set. |
| 0xProto | 1.2 MB | 3 | Legibility-first shapes; ships no bold italic. |
| Fira Code | 2.3 MB | 2 | No italic upstream, so italics stay synthetic. |
| Monaspace Neon | 4.0 MB | 2 | Variable wght 200-800, texture healing; ligatures live in stylistic sets. |
| Comic Shanns Mono | 8.6 MB | 2 | Comic letterforms on monospace metrics, icons already patched in. |
| Victor Mono | 8.8 MB | 4 | Semi-connected cursive italics. |
| MesloLGS NF | 9.8 MB | 4 | The Powerlevel10k face; prompt glyphs align with no tweaking. |
| Cascadia Code | 23.7 MB | 4 | Variable wght 200-700, cursive italics. One large upstream archive. |
| Rec Mono Casual | 48.1 MB | 4 | Brush-loop letterforms across all four faces. |
| Iosevka Term | 56.2 MB | 4 | Narrow slab grotesk, enormous Unicode coverage; Nerd Font build. |
Most are SIL Open Font License 1.1. The exceptions: Hack is MIT plus the Bitstream Vera License, Comic Shanns Mono is MIT, MesloLGS NF is Apache License 2.0, and the bundled Symbols Nerd Font Mono is MIT. Installing writes a LICENSE.txt next to the faces so the attribution travels with the files.
The picker never creates, overwrites or deletes ~/.termux/font.ttf or font-italic.ttf; a font you placed there yourself is safe.
The simple path: font.ttf
Unchanged from upstream Termux. Drop a TrueType file at ~/.termux/font.ttf, optionally ~/.termux/font-italic.ttf, or let Termux:Styling do it, then run termux-reload-settings. No fonts.conf, no fonts.d, no picker needed. Bold and bold italic are synthesized from the regular face; italic comes from font-italic.ttf when you supply it and is synthesized otherwise.
The power path: fonts.conf
~/.termux/fonts.conf is a kitty-style config. A fully commented reference copy is refreshed at ~/.termux/launcher/examples/fonts.conf on every app start, and termux-reload-settings applies your edits without restarting the app.
font_family path=~/.termux/fonts/maple-mono/regular.ttf
bold_font path=~/.termux/fonts/maple-mono/bold.ttf
italic_font path=~/.termux/fonts/maple-mono/italic.ttf
bold_italic_font path=~/.termux/fonts/maple-mono/bold-italic.ttf
font_variations regular wght=400
font_variations bold wght=700
font_features regular +zero
disable_ligatures cursor
modify_font cell_width 95%
Worth knowing:
- Face sources are
path=(absolute or starting~/) orfamily=for an Android system family. Paths are the reliable case on Android; afamily=lookup is best-effort. disable_ligaturestakesnever(the default),cursororalways, and touches programming ligatures only; Arabic, Indic, emoji and combining-mark shaping are never affected.font_featuresandfont_variationstargetregular,bold,italic,bold_italic,symbols, or a named symbol map.noneclears a target.modify_fontadjustscell_width,cell_height,baseline,underline_position,underline_thickness,strikethrough_positionandstrikethrough_thickness. A percentage (10% to 500%) replaces the font-derived metric; a bare number or one ending inpxadds pixels (-256 to 256).- Kitty’s own spellings are accepted (
font_family Fira Code,italic_font auto, a line starting with\continues the one above). A family name is looked up in~/.termux/fonts,~/.fontsand~/.local/share/fontsbefore Android’s families. - Bad lines are skipped and counted in a toast, with the full text in logcat; the rest of the file keeps working.
- Limits per file: 64 KiB, 512 lines, 4096 characters per line.
Keep selected symbols narrow
Kitty-compatible symbol expansion lets an icon use blank cells after it, which makes Nerd Font glyphs match the text height around them. Use narrow_symbols when a range must stay within a fixed number of cells:
narrow_symbols U+E0A0-U+E0A3,U+E0C0-U+E0C7
narrow_symbols U+F0000-U+FFFFD 3
The optional trailing cell ceiling defaults to 1 and may be 1 through 5; when several lines match a code point, the last one wins. Synthesized Powerline separators need no rule; powerline_symbols font hands them back to the font and makes them subject to narrow_symbols.
image: assets/screenshots/narrow-symbols-comparison.webp
title: Symbol width comparison
caption: Default symbol expansion on the left; the same Material symbols constrained to one cell on the right.
shape: wide
layout: full
Anything valid in fonts.conf is valid in a ~/.termux/fonts.d/*.conf drop-in. The app’s 10-launcher.conf is one of them, so a 05- file lands before it and a 20- file after. Drop-ins are capped at 32 files and 256 KiB in total, separate from fonts.conf’s own allowance. Symlinks pointing out of fonts.d are ignored.
Seamless box drawing
Fonts disagree about box drawing: a face designed for prose leaves seams between adjacent ─, puts the crossbar of ┼ off the centreline of │, and often lacks the block, braille and legacy-computing ranges entirely. The terminal draws those glyphs itself from the cell, snapped to the pixel edges adjacent cells share, so TUI frames, block ramps and braille graphs join cleanly at any font size, after a pinch-zoom, and after modify_font changes the cell. This is on by default, and it is what the sigye clock from tlstore leans on.
image: assets/screenshots/box-drawing-comparison.webp
title: Box drawing comparison
caption: Synthesized, seamless cell joins on the left; font glyph seams and misalignment on the right.
shape: wide
layout: full
box_drawing synthesizeis the default.box_drawing fontturns it all off and hands every one of those code points back to your font’s glyphs.box_drawing_scalesets the stroke widths of the four line weights (thin, light, heavy and very heavy) as multipliers of a base stroke derived from the cell height. Four values, comma or space separated, each above 0 and at most 8; the shipped default is0.001 1 1.5 2. Thin is deliberately near zero: it names a hairline, and the one-pixel floor produces one at any size.powerline_symbolsdefaults tosynthesize, so separator edges sit flush with the cell and two consecutive separators butt together with no sliver of background between them. It needsbox_drawing synthesizeas well. Set it tofontfor a patched Nerd Font whose author drew their own.- An explicit
symbol_mapalways wins: a range you deliberately routed to a font is respected over the geometry.
Synthesized ranges: Box Drawing (U+2500-U+257F), Block Elements (U+2580-U+259F), the four corner triangles of Geometric Shapes (U+25E2-U+25E5), Braille Patterns (U+2800-U+28FF), Legacy Computing sextants (U+1FB00-U+1FB3B) and eighth bars, corners and half medium shades (U+1FB70-U+1FB8F), and the Powerline separators U+E0B0-U+E0B7 and U+E0BA-U+E0BD unless powerline_symbols font is set.
Not synthesized, by design (they come from your font or your symbol_map): the rest of Geometric Shapes (U+25A0-U+25E1 and U+25E6-U+25FF), the Legacy Computing wedges and diagonals (U+1FB3C-U+1FB6F), the inverse shades, pattern fills, arrows and segmented digits (U+1FB90-U+1FBFF), and the diagonal Powerline separators (U+E0B8-U+E0B9 and U+E0BE-U+E0BF) even in synthesize mode.
Many fonts at once
symbol_map routes chosen Unicode ranges to another font file without changing the cell width, so the grid stays intact. It is repeatable, a later overlapping map wins, and each map can carry its own name so its shaping is tuned separately:
symbol_map name=icons U+E000-U+F8FF,U+F0000-U+FFFFD path=~/.termux/fonts/symbols/SymbolsNerdFontMono.ttf
symbol_map name=cjk U+4E00-U+9FFF path=~/.termux/fonts/NotoSansMonoCJK-Regular.otf
font_features icons +ss01
font_variations cjk wght=450
fallback_font path=~/.termux/fonts/NotoEmoji-Regular.ttf
fallback_font family="Noto Sans Symbols 2"
- Names are 1 to 32 characters of
A-Z a-z 0-9 _ -and cannot reuse a face target name; naming an undeclared map is reported and dropped. - A named map’s own
font_featuresandfont_variationswin for its cells, so neighbouring maps shape independently. Anything a map does not declare comes from the sharedsymbolstarget, which unnamed maps also use. - An axis a mapped face cannot honour is reported once and dropped.
- Ceilings: 256
symbol_maplines, 1024 ranges in total, and 8fallback_fontentries.
fallback_font is the answer to “Android picked an emoji or CJK font I did not choose”. Per cell, the order is: an explicit symbol_map, synthesized box drawing, the cell’s own face, the fallback_font chain in the order written, then Android’s platform fallback. The chain is consulted only when the cell’s own face lacks the glyph, and the first configured face that has it wins.
Why only four faces
ANSI SGR only distinguishes bold and italic, so there are exactly four addressable faces: regular, bold, italic, bold italic. No escape sequence asks for a fifth; that is a limit of the protocol, not of this config. The number of font files in play is much higher (four faces, up to 256 symbol_map targets, 8 fallback_font entries), and font_variations can move a face along an axis such as weight.
Full details: Terminal fonts
Extras
tlstore
The launcher works with whatever shell setup you already have. If you want a ready-made one (fish shell, a Material-themed prompt that follows your wallpaper, nicer ls, smart cd) plus a handful of terminal apps worth having, the launcher ships a small store that gets them for you.
tlstore
tlstore is the launcher’s own package manager for tools and configs it shows off but does not ship in the APK: a fish setup, a few terminal programs, and three coding agents, all installed and kept up to date by one command. It is already on your PATH; the app puts it there, along with the shorter tl and tls, so all three names run the same store.
tlstore install fish-shell # fish, the prompt, eza, zoxide and the plugins, in one go
tlstore install # a picker over everything else
tlstore update # bring what you have up to date
tlstore remove kitten # remove an item tlstore installed
Plain tlstore inside the launcher opens the store window; anywhere else it prints the list.
What’s in the store
Ten items, and each brings whatever it needs along with it:
| Item | What you get |
|---|---|
fish-shell |
fish with the launcher’s config, eza as ls, zoxide as cd, and fisher with the puffer-fish and autopair plugins. |
fastfetch |
System information beside an animated logo. Drop any GIF at ~/Pictures/gif/skel.gif and it plays there. |
sigye |
A clock for the terminal. |
kitten |
Kitty’s companion tool, for images and files in the terminal. |
dawn |
A writing pad for the terminal: markdown that takes shape as you type. With a model picked for Assistant and endpoint in the Model centre’s Functions segment (On-device AI), Ctrl+/ asks it to rewrite the selected text or write at the cursor. The launcher’s Dawn notes integration function lets Dawn find notes by meaning. |
claude-code |
Anthropic’s Claude Code, about 200 MB. Sign in by running claude; tlstore update keeps it current. |
opencode |
opencode, about 200 MB. Sign in by running opencode; tlstore update keeps it current. |
codex |
OpenAI’s Codex, as built for Android by codex-termux, about 275 MB. Sign in by running codex; its own update check is off and tlstore update keeps it current. |
btop |
A resource monitor that sees the whole phone (every process, disk and network interface), run as the shell user through Shizuku. Needs Shizuku connected, or it says the lane isn’t there. Launcher only. |
termux-api-shims |
termux-clipboard-get, termux-clipboard-set, termux-notification, termux-notification-remove, termux-notification-list, termux-toast, termux-vibrate, termux-torch, termux-battery-status, termux-volume and termux-wallpaper, working through launcherctl without the Termux:API app. Launcher only; it refuses to install while the real termux-api package is present. |
Ask about any item first with tlstore info <name>.
The wallpaper-matching oh-my-posh prompt theme and a matching Neovim colour scheme are not in tlstore: they are set up from Tools that follow the terminal colours on the Theme & fonts settings page (search Settings for it, or run Look and feel settings from the command palette). See Wallpaper colors in the shell below.
A config you already have is never replaced silently: tlstore update shows you the change and asks, and the answer defaults to no. Say yes and it leaves a timestamped backup right beside your file. Everything tlstore installs lives under ~/.local, never in Termux’s own bin, so a bootstrap reinstall never takes your tools with it.
image: assets/docs/figures/fish-prompt.webp
size: 576x440
shape: wide
title: fish-shell result
caption: The shell after tlstore install fish-shell - fish, wallpaper-Material prompt, eza listings.
Commands
| Command | What it does |
|---|---|
tlstore list [-i\|-a] |
Everything in the store; -i only what you have, -a only what you do not |
tlstore search <word> |
Find an item by name or description |
tlstore info <name> |
What an item is, its version, and where it goes |
tlstore install [--dry-run] <name>... |
Install items, or open the picker with no name |
tlstore remove [--dry-run] <name> |
Remove an item tlstore installed |
tlstore update [--check\|--offline\|--dry-run] |
Bring everything up to date, or only check |
tlstore rollback [--dry-run] <name> |
Go back one recorded version and hold the item there |
tlstore hold <name> / unhold <name> |
Keep an item at its version, or let it update again |
tlstore refresh |
Fetch the newest item list without changing anything |
tlstore display |
Set up graphics for Linux apps (runs termux-x11-gpu-setup; see Linux display) |
tlstore doctor |
Check that everything is in place |
tlstore self-update |
Bring tlstore itself up to date |
tlstore version |
Show the tlstore and item-list versions |
tlstore readme <name> |
Where a copy of the item’s own README is saved |
tlstore help [command] |
The command list, or one command’s page (<command> --help too) |
--tsv on list, search, info and update --check prints tab-separated columns for scripts. -y says yes to everything except replacing one of your config files; --configs on install and update answers that too. Exit status 0 means done, 1 means something failed, 2 means a mistake in what you typed.
Going back a version
tlstore rollback <name> puts an item back at the version it had before its last update and holds it there, so the next tlstore update skips it. tlstore list -i and tlstore info mark a held item. tlstore update <name> updates it anyway and releases the hold; tlstore unhold <name> only releases it. Rollback does not work for package items, bundles or fish plugins.
Wallpaper colors in the shell
The launcher writes your current Material palette to ~/.termux/material-colors.sh whenever the wallpaper theme changes. The installed fish config sources it and re-checks it on every prompt, so open shells pick up a new wallpaper theme without restarting. The oh-my-posh themes and your scripts can use the exported TERMUX_MATERIAL_* variables (primary, surface, error, the full terminal 16-colour set and more).
Fonts
Fonts are not part of the store. The Terminal fonts row on the Theme & fonts page downloads curated families, Nerd Font builds included, and wires them up for you. For manual control, ~/.termux/fonts.conf still works; see Terminal fonts.
Extras in the repo
The examples folder has more you can grab by hand: a tmux config with a matching Material theme, and a system monitor and weather widget for status bars.
Things worth installing
Two that show off the terminal’s graphics and fonts. sigye is in the tlstore catalogue; kew is a separate music player worth a look.
name: kew
crop: 0.13 0.28 0.74 0.34
title: kew
caption: kew - music in the terminal, cover art drawn through the kitty graphics protocol.
name: sigye
crop: 0.13 0.40 0.74 0.22
title: sigye
caption: sigye - the clock in box-drawing glyphs, which join because the launcher computes them as geometry.
Full details: Tlstore
Extras
On-device AI
The launcher can run language, speech and image models on your phone. Nothing leaves the device unless you set up a remote model or open the API to your network. Everything lives in Settings → On-device AI, in the Workspace group right after Display.
Two runtimes are built in:
| LiteRT-LM (Google) | MNN-LLM (Alibaba) | |
|---|---|---|
| Model format | .litertlm / .task packages |
MNN-converted models (config.json) |
| Chat input | ✓ | ✓ |
| Image, audio input | ✓ | ✗ |
| Tool calling | native | prompt-based |
| Thinking / reasoning traces | ✓ | - |
| Embeddings | ✓ (.tflite, EmbeddingGemma 2 .litertlm) |
✓ (where the model advertises it) |
| Runs on | CPU or GPU | CPU or GPU |
Loaded models are served over OpenAI-compatible and Ollama-compatible APIs on localhost, so existing clients and SDKs work against your phone. The command-line tool is tai. Dictation, cleanup and read aloud are covered in Voice & speech; they use the speech and voice models from the Model centre below.
What runs on this phone
The first time you open On-device AI, a What runs on this phone card lists what fits your phone: Voice typing, Read aloud, Assistant and tidy dictation and Dawn notes integration. Tick what you want, leave Wi-Fi only on or switch it off, and tap Download selected, or Later. Reopen the card any time from the What runs on this phone row.
The phone’s RAM puts it in a tier, shown in the Model centre header as “Tier N · X GB · chip · Android N”. The tier decides what Automatic means for each function:
| Tier | RAM | Automatic picks |
|---|---|---|
| 1 | 6 GB and under | No assistant; Whisper Base for voice typing; raw text for tidy dictation; app categories off; EmbeddingGemma 2 270M |
| 2 | 8 to 12 GB | Gemma 4 E2B for the assistant, tidy dictation and app categories; Whisper Small; EmbeddingGemma 2 440M |
| 3 | 16 GB and more | Gemma 4 E4B for the assistant (E2B when there is no GPU path) and app categories; E2B for tidy dictation; Whisper Small; EmbeddingGemma 2 440M |
Voice typing uses the English-only Whisper file when the phone is set to English. Read aloud uses KittenTTS on every tier.
The Model centre
Settings → On-device AI → Model centre has three segments:
- Functions: one row per job, showing what it uses: Assistant and endpoint, Voice typing, Tidy dictation, Read aloud, App categories, Dawn notes integration. Tap a row to pick from Automatic, On this phone, Remote, Without a model (such as Raw text or Off) and Get a model. The sheet’s Settings part holds the extras: Runs on GPU or CPU (with Answers look wrong? Use the CPU and Try the GPU again), Cleanup level for Tidy dictation, Voice and Speed for Read aloud, and Voice typing window.
- Installed: every model on the phone. Each shows a “Used by:” line and a Use for… action; deleting a model that serves a function warns “In use by …” first. Installed image models sit under Image generation.
- Get models: the catalogue, grouped under Assistants, Speech, Voice output and Search, with the import bar on top.
| Model | Group | Download | RAM |
|---|---|---|---|
| Gemma 4 E2B IT (recommended) | Assistants | 2.4 GB | 8 GB+ |
| Gemma 4 E4B IT | Assistants | 3.7 GB | 12 GB+ |
| Whisper ACFT Base / Base (English) | Speech | 97 MB | 6 GB+ |
| Whisper ACFT Small / Small (English) | Speech | 273 MB | 8 GB+ |
| Parakeet TDT 0.6B v3 (25 European languages) | Speech | 586 MB | 8 GB+ |
| KittenTTS Nano 0.8 (English) | Voice output | 90 MB | 4 GB+ |
| EmbeddingGemma 2 Text+Vision 440M (recommended) | Search | 388 MB | 4 GB+ |
| EmbeddingGemma 2 Text 270M | Search | 165 MB | 4 GB+ |
| EmbeddingGemma 300M (gated, needs a token) | Search | ~520 MB | 4 GB+ |
Each catalogue row shows a License: line before you download. Gemma 4 and EmbeddingGemma 2 are Apache-2.0 and need no token. Simultaneous downloads sets how many run at once. A preflight check runs before every load and refuses if the phone does not have the memory. GGUF, safetensors and other raw-weight formats are not supported; run those under llama.cpp in Termux.
Adding your own model
- Set a Hugging Face token under Settings → On-device AI → Hugging Face token. A classic Read token, or a fine-grained one with
Contents: Read, is enough. For a gated model, accept its terms on huggingface.co first. Gated rows and refused downloads also offer Add token. - Paste a link to any compatible LiteRT-LM or MNN model into Paste a Hugging Face link… at the top of Get models and tap Add, or tap File to pick a
.litertlm,.taskor.tflitefile. An MNN model folder goes through More ways to add a model → Add a model folder (MNN).
Remote model
Settings → On-device AI → Remote model lets a function use a model on another server with your own key. Set the Server address (presets OpenAI, OpenRouter and Server on this phone or LAN; plain http is allowed only for this phone and your own network), the API key (stored encrypted), the Model, whether it Understands images, and When to use it: Prefer remote or Only when no local model fits. Test connection sends one short message and times the answer; Remove forgets the address, key and model. Function pickers then offer it as “Remote ·
Search by meaning
The Dawn notes integration function lets Dawn notes find notes by meaning, not only by the words you typed. Automatic picks EmbeddingGemma 2 (440M, or 270M on Tier 1) and falls back to the other EmbeddingGemma 2 file, then to EmbeddingGemma 300M. EmbeddingGemma 2 files are single .litertlm files with a 2048-token window and text input.
Image generation
tai image draws a picture with an MNN diffusion model: Stable Diffusion 1.5 and Taiyi (512x512 only), or Sana (sizes in multiples of 32, from 256 to 2048). It runs on the GPU through OpenCL unless you add --cpu. There is no catalogue entry: add a model by pasting a Hugging Face repo link (for example taobao-mnn/stable-diffusion-v1-5-mnn-opencl), with Add a model folder (MNN), or with tai import <folder>. Image models are not listed on /v1/models and cannot be loaded with tai load. Other options: --model ID or --model-dir DIR, --type sd15|taiyi|sana, --steps N, --seed N, --size WxH, --cfg X, --image IN.png, --memory-mode 0|1|2.
tai image "a lighthouse at dusk" --out lighthouse.png
tai image --stop
Images and audio
A multimodal LiteRT model (Gemma 4) is advertised on the API as up to three ids sharing one downloaded file: model-id for text (the default, canonical id), model-id-vision for text and image, model-id-audio for text and audio. Only one mode loads at a time, which keeps the memory cost down. Endpoint exposure (under Advanced → Parameters) changes this: Combined serves every modality from the bare id alone, at a higher memory cost; Both keeps that combined id and adds the splits, with the text-only one as model-id-text.
Managing it from the shell
tai status # what's running, current settings
tai runtime [--clear-history] # runtime process state and history
tai logs [--lines N] [--clear] # the diagnostics log
tai models # installed models and their capabilities
tai import <path> [model-id] # add a file or folder already on the phone
tai download <model-id> <https-url> --accept-terms
tai downloads # the download queue
tai delete <model-id>
tai preflight [model] [--auto|--cpu|--gpu]
tai load [model] [--auto|--cpu|--gpu] [--fresh]
tai unload # release all model memory
tai keep-warm [model] [--minutes N] [--auto|--cpu|--gpu]
tai cancel # stop the running generation or benchmark
tai benchmark [model...] [--preset quick|standard]
tai transcribe <file.wav> # speech to text
tai speak [text] # read aloud; tai speak --stop
tai doctor # runtime and server health in one shot
A queued download is steered with tai download-pause, download-resume, download-now and download-cancel <model-id>; tai image is under Image generation above. For machine-readable output put --json (or -j) first, as in tai --json models. --fresh on tai load rebuilds an MNN model’s weight cache, for when a loaded model starts giving degenerate replies.
Benchmark
Settings → On-device AI → Benchmark (“Which models this phone runs well”) measures how models actually run here: choose models, pass the checks (battery, heat, screen) and run, with a cool-down between models and a result per entry. An installed chat model also has Benchmark in its overflow menu, and once you have run one, installed chat rows show a speed pill. From the shell it is tai benchmark.
Connecting a client
The OpenAI endpoint row in the Server group opens Endpoint & access, with the base URL, the bearer token and Recreate token. Require API token, LAN access and Model autoload sit beside it. The same values are written to disk:
export OPENAI_BASE_URL="$(cat ~/.launcherctl/endpoint)/v1"
export OPENAI_API_KEY="$(cat ~/.launcherctl/token)"
svg: ai-endpoint
title: Endpoint & access
caption: Any OpenAI-compatible client talks to the model on the phone through the base URL and bearer token.
- OpenAI-shaped:
/v1/chat/completions,/v1/responses,/v1/completions,/v1/embeddings,/v1/tokenize,/v1/audio/transcriptions,/v1/audio/speech, with streaming. - Ollama-shaped (same port, no
/v1):/api/chat,/api/generate,/api/embed,/api/tags.
The server listens on localhost only by default. LAN access always requires the token and turns itself off after 12 hours. The full endpoint reference is on the On-device AI API page.
Quickstart: AIChat
AIChat is a single-binary terminal chat client (pkg i -y aichat). On first run it offers to build ~/.config/aichat/config.yaml; take the base URL and token from Endpoint & access:
clients:
- type: openai-compatible
name: tai
api_base: http://127.0.0.1:54298/v1 # your endpoint
api_key: YOUR_TOKEN # your bearer token
models:
- name: gemma-4-e2b-it-litert-lm # any installed model id
How memory is handled
- One generation model is resident at a time. Loading another, even on the other backend, unloads the current one first.
- Embedding and speech models do not occupy that slot; they load on demand beside the chat model.
- Idle models unload after 10 minutes by default (Idle unload, under Advanced).
tai keep-warmextends it. - Models run in a separate process, so a native crash cannot take the launcher down with it.
- Share diagnostics log (under Advanced) saves loads, evictions, failures, the runtime history and the last crash as one text file, with no prompts or replies.
Full details: On-device AI
Extras
Voice & speech
Voice input turns speech into text, entirely on the phone. Read aloud does the opposite: it speaks terminal text back to you. Both run on small local models and need nothing installed to try; the launcher falls back to Android’s own speech recognizer until you get one.
Starting a dictation
Swipe up on the built-in keyboard’s Enter key and speak; allow the microphone the first time. A second swipe pauses. Swiping up and holding opens Android’s Choose speech recognizer list instead.
A dictation from the keyboard belongs to the keyboard: hiding it stops the dictation. To dictate
with the keyboard down, on Home or the Linux display, add the Dictate action
(tool:voice.dictate) from Settings → Keyboard → Terminal extra keys; it starts or stops a
dictation wherever you are.
The dictation card
Nothing is typed while you speak. What you say collects in a card at the top right of the place you are on. The card is whole from the start, in three parts:
- The strip on top: the state (Listening…, Transcribing…, Cleaning up…, then Ready, Cleaned up or Kept as heard), a small ring while something runs, a few words of detail (· warming up, · cleanup undone, · at the cursor, · to the clipboard), a handle, and ×, which discards and closes. Swiping the card sideways also discards.
- The text, up to seven lines, the oldest scrolling off the top.
- One control bar: Pause (carrying the waveform) or Resume, Undo and Redo for the cleanup, Copy and Insert.
You can tap Copy or Insert early, while still listening: the dictation stops and the action runs once the text is final. Afterwards the strip says Inserted or Copied.
Drag the strip to move the card; double-tap it to put the card back in the top-right corner. Portrait and landscape each remember their own spot.
Where the text goes
Insert types where the keyboard would type: a focused field first (command palette, app drawer search, a rename field), then the Linux display if that is what is showing, then a launcher field such as a widget editor, then the terminal’s focused pane. On Home with nothing focused there is nowhere to type into, so the text is copied instead.
Cleanup with a local model
Once a dictation stops, a chat model can pass over the whole thing to fix punctuation, fillers, grammar and self-corrections (“at five, no, six” becomes “at six”). Changed words are marked in the card; Undo takes the cleanup back.
Cleanup is on by default and needs a Gemma chat model installed (see On-device AI), or a remote model for Polished. The switch is Clean up dictation, in the Dictation cleanup group of Settings → Keyboard → Voice input; Cleanup details explains what it does. While it is on:
| Setting | Choices | Default |
|---|---|---|
| Cleanup level | Light: punctuation, fillers and grammar, in your words, on this phone. Polished: also paragraphs and lists, using your remote model when one is set up | Polished |
| Cleanup model | Opens the Tidy dictation picker: Automatic, an installed model, a Remote model, or Raw text (no model) | Automatic |
Automatic means Gemma 4 E2B, then E4B, then raw text. On a phone with 6 GB of RAM or less, Automatic means Raw text. A Polished result keeps its line breaks only in programs that accept bracketed paste; elsewhere they are joined with spaces.
A dictation is kept exactly as heard, with no cleanup, when it is under 4 words, when it is a spoken command (below), or when the model is too slow, unavailable, or its answer looks wrong rather than a cleanup.
Spoken commands
Say a command name first and the dictation is written as a command, with fixed rules and no model:
“ls dash la” becomes ls -la, “cd slash home slash user” becomes cd /home/user, “git add dot”
becomes git add .. Spelled letters join up (“L S” is ls), and “dash”, “dot”, “slash”, “tilde”,
“pipe”, “star” and “equals” become their symbols.
Speech models
With no speech model installed, voice input uses Android’s own recognizer, which types straight to the cursor with no card. Install a model from Settings → On-device AI → Model centre → Get models → Speech, or from the Voice typing row on the Model centre’s Functions segment:
| Model | Languages | Download |
|---|---|---|
| Whisper ACFT Base | many languages, or English only | 97 MB |
| Whisper ACFT Small | many languages, or English only | 273 MB |
| Parakeet TDT 0.6B v3 | 25 European languages, auto-detected | 586 MB |
Base is faster; Small is more accurate for long sentences and noisy rooms. The first model you finish downloading becomes the one voice input uses. With more than one installed, switch with Settings → Keyboard → Voice input → Speech model.
The Speech group of that page also has Speech engine (Android system or On-device; it switches to On-device by itself once a model is installed) and Voice language.
Listening settings
In the Listening group of Settings → Keyboard → Voice input:
- Phrase pause: how long a pause sends what you have said off to be transcribed.
- Stop after silence: how long without speech ends the dictation (5 s, 10 s, 30 s, or Until tap, which only stops on a tap or the keyboard going down). Default 10 s.
- Mic sensitivity: Normal: ignores background talk, or High: for soft speech; may hear others.
- Listening sounds: a short sound when listening starts or stops.
Read aloud with KittenTTS
Install KittenTTS Nano 0.8 from Model centre → Get models → Voice output (about 90 MB). It is a small voice model, English only, that runs on the phone.
Long-press text in the terminal to select it, then tap Read aloud in the selection toolbar. A reading card opens in the same place as the dictation card:
- The selected text, with the sentence being heard in the accent colour and what was already read dimmed; it scrolls by itself.
- The strip says Reading with · preparing until the first sound, then · N of M sentences; Paused while paused; Done at the end, then the card closes.
- Pause and Resume stop and continue in place, one sentence at a time.
- The voice button (Bruno, Hugo, Jasper or Rosie) changes the voice from the next sentence and saves it as the default.
- The stop button, ×, a sideways swipe, or Stop reading in the selection toolbar stop and close.
You can also set the voice and speed in Settings → Keyboard → Voice input → Speech model → Voice output, or with Voice and Speed on the Read aloud row of the Model centre’s Functions segment.
Read aloud only appears once the voice model is installed, and it reads English only; other languages come out wrong.
From the shell: tai transcribe and tai speak
tai transcribe recording.wav
tai speak "The build finished."
some-agent | tai speak
tai speak --stop
tai transcribe runs the installed speech model over an audio file and prints the plain text.
tai speak reads text aloud with the installed voice model, or saves it to a file with --out.
Piped text is spoken sentence by sentence as it arrives, with colour codes and Markdown marks
dropped; --whole collects everything first and --stream forces streaming. tai speak --stop
or Ctrl-C stops it. The whole command surface is in On-device AI; the API
routes (/v1/audio/transcriptions, /v1/audio/speech, /v1/ai/speak) are on the
On-device AI API page.
Full details: Voice input; read aloud is covered in Text to speech.
Extras
Linux display
The Linux display is the third place on the pane wall, alongside Terminal and Widgets: a real X11 display where Linux desktop apps run beside the terminal. Once it is on, hold the terminal’s border, then drag sideways, or tap the screen glyph peeking in from the status bar’s edge, to reach it.
Turn it on
The Display place is only on the wall while the display is switched on. Any of these turns it on:
- Settings → Display → Display.
- Settings → Launcher mode → Terminal + Home + Display (“Also run Linux desktop apps”).
- The first-run card, or tapping a Linux app in the drawer.
Turning it on puts termux-x11 on your $PREFIX/bin, so a display can be started from any shell:
termux-x11 :0 &
export DISPLAY=:0
or tap Start display on the Display place, which runs the same thing. Turning the display off removes the commands and the place. If a display server is still running you are asked to Stop it or Leave it running.
Getting apps onto it
Settings → Display → Linux apps → Set up Linux apps (“Select apps and copy the install command”) builds the install command for you. Pick a Source (the Termux X11 repo for individual apps, or a full Linux distro), tick what you want, and tap Copy the command. Paste it into the terminal and press Enter; it installs everything, including the keyboard layouts and fonts the display needs.
Apps installed inside a distro (a proot, via proot-distro) work too, but expect
rougher edges. A whole desktop environment (XFCE, LXQt)
is catalogued the same way, under its own group in the drawer.
Once installed, apps appear in the app drawer with their own name and icon, beside your Android apps. A tap starts the display if it is not running and opens the app on it, full size. An app whose menu entry asks for a terminal opens in a terminal pane instead. The same Linux apps page has Show Linux apps in drawer, Hidden Linux apps, Window manager and Display badge.
Mouse mode and the touchpad
The Mouse mode action (add it to the extra-keys row, the in-app keyboard, or a key chord) swaps the keyboard for a touchpad the same size, for apps that expect a mouse:
- One finger moves the pointer; tap to click, hold (or tap-and-hold) then move to drag.
- Two fingers scroll, with a flick that keeps going; pinch to zoom; tap together for a right click.
- Three fingers tap for a middle click, or swipe left/right to switch windows and down to bring the keyboard back.
- A strip down the touchpad’s trailing edge scrolls with one thumb.
Over a split keyboard, the touchpad stands in the gap so both halves keep typing. Tapping a text field on the display brings the keyboard to the front without leaving mouse mode; the touchpad is back the moment the field lets go.
Display settings
Settings → Display is split into subpages:
| Subpage | What it holds |
|---|---|
| Input | Touch, Use Android keyboard, OSK auto-show, Share the clipboard |
| Resolution and scaling | Resolution, Scale, Fixed size, Custom size, Scaling (filtering), Display density |
| Linux apps | Show Linux apps in drawer, Set up Linux apps, Hidden Linux apps, Window manager, Display badge |
| Startup | Start with the launcher, Start command, Connect new shells |
| Troubleshooting | Compatibility drawing, Swap colour channels |
Touch has three modes. Touchscreen treats fingers as touches (scroll, pinch, drag) the way a tablet would; apps that only understand a mouse still get clicks. Trackpad turns the whole display into a laptop touchpad. Direct touch sends the same touches as Touchscreen without the launcher’s own pinch and keyboard behaviour layered on top.
In Touchscreen mode the keyboard follows text fields: tapping a text field on the display brings the keyboard up, and tapping elsewhere puts it down again (Settings → Display → Input → OSK auto-show). A keyboard you opened yourself stays open until you close it.
In Touchscreen mode an interrupted gesture leaves no ghost finger behind, so a later one-finger touch is never read as a pinch.
GPU acceleration
The display server itself always draws in software, but apps you run on it, such as a game or a browser, can be accelerated.
- Settings → Display → Linux apps → Set up Linux apps → Graphics shows Graphics acceleration (which driver profile fits this phone) and Copy the graphics command, which puts the install command and the environment variables on your clipboard.
termux-x11-gpu-setuptries every profile with a short 3D test, keeps the fastest real GPU path, removes the losers, repeats the test inside a Debian proot if one is installed, and writes~/.config/termux-launcher/x11-gpu.env. Options:--keep,--skip-proot,--yes,--display=N. tlstore runs it for you astlstore display.launcherctl x11 gpusays which profile fits;launcherctl x11 gpu --envprints the exports.
Export those variables before starting an accelerated app from a shell. Apps opened from the drawer get the variables of the installed profile automatically.
The Nix route
On the Nix edition, graphical apps come from nixpkgs instead of a proot or the
Termux X11 repo: add the app and xkeyboard-config to your flake and nix-on-droid switch picks
them up for the drawer.
When something is off
- “No display is running” although you started one. The keyboard layouts are missing: install
them from Set up Linux apps, or run
termux-x11 :0in the foreground to read its own message. - An app opens and closes again. Usually a fresh distro missing fonts; Set up Linux apps installs them.
- Nothing is accelerated. Check
glmark2-es2’s first lines for the renderer it picked:llvmpipemeans the GPU variables are missing or do not fit this phone. Runtermux-x11-gpu-setupor Set up Linux apps → Graphics again.
Full details: The Linux display
Reference
Keybindings config
Termux Launcher routes panes, windows, sessions, terminal controls, and app shortcuts through one action registry. The command palette, physical keyboard, embedded in-app keyboard, and Termux Extra Keys can therefore reach the same actions, but each surface has a different configuration syntax.
~/.termux/termux-launcher-bindings.conf is the full keybinding layer used by the in-app multiplexer. See Action reference for action IDs and arguments, Keyboard layout schema for embedded keyboard swipe slots, and Extra Keys recipes for termux.properties.
Start from the shipped example
The app creates the live binding file, fully commented out, if it is absent, and refreshes a pristine reference copy on every app start:
~/.termux/termux-launcher-bindings.conf
~/.termux/launcher/examples/termux-launcher-bindings.conf
Edit only the live file, then reload without restarting the launcher:
termux-reload-settings
The grammar is:
map [options] <key-sequence> <action-id> [arguments...]
unmap [--mode <name>] <key-sequence>
leader <stroke>
Examples:
map ctrl+alt+g pane.equalize
map ctrl+alt+space>1 pane.layout grid
map --label WhatsApp ctrl+alt+space>w app.launch com.whatsapp
map ctrl+alt+t send-text "printf 'hello from a binding\n'\n"
map ctrl+alt+space>c send-key ctrl+c
unmap ctrl+alt+u
Mentioning a root sequence with map or unmap replaces every built-in mapping for that exact sequence. Repeating the same sequence and condition appends another action, so the actions run from top to bottom. A line such as map ctrl+alt+w app.launch com.whatsapp is valid, but it silently replaces the default Ctrl+Alt+W (kill focused pane); check the table below before taking a root stroke.
You do not have to write app chords by hand: long-press an app row in the command palette (or press Ctrl+Alt+Enter on it) and press a key combination. The palette appends the mapping under a managed header, labelled with the app’s name, and keeps your comments, blank lines and order.
Built-in multiplexer shortcuts
Bindings match Android key codes: they follow physical US key positions, not the character produced by the current language layout.
| Shortcut | Split panes on | Split panes off / compatibility mode |
|---|---|---|
Ctrl+Alt+Enter |
New pane (splits along the focused pane’s longer side) | Unclaimed |
Ctrl+Alt+V |
Split vertically, creating side-by-side panes | Paste |
Ctrl+Alt+H |
Split horizontally, creating stacked panes | Unclaimed |
Alt+Arrow |
Move pane focus in that direction | Unclaimed |
Ctrl+Alt+Left / Ctrl+Alt+Right |
Previous/next window | Close/open the sessions drawer |
Ctrl+Alt+Up / Ctrl+Alt+Down |
Previous/next session | Previous/next session |
Ctrl+Alt+Shift+Arrow |
Grow the focused pane in that direction | Unclaimed |
Ctrl+Alt+W |
Kill the focused pane | Unclaimed |
Ctrl+Alt+C |
New window | New session |
Ctrl+Alt+X |
Close the current window | Unclaimed |
Ctrl+Alt+[ / Ctrl+Alt+] |
Previous/next window | Unclaimed |
Ctrl+Alt+L |
Next automatic pane layout | Unclaimed |
Ctrl+Alt+F |
Float or dock the focused pane | Unclaimed |
Ctrl+Alt+R |
Rename window prompt | Rename pane prompt |
Ctrl+Alt+Shift+R |
Rename session prompt | Unclaimed |
Ctrl+Alt+Shift+C |
New session | New session |
Ctrl+Alt+Shift+X |
Close the current session | Unclaimed |
Ctrl+Alt+N / Ctrl+Alt+P |
Next/previous session | Next/previous session |
Ctrl+Alt+1 … Ctrl+Alt+9 |
Switch to window 1 … 9 | Switch to session 1 … 9 |
Ctrl+Alt+Shift+1 … Ctrl+Alt+Shift+9 |
Switch to session 1 … 9 | Switch to session 1 … 9 |
Ctrl+Alt+Shift+S |
Toggle sessions panel | Toggle sessions panel |
Ctrl+Alt+K |
Toggle keyboard | Toggle keyboard |
Ctrl+Alt++ (or Ctrl+Alt+Shift+=) / Ctrl+Alt+- |
Increase/decrease font size | Increase/decrease font size |
Ctrl+Alt+M |
Terminal actions | Terminal actions |
Ctrl+Alt+U |
Quick select | Quick select |
Ctrl+Alt+S |
Search scrollback | Search scrollback |
Ctrl+Alt+Shift+P |
Command palette | Command palette |
Ctrl+Alt+Space, then P |
Command palette | Command palette |
Ctrl+Alt+` |
Toggle scratchpad | Unclaimed |
Alt+Arrow passes the stroke on to the shell when there is no pane in that direction.
The same stroke can safely have two meanings when their conditions cannot overlap. Use --when to do that in your own file:
map --when splits-on ctrl+alt+v pane.split_vertical
map --when splits-off ctrl+alt+v clipboard.paste
--when accepts always (the default), splits-on, or splits-off.
Supported key names
A sequence can contain one to eight strokes separated by >. Modifier names and multi-character keys (Left, PageUp) are case-insensitive, and control is accepted as an alias for ctrl. A single upper-case letter means Shift: Ctrl+Alt+R is ctrl+alt+shift+r, a different stroke from ctrl+alt+r.
| Kind | Tokens |
|---|---|
| Modifiers | ctrl, alt, shift |
| Letters and digits | a … z, 0 … 9 |
| Function keys | f1 … f12 |
| Navigation | left, right, up, down, home, end, pageup, pagedown |
| Editing | space, tab, enter, escape, backspace, delete |
| Punctuation | [, ], minus, equals, plus, /, \, ;, ', ,, ., ` |
Examples:
map ctrl+alt+space>g pane.rotate
map ctrl+alt+f12 terminal.toggle_toolbar
map ctrl+alt+shift+k app.key_inspector
Avoid bare letters and plain Alt+letter at the root unless you deliberately want to intercept them. Shells and editors commonly use Alt as an Escape prefix. Ctrl+Alt or a leader chord is less disruptive.
Leader and labels
leader <stroke> declares a tmux-style prefix. Every root ctrl+alt+… binding then also answers to the leader followed by the key, which helps on keyboards where three-key chords are awkward. The first leader line wins; a second one is reported as an error.
leader ctrl+b
--label "Name" names a binding in the keybind legend and the palette’s shortcut column, 1 to 32 characters. Without it a binding keeps its action’s own name, so every app.launch chord would read “Launch app”. When several lines build one binding, the first label among them wins.
Ctrl+Space reaches your bindings only while Settings → Keyboard → Hardware keyboard → Android language shortcut is off (the default). While it is on, Ctrl+Space switches Android keyboard languages and a ctrl+space binding is bypassed.
Action arguments
Required arguments can be positional in schema order. Any argument can instead use name=value; quote values containing spaces.
map ctrl+alt+space>1 pane.layout grid
map ctrl+alt+space>e pane.move_to_edge edge=left
map ctrl+alt+shift+w window.select index=0
map ctrl+alt+shift+n session.new name=build failsafe=false
map ctrl+alt+space>s workspace.save name=project overwrite=true
map ctrl+alt+space>r session.rename "build shell"
Values are checked against the action schema. Integers must be in range, booleans must be exactly true or false, and enum values must match the listed spelling. Unknown arguments invalidate only that line.
Arrow strokes automatically provide a direction, and Ctrl+Alt+1 … 9 automatically provide a zero-based index. An argument written in the file overrides the value inferred from the key.
Keyboard actions have no default keys. For example:
map ctrl+alt+space>k keyboard.cycle_layout
map --label Dvorak ctrl+alt+space>d keyboard.select_layout latn_dvorak
map ctrl+alt+space>f keyboard.cycle_form
map ctrl+alt+space>o keyboard.set_form floating
map ctrl+alt+space>h keyboard.hide
Send text or a terminal key
send-text writes decoded text directly to the focused shell. Double-quoted and unquoted values understand \n, \r, \t, and \e; single-quoted values stay literal.
map ctrl+alt+j send-text "cd ~/src\n"
map ctrl+alt+j send-text "git status\n"
map ctrl+alt+space>c send-key ctrl+c
map ctrl+alt+space>x send-key escape
send-key accepts one supported stroke, not a chord. It uses the terminal’s current cursor-key and keypad modes when encoding special keys.
Modal keymaps
A mode is a persistent leader layer. This keeps ordinary shell shortcuts free while putting many launcher actions behind one prefix:
map --new-mode nav --timeout 10 --on-unknown passthrough --on-action keep ctrl+alt+space
map --mode nav h window.previous
map --mode nav l window.next
map --mode nav v pane.split_vertical
map --mode nav s pane.split_horizontal
map --mode nav w app.launch com.whatsapp
map --mode nav q pop-mode
map --mode nav escape pop-mode
| Option | Values | Default |
|---|---|---|
--new-mode |
A name using letters, digits, _, -, or ., up to 32 characters |
none |
--timeout |
0 … 3600 seconds |
2 |
--on-unknown |
beep, ignore, end, passthrough |
beep |
--on-action |
keep, end |
keep |
--mode |
Add a mapping to an existing named mode | root map |
Modes can stack. pop-mode (or its compatibility alias pop_keyboard_mode) exits the top mode. The launcher displays the pending chord and active mode in a non-focusable overlay.
Using bindings from the in-app keyboard
The embedded keyboard emits the same Android key events used by physical keyboards. Latch Ctrl and Alt, then press the suffix key to trigger a binding; while the prefix is latched, the launcher shows the available suffixes and highlights matching keys.
For a one-gesture button or swipe, assign the registry action directly in ~/.termux/keyboard/layout.xml instead. Direct layout actions cannot carry arguments, while the binding file can. See Keyboard layout schema.
Limits and diagnostics
The binding file is limited to 256 KiB, 4,096 lines, and 4,096 characters per line. Going over any of these limits discards the whole file: none of your bindings load and an error is reported. Within the limits, an invalid line is skipped and reported while the other mappings stay active.
Open Key inspector from the command palette to see the Android key code, normalized stroke, action that claimed it, active Kitty keyboard flags, and bytes sent to the shell. It is intentionally unbound by default, but you can add:
map ctrl+alt+shift+k app.key_inspector
If all launcher shortcuts stop working, check that hardware keyboard shortcuts are not disabled in termux.properties, then run termux-reload-settings again.
Full details: Custom keybindings
Reference
Action reference
These are the current action IDs accepted by ~/.termux/termux-launcher-bindings.conf, the command palette, tool: keys in the embedded keyboard, and tool: entries in Termux Extra Keys. Palette titles are given in quotes where they differ from the description.
Which surfaces accept arguments?
| Surface | Action syntax | Arguments |
|---|---|---|
| Binding file | map … <action-id> [arguments] |
Positional required values and name=value |
| In-app keyboard XML | tool:<action-id>:<glyph> |
None; the suffix is a display glyph, not arguments |
termux.properties Extra Keys |
tool:<action-id>:name=value,name=value |
Named values after the second colon |
| Command palette | Search by action title or ID | An action with exactly one required text argument is offered too: a choice list opens a submenu with one row per value, free text puts the palette in argument mode |
An action with a required argument is unsuitable for a direct in-app keyboard tool: slot. Put it in the binding file, or use an Extra Key that can carry arguments; the Extra Keys picker offers one row per value for a choice-list argument.
Argument notation
In the tables below, required means the value must be supplied. A value marked optional uses the shown default when omitted.
Panes
Pane actions need split panes on, except pane.kill_focused, pane.rename and pane.rename_prompt, which need only a session.
| Action ID | Arguments | What it does |
|---|---|---|
pane.split |
none | “New pane”: split the focused pane along its longer side |
pane.split_vertical |
none | Split the focused pane side by side |
pane.split_horizontal |
none | Split the focused pane into a stacked pair |
pane.focus_direction |
direction: left, right, up, or down (required) |
Focus the neighbouring pane; with no pane that way, the stroke goes to the shell |
pane.resize |
direction: left, right, up, or down (required) |
Grow the focused pane toward an edge |
pane.kill_focused |
none | Terminate the focused pane’s shell |
pane.layout |
layout: stack, grid, dwindle, tall, fat, horizontal, or vertical (required, grid) |
Apply and retain an automatic layout; dwindle tiles each new pane into the focused pane’s longer side, Hyprland-style |
pane.equalize |
none | Reset all current dividers to equal ratios |
pane.rotate |
direction: clockwise or counterclockwise (optional, clockwise) |
Rotate the pane tree |
pane.move_to_edge |
edge: left, right, up, or down (required) |
Move the focused pane to an outer edge |
pane.next_layout |
none | Cycle grid → dwindle → tall → fat → horizontal → vertical → stack |
pane.toggle_float |
none | Float the focused pane or dock it again |
pane.rename |
name (required; empty restores the default) |
Rename the focused pane’s shell |
pane.rename_prompt |
none | Open the interactive rename editor for the focused pane |
Examples:
map ctrl+alt+g pane.equalize
map ctrl+alt+space>1 pane.layout dwindle
map ctrl+alt+space>e pane.move_to_edge left
map ctrl+alt+space>r pane.rotate direction=counterclockwise
Windows
A window is a multiplexer workspace inside the current launcher session and may contain several panes. Window actions need split panes on.
| Action ID | Arguments | What it does |
|---|---|---|
window.new |
none | Create a new window with a fresh shell |
window.close |
none | Close the current window and all of its panes |
window.next |
none | Switch to the next window; on the Display place, to the next app |
window.previous |
none | Switch to the previous window; on the Display place, to the previous app |
window.select |
index: 0 … 64 (required, zero-based) |
Select a window by index |
window.rename |
name (required; display is capped at 14 characters; empty restores the automatic label) |
Rename the current multiplexer window label |
window.rename_prompt |
none | Open the interactive rename dialog |
Sessions and workspaces
| Action ID | Arguments | What it does |
|---|---|---|
session.new |
name (optional), failsafe (optional, false) |
Create a terminal session |
session.browser |
none | “Sessions”: open the searchable session/window/pane browser |
session.panel |
none | “Toggle sessions”: toggle the sessions panel under the status row |
session.clone_current |
none | Create a fresh session at the focused pane’s CWD |
session.next |
none | Activate the next session |
session.previous |
none | Activate the previous session |
session.close_current |
none | Close the current session and its windows/panes |
session.activate_by_index |
index: 0 … 64 (required, zero-based) |
Activate a session by drawer position |
session.rename |
name (required; capped at 8 characters; empty clears it) |
Rename the current session’s drawer label |
session.rename_at_index |
index: 0 … 64, name (both required; name capped at 8 characters) |
Rename a session by its zero-based drawer index |
session.rename_prompt |
none | Open the interactive session rename dialog |
workspace.picker |
none | “Load workspace”: open the saved-workspace picker |
workspace.save_prompt |
none | Prompt for a name and save the live topology |
workspace.save |
name (required), overwrite (optional, false), captureCommands (optional, false) |
Save sessions, windows, panes, ratios, focus, and CWDs |
workspace.load |
name (required), mode: append or replace (optional, append), runCommands (optional, false) |
Restore a saved workspace |
workspace.list |
none | Return the saved workspace list; mainly useful to internal callers |
workspace.delete |
name (required) |
Delete a saved workspace definition |
Workspace names are at most 64 Unicode code points. They must begin with a letter or digit and may then contain letters, digits, spaces, _, -, or .. Do not include .json.
Places on the wall
| Action ID | Arguments | What it does |
|---|---|---|
wall.go |
page: widgets, terminal, display, left, or right (required, terminal) |
“Go to place”: show a chosen place, or the one to the left or right |
wall.widgets |
none | “Go to Widgets”: show the widget grid |
wall.terminal |
none | “Go to Terminal”: show the terminal |
wall.display |
none | “Go to Display”: show the Linux display |
Keyboard
All of these need the in-app keyboard, except keyboard.toggle_enabled, voice.dictate and mouse.toggle.
| Action ID | Arguments | What it does |
|---|---|---|
keyboard.cycle_layout |
direction: forward or backward (optional, forward) |
“Cycle keyboard layout”: next layout in the keyboard’s ring |
keyboard.select_layout |
layout (required): a catalogue id such as latn_dvorak, or main for ~/.termux/keyboard/layout.xml |
“Switch keyboard layout” |
keyboard.cycle_form |
direction: forward or backward (optional, forward) |
“Next keyboard type”: docked, floating, split |
keyboard.set_form |
form: docked, floating, or split (required) |
“Keyboard type” |
keyboard.show |
source: manual or focus (optional, manual) |
Open the on-screen keyboard |
keyboard.hide |
source: manual or focus (optional, manual) |
Close the on-screen keyboard |
keyboard.clipboard |
none | “Clipboard history”: open or close the list of things you copied |
keyboard.toggle_enabled |
none | “Keyboard on/off”: while off, tapping the terminal no longer raises it |
voice.dictate |
none | “Dictate”: start or stop a dictation on any place |
mouse.toggle |
none | “Mouse mode”: touches become mouse clicks and drags in the terminal; on the Display place a touchpad takes the keyboard’s place |
keyboard.select_layout and keyboard.set_form have their own rows in the palette’s Keyboard section rather than an argument prompt.
Terminal and clipboard
| Action ID | Arguments | What it does |
|---|---|---|
terminal.toggle_scratchpad |
none | Show or hide the persistent scratchpad shell |
terminal.toggle_soft_keyboard |
none | Show or hide the keyboard |
terminal.toggle_toolbar |
none | “Toggle dock”: show or hide the terminal dock |
terminal.font_size_increase |
none | Increase font size |
terminal.font_size_decrease |
none | Decrease font size |
terminal.select_url |
none | Open the URL picker for scrollback links |
terminal.select_at_cursor |
none | “Select at cursor”: start a selection on the word under the shell cursor |
terminal.select_all |
none | “Select all”: select the whole buffer, scrollback included |
terminal.hints |
none | “Quick select”: label visible URLs, paths, hashes, and source locations |
terminal.search_scrollback |
none | Search terminal history and jump to a result |
terminal.share_transcript |
none | Share the complete terminal transcript |
terminal.share_selected |
none | Share the currently selected terminal text |
terminal.reset |
none | Reset emulator state and scrollback without killing the shell |
terminal.jump_previous_prompt |
none | Jump to the previous OSC 133 prompt marker |
terminal.jump_next_prompt |
none | Jump to the next OSC 133 prompt marker |
terminal.action_sheet |
none | “Terminal actions”: open the curated terminal action sheet |
terminal.state |
resetPerformance (optional, false) |
Return terminal hierarchy/performance state; mainly useful to internal callers |
extrakeys.edit |
none | “Edit key row”: open the visual Extra Keys editor |
clipboard.paste |
none | Paste clipboard contents into the focused shell |
clipboard.copy_selected |
none | Copy the current terminal selection |
clipboard.copy_selected and terminal.share_selected are available only while text is selected; terminal.select_at_cursor and terminal.select_all start a selection for them. Prompt jumping needs OSC 133 shell integration; fish 4 emits it natively, while Bash and zsh can source the scripts installed under ~/.termux/shell-integration/.
Appearance
| Action ID | Arguments | What it does |
|---|---|---|
appearance.set_wallpaper |
none | Open the wallpaper picker |
appearance.toggle_wallpaper |
none | Enable or disable terminal wallpaper mode |
appearance.toggle_cursor_trail |
none | Enable or disable the animated cursor trail |
appearance.surface_editor |
none | “Surface editor”: tune the dock, keyboard, status bar, and terminal surfaces. The old id appearance.glass_lab still works |
fonts.pick |
none | “Terminal fonts”: open the terminal font picker |
fonts.install |
id (required), nerd_icons (optional, true), ligatures: never, cursor, or always (optional, cursor), weight: 0 … 1000 (optional, 0) |
Download, verify and activate a catalogue font family |
Launcher and apps
| Action ID | Arguments | What it does |
|---|---|---|
app.open_settings |
none | Open launcher settings |
app.open_help |
none | “Help”: open help for the place you are on |
app.open_look_and_feel |
none | “Look and feel settings”: open the Theme & fonts page |
app.open_apps_bar |
none | Open Apps bar settings |
app.command_palette |
none | Open the searchable command palette |
app.launch |
query (required) |
Launch by exact package, app label, or stable ID, with fuzzy ranking fallback |
app.key_inspector |
none | Toggle the key-event and terminal-byte inspector |
app.open_app_drawer |
none | “App drawer”: open the app drawer |
app.open_drawer |
none | “Open sessions”: open the sessions drawer |
app.close_drawer |
none | “Close sessions”: close the sessions drawer |
app.launch is not offered as a palette row with a prompt: the palette’s Apps section already gives one row per app.
Examples:
map --label WhatsApp ctrl+alt+space>w app.launch com.whatsapp
map --label Maps ctrl+alt+shift+m app.launch "Google Maps"
map ctrl+alt+shift+k app.key_inspector
Actions for scripts only
Some actions exist for launcherctl and the HTTP API, so a program in a shell can drive panes. They never appear in the palette:
| Action ID | Arguments | What it does |
|---|---|---|
pane.open |
command, cwd, title, focus (true), tag (all optional) |
Open a pane, optionally running a command |
pane.list |
none | List windows and panes with their ids |
pane.focus / pane.close |
id (required) |
Focus a pane, or close one the API opened |
pane.write |
id (required), text (required, up to 16 KiB), enter (optional, false) |
Type into a pane the API opened |
pane.read |
id (required), lines (1 … 500, optional, 60) |
Read the last lines of a pane the API opened |
These are in the registry, so a binding can name them, though they are meant for scripts. A few other launcherctl commands (window open, agent, notify, progress, clipboard copy and clipboard paste) run on ids that are not in the registry at all (window.open, agent.status, shell.notify, shell.progress, clipboard.write, clipboard.read); the binding file rejects them.
Actions versus shell input
The binding file also provides three binding-only operations that are not registry IDs:
| Operation | Form | Purpose |
|---|---|---|
| Send text | send-text "text\n" |
Write literal decoded text to the focused shell |
| Send a key | send-key ctrl+c |
Encode one terminal key stroke |
| Leave a modal keymap | pop-mode |
Pop the current custom mode |
Use the command palette to discover current action IDs on the phone.
Full details: Command palette and actions
Reference
Keyboard layout schema
The embedded keyboard is a Termux-focused port of Unexpected Keyboard. A custom file at ~/.termux/keyboard/layout.xml replaces the complete bundled layout, including every center key and swipe slot.
Start by copying the example layout shipped with your installed build:
mkdir -p ~/.termux/keyboard
cp ~/.termux/launcher/examples/keyboard-layout.xml ~/.termux/keyboard/layout.xml
termux-reload-settings
The example is close to the bundled layout but not identical: the bundled bottom row marks its keys with role="action" and role="space_bar", and its Ctrl key has clipboard history on the north-east swipe (ne="loc switch_clipboard" sw="loc meta"), where the example has those two swapped.
Delete the live file to return to the bundled layout. If an edit is invalid, the launcher keeps the last working layout and then falls back to the bundled one.
The layout ring
Your file is one layout among several. Settings → Keyboard → Layouts → Layouts (“Choose the layouts the keyboard cycles through”) sets the ring, up to 16 layouts. Your layout.xml appears there as main (the bundled QWERTY when the file is absent). From a binding or an Extra Key, keyboard.select_layout main switches to it and keyboard.cycle_layout moves through the ring; see Action reference.
The label font and key radius are settings, not layout attributes: Typeface on the Keyboard theme page (the font picker browses internal storage, and icon glyphs keep the symbols font), and Key radius in the Layout editor.
Document structure
<?xml version="1.0" encoding="utf-8"?>
<keyboard bottom_row="false" name="My terminal keyboard" script="latin">
<row>
<key c="q" ne="1" se="loc esc"/>
<key c="w" nw="~" ne="2"/>
</row>
<row height="0.95">
<key width="1.7" c="ctrl" nw="fn"/>
<key width="4.0" c="space" w="cursor_left" e="cursor_right"/>
<key width="1.7" c="enter"/>
</row>
<modmap>
<fn a="q" b="f1"/>
<fn a="a" b="esc"/>
</modmap>
</keyboard>
<keyboard> attributes
| Attribute | Meaning |
|---|---|
name |
Display name for the layout |
script |
Main script identifier; must not be empty when present |
numpad_script |
Optional script for the numeric layout; defaults to script |
bottom_row |
Whether the keyboard adds its bundled bottom row; default true |
embedded_number_row |
Whether to insert the optional number row inside this layout; default false |
locale_extra_keys |
Whether enabled optional keyboard keys may be merged into free loc slots; default true |
width |
Optional total layout width in relative units; otherwise computed from the widest row |
<row> attributes
| Attribute | Meaning |
|---|---|
height |
Relative row height; default 1 |
shift |
Empty vertical space above the row; default 0 |
scale |
Rescale all keys so the row reaches this total width |
<key> attributes
Each key has a center value plus eight swipe directions:
nw / key1 n / key7 ne / key2
w / key5 c / key0 e / key6
sw / key3 s / key8 se / key4
The short compass names and long key0 … key8 names are synonyms; do not put both synonyms on the same key.
| Attribute | Meaning |
|---|---|
c or key0 |
Center/tap value |
nw, n, ne, w, e, sw, s, se |
Swipe values |
width |
Relative key width; default 1 |
shift |
Empty horizontal space before the key; default 0 |
anticircle |
Value produced by the counter-clockwise circle gesture |
indication |
Label drawn on the key without changing its output |
role |
Rendering/behaviour role: normal (default), action, space_bar, or suggestion |
split_before |
"true" starts the right half of the split keyboard at this key |
split_at |
Cuts a wide key this many units from its left edge when the keyboard is split |
Prefix a value with loc to reserve its position while letting the keyboard’s optional-key setting decide whether it is visible:
<key c="a" nw="loc tab"/>
<key c="backspace" ne="loc delete"/>
Where the split keyboard cuts
When the keyboard is in its split form, each row is parted in two. You choose where with two marks:
split_before="true"on a key starts the right half at that key.split_at="<units>"on a wide key cuts that key the given number of units from its left edge. Both pieces type the centre value; west-side swipes stay on the left piece, east-side swipes on the right, and north, south and the circle gesture go to the wider piece.
A row with neither mark is cut at its middle. Putting both marks on one key is an error, as is a negative or non-finite split_at. Both halves are the same width, set against the screen edges with one straight gap between them.
The bundled and example layouts cut q–t |
y–p, a–g |
h–l, shift z–v |
b–m backspace, and the space bar with split_at="2.5": |
<key c="y" split_before="true" ne="6" sw="^"/>
<key width="4.0" role="space_bar" c="space" split_at="2.5" w="cursor_left" e="cursor_right"/>
Put launcher actions on keys and swipes
Any slot can call an argument-free launcher action:
tool:<action-id>
tool:<action-id>:<display-glyph>
The optional suffix is only the glyph drawn on the key. It is not an argument list.
This space bar keeps cursor sliding on east/west and puts multiplexer navigation on the corners:
<key width="4.0" c="space"
w="cursor_left" e="cursor_right" s="switch_backward"
n="tool:app.command_palette:⌘"
nw="tool:window.previous:◧"
ne="tool:window.next:◨"
sw="tool:session.previous:↰"
se="tool:session.next:↳"/>
You can also dedicate ordinary keys or unused swipe slots:
<row height="0.8">
<key c="tool:pane.split_vertical:⇳" n="tool:pane.split_horizontal:⇔"/>
<key c="tool:pane.equalize:=" n="tool:pane.next_layout:⟳"/>
<key c="tool:pane.toggle_float:◈" n="tool:terminal.toggle_scratchpad:▣"/>
<key c="tool:terminal.search_scrollback:⌕" n="tool:terminal.hints:?"/>
</row>
Actions that require arguments cannot run from layout.xml. For example, app.launch requires a query, pane.layout requires a layout name, and pane.move_to_edge requires an edge. Assign those in termux-launcher-bindings.conf or use an argument-capable Termux Extra Key.
Modifier layers
<modmap> remaps one key value to another while a keyboard modifier is active. Supported mapping tags are <fn>, <shift>, and <ctrl>; a is the original value and b is the mapped value.
<modmap>
<fn a="q" b="f1"/>
<fn a="w" b="f2"/>
<fn a="a" b="esc"/>
<fn a="s" b="tab"/>
<fn a="n" b="^C:ctrl,c"/>
<fn a="m" b="^D:ctrl,d"/>
</modmap>
The shipped layout maps Fn+Q … Fn+P to F1 … F10, Fn+Z/X to F11/F12, navigation across the home row, and Fn+N/M to Ctrl+C/D.
Key values
Values may be literal Unicode text, named keyboard keys such as esc, tab, enter, backspace, delete, home, page_up, left, or f1, keyboard modifiers such as ctrl, alt, shift, and fn, built-in gestures such as cursor_left, or tool: launcher actions.
The shipped layout also uses these named values: switch_clipboard (clipboard history), switch_numeric, switch_backward, config, change_method, voice_typing, compose, meta, insert and page_down.
XML-reserved characters must be escaped: write &, <, >, and ". The shipped example also demonstrates escaped keyboard parser values such as \?, \#, \@, and \\.
Safe editing and limits
- Maximum file size: 512 KiB.
- Maximum rows: 16.
- Maximum keys per row: 32.
- Maximum total keys: 512.
- Only one
<modmap>is allowed. - Save atomically when possible: write a temporary file beside
layout.xml, then rename it. - Run
termux-reload-settingsafter every edit; no app restart is required.
When debugging, begin with the shipped example and make one change at a time. The command palette lists action IDs, while Key inspector shows the key event and terminal bytes produced by ordinary keyboard values.
Full details: Keyboard
Reference
Config files
The launcher keeps its files in your home directory, almost all of them under ~/.termux/. After editing a file, apply it without restarting:
termux-reload-settings
A running terminal session keeps the environment it started with, so terminal-term reaches only sessions opened after the reload.
Files you edit
| File | What it controls |
|---|---|
~/.termux/termux.properties |
Classic Termux properties plus the extra-keys row |
~/.config/termux/termux.properties |
Read only when ~/.termux/termux.properties is absent; only one properties file is ever read |
~/.termux/termux-launcher-bindings.conf |
Custom keybindings, see Keybindings config |
~/.termux/fonts.conf |
Fonts, nerd-font symbols, ligatures, box drawing, see Terminal fonts |
~/.termux/fonts.d/ |
Drop-in font fragments, including the app-managed 10-launcher.conf |
~/.config/kitty/kitty.conf |
Font directives only, read before fonts.d/; every other kitty setting is ignored, and the app never creates this file |
~/.termux/font.ttf, font-italic.ttf |
The classic Termux fonts, also what Termux:Styling writes |
~/.termux/colors.properties |
Terminal colours (only when Wallpaper colors is off) |
~/.termux/launcher-theme.properties |
Overrides for single launcher interface colour tokens; applies on top even with Wallpaper colors on |
~/.termux/theme-templates/<id>/ |
Your own templates for Tools that follow the terminal colours: a template.properties manifest, the file to render and its hooks |
~/.termux/keyboard/layout.xml |
In-app keyboard layout, see Keyboard layout schema |
~/.termux/app-categories.conf |
The drawer’s app categories, one section per category, editable by hand |
~/.termux/boot/ |
Scripts the Termux:Boot add-on runs after a restart (a Termux:Boot fork exists for every edition) |
Files the launcher writes
| File | What it holds |
|---|---|
~/.termux/material-colors.sh, .properties |
The palette in use right now, exported for scripts and prompts |
~/.termux/material-colors-dark.sh, .properties and material-colors-light.sh, .properties |
The same keys for each mode, written on every palette pass |
~/.termux/fonts/ |
Font files the font picker installs |
~/.termux/fonts.d/10-launcher.conf |
The font picker’s managed selection |
~/.termux/workspaces/<name>.json |
Workspaces saved with workspace.save |
~/.termux/shell-integration/termux-launcher.{bash,zsh} |
Prompt-marking scripts, see Shell integration |
~/.termux/theme-templates/.applied |
The list of templates that are switched on |
The examples folder
~/.termux/launcher/examples/ holds reference copies, rewritten every time the app starts: README.md, termux-launcher-bindings.conf, fonts.conf, termux.properties, keyboard-layout.xml and kitty.conf. Do not edit them; edit the live files.
termux-launcher-bindings.conf, fonts.conf and termux.properties are seeded on install, fully commented out, if they are absent, so a fresh install behaves as if they did not exist. They are written only when missing, so your edits survive updates. layout.xml is never seeded: as soon as it exists it replaces the bundled keyboard, so copy it yourself:
mkdir -p ~/.termux/keyboard
cp ~/.termux/launcher/examples/keyboard-layout.xml ~/.termux/keyboard/layout.xml
Keybindings
~/.termux/termux-launcher-bindings.conf binds keys to any action from the Command Palette; the palette is also where you discover action ids and see which keys are already taken. The format is kitty-inspired:
# map <keys> <action> [arguments]
map --label WhatsApp ctrl+alt+space>w app.launch com.whatsapp
map ctrl+alt+shift+n session.new name=build
map ctrl+alt+t send-text "echo hi\n"
map ctrl+alt+space>c send-key ctrl+c
# chords: press the first stroke, release, press the next
map ctrl+alt+space>t app.launch org.telegram.messenger
# a tmux-style prefix for every ctrl+alt binding
leader ctrl+b
# remove a default binding
unmap ctrl+alt+s
Worth knowing:
- Modifier names and multi-character keys are case-insensitive, and
controlis an alias forctrl. A single upper-case letter means Shift:Ctrl+Alt+Risctrl+alt+shift+r, notctrl+alt+r. - Binding a key that already has a default replaces the default:
map ctrl+alt+w …removes “kill focused pane”,map ctrl+alt+enter …removes “New pane”. Repeating the same keys on multiple lines runs the actions in order. - Arguments can be positional or
name=value. --when splits-on/--when splits-offmakes a binding apply only in one terminal mode.--label "Name"(up to 32 characters) names the binding in the keybind legend.leader <stroke>gives every rootctrl+alt+…binding a second spelling: the leader, then the key.- Bad lines are skipped and reported; the rest of the file keeps working. A file over 256 KiB, 4,096 lines or 4,096 characters on a line is ignored whole.
- The Key inspector action in the palette shows you exactly what the app sees when you press something.
termux.properties
All the upstream Termux properties work. The shipped file is nearly all commented out and documents the launcher’s own keys:
terminal-term: theTERMexported to new sessions (defaultxterm-256color).volume-keys: the volume keys change the volume by default;volume-keys = virtualrestores upstream Termux behaviour.extra-keysandextra-keys2: the extra-keys row and its second page. The launcher’s own row is the built-in default, so there is nothing to set to get it; the property only overrides it. Theextrakeys.editaction (“Edit key row”) opens an editor that writes the property for you.
Extra keys can trigger any palette action with the tool: syntax:
extra-keys = [[ \
{macro: "tool:workspace.picker", display: "▤"}, \
{macro: "tool:terminal.toggle_scratchpad", display: "▣"}, \
{macro: "tool:pane.move_to_edge:edge=left", display: "⇤"} \
]]
tool:<action-id> runs the action; arguments ride along as :name=value pairs. The shipped example has only a commented upstream-style row.
Fonts
~/.termux/fonts.conf is a kitty-style font config. Without it, the classic ~/.termux/font.ttf / font-italic.ttf still work. With it you get per-style fonts, nerd-font symbol mapping and ligature control:
font_family path=~/.termux/fonts/MapleMono[wght].ttf
italic_font path=~/.termux/fonts/MapleMono-Italic[wght].ttf
font_variations regular wght=400
font_variations bold wght=700
# pull icon glyphs from a nerd font without affecting text width
symbol_map U+E000-U+F8FF path=~/.termux/fonts/MapleMono-NF-Regular.ttf
disable_ligatures cursor
font_features regular +zero
Also available: bold_font, bold_italic_font, family="…" to use an installed family instead of a file, and modify_font to nudge cell width/height, baseline and underline metrics. The example file documents every directive.
Precedence, strongest first: ~/.termux/fonts.conf, then ~/.termux/fonts.d/*.conf in filename order, then font.ttf / font-italic.ttf. Below all three, ~/.config/kitty/kitty.conf is read first, for its font directives only.
Colors
By default the terminal is themed from your wallpaper (Material You), with Wallpaper colors on the Theme & fonts settings page (search Settings for it, or run Look and feel settings from the command palette). While that is on, colors.properties is ignored. Turn it off, or apply a Termux:Styling scheme (which turns it off), to use your own colors.properties, same format as upstream Termux. With the switch off, an edited colors.properties is re-read when it changes, and the material-colors exports are rewritten.
~/.termux/launcher-theme.properties overrides single interface colour tokens, one per line, on top of either palette:
primary = color3
surface_container_high = lighten(surface, 0.08)
The current palette is exported for scripts as ~/.termux/material-colors.sh / .properties, see tlstore.
Shell integration
Prompt-jumping (Jump to previous/next prompt in the palette) needs the shell to mark prompts (OSC 133). fish 4 does this out of the box. For bash or zsh, source the script the app keeps in place:
# ~/.bashrc
source ~/.termux/shell-integration/termux-launcher.bash
# ~/.zshrc
source ~/.termux/shell-integration/termux-launcher.zsh
The app updates those scripts itself but never touches your rc files.
Full details: Config files
Reference
Permissions & Shizuku
A home screen that is also a terminal ends up asking for a few permissions that look scary out of context. Every one of them is optional: deny a permission and only that one feature stops working.
Where to manage them
Settings → Permissions & services, in the App group, has two sections:
- Permissions: Files and media, Wallpaper access, Notification access, Accessibility service, App notifications, All app permissions.
- Connected services: Shizuku and Termux:API.
The big ones
Default home app. The whole point; Android asks you to confirm this the normal way. You can always switch back in system settings.
Notification access. Powers the notification dots on app icons, the dock popup you get when swiping up on an app with an unread notification (including the quick-reply box), and the now-playing media widget in the status bar. It also feeds Notification history (Settings → Notifications → Notification history, “Choose which apps are recorded for Termux”), which is off until you pick apps: only those apps are recorded, in ~/.launcherctl/launcher.db, for 30 days by default, with one-time codes masked by default. Scripts read it with launcherctl notifications. Without notification access: no dots, no quick reply, no media controls, no history.
Accessibility service. Used for exactly one thing: locking the screen when you double-tap the alphabets row. The service is declared with screen-reading and gesture abilities disabled; it can only send the “lock screen” action. If you would rather not enable an accessibility service, the Shizuku lock method does the same job.
Shizuku. The privileged backend, if you have Shizuku or Sui set up. It powers the nicer screen-lock method (a real power-button keypress, so the system’s screen-off animation plays and secure lock behaves normally), detailed CPU/memory and top-process data in the status bar, foreground-process labels on window pills, and the privileged lane that runs btop from tlstore. See Shizuku below.
Files and media (All files access). Only for the classic Termux ~/storage symlinks (termux-setup-storage), so the shell can reach your shared storage. The launcher itself does not touch your files.
Wallpaper access. Lets the glass bars blur the system wallpaper. Until it is allowed, the glass bars render flat.
Microphone. Asked the first time you dictate, from the keyboard’s Enter key or the Dictate action (see Voice & speech). The microphone is open only while a dictation runs. Termux:API’s termux-microphone-record also relies on it, since add-ons share the app’s identity.
Shizuku
Shizuku lets an app use selected system APIs with the privileges of ADB or root, after you approve
that app. No root is required: on supported Android versions Shizuku can start through Wireless
debugging. Without it, RAM totals remain available through Android’s ActivityManager, and CPU
uses a best-effort direct /proc fallback when the device allows it; Shizuku adds detailed
CPU/memory and process data, foreground-process labels on window pills, the Shizuku screen-lock
method, and the privileged lane.
Setting it up:
- Install the Shizuku app and start its service through Wireless debugging or root, per its own setup guide.
- In Termux Launcher, open Settings → Permissions & services → Shizuku.
- Choose Request Shizuku permission and approve the dialog that appears over the launcher.
The Shizuku page:
- Backend Status shows the backend and its state, for example
SHIZUKU · READY. - Backend policy: Enable privileged features, Prefer Shizuku backend, Allow shell
fallback (su or
rishmay be used when Shizuku is unavailable) and Request Shizuku permission. The three switches are on by default. - Privileged lane: Lane status (Running as shell, Connecting to Shizuku…, Shizuku isn’t
running, Shizuku permission missing, or Off) and Allow catalog tools to run as shell via
Shizuku (on by default). Catalog tools marked
priv=shizuku, such asbtop, start as the shell user from a terminal session; with the switch off, every request from the terminal is refused.
The privileged backend only initializes once you connect it from that page; until then the CPU
card uses whatever the direct /proc fallback can read, and detailed process data may be absent.
A Wireless-debugging start does not survive a reboot: start Shizuku again, then revisit the page to
reconnect; everything falls back to unprivileged data in the meantime.
rish in the terminal. Shizuku’s shell helper gives a command an ADB-privileged shell, which
can read system process data and run tools outside Termux’s app sandbox: anything installed under
/data/local/tmp runs from there, so tools the sandbox would otherwise refuse to execute work when
launched through rish.
Common failures:
- Permission denied, or the prompt never appears. Open the Shizuku app and check Termux Launcher’s authorization there (grant it, or reset a previous denial), then use Request Shizuku permission again. Also check that Enable privileged features and Prefer Shizuku backend are on, on the same page.
- Unavailable after a reboot. Expected for a Wireless-debugging start; start Shizuku again and reconnect from the page.
- The stats card shows
--,stale, or no processes. CPU percentages need two samples to compute a change, and the process list only samples while the card is open; leave it open a few seconds first. If it still does not recover, confirm the page reports SHIZUKU · READY, then restart Shizuku and re-check permission.
Regular permissions
| Permission | Used for |
|---|---|
| Internet | pkg installs, model downloads, weather |
| Approximate location | Weather, only when no city is set under Settings → Status bar → Weather → Location. With a city picked there, or on the Before you start card, no location permission is asked; Use my location there runs the permission prompt instead. Weather feeds the status-bar card and the Weather widget |
| Calendar | The built-in Agenda and Calendar widgets; asked when you place one |
| Bind widgets | Placing app widgets on the Widgets place (Android shows its own allow dialog) |
| Notifications | The persistent session notification, download progress, notifications sent from the shell (launcherctl notify, OSC 99) and the progress ring (launcherctl progress) |
| Wake lock | The “Acquire wakelock” action on the session notification, to keep long jobs alive |
| Battery optimization exemption | Asked when you take a wakelock, so Doze does not kill your session |
| Display over other apps | Lets a background command (Tasker / RUN_COMMAND) bring the terminal to the front; deny and you tap the notification instead |
| Vibrate | Terminal bell, keyboard haptics and launcherctl vibrate |
| Change audio settings | Volume control (launcherctl volume) |
| Set wallpaper | The wallpaper picker’s separate Home and Lock wallpapers, and launcherctl wallpaper set FILE --home\|--lock\|--both |
| Run at boot | Boot scripts (Termux:Boot style) |
| Install packages | So APKs opened from the terminal can be handed to the system installer |
One custom permission is defined by the app for running commands: com.termux.permission.RUN_COMMAND on the Termux edition, com.termux.launcher.nix.permission.RUN_COMMAND on the Nix edition, and io.vaj.tl.permission.RUN_COMMAND on the demo edition. Other apps must hold it, and you must approve them, before they can run commands in your shell. That protects you; the launcher does not ask you for it.
A second one, <package>.permission.X11_DISPLAY, guards the built-in X11 display server; only apps signed with the same key can hold it.
Inherited from upstream
A few declarations come along from the Termux base and do nothing in normal use: several system-level entries (READ_LOGS, DUMP, WRITE_SECURE_SETTINGS, usage stats) that Android will not grant to a regular app anyway; they only matter if you deliberately grant them over ADB, for example for the phantom-process-killer workaround. Also declared without a prompt: SET_ALARM, FOREGROUND_SERVICE_SPECIAL_USE, SET_WALLPAPER_HINTS and ACCESS_NETWORK_STATE.
Worth noting what’s absent: the app does not request QUERY_ALL_PACKAGES. The app drawer uses the normal launcher-app query every home screen uses.
Full details: Shizuku
Reference
Nix edition
The Nix edition (com.termux.launcher.nix) pairs the launcher with Nix-on-Droid: the full nixpkgs collection, declarative configs, generations and rollback, and it coexists with a stock Termux install. First bootstrap is bigger and slower than the standard edition; keep the app in the foreground and use decent wifi.
Getting it
Grab it from Releases. From v1.0.0 every edition shares one version number and the edition is in the tag: nix-vX.Y.Z for Nix, vX.Y.Z for the Termux edition, vaj-vX.Y.Z for VAJ. APKs are named termux-app_v<version>_<edition>_<variant>_<abi>. Older releases keep the -nix tags they shipped under.
Companions are the nix-v* tagged TLNix:API, TLNix:Styling and TLNix:Boot.
First setup
When the bootstrap asks about flakes, answer yes. Once you have a shell, initialize the launcher config:
cd ~/.config/nix-on-droid
rm flake.nix nix-on-droid.nix
nix flake init -t github:PickleHik3/nix-on-droid/launcher-nix#launcher
nix-on-droid switch --flake ~/.config/nix-on-droid
setup-toolkits
Note: the
rmreplaces the bootstrap config on purpose. Only run it in that directory, and back up first if you already customized it.
After a nix-on-droid switch, open a new session: the login shell and PATH only change for sessions started after the switch.
Toolkits
setup-toolkits is a checklist over the launcher flake’s optional toolkits; it is the Nix edition’s equivalent of tlstore. The toolkits are shell, eye-candy, editor, build, node, go, python and animated-logo. Shell, eye-candy, editor and build are on by default; editor implies build. Rerun it anytime:
setup-toolkits --list # current selection, no changes
setup-toolkits --all # everything prebuilt (not the animated logo)
setup-toolkits --essentials # shell + eye candy only
setup-toolkits --enable node,go # add toolkits, leave the rest
setup-toolkits --disable eyeCandy # remove one
setup-toolkits --enable python --no-switch # edit the file, switch later yourself
setup-toolkits --animated-logo # patched fastfetch with an animated GIF; compiles on the phone
Which file do I edit?
| File | What belongs here |
|---|---|
flake.nix |
Inputs (which nixpkgs, home-manager, the fork), overlays |
nix-on-droid.nix |
The environment: login shell, /etc, base packages, Android integration |
home.nix |
Your user: dotfiles, per-user packages, session variables |
toolkits.nix |
Which package groups home.nix installs; this is what setup-toolkits writes |
Daily commands
nix search nixpkgs ripgrep
nix profile install nixpkgs#ripgrep
nix profile list
nix profile remove ripgrep
nix-on-droid rollback
Put durable choices in the flake instead of piling up an unexplained profile; rollback is only useful when generations mean something. Your flake is your backup: with it you can rebuild the whole environment on a new phone.
Extras
- The Android filesystem is visible at
/android. - A small sshd toolset:
sshd-start,sshd-status,sshd-stopandsshd-autostart on|off, on port 8023 by default. - Graphical apps come from
nixpkgs, see Linux display.
Coming from the VAJ edition? The VAJ → Nix migration guide covers backing up your home, installing side by side and replacing APT packages from nixpkgs.
Full details: Nix edition: a beginner’s guide
Reference
On-device AI API
The local server behind On-device AI speaks OpenAI- and Ollama-compatible HTTP on localhost. The settings screens and the tai CLI are described there.
Base URL & auth
The server writes its current address and token to disk on every start:
BASE=$(sed -n '1p' ~/.launcherctl/endpoint) # e.g. http://127.0.0.1:54298
TOKEN=$(cat ~/.launcherctl/token)
export OPENAI_BASE_URL="$BASE/v1"
export OPENAI_API_KEY="$TOKEN"
OpenAI-compatible clients append /v1; Ollama-compatible clients use the base address as-is. Send the token as Authorization: Bearer <token> or X-Api-Key: <token>. GET / and OPTIONS never require it.
The default bind mode is localhost (127.0.0.1). Require API token (default on, in the Server group of Settings → On-device AI) lets you turn token checks off for localhost only; any placeholder key then works for local CLI clients. LAN access, beside it, is opt-in, always requires the token whatever that switch says, and turns itself off after 12 hours (the server goes back to localhost and the token is rotated). Rotate the token any time with POST /v1/auth/rotate.
Rate limits
Each route has its own per-minute token-bucket limit; a request over it gets 429 with Retry-After.
| Routes | Limit |
|---|---|
Reads (/v1/ai/status, /v1/ai/runtime, /v1/ai/models, GET /v1/models) |
120 / min |
| Chat, responses, completions, embeddings | 60 / min |
/v1/tokenize |
120 / min |
/v1/audio/transcriptions |
240 / min |
/v1/audio/speech, /v1/ai/speak |
60 / min |
/v1/ai/speak/stop |
120 / min |
/v1/ai/images/generations |
12 / min |
/v1/ai/images/cancel |
60 / min |
| Model load / download / import | 20 / min |
| Model delete / cancel download / unload / preflight / keep-warm | 30–60 / min |
GET /v1/ai/logs |
60 / min |
DELETE /v1/ai/logs, DELETE /v1/ai/runtime/history, POST /v1/ai/tier |
30 / min |
| Benchmark run (native or harness) | 6 / min |
| Benchmark records (read) | 120 / min |
POST /v1/ai/benchmarks/skip-wait |
60 / min |
| Token rotate | 5 / min |
Errors & status
OpenAI-style routes (/v1/*) return a nested OpenAI error object:
{
"error": {
"message": "Model 'MODEL_ID' does not exist",
"type": "invalid_request_error",
"code": "model_not_found"
}
}
Ollama-style routes (/api/*) return a flat {"error": "..."} string. Common codes: model_not_found (404), capability_not_supported (400), mnn_required_tool_call_missing (422), embedding_tokenizer_missing (409), embedding_memory (503, with Retry-After), unsupported_audio_output (501), benchmark_running (409). Missing or wrong bearer token is 401.
Streaming
/v1/chat/completions, /v1/responses and /v1/completions stream Server-Sent Events (text/event-stream) with "stream": true, ending in a data: [DONE] line. /api/chat and /api/generate stream newline-delimited JSON by default, the final line flagged "done": true. A non-streaming request returns one JSON body.
OpenAI-compatible
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/models |
List installed, loadable models with On-device AI metadata |
| GET | /v1/models/{id} |
Return one model object |
| POST | /v1/chat/completions |
Chat: text, image/audio input, tools, SSE streaming |
| POST | /v1/responses |
Stateless Responses adapter (text/image input, function calls) |
| POST | /v1/completions |
Legacy text completions, SSE streaming |
| POST | /v1/embeddings |
Embeddings for models advertising text_embeddings |
| POST | /v1/tokenize |
{model, input} in, {tokens: n} out, the model’s own tokenizer |
| POST | /v1/audio/transcriptions |
Speech to text with the voice input speech model |
| POST | /v1/audio/speech |
Speech output with the voice model (KittenTTS nano 0.8) |
| POST | /v1/ai/speak |
What tai speak uses: plays the text on the phone (JSON or plain-text body, ?voice=&speed=; ?format=wav returns the audio instead) |
| POST | /v1/ai/speak/stop |
Stops whatever the phone is reading aloud |
| POST | /v1/ai/images/generations |
Text to image with an MNN diffusion model (tai image) |
| POST | /v1/ai/images/cancel |
Discards the image generation in flight |
GET /v1/models metadata
Each entry carries On-device AI fields prefixed with an underscore, so plain OpenAI clients ignore them: _backend (litert-lm or mnn-llm), _capabilities (what this install currently serves: text_chat, image_input, audio_input, tool_use, text_embeddings, code, and so on; identical to _endpoint_capabilities), _source_capabilities (informational upstream claims only; never use these to decide what to send), _default_max_output_tokens, _endpoint_context_window and _source_context_window (the window this device serves against the model’s own limit), and _tool_mode (prompt_fallback for MNN, native when advertised for LiteRT). Descriptive fields: _format, _display_name, _architecture, _quantization, _size, _sha256, _license, _capabilities_verified, _capability_source, _capability_verification.
Embedding models (any text_embeddings entry) also carry:
_endpoint_dimensions: the native output width._endpoint_matryoshka_dims: the only sizesdimensionsaccepts; any other value gets400 invalid_dimensions._endpoint_normalized: true: every vector is L2-normalised._endpoint_max_batch(64) and_endpoint_throttle_while_generating: "priority"._revision: a hash of the model file’s name, size and modification time, to tell a client when to rebuild its index._endpoint_windows: the installed window sizes, ascending, for the.tfliteEmbeddingGemma. EmbeddingGemma 2.litertlmreports_endpoint_context_window: 2048and no_endpoint_windows.
The response also has a top-level models array: a Codex-style list of the chat models with tool use and a context of 16384 tokens or more. Image models are never listed. The speculative-decoding probe is cached per model file, so /v1/models answers in about a second.
Multimodal LiteRT ids
A multimodal LiteRT model shares one downloaded file across up to three ids. By default (the split exposure) the bare id is text-only, and only the extra modalities get a suffix:
gemma-4-e2b-it-litert-lm- text (default, canonical id)gemma-4-e2b-it-litert-lm-vision- text + imagegemma-4-e2b-it-litert-lm-audio- text + audio
Only one modality is loaded at a time. Under Endpoint exposure → Combined the bare id serves every modality and there are no suffixed ids; under Both the bare id is combined and the text-only split gets its own -text suffix. Sending media to a model id that does not cover it returns capability_not_supported.
POST /v1/embeddings
Only models advertising text_embeddings are accepted; others return capability_not_supported. The recommended model is embeddinggemma-2-text-vision-440m; embeddinggemma-2-text-270m is the smaller one, and embeddinggemma-300m still works. input is a string or array of strings, at most _endpoint_max_batch (currently 64); a larger batch is 413 batch_too_large, never a silent drop.
Extra fields beyond model, input, dimensions:
input_type:"query"or"document"(default). EmbeddingGemma was trained with a task prefix per input; the server adds it, counted inside the model’s window (task: search result | query:for a query,title: <title or none> | text:for a document). Other families ignore it.title: an optional document heading folded into the document prefix. Ignored forinput_type: "query".encoding_format:"float"(default) or"base64".
For the .tflite EmbeddingGemma, each data[i] also reports tokens (the count before truncation, prefix included) and truncated (whether the body was cut to fit the window; the prefix itself is never cut). EmbeddingGemma 2 .litertlm and MNN embedders expose no token count: their items have no tokens, truncated is always false and usage.prompt_tokens is 0, so treat a missing tokens as “length unknown”.
EmbeddingGemma 2 answers with _runtime: "litertlm-embedding", 768 dimensions with the same Matryoshka sizes and the same input_type/title prefixes, text input only. A .litertlm import whose file name contains embeddinggemma is classed as text_embeddings.
While a chat generation is running elsewhere, embeddings run at background thread priority so they do not slow the live reply. A load that does not fit in memory returns 503 with Retry-After and code: "embedding_memory", distinct from the plain rate-limit 429.
{"model": "embeddinggemma-2-text-vision-440m", "input": "hello world", "input_type": "document", "title": "note"}
POST /v1/audio/transcriptions and /v1/audio/speech
/v1/audio/transcriptions is speech to text with the installed voice-input speech model (Whisper ACFT or Parakeet): multipart file (WAV or raw 16 kHz PCM16), model, language, prompt, response_format (json, text or verbose_json). It needs no chat model loaded and is never queued behind a chat generation.
/v1/audio/speech speaks with the installed voice model (KittenTTS nano 0.8): input, voice, speed, response_format (wav for the whole clip, pcm streamed per sentence). Both return 400 (stt_model_not_configured / tts_model_not_installed) if the matching model is not installed.
POST /v1/ai/images/generations
Text to image through the MNN diffusion engine, on the GPU (OpenCL) by default. Fields: model (a registered image model) or model_path (a model folder), model_type (sd15, taiyi or sana), prompt, size (512x512 only for Stable Diffusion and Taiyi; multiples of 32 from 256 to 2048 for Sana), n (only 1), steps (1-100), seed, cfg_scale and image (Sana only), backend (opencl or cpu), memory_mode (0, 1 or 2), output (a .png path to write instead of returning base64), response_format (b64_json) and stream.
With "stream": true the answer is SSE image_generation.progress events, then one image_generation.completed. Errors: 409 image_generation_active while another image is being made, 409 insufficient_memory, 400 tokenizer_mtok_missing for a package without tokenizer.mtok, 501 mnn_image_unavailable. POST /v1/ai/images/cancel discards the result when the engine returns. Loading an image model with tai load answers 400 image_model_not_loadable.
POST /v1/tokenize
{model, input} in, {tokens: n} out: the model’s own tokenizer, no task prefix or BOS/EOS framing added, just the raw count, for splitting long text on real token counts. Only the .tflite EmbeddingGemma (300M) has a tokenizer; EmbeddingGemma 2 .litertlm and every other backend return 501 capability_not_supported.
Ollama-compatible
| Method | Path | Purpose |
|---|---|---|
| GET | /api/version |
Compatibility version |
| GET | /api/tags |
List installed models |
| POST | /api/show |
Show one model’s details and capabilities |
| GET | /api/ps |
Show the loaded model |
| POST | /api/chat |
Chat and tool calls, NDJSON streaming |
| POST | /api/generate |
Prompt-style generation, NDJSON streaming |
| POST | /api/embed |
Create embeddings when supported |
| POST | /api/embeddings |
Legacy alias: {model, prompt} in, {embedding: [...]} out |
| POST | /api/pull /api/create /api/push /api/copy /api/delete |
501, not emulated; use the catalogue or import flow instead |
Benchmark
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/ai/benchmarks/run |
Run a benchmark |
| GET | /v1/ai/benchmarks |
Records and the leaderboard |
| DELETE | /v1/ai/benchmarks |
Clear records, or one model’s with {"modelId": "…"} |
| POST | /v1/ai/benchmarks/skip-wait |
Skip the cool-down between models (tai benchmark --skip-wait) |
| POST | /v1/ai/runtime/benchmark |
LiteRT-LM’s own native benchmark() (tai benchmark --native) |
POST /v1/ai/benchmarks/run takes {"models": ["id", …], "preset": "quick|standard", "processors": ["cpu", "gpu"], "eagle": false, "compare": false, "force": false, "stream": false}. With "stream": true the response is a server-sent event stream (entry_start, phase_start, token, phase_done, entry_done, skipped, error, paused, done); without it, the response is the done summary. GET /v1/ai/benchmarks returns {records, leaderboard: {ranked, broken}, benchVersions}. While a benchmark runs, chat and load requests are refused with benchmark_running.
Model management
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/ai/status |
Overall status, settings, limitations |
| GET | /v1/ai/runtime |
Loaded model and runtime state |
| DELETE | /v1/ai/runtime/history |
Clear the runtime history (tai runtime --clear-history) |
| GET | /v1/ai/logs |
The diagnostics log (tai logs) |
| DELETE | /v1/ai/logs |
Clear the diagnostics log (tai logs --clear) |
| GET | /v1/ai/models |
Detailed model registry |
| GET | /v1/ai/models/downloads |
Download progress/history |
| POST | /v1/ai/models/import |
Register a supported local package |
| POST | /v1/ai/models/download |
Download a model from a URL |
| POST | /v1/ai/models/download-catalog |
Download a catalogue model |
| POST | /v1/ai/models/downloads/cancel | pause | resume | prioritize |
Manage a download |
| POST | /v1/ai/models/delete |
Delete an installed user model |
| POST | /v1/ai/models/load | /v1/ai/runtime/load |
Load a model into the runtime |
| POST | /v1/ai/models/unload | /v1/ai/runtime/unload |
Unload the active model |
| POST | /v1/ai/runtime/preflight |
Check whether a model can load safely |
| POST | /v1/ai/runtime/keep-warm |
Keep a model loaded temporarily |
| POST | /v1/ai/runtime/cancel |
Cancel the active generation or benchmark |
| POST | /v1/ai/tier |
Developer override of the RAM tier (no tai flag) |
| POST | /v1/auth/rotate |
Rotate the API token and rewrite ~/.launcherctl/ |
POST /v1/ai/runtime/preflight checks ABI, API level, bundled native libraries, model package readability/format, memory, accelerator policy and known backend history, without touching native runtime code.
Example requests
curl -sS -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID","messages":[{"role":"user","content":"hi"}]}' \
"$OPENAI_BASE_URL/chat/completions"
curl -sS -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"embeddinggemma-2-text-vision-440m","input":"hello world"}' \
"$OPENAI_BASE_URL/embeddings"
Full details: LauncherCtl API (Local AI Endpoint)