Hide context rings without live snapshots

This commit is contained in:
kris
2026-04-06 14:58:30 +08:00
parent 5782804df3
commit 6d90123092
4 changed files with 30 additions and 20 deletions

View File

@@ -297,7 +297,11 @@ public final class WechatSurfaceMapper {
}
private static boolean hasContextIndicator(JSONObject source) {
return true;
if (source.optBoolean("mustFinishBeforeCompaction", false)) {
return true;
}
JSONObject indicator = source.optJSONObject("contextBudgetIndicator");
return indicator != null && indicator.optBoolean("visible", false);
}
private static JSONObject resolveDeviceCapability(JSONObject device, String capabilityKey) {
@@ -441,12 +445,11 @@ public final class WechatSurfaceMapper {
JSONObject avatar = latest.optJSONObject("avatar");
safePut(folder, "avatar", avatar == null ? new JSONObject() : copyJson(avatar));
JSONObject indicator = topContext == null
? buildContextIndicator(100, "safe")
? buildHiddenContextIndicator()
: copyJson(topContext.optJSONObject("contextBudgetIndicator"));
if (indicator == null) {
indicator = buildContextIndicator(100, "safe");
indicator = buildHiddenContextIndicator();
}
safePut(indicator, "visible", true);
safePut(indicator, "style", "ring_percent");
safePut(folder, "contextBudgetIndicator", indicator);
putIfNotEmpty(folder, "contextBudgetSourceNodeId", topContext == null ? "" : topContext.optString("contextBudgetSourceNodeId", ""));
@@ -587,6 +590,13 @@ public final class WechatSurfaceMapper {
return indicator;
}
private static JSONObject buildHiddenContextIndicator() {
JSONObject indicator = new JSONObject();
safePut(indicator, "visible", false);
safePut(indicator, "style", "ring_percent");
return indicator;
}
private static void putIfNotEmpty(JSONObject target, String key, String value) {
if (target == null || value == null || value.trim().isEmpty()) {
return;

View File

@@ -65,11 +65,11 @@ public class WechatSurfaceMapperConversationStatusTest {
assertEquals("上下文稳定", row.contextStatusLabel);
assertEquals("safe", row.contextStatusLevel);
assertEquals(0, row.contextUsagePercent);
assertEquals(true, row.contextIndicatorVisible);
assertEquals(false, row.contextIndicatorVisible);
}
@Test
public void toConversationRow_usesSafeContextDefaultsWhenIndicatorMissing() {
public void toConversationRow_hidesContextIndicatorWhenIndicatorMissing() {
JSONObject item = new StubJSONObject()
.withString("threadTitle", "北区试产线回归")
.withInt("activityIconCount", 0);
@@ -79,7 +79,7 @@ public class WechatSurfaceMapperConversationStatusTest {
assertEquals("上下文稳定", row.contextStatusLabel);
assertEquals("safe", row.contextStatusLevel);
assertEquals(0, row.contextUsagePercent);
assertEquals(true, row.contextIndicatorVisible);
assertEquals(false, row.contextIndicatorVisible);
}
private static final class StubJSONObject extends JSONObject {

View File

@@ -397,10 +397,10 @@ function buildConversationItem(state: BossState, project: Project): Conversation
},
groupMembers,
contextBudgetIndicator: {
visible: true,
visible: Boolean(topThread),
style: "ring_percent",
percent: topThread?.contextBudgetRemainingPct ?? 100,
level: topThread?.contextBudgetLevel ?? "safe",
percent: topThread?.contextBudgetRemainingPct,
level: topThread?.contextBudgetLevel,
},
contextBudgetSourceNodeId: topThread?.nodeId,
contextBudgetUpdatedAt: topThread?.capturedAt,
@@ -592,10 +592,10 @@ export function getConversationHomeItems(state: BossState): ConversationItem[] {
primary: device?.avatar ?? latestItem.avatar.primary,
},
contextBudgetIndicator: {
visible: true,
visible: Boolean(topContextItem),
style: "ring_percent",
percent: topContextItem?.contextBudgetIndicator.percent ?? 100,
level: topContextItem?.contextBudgetIndicator.level ?? "safe",
percent: topContextItem?.contextBudgetIndicator.percent,
level: topContextItem?.contextBudgetIndicator.level,
},
contextBudgetSourceNodeId: topContextItem?.contextBudgetSourceNodeId,
contextBudgetUpdatedAt: topContextItem?.contextBudgetUpdatedAt,

View File

@@ -687,12 +687,12 @@ test("conversation items expose context status while keeping idle activity silen
assert.equal(threadConversation.contextBudgetIndicator.level, "urgent");
assert.equal(threadConversation.activityIconCount, 0);
assert.equal(masterAgent.activityIconCount, 0);
assert.equal(masterAgent.contextBudgetIndicator.visible, true);
assert.equal(masterAgent.contextBudgetIndicator.percent, 100);
assert.equal(masterAgent.contextBudgetIndicator.level, "safe");
assert.equal(masterAgent.contextBudgetIndicator.visible, false);
assert.equal(masterAgent.contextBudgetIndicator.percent, undefined);
assert.equal(masterAgent.contextBudgetIndicator.level, undefined);
});
test("conversation items keep a safe context ring even when no thread snapshot exists", async () => {
test("conversation items hide context ring when no thread snapshot exists", async () => {
await setup();
const state = await readState();
@@ -714,9 +714,9 @@ test("conversation items keep a safe context ring even when no thread snapshot e
const directThread = items.find((item) => item.projectId === "single-thread-no-context");
assert.ok(directThread);
assert.equal(directThread?.contextBudgetIndicator.visible, true);
assert.equal(directThread?.contextBudgetIndicator.percent, 100);
assert.equal(directThread?.contextBudgetIndicator.level, "safe");
assert.equal(directThread?.contextBudgetIndicator.visible, false);
assert.equal(directThread?.contextBudgetIndicator.percent, undefined);
assert.equal(directThread?.contextBudgetIndicator.level, undefined);
});
test("conversation items prefer latest observed codex activity over stale last message time", async () => {