Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c7dba0ef1 | ||
|
|
f1eb30cea0 | ||
|
|
a25a2d16b6 |
@@ -25,6 +25,7 @@ import android.os.Looper;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
|
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;
|
||||||
@@ -154,6 +155,7 @@ public final class MainActivity extends Activity {
|
|||||||
private int lastActiveSessionCount = -1;
|
private int lastActiveSessionCount = -1;
|
||||||
private TerminalScreenBuffer terminalScreen = new TerminalScreenBuffer(DEFAULT_TERMINAL_COLS, DEFAULT_TERMINAL_ROWS);
|
private TerminalScreenBuffer terminalScreen = new TerminalScreenBuffer(DEFAULT_TERMINAL_COLS, DEFAULT_TERMINAL_ROWS);
|
||||||
private final StringBuilder queuedTerminalInput = new StringBuilder();
|
private final StringBuilder queuedTerminalInput = new StringBuilder();
|
||||||
|
private final Set<String> terminalExpandedBlocks = new HashSet<>();
|
||||||
private final Set<String> terminalExpandedMessages = new HashSet<>();
|
private final Set<String> terminalExpandedMessages = new HashSet<>();
|
||||||
private final List<ConversationMessage> terminalConversationMessages = new ArrayList<>();
|
private final List<ConversationMessage> terminalConversationMessages = new ArrayList<>();
|
||||||
private boolean terminalConnected;
|
private boolean terminalConnected;
|
||||||
@@ -163,6 +165,7 @@ public final class MainActivity extends Activity {
|
|||||||
private boolean terminalFollowOutput = true;
|
private boolean terminalFollowOutput = true;
|
||||||
private int terminalViewMode = TERMINAL_VIEW_CHAT;
|
private int terminalViewMode = TERMINAL_VIEW_CHAT;
|
||||||
private int terminalKeyPage;
|
private int terminalKeyPage;
|
||||||
|
private int terminalHistoryOffset;
|
||||||
private int terminalReconnectAttempt;
|
private int terminalReconnectAttempt;
|
||||||
private int terminalConnectionGeneration;
|
private int terminalConnectionGeneration;
|
||||||
private int eventReconnectAttempt;
|
private int eventReconnectAttempt;
|
||||||
@@ -1696,6 +1699,8 @@ public final class MainActivity extends Activity {
|
|||||||
terminalSelectionEnabled = false;
|
terminalSelectionEnabled = false;
|
||||||
terminalFollowOutput = true;
|
terminalFollowOutput = true;
|
||||||
terminalKeyPage = 0;
|
terminalKeyPage = 0;
|
||||||
|
terminalHistoryOffset = 0;
|
||||||
|
terminalExpandedBlocks.clear();
|
||||||
terminalExpandedMessages.clear();
|
terminalExpandedMessages.clear();
|
||||||
terminalConversationMessages.clear();
|
terminalConversationMessages.clear();
|
||||||
terminalLiveStatusText = null;
|
terminalLiveStatusText = null;
|
||||||
@@ -1733,7 +1738,7 @@ public final class MainActivity extends Activity {
|
|||||||
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, dp(18));
|
||||||
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) ->
|
||||||
@@ -1746,7 +1751,6 @@ public final class MainActivity extends Activity {
|
|||||||
1
|
1
|
||||||
));
|
));
|
||||||
terminalAccessoryBar = createAccessoryBar();
|
terminalAccessoryBar = createAccessoryBar();
|
||||||
terminalAccessoryBar.setVisibility(terminalViewMode == TERMINAL_VIEW_CHAT ? View.GONE : View.VISIBLE);
|
|
||||||
root.addView(terminalAccessoryBar, new LinearLayout.LayoutParams(
|
root.addView(terminalAccessoryBar, new LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
dp(TERMINAL_KEYS_HEIGHT_DP)
|
dp(TERMINAL_KEYS_HEIGHT_DP)
|
||||||
@@ -1762,6 +1766,8 @@ public final class MainActivity extends Activity {
|
|||||||
});
|
});
|
||||||
inputField.post(() -> {
|
inputField.post(() -> {
|
||||||
inputField.requestFocus();
|
inputField.requestFocus();
|
||||||
|
inputField.setSelection(inputField.length());
|
||||||
|
inputField.setCursorVisible(true);
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1867,26 +1873,25 @@ public final class MainActivity extends Activity {
|
|||||||
renderTerminalConversation();
|
renderTerminalConversation();
|
||||||
} else {
|
} else {
|
||||||
terminalText.setHorizontallyScrolling(true);
|
terminalText.setHorizontallyScrolling(true);
|
||||||
terminalText.setGravity(Gravity.BOTTOM | Gravity.START);
|
|
||||||
terminalText.setMovementMethod(null);
|
terminalText.setMovementMethod(null);
|
||||||
|
terminalText.setGravity(Gravity.BOTTOM | Gravity.START);
|
||||||
terminalScroll.addView(terminalText, new ScrollView.LayoutParams(
|
terminalScroll.addView(terminalText, new ScrollView.LayoutParams(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
));
|
));
|
||||||
renderTerminalNow();
|
renderTerminalNow();
|
||||||
}
|
}
|
||||||
boolean chat = mode == TERMINAL_VIEW_CHAT;
|
|
||||||
if (terminalAccessoryBar != null) {
|
if (terminalAccessoryBar != null) {
|
||||||
terminalAccessoryBar.setVisibility(chat ? View.GONE : View.VISIBLE);
|
terminalAccessoryBar.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
if (terminalComposerTabs != null) {
|
if (terminalComposerTabs != null) {
|
||||||
terminalComposerTabs.setVisibility(chat ? View.GONE : View.VISIBLE);
|
terminalComposerTabs.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
if (inputField != null) {
|
if (inputField != null) {
|
||||||
inputField.setHint(chat ? "Message" : "type command or text");
|
inputField.setHint(mode == TERMINAL_VIEW_CHAT ? "message or command" : "type command or text");
|
||||||
}
|
}
|
||||||
if (announce) {
|
if (announce) {
|
||||||
setStatus(mode == TERMINAL_VIEW_CHAT ? "Chat view" : "Full terminal mode");
|
setStatus(mode == TERMINAL_VIEW_CHAT ? "Content view" : "Raw terminal mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2050,124 +2055,74 @@ public final class MainActivity extends Activity {
|
|||||||
|
|
||||||
private View conversationMessageRow(ConversationMessage message) {
|
private View conversationMessageRow(ConversationMessage message) {
|
||||||
boolean user = "user".equals(message.role);
|
boolean user = "user".equals(message.role);
|
||||||
boolean tool = message.isTool();
|
LinearLayout block = new LinearLayout(this);
|
||||||
LinearLayout row = new LinearLayout(this);
|
block.setOrientation(LinearLayout.VERTICAL);
|
||||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
block.setPadding(dp(10), dp(5), dp(10), dp(7));
|
||||||
row.setGravity(user ? Gravity.END : Gravity.START);
|
|
||||||
|
|
||||||
LinearLayout bubble = new LinearLayout(this);
|
TextView content = conversationContent(message, false);
|
||||||
bubble.setOrientation(LinearLayout.VERTICAL);
|
content.setText((user ? "› " : "• ") + defaultValue(message.content, "(empty)"));
|
||||||
bubble.setPadding(user || tool ? dp(12) : dp(2), dp(9), user || tool ? dp(12) : dp(4), dp(10));
|
content.setTextColor(user ? COLOR_ACCENT : COLOR_TEXT);
|
||||||
bubble.setBackground(tool
|
content.setTextSize(12);
|
||||||
? rounded(COLOR_PANEL, 8, COLOR_BORDER, 1)
|
content.setTypeface(Typeface.MONOSPACE, user ? Typeface.BOLD : Typeface.NORMAL);
|
||||||
: user
|
content.setPadding(0, 0, 0, 0);
|
||||||
? rounded(COLOR_ACCENT_DARK, 8, COLOR_ACCENT, 1)
|
block.addView(content, matchWrap());
|
||||||
: rounded(Color.TRANSPARENT, 0, Color.TRANSPARENT, 0));
|
|
||||||
|
|
||||||
TextView meta = new TextView(this);
|
if (!"complete".equals(message.status) && !message.status.isEmpty()) {
|
||||||
meta.setTextColor(user ? COLOR_ACCENT : tool ? COLOR_ACCENT_WARM : COLOR_TEXT_MUTED);
|
TextView status = bodyText(message.status);
|
||||||
meta.setTextSize(9);
|
status.setTextColor(message.status.toLowerCase(java.util.Locale.ROOT).contains("error")
|
||||||
meta.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
|
? COLOR_DANGER : COLOR_TEXT_DIM);
|
||||||
meta.setSingleLine(true);
|
status.setTextSize(9);
|
||||||
meta.setEllipsize(TextUtils.TruncateAt.END);
|
status.setTypeface(Typeface.MONOSPACE);
|
||||||
meta.setText(tool ? conversationToolTitle(message) : conversationStatusPart(message).replaceFirst("^ · ", ""));
|
status.setPadding(dp(14), dp(3), 0, 0);
|
||||||
if (tool || (!"complete".equals(message.status) && !message.status.isEmpty())) {
|
block.addView(status, matchWrap());
|
||||||
bubble.addView(meta, matchWrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean expanded = terminalExpandedMessages.contains(message.messageId);
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
if (!tool || expanded) {
|
|
||||||
bubble.addView(conversationContent(message, tool), matchWrap());
|
|
||||||
} else {
|
|
||||||
TextView collapsed = bodyText("Tap to show " + message.content.length() + " characters");
|
|
||||||
collapsed.setTextSize(10);
|
|
||||||
collapsed.setPadding(0, dp(5), 0, 0);
|
|
||||||
bubble.addView(collapsed, matchWrap());
|
|
||||||
}
|
|
||||||
if (tool) {
|
|
||||||
bubble.setOnClickListener(view -> {
|
|
||||||
if (!terminalExpandedMessages.add(message.messageId)) {
|
|
||||||
terminalExpandedMessages.remove(message.messageId);
|
|
||||||
}
|
|
||||||
renderTerminalConversation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tool && !user) {
|
|
||||||
row.addView(bubble, matchWrap());
|
|
||||||
} else if (!tool) {
|
|
||||||
LinearLayout.LayoutParams bubbleParams = new LinearLayout.LayoutParams(
|
|
||||||
0,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
||||||
0.82f
|
|
||||||
);
|
|
||||||
View spacer = new View(this);
|
|
||||||
LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0.18f
|
|
||||||
);
|
|
||||||
row.addView(spacer, spacerParams);
|
|
||||||
row.addView(bubble, bubbleParams);
|
|
||||||
} else {
|
|
||||||
row.addView(bubble, matchWrap());
|
|
||||||
}
|
|
||||||
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
);
|
);
|
||||||
rowParams.bottomMargin = dp(9);
|
params.bottomMargin = dp(5);
|
||||||
row.setLayoutParams(rowParams);
|
block.setLayoutParams(params);
|
||||||
return row;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
private View conversationToolGroup(List<ConversationMessage> tools) {
|
private View conversationToolGroup(List<ConversationMessage> tools) {
|
||||||
LinearLayout group = new LinearLayout(this);
|
LinearLayout group = new LinearLayout(this);
|
||||||
group.setOrientation(LinearLayout.VERTICAL);
|
group.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
String firstId = tools.isEmpty() ? "empty" : tools.get(0).messageId;
|
||||||
|
String lastId = tools.isEmpty() ? "empty" : tools.get(tools.size() - 1).messageId;
|
||||||
|
String groupKey = "tools:" + firstId + ":" + lastId;
|
||||||
|
boolean expanded = terminalExpandedMessages.contains(groupKey);
|
||||||
|
String firstTitle = tools.isEmpty() ? "Tool activity" : conversationToolTitle(tools.get(0));
|
||||||
|
TextView summary = new TextView(this);
|
||||||
|
summary.setText((expanded ? "▼ " : "▶ ") + tools.size() + " tool "
|
||||||
|
+ (tools.size() == 1 ? "activity" : "activities") + " · " + compactLabel(firstTitle, 44));
|
||||||
|
summary.setTextColor(COLOR_ACCENT_WARM);
|
||||||
|
summary.setTextSize(10);
|
||||||
|
summary.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
|
||||||
|
summary.setPadding(dp(10), dp(6), dp(10), dp(6));
|
||||||
|
summary.setOnClickListener(view -> {
|
||||||
|
if (!terminalExpandedMessages.add(groupKey)) {
|
||||||
|
terminalExpandedMessages.remove(groupKey);
|
||||||
|
}
|
||||||
|
renderTerminalConversation();
|
||||||
|
});
|
||||||
|
summary.setContentDescription((expanded ? "Collapse " : "Expand ") + tools.size() + " tool activities");
|
||||||
|
group.addView(summary, matchWrap());
|
||||||
|
|
||||||
HorizontalScrollView scroller = new HorizontalScrollView(this);
|
if (expanded) {
|
||||||
scroller.setHorizontalScrollBarEnabled(false);
|
for (ConversationMessage message : tools) {
|
||||||
LinearLayout chips = new LinearLayout(this);
|
LinearLayout detail = new LinearLayout(this);
|
||||||
chips.setOrientation(LinearLayout.HORIZONTAL);
|
detail.setOrientation(LinearLayout.VERTICAL);
|
||||||
chips.setGravity(Gravity.CENTER_VERTICAL);
|
detail.setPadding(dp(18), dp(3), dp(10), dp(7));
|
||||||
for (ConversationMessage message : tools) {
|
TextView title = bodyText(conversationToolTitle(message));
|
||||||
String label = compactLabel(conversationToolTitle(message), 18);
|
title.setTextColor(COLOR_ACCENT_WARM);
|
||||||
Button chip = compactButton(label, view -> {
|
title.setTextSize(9);
|
||||||
if (!terminalExpandedMessages.add(message.messageId)) {
|
title.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
|
||||||
terminalExpandedMessages.remove(message.messageId);
|
detail.addView(title, matchWrap());
|
||||||
}
|
detail.addView(conversationContent(message, true), matchWrap());
|
||||||
renderTerminalConversation();
|
group.addView(detail, matchWrap());
|
||||||
});
|
|
||||||
chip.setTextSize(9);
|
|
||||||
chip.setContentDescription("Toggle " + label);
|
|
||||||
chips.addView(chip);
|
|
||||||
}
|
|
||||||
scroller.addView(chips, new HorizontalScrollView.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
||||||
dp(34)
|
|
||||||
));
|
|
||||||
group.addView(scroller, matchWrap());
|
|
||||||
|
|
||||||
for (ConversationMessage message : tools) {
|
|
||||||
if (!terminalExpandedMessages.contains(message.messageId)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
LinearLayout detail = new LinearLayout(this);
|
|
||||||
detail.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
detail.setPadding(dp(10), dp(7), dp(10), dp(9));
|
|
||||||
detail.setBackground(rounded(COLOR_FIELD, 7, COLOR_BORDER_SOFT, 1));
|
|
||||||
TextView title = bodyText(conversationToolTitle(message));
|
|
||||||
title.setTextColor(COLOR_ACCENT_WARM);
|
|
||||||
title.setTextSize(9);
|
|
||||||
title.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
|
|
||||||
detail.addView(title, matchWrap());
|
|
||||||
detail.addView(conversationContent(message, true), matchWrap());
|
|
||||||
LinearLayout.LayoutParams detailParams = new LinearLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
detailParams.topMargin = dp(5);
|
|
||||||
group.addView(detail, detailParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
@@ -2204,43 +2159,48 @@ 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(12), dp(9), dp(12), dp(10));
|
panel.setPadding(dp(2), dp(10), dp(2), dp(8));
|
||||||
panel.setBackground(rounded(COLOR_FIELD, 8, COLOR_BORDER, 1));
|
|
||||||
panel.setOnClickListener(view -> showTerminalView(TERMINAL_VIEW_FULL, true));
|
|
||||||
|
|
||||||
LinearLayout heading = new LinearLayout(this);
|
LinearLayout heading = new LinearLayout(this);
|
||||||
heading.setOrientation(LinearLayout.HORIZONTAL);
|
heading.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
heading.setGravity(Gravity.CENTER_VERTICAL);
|
heading.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
TextView title = new TextView(this);
|
TextView title = new TextView(this);
|
||||||
title.setText("Live terminal");
|
title.setText("LIVE TERMINAL");
|
||||||
title.setTextColor(COLOR_TEXT);
|
title.setTextColor(COLOR_TEXT_DIM);
|
||||||
title.setTextSize(11);
|
title.setTextSize(9);
|
||||||
title.setTypeface(Typeface.DEFAULT_BOLD);
|
title.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
|
||||||
heading.addView(title, new LinearLayout.LayoutParams(0, dp(22), 1));
|
heading.addView(title, new LinearLayout.LayoutParams(0, dp(32), 1));
|
||||||
terminalLiveStatusText = new TextView(this);
|
terminalLiveStatusText = new TextView(this);
|
||||||
terminalLiveStatusText.setTextSize(9);
|
terminalLiveStatusText.setTextSize(9);
|
||||||
terminalLiveStatusText.setTypeface(Typeface.MONOSPACE);
|
terminalLiveStatusText.setTypeface(Typeface.MONOSPACE);
|
||||||
terminalLiveStatusText.setSingleLine(true);
|
terminalLiveStatusText.setSingleLine(true);
|
||||||
heading.addView(terminalLiveStatusText, new LinearLayout.LayoutParams(
|
heading.addView(terminalLiveStatusText, new LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
dp(22)
|
dp(32)
|
||||||
));
|
));
|
||||||
|
Button older = terminalToolButton("↑", view -> scrollTerminalHistory(-terminalRows));
|
||||||
|
older.setContentDescription("Load older terminal history");
|
||||||
|
heading.addView(older);
|
||||||
|
Button newer = terminalToolButton("↓", view -> scrollTerminalHistory(terminalRows));
|
||||||
|
newer.setContentDescription("Return toward live terminal output");
|
||||||
|
heading.addView(newer);
|
||||||
panel.addView(heading, matchWrap());
|
panel.addView(heading, matchWrap());
|
||||||
|
|
||||||
terminalLiveOutputText = new TextView(this);
|
terminalLiveOutputText = new TextView(this);
|
||||||
terminalLiveOutputText.setTextColor(COLOR_TEXT_MUTED);
|
terminalLiveOutputText.setTextColor(COLOR_TEXT_MUTED);
|
||||||
terminalLiveOutputText.setTextSize(10);
|
terminalLiveOutputText.setTextSize(11);
|
||||||
terminalLiveOutputText.setTypeface(Typeface.MONOSPACE);
|
terminalLiveOutputText.setTypeface(Typeface.MONOSPACE);
|
||||||
terminalLiveOutputText.setMaxLines(6);
|
terminalLiveOutputText.setIncludeFontPadding(false);
|
||||||
terminalLiveOutputText.setEllipsize(TextUtils.TruncateAt.END);
|
terminalLiveOutputText.setHorizontallyScrolling(true);
|
||||||
|
terminalLiveOutputText.setLineSpacing(0, 1.05f);
|
||||||
|
terminalLiveOutputText.setBackgroundColor(COLOR_TERMINAL_BG);
|
||||||
terminalLiveOutputText.setPadding(0, dp(5), 0, 0);
|
terminalLiveOutputText.setPadding(0, dp(5), 0, 0);
|
||||||
|
terminalLiveOutputText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
terminalLiveOutputText.setHighlightColor(Color.TRANSPARENT);
|
||||||
|
terminalLiveOutputText.setTextIsSelectable(true);
|
||||||
|
terminalLiveOutputText.addOnLayoutChangeListener((view, left, top, right, bottom,
|
||||||
|
oldLeft, oldTop, oldRight, oldBottom) -> resizeTerminalToViewport(false));
|
||||||
panel.addView(terminalLiveOutputText, matchWrap());
|
panel.addView(terminalLiveOutputText, matchWrap());
|
||||||
|
|
||||||
TextView hint = bodyText("Tap for full terminal");
|
|
||||||
hint.setTextSize(9);
|
|
||||||
hint.setTextColor(COLOR_TEXT_DIM);
|
|
||||||
hint.setPadding(0, dp(6), 0, 0);
|
|
||||||
panel.addView(hint, matchWrap());
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2250,10 +2210,19 @@ public final class MainActivity extends Activity {
|
|||||||
}
|
}
|
||||||
String socket = terminalSocketStatus.toLowerCase(java.util.Locale.ROOT);
|
String socket = terminalSocketStatus.toLowerCase(java.util.Locale.ROOT);
|
||||||
boolean connected = socket.contains("connected") && !socket.contains("disconnected");
|
boolean connected = socket.contains("connected") && !socket.contains("disconnected");
|
||||||
terminalLiveStatusText.setText(connected ? "● connected" : "● " + defaultValue(socket, "connecting"));
|
terminalLiveStatusText.setText(terminalHistoryOffset > 0
|
||||||
terminalLiveStatusText.setTextColor(connected ? COLOR_SUCCESS
|
? "● " + terminalHistoryOffset + " back"
|
||||||
|
: connected ? "● live" : "● " + defaultValue(socket, "connecting"));
|
||||||
|
terminalLiveStatusText.setTextColor(terminalHistoryOffset > 0 ? COLOR_ACCENT_WARM : connected ? COLOR_SUCCESS
|
||||||
: socket.contains("error") || socket.contains("disconnected") ? COLOR_DANGER : COLOR_ACCENT_WARM);
|
: socket.contains("error") || socket.contains("disconnected") ? COLOR_DANGER : COLOR_ACCENT_WARM);
|
||||||
terminalLiveOutputText.setText(terminalScreen.renderTail(6));
|
terminalLiveOutputText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
terminalLiveOutputText.setHighlightColor(Color.TRANSPARENT);
|
||||||
|
terminalLiveOutputText.setText(terminalScreen.renderFocused(terminalExpandedBlocks, key -> {
|
||||||
|
if (!terminalExpandedBlocks.add(key)) {
|
||||||
|
terminalExpandedBlocks.remove(key);
|
||||||
|
}
|
||||||
|
renderTerminalNow();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTerminalMeta() {
|
private void updateTerminalMeta() {
|
||||||
@@ -2532,14 +2501,10 @@ public final class MainActivity extends Activity {
|
|||||||
sendTerminalInput("\u0002z");
|
sendTerminalInput("\u0002z");
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
if (terminalSocket != null) {
|
scrollTerminalHistory(-terminalRows);
|
||||||
terminalSocket.scroll(-terminalRows);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
if (terminalSocket != null) {
|
scrollTerminalHistory(terminalRows);
|
||||||
terminalSocket.scroll(terminalRows);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
showRaw("Session status", () -> api.sessionStatus(sessionName));
|
showRaw("Session status", () -> api.sessionStatus(sessionName));
|
||||||
@@ -2588,7 +2553,6 @@ public final class MainActivity extends Activity {
|
|||||||
LinearLayout tabGrid = new LinearLayout(this);
|
LinearLayout tabGrid = new LinearLayout(this);
|
||||||
tabGrid.setOrientation(LinearLayout.VERTICAL);
|
tabGrid.setOrientation(LinearLayout.VERTICAL);
|
||||||
terminalComposerTabs = tabGrid;
|
terminalComposerTabs = tabGrid;
|
||||||
tabGrid.setVisibility(terminalViewMode == TERMINAL_VIEW_CHAT ? View.GONE : View.VISIBLE);
|
|
||||||
LinearLayout firstTabRow = terminalKeyRow();
|
LinearLayout firstTabRow = terminalKeyRow();
|
||||||
LinearLayout secondTabRow = terminalKeyRow();
|
LinearLayout secondTabRow = terminalKeyRow();
|
||||||
addAccessoryTab(firstTabRow, "Edit", 0);
|
addAccessoryTab(firstTabRow, "Edit", 0);
|
||||||
@@ -3548,6 +3512,8 @@ public final class MainActivity extends Activity {
|
|||||||
terminalConnected = true;
|
terminalConnected = true;
|
||||||
terminalConnecting = false;
|
terminalConnecting = false;
|
||||||
terminalReconnectAttempt = 0;
|
terminalReconnectAttempt = 0;
|
||||||
|
terminalHistoryOffset = 0;
|
||||||
|
terminalFollowOutput = true;
|
||||||
terminalSocketStatus = "terminal connected";
|
terminalSocketStatus = "terminal connected";
|
||||||
updateTerminalMeta();
|
updateTerminalMeta();
|
||||||
setStatus("Connected " + sessionName);
|
setStatus("Connected " + sessionName);
|
||||||
@@ -3601,18 +3567,23 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -3661,6 +3632,11 @@ public final class MainActivity extends Activity {
|
|||||||
}
|
}
|
||||||
TerminalSocketClient socket = terminalSocket;
|
TerminalSocketClient socket = terminalSocket;
|
||||||
if (socket != null && !socket.isClosed() && terminalConnected) {
|
if (socket != null && !socket.isClosed() && terminalConnected) {
|
||||||
|
if (terminalHistoryOffset > 0) {
|
||||||
|
socket.scroll(terminalHistoryOffset);
|
||||||
|
terminalHistoryOffset = 0;
|
||||||
|
terminalFollowOutput = true;
|
||||||
|
}
|
||||||
socket.sendInput(data);
|
socket.sendInput(data);
|
||||||
setStatus("Sent input");
|
setStatus("Sent input");
|
||||||
return;
|
return;
|
||||||
@@ -3776,6 +3752,9 @@ public final class MainActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void scrollTerminalBottom() {
|
private void scrollTerminalBottom() {
|
||||||
|
if (terminalHistoryOffset > 0) {
|
||||||
|
scrollTerminalHistory(terminalHistoryOffset);
|
||||||
|
}
|
||||||
terminalFollowOutput = true;
|
terminalFollowOutput = true;
|
||||||
if (terminalScroll != null) {
|
if (terminalScroll != null) {
|
||||||
terminalScroll.post(() -> terminalScroll.fullScroll(View.FOCUS_DOWN));
|
terminalScroll.post(() -> terminalScroll.fullScroll(View.FOCUS_DOWN));
|
||||||
@@ -3783,6 +3762,31 @@ public final class MainActivity extends Activity {
|
|||||||
setStatus("Following terminal output");
|
setStatus("Following terminal output");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scrollTerminalHistory(int lines) {
|
||||||
|
TerminalSocketClient socket = terminalSocket;
|
||||||
|
if (socket == null || socket.isClosed() || !terminalConnected || lines == 0) {
|
||||||
|
setStatus("Terminal history unavailable while disconnected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lines < 0) {
|
||||||
|
terminalHistoryOffset += Math.abs(lines);
|
||||||
|
terminalFollowOutput = false;
|
||||||
|
} else {
|
||||||
|
if (terminalHistoryOffset == 0) {
|
||||||
|
setStatus("Live terminal output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int amount = Math.min(lines, terminalHistoryOffset);
|
||||||
|
terminalHistoryOffset = Math.max(0, terminalHistoryOffset - amount);
|
||||||
|
terminalFollowOutput = terminalHistoryOffset == 0;
|
||||||
|
lines = amount;
|
||||||
|
}
|
||||||
|
socket.scroll(lines);
|
||||||
|
setStatus(terminalHistoryOffset == 0
|
||||||
|
? "Live terminal output"
|
||||||
|
: "Terminal history · " + terminalHistoryOffset + " lines back");
|
||||||
|
}
|
||||||
|
|
||||||
private void setTerminalKeyPage(int page) {
|
private void setTerminalKeyPage(int page) {
|
||||||
terminalKeyPage = (page + 4) % 4;
|
terminalKeyPage = (page + 4) % 4;
|
||||||
updateAccessoryTabs();
|
updateAccessoryTabs();
|
||||||
@@ -3827,10 +3831,21 @@ public final class MainActivity extends Activity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastTerminalRenderMs = System.currentTimeMillis();
|
lastTerminalRenderMs = System.currentTimeMillis();
|
||||||
terminalText.setMovementMethod(null);
|
if (terminalViewMode == TERMINAL_VIEW_CHAT) {
|
||||||
terminalText.setText(terminalScreen.render());
|
terminalText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
terminalText.setHighlightColor(Color.TRANSPARENT);
|
||||||
|
terminalText.setText(terminalScreen.renderFocused(terminalExpandedBlocks, key -> {
|
||||||
|
if (!terminalExpandedBlocks.add(key)) {
|
||||||
|
terminalExpandedBlocks.remove(key);
|
||||||
|
}
|
||||||
|
renderTerminalNow();
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
terminalText.setMovementMethod(null);
|
||||||
|
terminalText.setText(terminalScreen.render());
|
||||||
|
}
|
||||||
updateTerminalLivePanel();
|
updateTerminalLivePanel();
|
||||||
if (terminalViewMode != TERMINAL_VIEW_CHAT && terminalFollowOutput) {
|
if (terminalFollowOutput) {
|
||||||
terminalScroll.post(() -> terminalScroll.fullScroll(View.FOCUS_DOWN));
|
terminalScroll.post(() -> terminalScroll.fullScroll(View.FOCUS_DOWN));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3986,7 +4001,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) {
|
||||||
@@ -4019,6 +4039,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);
|
||||||
|
|||||||
@@ -146,33 +146,82 @@ final class TerminalScreenBuffer {
|
|||||||
|
|
||||||
CharSequence renderFocused(Set<String> expandedBlocks, FocusToggle toggle) {
|
CharSequence renderFocused(Set<String> expandedBlocks, FocusToggle toggle) {
|
||||||
SpannableStringBuilder output = new SpannableStringBuilder();
|
SpannableStringBuilder output = new SpannableStringBuilder();
|
||||||
List<String> hiddenRows = new ArrayList<>();
|
int row = 0;
|
||||||
int hiddenStart = -1;
|
while (row < rows) {
|
||||||
for (int row = 0; row < rows; row++) {
|
|
||||||
String text = rowText(row).trim();
|
String text = rowText(row).trim();
|
||||||
if (containsHan(text)) {
|
if (!isCollapsibleHeader(text)) {
|
||||||
if (!hiddenRows.isEmpty()) {
|
if (text.isEmpty()) {
|
||||||
appendFocusBlock(output, hiddenRows, hiddenStart, row - 1, expandedBlocks, toggle);
|
if (output.length() > 0) {
|
||||||
hiddenRows.clear();
|
output.append('\n');
|
||||||
hiddenStart = -1;
|
}
|
||||||
|
} else {
|
||||||
|
appendStyledRow(output, row);
|
||||||
}
|
}
|
||||||
appendLine(output, text);
|
row++;
|
||||||
} else if (!text.isEmpty()) {
|
continue;
|
||||||
if (hiddenStart < 0) {
|
|
||||||
hiddenStart = row;
|
|
||||||
}
|
|
||||||
hiddenRows.add(text);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!hiddenRows.isEmpty()) {
|
int startRow = row;
|
||||||
appendFocusBlock(output, hiddenRows, hiddenStart, rows - 1, expandedBlocks, toggle);
|
List<String> hiddenRows = new ArrayList<>();
|
||||||
|
hiddenRows.add(text);
|
||||||
|
row++;
|
||||||
|
while (row < rows) {
|
||||||
|
String next = rowText(row).trim();
|
||||||
|
if (next.isEmpty() || isTopLevelLine(next)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hiddenRows.add(next);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
appendFocusBlock(output, hiddenRows, startRow, row - 1, expandedBlocks, toggle);
|
||||||
}
|
}
|
||||||
if (output.length() == 0) {
|
if (output.length() == 0) {
|
||||||
output.append("暂无可读内容");
|
output.append("Waiting for terminal output");
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCollapsibleHeader(String text) {
|
||||||
|
boolean activity = text.startsWith("• ") || text.startsWith("● ");
|
||||||
|
String header = stripActivityMarker(text);
|
||||||
|
return activity && (header.startsWith("Ran ")
|
||||||
|
|| header.equals("Explored")
|
||||||
|
|| header.startsWith("Explored ")
|
||||||
|
|| header.startsWith("Searched ")
|
||||||
|
|| header.startsWith("Read ")
|
||||||
|
|| header.startsWith("List ")
|
||||||
|
|| header.startsWith("Viewed ")
|
||||||
|
|| header.startsWith("Opened ")
|
||||||
|
|| header.startsWith("Fetched ")
|
||||||
|
|| header.startsWith("Downloaded ")
|
||||||
|
|| header.startsWith("Wrote ")
|
||||||
|
|| header.startsWith("Edited ")
|
||||||
|
|| header.startsWith("Applied ")
|
||||||
|
|| header.startsWith("Updated ")
|
||||||
|
|| header.startsWith("Checked ")
|
||||||
|
|| header.startsWith("Inspected ")
|
||||||
|
|| header.startsWith("Waited ")
|
||||||
|
|| header.startsWith("Working")
|
||||||
|
|| header.startsWith("Stop hook"))
|
||||||
|
|| text.startsWith("─ Worked for ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTopLevelLine(String text) {
|
||||||
|
return text.startsWith("• ")
|
||||||
|
|| text.startsWith("● ")
|
||||||
|
|| text.startsWith("› ")
|
||||||
|
|| text.startsWith("> ")
|
||||||
|
|| text.startsWith("─ ")
|
||||||
|
|| isCollapsibleHeader(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stripActivityMarker(String text) {
|
||||||
|
if (text.startsWith("• ") || text.startsWith("● ")) {
|
||||||
|
return text.substring(2).trim();
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
private void appendFocusBlock(
|
private void appendFocusBlock(
|
||||||
SpannableStringBuilder output,
|
SpannableStringBuilder output,
|
||||||
List<String> lines,
|
List<String> lines,
|
||||||
@@ -208,25 +257,22 @@ final class TerminalScreenBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String focusSummary(List<String> lines) {
|
private String focusSummary(List<String> lines) {
|
||||||
|
String first = stripActivityMarker(lines.get(0));
|
||||||
String joined = String.join(" ", lines).toLowerCase(Locale.ROOT);
|
String joined = String.join(" ", lines).toLowerCase(Locale.ROOT);
|
||||||
String type;
|
String type;
|
||||||
if (joined.contains("error") || joined.contains("failed") || joined.contains("exception")) {
|
if (first.startsWith("Explored") || first.startsWith("Searched")
|
||||||
type = "错误输出";
|
|| first.startsWith("Read ") || first.startsWith("List ")
|
||||||
} else if (joined.contains("working") || joined.contains("running")
|
|| first.startsWith("Viewed ") || first.startsWith("Inspected ")) {
|
||||||
|| joined.contains("waiting") || joined.contains("interrupt")) {
|
type = "Explored";
|
||||||
type = "运行状态";
|
} else if (first.startsWith("Working") || first.startsWith("Stop hook")
|
||||||
} else if (joined.contains("test") || joined.contains("build") || joined.contains("compile")
|
|| first.startsWith("─ Worked for ")) {
|
||||||
|| joined.contains("gradle") || joined.contains("webpack") || joined.contains("npm")) {
|
type = "Status";
|
||||||
type = "构建/测试";
|
} else if (joined.contains("error") || joined.contains("failed") || joined.contains("exception")) {
|
||||||
} else if (joined.contains("git ") || joined.contains("commit") || joined.contains("push")) {
|
type = "Command failed";
|
||||||
type = "Git 操作";
|
|
||||||
} else if (lines.get(0).startsWith(">") || lines.get(0).startsWith("$")
|
|
||||||
|| lines.get(0).startsWith("!")) {
|
|
||||||
type = "命令与输出";
|
|
||||||
} else {
|
} else {
|
||||||
type = "终端细节";
|
type = "Command output";
|
||||||
}
|
}
|
||||||
return type + " · " + lines.size() + " 行 · " + compactSummary(lines.get(0), 42);
|
return type + " · " + lines.size() + " lines · " + compactSummary(first, 48);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String compactSummary(String text, int maxChars) {
|
private String compactSummary(String text, int maxChars) {
|
||||||
@@ -244,6 +290,18 @@ final class TerminalScreenBuffer {
|
|||||||
output.append(text);
|
output.append(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendStyledRow(SpannableStringBuilder output, int row) {
|
||||||
|
if (output.length() > 0) {
|
||||||
|
output.append('\n');
|
||||||
|
}
|
||||||
|
int limit = cols;
|
||||||
|
while (limit > 0 && cells[row][limit - 1].value == ' '
|
||||||
|
&& cells[row][limit - 1].bg == DEFAULT_BG) {
|
||||||
|
limit--;
|
||||||
|
}
|
||||||
|
appendRow(output, row, limit);
|
||||||
|
}
|
||||||
|
|
||||||
private int handleEscape(String text, int index) {
|
private int handleEscape(String text, int index) {
|
||||||
if (index + 1 >= text.length()) {
|
if (index + 1 >= text.length()) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -583,15 +641,19 @@ final class TerminalScreenBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void appendRow(SpannableStringBuilder output, int row) {
|
private void appendRow(SpannableStringBuilder output, int row) {
|
||||||
|
appendRow(output, row, cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendRow(SpannableStringBuilder output, int row, int limit) {
|
||||||
int col = 0;
|
int col = 0;
|
||||||
while (col < cols) {
|
while (col < limit) {
|
||||||
Cell first = cells[row][col];
|
Cell first = cells[row][col];
|
||||||
int start = output.length();
|
int start = output.length();
|
||||||
int fgColor = first.fg;
|
int fgColor = first.fg;
|
||||||
int bgColor = first.bg;
|
int bgColor = first.bg;
|
||||||
boolean isBold = first.bold;
|
boolean isBold = first.bold;
|
||||||
boolean isDim = first.dim;
|
boolean isDim = first.dim;
|
||||||
while (col < cols) {
|
while (col < limit) {
|
||||||
Cell cell = cells[row][col];
|
Cell cell = cells[row][col];
|
||||||
if (cell.fg != fgColor || cell.bg != bgColor || cell.bold != isBold || cell.dim != isDim) {
|
if (cell.fg != fgColor || cell.bg != bgColor || cell.bold != isBold || cell.dim != isDim) {
|
||||||
break;
|
break;
|
||||||
@@ -626,18 +688,6 @@ final class TerminalScreenBuffer {
|
|||||||
return text.toString();
|
return text.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsHan(String text) {
|
|
||||||
for (int index = 0; index < text.length(); index++) {
|
|
||||||
Character.UnicodeBlock block = Character.UnicodeBlock.of(text.charAt(index));
|
|
||||||
if (block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|
|
||||||
|| block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|
|
||||||
|| block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveCursor() {
|
private void saveCursor() {
|
||||||
savedRow = cursorRow;
|
savedRow = cursorRow;
|
||||||
savedCol = cursorCol;
|
savedCol = cursorCol;
|
||||||
|
|||||||
Reference in New Issue
Block a user