From 6d901230920854fb0015504f010ff463328352f8 Mon Sep 17 00:00:00 2001 From: kris Date: Mon, 6 Apr 2026 14:58:30 +0800 Subject: [PATCH] Hide context rings without live snapshots --- .../com/hyzq/boss/WechatSurfaceMapper.java | 18 ++++++++++++++---- ...hatSurfaceMapperConversationStatusTest.java | 6 +++--- src/lib/boss-projections.ts | 12 ++++++------ tests/conversation-home-items.test.ts | 14 +++++++------- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java b/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java index ad6b2de..e1d3196 100644 --- a/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java +++ b/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java @@ -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; diff --git a/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperConversationStatusTest.java b/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperConversationStatusTest.java index 9f3df47..b9d86e0 100644 --- a/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperConversationStatusTest.java +++ b/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperConversationStatusTest.java @@ -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 { diff --git a/src/lib/boss-projections.ts b/src/lib/boss-projections.ts index 367a883..0d92294 100644 --- a/src/lib/boss-projections.ts +++ b/src/lib/boss-projections.ts @@ -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, diff --git a/tests/conversation-home-items.test.ts b/tests/conversation-home-items.test.ts index 4651c1c..e8a4153 100644 --- a/tests/conversation-home-items.test.ts +++ b/tests/conversation-home-items.test.ts @@ -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 () => {