Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/src/main/java/cn/lineai/mvp/ChatUiStateAssembler.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,16 @@ public ChatUiState assemble(
hasConfiguredModel,
aiSettings.isThinkingScrollEnabled(),
aiSettings.isThinkingAutoExpandEnabled(),
outputSettings.isProcessAutoExpandEnabled(),
outputSettings.isCodeWrapEnabled(),
outputSettings.getBrowserMode(),
inputSettings.getEnterKeyBehavior(),
activeChatMode,
conversationId,
messages,
selectedModelId,
availableModels
availableModels,
null
);
}

Expand Down
10 changes: 0 additions & 10 deletions app/src/main/java/cn/lineai/mvp/GenerationFlowController.java
Original file line number Diff line number Diff line change
Expand Up @@ -735,16 +735,6 @@ private void handleToolExecutionBatch(
toolMessageController.addOrReplaceToolResults(batch.getCompletedResults());
int executedCount = usedToolCallCount + batch.getCompletedResults().size();
if (batch.getPendingCall() != null) {
ToolResult pendingResult = ToolResult.withReview(
batch.getPendingCall().getId(),
batch.getPendingCall().getName(),
"",
false,
"",
"pending",
""
);
addOrReplaceToolResult(pendingResult);
toolConfirmationController.setPendingToolExecution(new PendingToolExecution(
generationId,
selectedModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,6 @@ public void refreshFileTreeAfterRevert(String filePath) {
coordinator.refreshFileTreeAfterRevert(filePath);
}

@Override
public void persistCurrentConversation() {
coordinator.persistCurrentConversation();
}

@Override
public void render() {
coordinator.render();
Expand Down
15 changes: 13 additions & 2 deletions app/src/main/java/cn/lineai/mvp/MainCoordinator.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import cn.lineai.security.UrlPolicy;
import cn.lineai.tool.BaseTool;
import cn.lineai.model.ChatMessage;
import cn.lineai.model.ChatUiState;
import cn.lineai.model.FileTreeNode;
import cn.lineai.model.KeepAliveSettings;
import cn.lineai.model.StorageStatsUiModel;
Expand Down Expand Up @@ -624,6 +625,11 @@ public void onCodeWrapChanged(boolean enabled) {
settingsManagementController.setCodeWrapEnabled(enabled);
}

@Override
public void onProcessAutoExpandChanged(boolean enabled) {
settingsManagementController.setProcessAutoExpandEnabled(enabled);
}

@Override
public void onBrowserModeChanged(String mode) {
settingsManagementController.setBrowserMode(mode);
Expand Down Expand Up @@ -1039,15 +1045,20 @@ void render() {
return;
}
String activeChatMode = syncModePermission();
viewProxy.render(chatUiStateAssembler.assemble(
ChatUiState uiState = chatUiStateAssembler.assemble(
projectState.label(),
projectState.source(),
projectState.path(),
chatSessionStore.getCurrentConversationId(),
activeChatMode,
chatSessionStore.isStreaming(),
messages
).withToolApproval(generationFlowController == null ? null : generationFlowController.pendingToolApproval()));
);
if (toolReviewController != null) {
uiState = uiState.withDisplayMessages(toolReviewController.applyLocalReviews(messages));
}
viewProxy.render(uiState.withToolApproval(
generationFlowController == null ? null : generationFlowController.pendingToolApproval()));
}

void resetTodoState() {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/cn/lineai/mvp/OutputSettingsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public interface OutputSettingsController {

void onCodeWrapChanged(boolean enabled);

void onProcessAutoExpandChanged(boolean enabled);

void onBrowserModeChanged(String mode);

void onBrowserJavaScriptChanged(boolean enabled);
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/cn/lineai/mvp/SettingsManagementController.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ interface SettingsStore {

void setCodeWrapEnabled(boolean enabled);

void setProcessAutoExpandEnabled(boolean enabled);

void setBrowserMode(String mode);

void setBrowserJavaScriptEnabled(boolean enabled);
Expand Down Expand Up @@ -232,6 +234,11 @@ public void setCodeWrapEnabled(boolean enabled) {
outputSettingsRepository.setCodeWrapEnabled(enabled);
}

@Override
public void setProcessAutoExpandEnabled(boolean enabled) {
outputSettingsRepository.setProcessAutoExpandEnabled(enabled);
}

@Override
public void setBrowserMode(String mode) {
outputSettingsRepository.setBrowserMode(mode);
Expand Down Expand Up @@ -427,6 +434,11 @@ public void setCodeWrapEnabled(boolean enabled) {
host.render();
}

public void setProcessAutoExpandEnabled(boolean enabled) {
settingsStore.setProcessAutoExpandEnabled(enabled);
host.render();
}

public void setBrowserMode(String mode) {
settingsStore.setBrowserMode(mode);
host.render();
Expand Down
11 changes: 0 additions & 11 deletions app/src/main/java/cn/lineai/mvp/ToolConfirmationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -274,17 +274,6 @@ private void handlePendingToolReview(PendingToolExecution pending, String state)
rememberSessionAutoConfirmation(pending.getToolCall());
}

ToolResult accepted = ToolResult.withReview(
pending.getToolCall().getId(),
pending.getToolCall().getName(),
"",
false,
"",
"accepted",
""
);
callback.addOrReplaceToolResult(accepted);
callback.persistCurrentConversation();
callback.render();
callback.executeAcceptedPendingTool(pending);
}
Expand Down
148 changes: 128 additions & 20 deletions app/src/main/java/cn/lineai/mvp/ToolReviewController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import cn.lineai.data.repository.DiffRepository;
import cn.lineai.data.repository.DiffStore;
import cn.lineai.data.service.FileRestorer;
import cn.lineai.model.ChatMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONObject;

final class ToolReviewController {
interface Host {
void refreshFileTreeAfterRevert(String filePath);

void persistCurrentConversation();

void render();
}

Expand All @@ -19,6 +24,7 @@ interface Host {
private final BackgroundTaskRunner backgroundTasks;
private final MainThreadDispatcher mainThread;
private final Host host;
private final Map<String, DiffRecord> localReviewCache = new HashMap<>();

ToolReviewController(
DiffStore diffRepository,
Expand Down Expand Up @@ -49,8 +55,10 @@ void review(String toolCallId, String state, String diffId) {
return;
}
}
toolMessageController.updateToolReview(toolCallId, resolvedDiffId, normalizedState, "");
host.persistCurrentConversation();
if (resolvedDiffId.length() == 0) {
resolvedDiffId = toolMessageController.findToolMessageDiffId(toolCallId);
}
setLocalReview(resolvedDiffId, normalizedState, "");
host.render();
}

Expand All @@ -61,8 +69,7 @@ private void rejectWithRevert(String toolCallId, String diffId) {
DiffRepository.RevertResult result = diffRepository.revertDiff(diffId);
if (!result.isSuccess()) {
mainThread.post(() -> {
toolMessageController.updateToolReview(toolCallId, diffId, "", result.getMessage());
host.persistCurrentConversation();
setLocalReview(diffId, "", result.getMessage());
host.render();
});
return;
Expand All @@ -72,30 +79,131 @@ private void rejectWithRevert(String toolCallId, String diffId) {
FileRestorer.restoreOldContent(result.getDiffRecord());
} catch (Exception e) {
mainThread.post(() -> {
toolMessageController.updateToolReview(
toolCallId,
diffId,
"",
"File restore failed: " + e.getMessage()
);
host.persistCurrentConversation();
setLocalReview(diffId, "", "File restore failed: " + e.getMessage());
host.render();
});
return;
}
diffRepository.markReverted(diffId);
}
mainThread.post(() -> {
toolMessageController.updateToolReview(
toolCallId,
diffId,
"rejected",
"Reverted change to " + filePath
);
setLocalReview(diffId, "rejected", "Reverted change to " + filePath);
host.refreshFileTreeAfterRevert(filePath);
host.persistCurrentConversation();
host.render();
});
});
}

List<ChatMessage> applyLocalReviews(List<ChatMessage> source) {
ArrayList<ChatMessage> display = new ArrayList<>();
if (source == null) {
return display;
}
for (ChatMessage message : source) {
ChatMessage next = message;
if (message != null && message.getRole() == ChatMessage.Role.TOOL) {
DiffRecord direct = localReview(message.getDiffId());
if (direct != null) {
next = next.withToolReview(
message.getDiffId(),
reviewState(direct),
direct.getReviewMessage()
);
}
String content = applyNestedLocalReviews(next.getContent());
if (!content.equals(next.getContent())) {
next = next.withContent(content, next.getReasoningContent(), next.isStreaming());
}
}
display.add(next);
}
return display;
}

private String applyNestedLocalReviews(String content) {
if (content == null || content.trim().length() == 0) {
return content == null ? "" : content;
}
try {
JSONObject root = new JSONObject(content);
return applyNestedLocalReviews(root) ? root.toString() : content;
} catch (Exception ignored) {
return content;
}
}

private boolean applyNestedLocalReviews(JSONObject object) throws Exception {
if (object == null) {
return false;
}
boolean changed = applyLocalReviews(object.optJSONArray("tool_calls"));
JSONArray agents = object.optJSONArray("agents");
if (agents != null) {
for (int i = 0; i < agents.length(); i++) {
JSONObject agent = agents.optJSONObject(i);
changed = applyNestedLocalReviews(agent) || changed;
}
}
return changed;
}

private boolean applyLocalReviews(JSONArray calls) throws Exception {
if (calls == null) {
return false;
}
boolean changed = false;
for (int i = 0; i < calls.length(); i++) {
JSONObject item = calls.optJSONObject(i);
if (item == null) {
continue;
}
JSONObject result = item.optJSONObject("result");
DiffRecord local = localReview(result == null ? "" : result.optString("diff_id"));
if (local != null) {
result.put("review_state", reviewState(local));
result.put("review_message", local.getReviewMessage());
changed = true;
}
changed = applyNestedLocalReviews(item) || changed;
}
return changed;
}

private DiffRecord localReview(String diffId) {
if (diffId == null || diffId.length() == 0) {
return null;
}
DiffRecord record;
synchronized (localReviewCache) {
if (localReviewCache.containsKey(diffId)) {
record = localReviewCache.get(diffId);
} else {
record = diffRepository.getDiff(diffId);
localReviewCache.put(diffId, record);
}
}
if (record == null || reviewState(record).length() == 0 && record.getReviewMessage().length() == 0) {
return null;
}
return record;
}

private void setLocalReview(String diffId, String state, String message) {
if (diffId == null || diffId.length() == 0) {
return;
}
diffRepository.setReview(diffId, state, message);
synchronized (localReviewCache) {
localReviewCache.remove(diffId);
}
}

private String reviewState(DiffRecord record) {
if (record == null) {
return "";
}
return record.getReviewState().length() > 0
? record.getReviewState()
: record.isReverted() ? "rejected" : "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public AboutScreenView(Context context, Listener listener) {
super(context, context.getString(R.string.screen_about_title), listener::onBack, null);
VersionInfo versionInfo = readVersionInfo(context);
LinearLayout content = getContent();
LineTheme.padding(content, 28, 8, 28, 48);
LineTheme.padding(content, LineTheme.LG, LineTheme.LG, LineTheme.LG, 100);

LinearLayout header = new LinearLayout(context);
header.setOrientation(VERTICAL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public ActionRowView(Context context, int iconType, String label, String desc, b

FrameLayout iconWrap = new FrameLayout(context);
iconWrap.setBackground(LineTheme.rounded(context, destructive ? LineTheme.DANGER_MUTED : LineTheme.ACCENT_MUTED, 8));

IconButtonView icon = new IconButtonView(context, iconType);
icon.setIconColor(destructive ? LineTheme.DANGER : LineTheme.ACCENT);
icon.setIconSizeDp(36, 20);
Expand Down
Loading
Loading