Compare commits

...
3 Commits
Author SHA1 Message Date
Codex 974abed278 Connect touch scrolling to tmux history
Gitea Smoke / smoke (push) Successful in 2s
Gitea Android APK / build (push) Failing after 55s
2026-07-14 15:52:43 +00:00
Codex 47af792d62 Pin tmux status above soft keys
Gitea Smoke / smoke (push) Successful in 0s
Gitea Android APK / build (push) Failing after 8m24s
2026-07-14 15:36:49 +00:00
Codex 0c7dba0ef1 Fix terminal composer and status layout
Gitea Smoke / smoke (push) Successful in 0s
Gitea Android APK / build (push) Successful in 12m20s
2026-07-14 15:23:22 +00:00
@@ -29,6 +29,7 @@ import android.text.method.LinkMovementMethod;
import android.view.Gravity; import android.view.Gravity;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsets; import android.view.WindowInsets;
@@ -166,6 +167,8 @@ public final class MainActivity extends Activity {
private int terminalViewMode = TERMINAL_VIEW_CHAT; private int terminalViewMode = TERMINAL_VIEW_CHAT;
private int terminalKeyPage; private int terminalKeyPage;
private int terminalHistoryOffset; private int terminalHistoryOffset;
private float terminalTouchStartY;
private int terminalTouchStartScrollY;
private int terminalReconnectAttempt; private int terminalReconnectAttempt;
private int terminalConnectionGeneration; private int terminalConnectionGeneration;
private int eventReconnectAttempt; private int eventReconnectAttempt;
@@ -1734,17 +1737,19 @@ public final class MainActivity extends Activity {
terminalText.setLineSpacing(0, 1.05f); terminalText.setLineSpacing(0, 1.05f);
terminalText.setGravity(Gravity.BOTTOM | Gravity.START); terminalText.setGravity(Gravity.BOTTOM | Gravity.START);
terminalText.setTextIsSelectable(terminalSelectionEnabled); terminalText.setTextIsSelectable(terminalSelectionEnabled);
terminalText.setPadding(dp(2), dp(8), dp(2), dp(8)); terminalText.setPadding(dp(2), dp(8), dp(2), 0);
terminalText.setBackgroundColor(COLOR_TERMINAL_BG); terminalText.setBackgroundColor(COLOR_TERMINAL_BG);
terminalChatList = new LinearLayout(this); terminalChatList = new LinearLayout(this);
terminalChatList.setOrientation(LinearLayout.VERTICAL); terminalChatList.setOrientation(LinearLayout.VERTICAL);
terminalChatList.setPadding(dp(10), dp(12), dp(10), dp(18)); terminalChatList.setPadding(0, dp(12), 0, 0);
terminalChatList.addView(projectStateText("Loading conversation..."), matchWrap()); terminalChatList.addView(projectStateText("Loading conversation..."), matchWrap());
showTerminalView(terminalViewMode, false); showTerminalView(terminalViewMode, false);
terminalScroll.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> terminalScroll.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
resizeTerminalToViewport(false)); resizeTerminalToViewport(false));
terminalScroll.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> terminalScroll.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) ->
terminalFollowOutput = !view.canScrollVertically(1)); terminalFollowOutput = !view.canScrollVertically(1));
terminalScroll.setOverScrollMode(View.OVER_SCROLL_ALWAYS);
terminalScroll.setOnTouchListener(this::handleTerminalScrollTouch);
root.addView(terminalScroll, new LinearLayout.LayoutParams( root.addView(terminalScroll, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
0, 0,
@@ -1766,6 +1771,8 @@ public final class MainActivity extends Activity {
}); });
inputField.post(() -> { inputField.post(() -> {
inputField.requestFocus(); inputField.requestFocus();
inputField.setSelection(inputField.length());
inputField.setCursorVisible(true);
hideKeyboard(); hideKeyboard();
}); });
} }
@@ -2055,7 +2062,7 @@ public final class MainActivity extends Activity {
boolean user = "user".equals(message.role); boolean user = "user".equals(message.role);
LinearLayout block = new LinearLayout(this); LinearLayout block = new LinearLayout(this);
block.setOrientation(LinearLayout.VERTICAL); block.setOrientation(LinearLayout.VERTICAL);
block.setPadding(dp(2), dp(5), dp(2), dp(7)); block.setPadding(dp(10), dp(5), dp(10), dp(7));
TextView content = conversationContent(message, false); TextView content = conversationContent(message, false);
content.setText((user ? " " : "") + defaultValue(message.content, "(empty)")); content.setText((user ? " " : "") + defaultValue(message.content, "(empty)"));
@@ -2098,7 +2105,7 @@ public final class MainActivity extends Activity {
summary.setTextColor(COLOR_ACCENT_WARM); summary.setTextColor(COLOR_ACCENT_WARM);
summary.setTextSize(10); summary.setTextSize(10);
summary.setTypeface(Typeface.MONOSPACE, Typeface.BOLD); summary.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
summary.setPadding(dp(2), dp(6), dp(2), dp(6)); summary.setPadding(dp(10), dp(6), dp(10), dp(6));
summary.setOnClickListener(view -> { summary.setOnClickListener(view -> {
if (!terminalExpandedMessages.add(groupKey)) { if (!terminalExpandedMessages.add(groupKey)) {
terminalExpandedMessages.remove(groupKey); terminalExpandedMessages.remove(groupKey);
@@ -2112,7 +2119,7 @@ public final class MainActivity extends Activity {
for (ConversationMessage message : tools) { for (ConversationMessage message : tools) {
LinearLayout detail = new LinearLayout(this); LinearLayout detail = new LinearLayout(this);
detail.setOrientation(LinearLayout.VERTICAL); detail.setOrientation(LinearLayout.VERTICAL);
detail.setPadding(dp(14), dp(3), dp(2), dp(7)); detail.setPadding(dp(18), dp(3), dp(10), dp(7));
TextView title = bodyText(conversationToolTitle(message)); TextView title = bodyText(conversationToolTitle(message));
title.setTextColor(COLOR_ACCENT_WARM); title.setTextColor(COLOR_ACCENT_WARM);
title.setTextSize(9); title.setTextSize(9);
@@ -2157,7 +2164,7 @@ public final class MainActivity extends Activity {
private View conversationLiveTerminalPanel() { private View conversationLiveTerminalPanel() {
LinearLayout panel = new LinearLayout(this); LinearLayout panel = new LinearLayout(this);
panel.setOrientation(LinearLayout.VERTICAL); panel.setOrientation(LinearLayout.VERTICAL);
panel.setPadding(dp(2), dp(10), dp(2), dp(8)); panel.setPadding(dp(2), dp(10), dp(2), 0);
LinearLayout heading = new LinearLayout(this); LinearLayout heading = new LinearLayout(this);
heading.setOrientation(LinearLayout.HORIZONTAL); heading.setOrientation(LinearLayout.HORIZONTAL);
@@ -2188,10 +2195,17 @@ public final class MainActivity extends Activity {
terminalLiveOutputText.setTextColor(COLOR_TEXT_MUTED); terminalLiveOutputText.setTextColor(COLOR_TEXT_MUTED);
terminalLiveOutputText.setTextSize(11); terminalLiveOutputText.setTextSize(11);
terminalLiveOutputText.setTypeface(Typeface.MONOSPACE); terminalLiveOutputText.setTypeface(Typeface.MONOSPACE);
terminalLiveOutputText.setIncludeFontPadding(false);
terminalLiveOutputText.setHorizontallyScrolling(false);
terminalLiveOutputText.setLineSpacing(0, 1.05f);
terminalLiveOutputText.setGravity(Gravity.BOTTOM | Gravity.START);
terminalLiveOutputText.setBackgroundColor(COLOR_TERMINAL_BG);
terminalLiveOutputText.setPadding(0, dp(5), 0, 0); terminalLiveOutputText.setPadding(0, dp(5), 0, 0);
terminalLiveOutputText.setMovementMethod(LinkMovementMethod.getInstance()); terminalLiveOutputText.setMovementMethod(LinkMovementMethod.getInstance());
terminalLiveOutputText.setHighlightColor(Color.TRANSPARENT); terminalLiveOutputText.setHighlightColor(Color.TRANSPARENT);
terminalLiveOutputText.setTextIsSelectable(true); terminalLiveOutputText.setTextIsSelectable(true);
terminalLiveOutputText.addOnLayoutChangeListener((view, left, top, right, bottom,
oldLeft, oldTop, oldRight, oldBottom) -> resizeTerminalToViewport(false));
panel.addView(terminalLiveOutputText, matchWrap()); panel.addView(terminalLiveOutputText, matchWrap());
return panel; return panel;
} }
@@ -3559,24 +3573,33 @@ public final class MainActivity extends Activity {
if (terminalText == null || terminalScroll == null) { if (terminalText == null || terminalScroll == null) {
return; return;
} }
int width = terminalScroll.getWidth(); TextView viewportText = terminalViewMode == TERMINAL_VIEW_CHAT && terminalLiveOutputText != null
? terminalLiveOutputText : terminalText;
int width = viewportText.getWidth();
if (width <= 0) {
width = terminalScroll.getWidth();
}
int height = terminalScroll.getHeight(); int height = terminalScroll.getHeight();
if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {
return; return;
} }
int horizontalPadding = terminalText.getPaddingLeft() + terminalText.getPaddingRight(); int horizontalPadding = viewportText.getPaddingLeft() + viewportText.getPaddingRight();
int verticalPadding = terminalText.getPaddingTop() + terminalText.getPaddingBottom(); int verticalPadding = viewportText.getPaddingTop() + viewportText.getPaddingBottom();
float charWidth = terminalText.getPaint().measureText("0000000000") / 10f; float charWidth = viewportText.getPaint().measureText("0000000000") / 10f;
if (charWidth <= 0f) { if (charWidth <= 0f) {
charWidth = dp(8); charWidth = dp(8);
} }
int lineHeight = terminalText.getLineHeight(); int lineHeight = viewportText.getLineHeight();
if (lineHeight <= 0) { if (lineHeight <= 0) {
lineHeight = dp(16); lineHeight = dp(16);
} }
int usableWidth = Math.max(1, width - horizontalPadding); int usableWidth = Math.max(1, width - horizontalPadding);
int cols = clamp((int) Math.floor(usableWidth / charWidth), MIN_TERMINAL_COLS, MAX_TERMINAL_COLS); int cols = clamp((int) Math.floor(usableWidth / charWidth), MIN_TERMINAL_COLS, MAX_TERMINAL_COLS);
int rows = clamp((height - verticalPadding) / lineHeight, MIN_TERMINAL_ROWS, MAX_TERMINAL_ROWS); int rows = clamp((height - verticalPadding) / lineHeight, MIN_TERMINAL_ROWS, MAX_TERMINAL_ROWS);
if (terminalLiveOutputText != null) {
terminalLiveOutputText.setMinHeight(rows * lineHeight);
terminalLiveOutputText.setGravity(Gravity.BOTTOM | Gravity.START);
}
if (cols == terminalCols && rows == terminalRows && !forceSend) { if (cols == terminalCols && rows == terminalRows && !forceSend) {
return; return;
} }
@@ -3774,6 +3797,31 @@ public final class MainActivity extends Activity {
: "Terminal history · " + terminalHistoryOffset + " lines back"); : "Terminal history · " + terminalHistoryOffset + " lines back");
} }
private boolean handleTerminalScrollTouch(View view, MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
terminalTouchStartY = event.getY();
terminalTouchStartScrollY = terminalScroll == null ? 0 : terminalScroll.getScrollY();
return false;
}
if (event.getActionMasked() != MotionEvent.ACTION_UP || terminalScroll == null) {
return false;
}
float distance = event.getY() - terminalTouchStartY;
if (Math.abs(distance) < dp(48)) {
return false;
}
int movedLocally = Math.abs(terminalScroll.getScrollY() - terminalTouchStartScrollY);
int lineHeight = terminalLiveOutputText == null ? dp(16) : terminalLiveOutputText.getLineHeight();
int lines = clamp(Math.round(Math.abs(distance) / Math.max(1, lineHeight)), 4, terminalRows);
if (distance > 0 && !terminalScroll.canScrollVertically(-1) && movedLocally <= dp(24)) {
terminalScroll.post(() -> scrollTerminalHistory(-lines));
} else if (distance < 0 && terminalHistoryOffset > 0
&& !terminalScroll.canScrollVertically(1) && movedLocally <= dp(24)) {
terminalScroll.post(() -> scrollTerminalHistory(lines));
}
return false;
}
private void setTerminalKeyPage(int page) { private void setTerminalKeyPage(int page) {
terminalKeyPage = (page + 4) % 4; terminalKeyPage = (page + 4) % 4;
updateAccessoryTabs(); updateAccessoryTabs();
@@ -3988,7 +4036,12 @@ public final class MainActivity extends Activity {
input.setHintTextColor(COLOR_TEXT_DIM); input.setHintTextColor(COLOR_TEXT_DIM);
input.setTextSize(13); input.setTextSize(13);
input.setPadding(dp(8), dp(5), dp(8), dp(5)); input.setPadding(dp(8), dp(5), dp(8), dp(5));
input.setBackground(inputBackground()); input.setBackground(composerInputBackground());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
GradientDrawable cursor = rounded(COLOR_ACCENT, 1, Color.TRANSPARENT, 0);
cursor.setSize(dp(2), dp(20));
input.setTextCursorDrawable(cursor);
}
} }
private Button compactButton(String label, View.OnClickListener listener) { private Button compactButton(String label, View.OnClickListener listener) {
@@ -4021,6 +4074,14 @@ public final class MainActivity extends Activity {
return rounded(COLOR_FIELD, 8, COLOR_BORDER, 1); return rounded(COLOR_FIELD, 8, COLOR_BORDER, 1);
} }
private StateListDrawable composerInputBackground() {
StateListDrawable states = new StateListDrawable();
states.addState(new int[]{android.R.attr.state_focused},
rounded(COLOR_CARD_ALT, 7, COLOR_ACCENT, 1));
states.addState(new int[]{}, rounded(COLOR_CARD, 7, COLOR_BORDER, 1));
return states;
}
private GradientDrawable rounded(int color, int radiusDp, int strokeColor, int strokeDp) { private GradientDrawable rounded(int color, int radiusDp, int strokeColor, int strokeDp) {
GradientDrawable drawable = new GradientDrawable(); GradientDrawable drawable = new GradientDrawable();
drawable.setColor(color); drawable.setColor(color);