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

  1. Hold a pane corner The corner tab opens; tap ? to see what every control does.
  2. Hold the apps row Choose your pinned apps.
  3. Swipe down on the apps row The app drawer; Home brings you back.
  4. Swipe up on the bottom border The keyboard comes up; swipe down to put it away.
  5. Swipe up on the space bar The command palette.
  6. Hold on terminal text Select and copy; in htop or nvim your finger becomes the mouse.
  7. Hold Ctrl + Alt Every key shows its shortcut.
  8. 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

  1. Download the main APK for your edition from the project’s Releases.
  2. Optionally, add the matching Termux:API or Termux:Styling. Styling is rarely needed, since fonts and colours are handled in the app.

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

  1. The app downloads and unpacks the Termux bootstrap.
  2. 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.
  3. 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:

  1. How you will use it: the usage mode (see Launcher mode below).
  2. Hold a corner of a pane, then tap ? for help on what you are touching.
  3. Hold the page border and drag to slide to the next place.
  4. Swipe up on the bottom border to raise the keyboard, down to put it away.
  5. Swipe down on the top border to open the status bar, up to fold it.
  6. Hold an empty spot in the apps row to pin apps.
  7. Swipe down on the apps row to open the app drawer (swipe right on a left rail, left on a right rail).
  8. Swipe up on the space bar for the command palette.
  9. After an update only: the offer of the new extra-keys row.
  10. Use Termux Launcher as your home screen.
  11. A closing card with tips: holding on the terminal, the Ctrl+Alt shortcut strip, the Appearance corner, minimal mode, dictation, tlstore extras 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

  1. Hold an empty spot on the grid. An empty page says “Long-press to add widgets”.
  2. Choose Add widget. The same menu has Edit widgets and Remove page.
  3. 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.

  1. 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.
  2. 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, not WhatsApp. Leave it blank for any app.
  • Keywords in title or text: a plain substring, ignoring case, not a pattern. otp matches “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.properties too (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 the tool: 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 PGUP on top and TAB CTRL ALT LEFT DOWN RIGHT PGDN below. 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.toggle to 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.

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, ftp and ftps links can be opened; anything else, file links 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, chafa and 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-hooks wires 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 an OSC 99 request: named, replaceable, with an urgency level) or set a progress ring on its window’s pill (launcherctl progress, the same as OSC 9;4). OSC 99 buttons become notification actions and report back which one you pressed; launcherctl notify --close closes 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 source line (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+Arrow resizes 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+C opens a new one.
  • Ctrl+Alt+[ / Ctrl+Alt+] or Ctrl+Alt+Left / Ctrl+Alt+Right switch to the previous or next window.
  • Ctrl+Alt+1…9 jump to a window by number.
  • Ctrl+Alt+R renames the current window.
  • Ctrl+Alt+X closes 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+Down step to the previous or next session, and Ctrl+Alt+Shift+1…9 jump to a session by number.
  • Ctrl+Alt+Shift+C starts a new session, Ctrl+Alt+Shift+R renames the current one, and Ctrl+Alt+Shift+X closes 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:

  1. Swipe up from the space bar of the built-in keyboard.
  2. Ctrl + Alt + Shift + P on a hardware keyboard.
  3. Ctrl + Alt + Space, release, then P (two-stroke chord).
  4. 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+v finds 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 args ask 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 args row 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:

  1. ~/.config/kitty/kitty.conf: only its font directives are read (one level of include is followed), so your kitty setup carries over.
  2. ~/.termux/fonts.d/*.conf: drop-ins, in ascending filename order. The app writes exactly one of them, 10-launcher.conf.
  3. ~/.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.conf manages it.
  • Tuning: Nerd Font icons routes U+E000-U+F8FF and U+F0000-U+FFFFD to 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 three disable_ligatures values: always shaped, un-fuse under the cursor, or off. Weight is a wght slider 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.conf and nothing else; your fonts.conf and the installed font files stay. Hand-edited config explains writing your own fonts.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 ~/) or family= for an Android system family. Paths are the reliable case on Android; a family= lookup is best-effort.
  • disable_ligatures takes never (the default), cursor or always, and touches programming ligatures only; Arabic, Indic, emoji and combining-mark shaping are never affected.
  • font_features and font_variations target regular, bold, italic, bold_italic, symbols, or a named symbol map. none clears a target.
  • modify_font adjusts cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position and strikethrough_thickness. A percentage (10% to 500%) replaces the font-derived metric; a bare number or one ending in px adds 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, ~/.fonts and ~/.local/share/fonts before 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 synthesize is the default. box_drawing font turns it all off and hands every one of those code points back to your font’s glyphs.
  • box_drawing_scale sets 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 is 0.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_symbols defaults to synthesize, so separator edges sit flush with the cell and two consecutive separators butt together with no sliver of background between them. It needs box_drawing synthesize as well. Set it to font for a patched Nerd Font whose author drew their own.
  • An explicit symbol_map always 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_features and font_variations win for its cells, so neighbouring maps shape independently. Anything a map does not declare comes from the shared symbols target, which unnamed maps also use.
  • An axis a mapped face cannot honour is reported once and dropped.
  • Ceilings: 256 symbol_map lines, 1024 ranges in total, and 8 fallback_font entries.

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, .task or .tflite file. 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-warm extends 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-setup tries 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 as tlstore display.
  • launcherctl x11 gpu says which profile fits; launcherctl x11 gpu --env prints 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 :0 in 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: llvmpipe means the GPU variables are missing or do not fit this phone. Run termux-x11-gpu-setup or 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.

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 &amp;, &lt;, &gt;, and &quot;. 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-settings after 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 control is an alias for ctrl. A single upper-case letter means Shift: Ctrl+Alt+R is ctrl+alt+shift+r, not ctrl+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-off makes 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 root ctrl+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: the TERM exported to new sessions (default xterm-256color).
  • volume-keys: the volume keys change the volume by default; volume-keys = virtual restores upstream Termux behaviour.
  • extra-keys and extra-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. The extrakeys.edit action (“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:

  1. Install the Shizuku app and start its service through Wireless debugging or root, per its own setup guide.
  2. In Termux Launcher, open Settings → Permissions & services → Shizuku.
  3. 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 rish may 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 as btop, 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 rm replaces 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-stop and sshd-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 sizes dimensions accepts; any other value gets 400 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 .tflite EmbeddingGemma. EmbeddingGemma 2 .litertlm reports _endpoint_context_window: 2048 and 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 + image
  • gemma-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 for input_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)