氣泡改成兩行,第一行顯示對話名稱或專案名
摘要: 氣泡上方加一行標題(/rename 設的對話名稱,沒設就用專案資料夾名),訊息移到第二行。 根本原因: 原本把專案名以「(專案名)」的形式接在訊息尾巴,訊息本身就短, 加了括號後更容易被氣泡的單行省略號截掉,而且同時開多個 session 時 要看到最後才知道是哪一個在叫你。 影響: 多 session 並行時不容易分辨氣泡是哪個專案發出的;訊息也被括號擠掉。 修法: - index.html 氣泡內加 #bubble-title;style.css 把單行限制從 #bubble 移到 #bubble-title 與 #bubble-text 各自身上,兩行各自省略, 並以 :empty 讓沒有標題時整行收起來,不留空白。 - say() 增加第四個參數 title,事件處理處的八個呼叫點改傳標題、移除 suffix。 - main.js 的 latestNarration 改名為 readTranscript,同一次反向掃描同時取出 助理說明與 custom-title(/rename 會把它寫進 transcript),兩者都拿到就提早結束; 收到事件時把 sessionTitle 一併併進 payload。 - 標題優先序:對話名稱 > 專案資料夾名 > 不顯示。 驗證: - 單元測試以真實 transcript 執行 readTranscript:取到對話名稱 "claude-pet"、 同時取到助理說明、快取一致、路徑不存在或為 null 時回 null,6 項全過。 - 實機截圖確認兩行版面正確。截圖當下剛好收到另一個 session 的真實事件, 標題顯示 "telegram-codex-bot"、第二行是該 session 的助理說明, 等於用實際流量驗證了多 session 下的標題辨識。 - 以 ELECTRON_RUN_AS_NODE 讀打包後 app.asar,確認版本 2.0.5 與十項改動都在出貨檔案裡, 包含舊的 suffix 已完全移除。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="stage">
|
||||
<div id="bubble" class="hidden"><span id="bubble-text"></span></div>
|
||||
<div id="bubble" class="hidden"><span id="bubble-title"></span><span id="bubble-text"></span></div>
|
||||
<canvas id="sprite" width="126" height="137"></canvas>
|
||||
</div>
|
||||
<script src="renderer.js"></script>
|
||||
|
||||
+22
-15
@@ -76,6 +76,7 @@ const TOOL_LABELS = {
|
||||
const canvas = document.getElementById("sprite");
|
||||
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
||||
const bubble = document.getElementById("bubble");
|
||||
const bubbleTitle = document.getElementById("bubble-title");
|
||||
const bubbleText = document.getElementById("bubble-text");
|
||||
|
||||
let sheet = null;
|
||||
@@ -347,18 +348,23 @@ function shortTarget(target, toolName) {
|
||||
return t.slice(0, 44);
|
||||
}
|
||||
|
||||
// 氣泡第一行:對話名稱(/rename 設的)優先,其次專案資料夾名
|
||||
function bubbleTitleOf(p, cwd) {
|
||||
return p.sessionTitle || projectName(cwd) || "";
|
||||
}
|
||||
|
||||
// 有什麼就顯示什麼:優先顯示新的助理說明,否則顯示正在用的工具
|
||||
function sayActivity(p) {
|
||||
function sayActivity(p, title) {
|
||||
if (baseState() === "waiting") return; // 別蓋掉在等授權的固定氣泡
|
||||
if (p.narration && p.narrationId && p.narrationId !== lastNarrationId) {
|
||||
lastNarrationId = p.narrationId;
|
||||
say(`💬 ${firstSentence(p.narration)}`, ACTIVITY_MS);
|
||||
say(`💬 ${firstSentence(p.narration)}`, ACTIVITY_MS, false, title);
|
||||
return;
|
||||
}
|
||||
if (!p.toolName) return;
|
||||
const label = TOOL_LABELS[p.toolName] || `🔧 ${p.toolName}`;
|
||||
const detail = p.toolName === "Task" && p.agentType ? p.agentType : p.toolTarget;
|
||||
say(detail ? `${label} ${shortTarget(detail, p.toolName)}` : label, ACTIVITY_MS);
|
||||
say(detail ? `${label} ${shortTarget(detail, p.toolName)}` : label, ACTIVITY_MS, false, title);
|
||||
}
|
||||
|
||||
function projectName(cwd) {
|
||||
@@ -368,10 +374,12 @@ function projectName(cwd) {
|
||||
|
||||
// ---------- 氣泡 ----------
|
||||
|
||||
function say(text, ms, sticky = false) {
|
||||
// title 是第一行(對話名稱或專案名),text 是第二行
|
||||
function say(text, ms, sticky = false, title = "") {
|
||||
clearTimeout(bubbleTimer);
|
||||
bubbleTitle.textContent = title || "";
|
||||
bubbleText.textContent = text;
|
||||
bubble.title = text;
|
||||
bubble.title = title ? `${title}\n${text}` : text;
|
||||
bubble.classList.remove("hidden");
|
||||
bubbleSticky = sticky;
|
||||
if (ms > 0) bubbleTimer = setTimeout(hideBubble, ms);
|
||||
@@ -390,14 +398,13 @@ function handleEvent(p) {
|
||||
if (p.event === "__test") return handleTest(p);
|
||||
|
||||
const s = session(p.sessionId, p.cwd);
|
||||
const proj = projectName(s.cwd);
|
||||
const suffix = proj ? `(${proj})` : "";
|
||||
const title = bubbleTitleOf(p, s.cwd);
|
||||
|
||||
switch (p.event) {
|
||||
case "SessionStart":
|
||||
setState(s, "idle");
|
||||
// Codex 的 waving 只在寵物第一次醒來(first-awake),新 thread 不會揮手;這裡只留資訊氣泡
|
||||
if (!p.source || p.source === "startup") say(`👋 嗨!${suffix}`, 3000);
|
||||
if (!p.source || p.source === "startup") say("👋 嗨!", 3000, false, title);
|
||||
break;
|
||||
case "UserPromptSubmit":
|
||||
setState(s, "running");
|
||||
@@ -406,13 +413,13 @@ function handleEvent(p) {
|
||||
case "PreToolUse":
|
||||
if (p.toolName === "AskUserQuestion") {
|
||||
setState(s, "waiting");
|
||||
say(`❓ 有問題想問你${suffix}`, 0, true);
|
||||
say("❓ 有問題想問你", 0, true, title);
|
||||
} else if (p.toolName === "ExitPlanMode") {
|
||||
setState(s, "waiting");
|
||||
say(`📋 計畫等你確認${suffix}`, 0, true);
|
||||
say("📋 計畫等你確認", 0, true, title);
|
||||
} else {
|
||||
setState(s, "running");
|
||||
sayActivity(p);
|
||||
sayActivity(p, title);
|
||||
}
|
||||
break;
|
||||
case "PostToolUse":
|
||||
@@ -431,13 +438,13 @@ function handleEvent(p) {
|
||||
const t = p.notificationType;
|
||||
if (t === "permission_prompt") {
|
||||
setState(s, "waiting");
|
||||
say(`🔐 需要你授權${suffix}`, 0, true);
|
||||
say("🔐 需要你授權", 0, true, title);
|
||||
} else if (t === "idle_prompt") {
|
||||
// Codex 的 waiting 只對應授權、提問、計畫確認;單純等你下指令不算,狀態不變
|
||||
say(`💤 在等你回覆${suffix}`, 4000);
|
||||
say("💤 在等你回覆", 4000, false, title);
|
||||
} else if (t === "elicitation_dialog") {
|
||||
setState(s, "waiting");
|
||||
say(`❓ 有問題想問你${suffix}`, 0, true);
|
||||
say("❓ 有問題想問你", 0, true, title);
|
||||
} else if (p.message) {
|
||||
say(`💬 ${p.message}`, 4000);
|
||||
}
|
||||
@@ -448,7 +455,7 @@ function handleEvent(p) {
|
||||
// Codex:turn 完成且未讀 → review(播 review 3 次後沉澱),直到使用者回到該 thread 才回 idle。
|
||||
// 這裡對應成:維持 review 直到該 session 有下一個動作(UserPromptSubmit)、結束或 7 天到期。
|
||||
setState(s, "review");
|
||||
say(`✅ 完成!${suffix}`, 8000);
|
||||
say("✅ 完成!", 8000, false, title);
|
||||
break;
|
||||
case "StopFailure":
|
||||
// Codex:turn 失敗 → failed 狀態(播 failed 3 次後沉澱),維持到下一個 turn 或 1 小時到期
|
||||
|
||||
+19
-2
@@ -30,11 +30,28 @@ html, body {
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
|
||||
overflow: hidden;
|
||||
transition: opacity 0.18s ease, transform 0.18s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 標題與內文各自佔一行,各自省略;沒有標題時整行收起來 */
|
||||
#bubble-title,
|
||||
#bubble-text {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: opacity 0.18s ease, transform 0.18s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#bubble-title {
|
||||
font-size: 11px;
|
||||
line-height: 1.3;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
#bubble-title:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#bubble::after {
|
||||
|
||||
Reference in New Issue
Block a user