Compare commits

...
2 Commits
Author SHA1 Message Date
Codex b64ef21831 Add Tailscale API probe action
Gitea Smoke / smoke (push) Successful in 0s
Gitea Android APK / build (push) Successful in 11m40s
2026-07-06 07:14:01 +00:00
Codex e077fa5d14 Add tmux terminal shortcut controls
Gitea Smoke / smoke (push) Successful in 0s
Gitea Android APK / build (push) Successful in 12m0s
2026-07-06 03:47:56 +00:00
3 changed files with 92 additions and 23 deletions
+8 -1
View File
@@ -9,6 +9,8 @@ APIs directly:
- server URL management for Tailscale `100.x.y.z:3000` APIs
- quick server selectors for `100.89.0.2`, `100.89.0.4`, `100.89.0.9`,
`100.89.0.11`, and `100.89.0.116`
- one-tap probe for the preset Tailscale APIs, showing health/version and
session counts
- native tmux session list
- create, rename, send command, split pane, select pane, kill pane, pin, mute,
and kill session through HTTP API
@@ -18,7 +20,8 @@ APIs directly:
- native API action center for health, server status, timeline, preferences,
kanban projects, group messages, hook events, image file/URL upload, image
preview info, and native image preview display
- mobile soft-key row for tmux-oriented input
- mobile soft-key row for tmux-oriented input, including tmux prefix, detach,
new window, previous/next window, Ctrl keys, arrows, page keys, and paste
- automatic update checks against a GitHub Release manifest
- APK download, SHA-256 verification, and installer handoff
@@ -101,6 +104,10 @@ escape filtering. It is enough for shell-oriented remote testing, but it is not
yet a complete xterm-compatible renderer for full-screen TUIs such as `vim` or
`top`.
The terminal toolbar and shortcut row include tmux prefix helpers. The app sends
the same control bytes a keyboard would send, for example `Ctrl+B`, `Ctrl+B d`,
`Ctrl+B c`, `Ctrl+B n`, and `Ctrl+B p`.
All app features are native Android controls. Complex server objects such as
kanban projects, preferences, timeline events, group messages, and image metadata
currently use native forms plus native JSON detail dialogs; image preview uses a
@@ -447,6 +447,7 @@ public final class MainActivity extends Activity {
private void showMainActions() {
String[] items = {
"Health",
"Probe Tailscale APIs",
"Server status",
"Timeline",
"Preferences",
@@ -476,63 +477,66 @@ public final class MainActivity extends Activity {
showRaw("Health", () -> api.health());
break;
case 1:
showRaw("Server status", () -> api.serverStatus());
probeServerProfiles();
break;
case 2:
showRaw("Timeline", () -> api.timeline(50));
showRaw("Server status", () -> api.serverStatus());
break;
case 3:
showRaw("Preferences", () -> api.preferences());
showRaw("Timeline", () -> api.timeline(50));
break;
case 4:
showRaw("All session details", () -> api.sessionsAll());
showRaw("Preferences", () -> api.preferences());
break;
case 5:
showRaw("Pane details", () -> api.sessionsPanes());
showRaw("All session details", () -> api.sessionsAll());
break;
case 6:
showRaw("Kanban projects", () -> api.kanbanProjects());
showRaw("Pane details", () -> api.sessionsPanes());
break;
case 7:
promptCreateKanbanProject();
showRaw("Kanban projects", () -> api.kanbanProjects());
break;
case 8:
promptDeleteKanbanProject();
promptCreateKanbanProject();
break;
case 9:
promptRemoveKanbanSession();
promptDeleteKanbanProject();
break;
case 10:
promptGroupMessages();
promptRemoveKanbanSession();
break;
case 11:
promptSendGroupMessage();
promptGroupMessages();
break;
case 12:
promptScanGroupMessage();
promptSendGroupMessage();
break;
case 13:
promptPostHookEvent();
promptScanGroupMessage();
break;
case 14:
promptUploadImageFile();
promptPostHookEvent();
break;
case 15:
promptUploadImageUrl();
promptUploadImageFile();
break;
case 16:
promptImagePreviewInfo();
promptUploadImageUrl();
break;
case 17:
promptOpenImagePreview();
promptImagePreviewInfo();
break;
case 18:
openExternalUrl(BuildConfig.DEFAULT_APK_URL);
promptOpenImagePreview();
break;
case 19:
showUpdateSourcePicker();
openExternalUrl(BuildConfig.DEFAULT_APK_URL);
break;
case 20:
showUpdateSourcePicker();
break;
case 21:
showPermissionsAndUpdateStatus();
break;
default:
@@ -620,7 +624,12 @@ public final class MainActivity extends Activity {
"Page up",
"Page down",
"Session status",
"Send command"
"Send command",
"Tmux prefix",
"Detach tmux client",
"New tmux window",
"Next tmux window",
"Previous tmux window"
};
new AlertDialog.Builder(this)
.setTitle(sessionName)
@@ -655,6 +664,21 @@ public final class MainActivity extends Activity {
case 6:
promptSendCommand(sessionName);
break;
case 7:
sendTerminalInput("\u0002");
break;
case 8:
sendTerminalInput("\u0002d");
break;
case 9:
sendTerminalInput("\u0002c");
break;
case 10:
sendTerminalInput("\u0002n");
break;
case 11:
sendTerminalInput("\u0002p");
break;
default:
break;
}
@@ -1000,6 +1024,36 @@ public final class MainActivity extends Activity {
showMessage("Update source: " + url);
}
private void probeServerProfiles() {
progressBar.setVisibility(View.VISIBLE);
setStatus("Probing Tailscale APIs...");
executor.execute(() -> {
StringBuilder text = new StringBuilder();
for (String url : SERVER_PROFILES) {
text.append(url).append('\n');
try {
SessionApiClient client = new SessionApiClient(url);
JSONObject health = new JSONObject(client.health());
List<SessionSummary> sessions = client.getSessions();
text.append(" ok: true\n");
text.append(" version: ")
.append(health.optString("version", "unknown"))
.append(" ")
.append(health.optString("commit", ""))
.append('\n');
text.append(" sessions: ").append(sessions.size()).append('\n');
} catch (Exception error) {
text.append(" failed: ").append(error.getMessage()).append('\n');
}
text.append('\n');
}
runOnUiThread(() -> {
progressBar.setVisibility(View.GONE);
showTextDialog("Tailscale APIs", text.toString());
});
});
}
private void openInstallPermissionSettings() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
showMessage("Install permission is allowed on this Android version");
@@ -1169,6 +1223,13 @@ public final class MainActivity extends Activity {
addSoftKey(row, "^A", "\u0001");
addSoftKey(row, "^E", "\u0005");
addSoftKey(row, "^V", "\u0016");
addSoftKey(row, "^Z", "\u001a");
addSoftKey(row, "^\\", "\u001c");
addSoftKey(row, "Tmux", "\u0002");
addSoftKey(row, "Detach", "\u0002d");
addSoftKey(row, "NewWin", "\u0002c");
addSoftKey(row, "NextWin", "\u0002n");
addSoftKey(row, "PrevWin", "\u0002p");
addSoftKey(row, "Left", "\u001b[D");
addSoftKey(row, "Down", "\u001b[B");
addSoftKey(row, "Up", "\u001b[A");
+3 -2
View File
@@ -68,14 +68,15 @@ not kill a tmux session.
Implemented now:
- configurable base URL, defaulting to `http://127.0.0.1:3000`
- configurable base URL, defaulting to `http://100.89.0.116:3000`
- support for Tailscale URLs such as `http://100.x.y.z:3000`
- native session list from `GET /api/sessions`
- create, rename, command send, split, pane select, pane kill, pin, mute, and
kill session through documented session/preference endpoints
- basic live terminal through `/ws/terminal`
- native event stream through `/ws/events`
- bottom shortcut bar for `Esc`, `Tab`, `Ctrl+C`, arrows, page keys, and paste
- bottom shortcut bar for `Esc`, `Tab`, `Ctrl+C`, `Ctrl+V`, arrows, page keys,
tmux prefix actions, and paste
- shortcut delivery through the terminal WebSocket `input` message
- native action center for health, server status, timeline, preferences, kanban
projects, group messages, hook events, image file/URL upload, image preview