minecodex 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/features/session-status/DESIGN.md +1 -1
- package/features/session-status/src/session-status.mjs +5 -2
- package/features/unread-inbox/DESIGN.md +18 -10
- package/features/unread-inbox/src/unread-inbox.mjs +128 -42
- package/features/usage-meter/DESIGN.md +3 -3
- package/features/usage-meter/src/usage-meter.mjs +1 -1
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
| 字体 | `--font-sans-default`、`--text-xs` | 与原生辅助信息一致 |
|
|
19
19
|
| 圆角与阴影 | `--radius-xl`、`--shadow-lg` | 明细弹层 |
|
|
20
20
|
|
|
21
|
-
状态行和输入框共用内容宽度,单行高度为 24px,顶部留 2px 内边距;每个状态按钮高 22px,内边距为上下 3px、左右
|
|
21
|
+
状态行和输入框共用内容宽度,单行高度为 24px,顶部留 2px 内边距;每个状态按钮高 22px,内边距为上下 3px、左右 6px。按钮之间 gap 为 4px,两组可见内容间距为 16px(6 + 4 + 6);按钮内图标与文字 gap 为 6px。状态行展开且可见时,将包含它的原生 `.bottom-0.pb-4` 容器底部 padding 从 16px 覆盖为 4px;收起、隐藏或卸载后自动恢复原生留白,展开的状态行使用 -4px 顶部外边距,将输入框下方原生 8px gap 的实际距离收为4px,收起时恢复。宽度不足时自然换行,不覆盖输入框或悬浮控件。统计图标采用 Lucide Gauge,用量图标采用 Phosphor Database 单路径版本,两者以 14px 绘制;上下文使用与 Codex 原生相同参数的 12px 百分环。弹层由按钮锚定,键盘可聚焦,Escape 和点击外部关闭。遵守系统减少动态效果设置,不添加持续动画。
|
|
22
22
|
|
|
23
23
|
状态行以当前输入框所在的会话为准,不依赖侧栏中可能因项目折叠而卸载的 Thread 行。新聊天没有会话 ID 时隐藏旧数据;首条消息建立会话后默认展开详细状态行;若用户曾收起,则沿用其选择。尚未取得的指标用 `—` 占位,不等待日志或 Token 用量;切换会话时立即清空旧数值,详情展开状态保持不变。
|
|
24
24
|
|
|
@@ -29,14 +29,17 @@ document.getElementById(STYLE_ID)?.remove();
|
|
|
29
29
|
const style = document.createElement("style");
|
|
30
30
|
style.id = STYLE_ID;
|
|
31
31
|
style.textContent = `
|
|
32
|
-
[${MARKER}] { box-sizing:border-box; display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:0
|
|
32
|
+
[${MARKER}] { box-sizing:border-box; display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:0 4px; margin:0 auto; padding-top:2px; max-width:100%; min-height:24px; min-width:0; font-family:var(--font-sans-default,inherit); font-size:var(--text-xs,12px); line-height:16px; font-variant-numeric:tabular-nums; color:var(--color-text-tertiary); }
|
|
33
33
|
[${MARKER}][hidden] { display:none; }
|
|
34
34
|
[data-minecodex-session-reveal] { display:grid; grid-template-rows:0fr; opacity:0; transition:grid-template-rows 200ms ease,opacity 160ms ease; }
|
|
35
35
|
[data-minecodex-session-reveal][data-expanded] { grid-template-rows:1fr; opacity:1; }
|
|
36
36
|
@starting-style { [data-minecodex-session-reveal][data-expanded] { grid-template-rows:0fr; opacity:0; } }
|
|
37
|
+
[data-minecodex-session-reveal][data-expanded]:not([hidden]) { margin-top:-4px; }
|
|
37
38
|
[data-minecodex-session-reveal] > div { min-height:0; overflow:hidden; }
|
|
38
39
|
[data-minecodex-session-reveal]:not([data-expanded]) { pointer-events:none; }
|
|
39
40
|
[data-minecodex-session-reveal][hidden] { display:none; }
|
|
41
|
+
/* 仅展开状态行时缩减原生底部留白;收起或卸载后自动恢复。 */
|
|
42
|
+
.bottom-0.pb-4:has([data-minecodex-session-reveal][data-expanded]:not([hidden])) { padding-bottom:4px; }
|
|
40
43
|
[data-minecodex-context-trigger] { display:inline-flex; align-items:center; justify-content:center; flex:none; width:24px; height:var(--height-token-button-composer,28px); padding:0; border:0; border-radius:var(--radius-full); background:transparent; color:var(--color-text-tertiary); cursor:pointer; }
|
|
41
44
|
[data-minecodex-context-trigger][hidden] { display:none; }
|
|
42
45
|
[data-context-progress] { transition:stroke-dashoffset 120ms ease-out; }
|
|
@@ -46,7 +49,7 @@ style.textContent = `
|
|
|
46
49
|
[data-minecodex-native-context] { display:none !important; }
|
|
47
50
|
[data-minecodex-context-tooltip] { position:fixed; z-index:2147483000; pointer-events:none; width:max-content; padding:5px 12px; border:1px solid var(--color-border-tooltip); border-radius:var(--radius-2xl); background:var(--color-background-tooltip); color:var(--color-text-tooltip); box-shadow:var(--shadow-tooltip); font-family:var(--font-sans-default); font-size:var(--text-sm); line-height:18px; text-align:center; white-space:pre-line; }
|
|
48
51
|
@media (prefers-reduced-motion:reduce) { [data-minecodex-session-reveal], [data-context-progress] { transition:none; } }
|
|
49
|
-
[${MARKER}] button { box-sizing:border-box; display:inline-flex; align-items:center; gap:
|
|
52
|
+
[${MARKER}] button { box-sizing:border-box; display:inline-flex; align-items:center; gap:6px; height:22px; color:inherit; font:inherit; border:0; border-radius:var(--radius-xl,12px); background:transparent; padding:3px 6px; cursor:pointer; white-space:nowrap; }
|
|
50
53
|
[${MARKER}] svg, [data-minecodex-session-status-panel] h3 svg { width:14px; height:14px; flex:none; stroke-width:1.75; }
|
|
51
54
|
[${MARKER}] [data-kind="context"] svg, [data-minecodex-session-status-panel] h3 .context-ring { width:12px; height:12px; stroke-width:2; }
|
|
52
55
|
[${MARKER}] button:hover, [${MARKER}] button[aria-expanded="true"] { background:var(--color-token-list-hover-background); }
|
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
# 任务切换
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
仅在线程侧栏收起时显示入口;展开时移除入口并关闭浮层。入口随 Codex 标题栏布局切换:
|
|
4
4
|
- 普通/分栏:在主 Chat 标题前显示原生 list-bullet 图标;整段标题可点击。
|
|
5
5
|
- 全宽标签页:在 Chat 标签页前注入独立的任务图标按钮。
|
|
6
|
-
-
|
|
7
|
-
-
|
|
6
|
+
- 移除扩展铃铛,注入入口存在时,用原生完整 SVG 显示无蓝点的 Sidebar 图标;保留 React 原节点,卸载或展开侧栏时移除替代图标并恢复原节点。
|
|
7
|
+
- 悬停入口150ms展开;鼠标离开入口与浮层250ms后关闭,跨越8px间隙进入浮层会取消关闭。点击入口确保打开;点击外部、Escape 关闭。Enter、空格、ArrowDown 可打开。
|
|
8
8
|
|
|
9
9
|
样式使用原生字体、text-secondary、radius-lg、primary-ghost-hover 与 ring token。入口仅 hover 显示背景,不保持选中底色。标题区域 no-drag,其他标题栏区域保留窗口拖拽。
|
|
10
10
|
|
|
11
|
-
浮层宽320px(窄窗口缩小),最大高
|
|
11
|
+
浮层宽320px(窄窗口缩小),最大高440px(约5条,受视口高度限制);沿用 dropdown 背景与 border,圆角16px、shadow-md,外层上下 padding 6px、左右0;滚动容器左右 padding 6px,使滚动条靠近浮层边缘。位置对齐标题触发区域左侧、底缘下方8px,并受窗口边界约束。
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
1.
|
|
15
|
-
2.
|
|
16
|
-
3. 所属 Project
|
|
13
|
+
列表集合为等待回复/批准、工作中、最近1小时打开过、未读的会话,按 Thread ID 去重。按需要回复、等待批准、未读、执行中、空闲排序,同级按消息时间倒序;排除当前会话。原生流式摘要变化时更新观测消息时间,不因菜单重绘更新时间。最近访问记录限定当前窗口运行期间。每条显示:
|
|
14
|
+
1. 单行标题(13px/18px),远程标识紧随标题且不显示主机名;右侧状态优先级为待确认标签、原生 loading、相对时间。背景仅 hover 时显示。
|
|
15
|
+
2. 单行省略的 AI 回答摘要(12px/16px)。
|
|
16
|
+
3. 所属 Project 原生图标(12px)与名称(若有),11px/16px 弱化文字,不合并分组。条目 padding 10px 12px,行间 gap 4px,圆角10px。
|
|
17
17
|
|
|
18
18
|
时间使用原生 hover card 的 recencyAt,缺失时 updatedAt;每30秒刷新。工作状态读取原生 statusState.type=loading。缓存会话数据与原生导航回调,复用导航和已读更新,不缓存 DOM 行,不发送消息。
|
|
19
19
|
|
|
20
20
|
打开时临时启用 Activity 以取得完整列表,关闭或卸载时恢复原先模式。只读取原生数据,预览不标记已读。界面及无障碍文案适配中英文。
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
入口只显示任务图标,不显示数量。浮层标题为 Recent activity/最近活动,反映工作中、未读与最近访问的集合;不将最近访问误称为待处理。浮层无页面遮罩,点击外部关闭,Escape 关闭并返回入口焦点。
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
标题固定、条目容器独立滚动;上下保留6px内边距;条目内容左右保留6px,滚动条不受外层横向内边距挤压。
|
|
25
25
|
|
|
26
26
|
点击后先同步显示浮层与可用数据,首帧绘制后再切换原生 Activity 补全条目;关闭或卸载会取消尚未开始的切换。
|
|
27
27
|
|
|
28
28
|
消息刷新时保留列表滚动位置与条目键盘焦点。当前身份消失时清除选中状态,最近访问缓存按一小时过期。
|
|
29
|
+
|
|
30
|
+
待确认状态读取原生行的 `statusPill.label.props.id=codex.localTaskRow.needsInput` 或 `chips[].id=awaiting-approval`,不根据摘要推断。Needs input/需要回复沿用原生 chart-blue 15% 背景,Awaiting approval/等待批准沿用 chart-green 20% 背景与圆角胶囊;适配紧凑行使用12px/16px文字。点击条目仍仅导航,不执行批准或原生 Snooze 动作。
|
|
31
|
+
|
|
32
|
+
滚动条与原生 overflow 容器一致:thin,静止使用 `--color-border`,hover/active 使用 `--color-border-strong`,轨道透明;不使用全局较深的 `--scrollbar-color`。
|
|
33
|
+
|
|
34
|
+
入口提醒点:排除当前会话后,有需要回复或批准时为红色 `#FF3B30`,否则存在未读为原生蓝色 `--blue-400`;只有执行中或空闲则无点。点直径6px。无其他命中会话时不打开浮层,包括仅当前会话命中的情况。原生侧栏图标与 MineCodex 提醒随注入/卸载同步恢复。
|
|
35
|
+
|
|
36
|
+
浮层标题颜色与原生 Sidebar 类别标题一致,使用 `--color-text-tertiary`。
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// 原生完整侧栏图标,取自当前 Codex 16px/20px 资源。
|
|
2
|
+
const sidebarGlyphs = {"16": "<svg aria-hidden=\"true\" class=\"_Icon_uttcz_1\" focusable=\"false\" height=\"16\" viewBox=\"0 0 16 16\" width=\"16\"><path d=\"M4.66699 4.80859C4.95683 4.80873 5.19238 5.04412 5.19238 5.33398V10.667C5.19238 10.9569 4.95683 11.1923 4.66699 11.1924C4.37704 11.1924 4.1416 10.9569 4.1416 10.667V5.33398C4.1416 5.04403 4.37704 4.80859 4.66699 4.80859Z\" fill=\"currentColor\"></path> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M11.5 2.30762C13.1707 2.30762 14.5254 3.66235 14.5254 5.33301V10.666C14.5254 12.3367 13.1707 13.6914 11.5 13.6914H4.5C2.82934 13.6914 1.47461 12.3367 1.47461 10.666V5.33301C1.47461 3.66235 2.82934 2.30762 4.5 2.30762H11.5ZM4.5 3.3584C3.40924 3.3584 2.52539 4.24225 2.52539 5.33301V10.666C2.52539 11.7568 3.40924 12.6416 4.5 12.6416H11.5C12.5908 12.6416 13.4746 11.7568 13.4746 10.666V5.33301C13.4746 4.24225 12.5908 3.3584 11.5 3.3584H4.5Z\" fill=\"currentColor\"></path></svg>", "20": "<svg aria-hidden=\"true\" class=\"_Icon_uttcz_1\" focusable=\"false\" height=\"20\" viewBox=\"0 0 20 20\" width=\"20\"><path d=\"M5.83301 6.00098C6.20028 6.00098 6.49805 6.29875 6.49805 6.66602V13.333C6.49787 13.7001 6.20017 13.998 5.83301 13.998C5.46596 13.9979 5.16814 13.7 5.16797 13.333V6.66602C5.16797 6.29883 5.46585 6.00111 5.83301 6.00098Z\" fill=\"currentColor\"></path> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M14.5 2.87695C16.5241 2.87695 18.165 4.51787 18.165 6.54199V13.459C18.1649 15.483 16.524 17.124 14.5 17.124H5.5C3.47598 17.124 1.83514 15.483 1.83496 13.459V6.54199C1.83496 4.51787 3.47588 2.87695 5.5 2.87695H14.5ZM5.5 4.20703C4.21042 4.20703 3.16504 5.25241 3.16504 6.54199V13.459C3.16521 14.7484 4.21052 15.7939 5.5 15.7939H14.5C15.7895 15.7939 16.8348 14.7484 16.835 13.459V6.54199C16.835 5.25241 15.7896 4.20703 14.5 4.20703H5.5Z\" fill=\"currentColor\"></path></svg>"};
|
|
1
3
|
const taskGlyph = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M13.3336 11.4749C13.6234 11.475 13.858 11.7104 13.858 12.0002C13.8579 12.2899 13.6233 12.5255 13.3336 12.5256H6.66663C6.37678 12.5256 6.1414 12.2901 6.14124 12.0002C6.14124 11.7103 6.37668 11.4749 6.66663 11.4749H13.3336Z\" fill=\"currentColor\"/> <path d=\"M3.07874 10.9827C3.50002 10.9827 3.84226 11.3241 3.84241 11.7454C3.84241 12.1667 3.50011 12.509 3.07874 12.509C2.65748 12.5089 2.31604 12.1666 2.31604 11.7454C2.31619 11.3242 2.65758 10.9828 3.07874 10.9827Z\" fill=\"currentColor\"/> <path d=\"M13.3336 7.47485C13.6234 7.47503 13.858 7.7104 13.858 8.00024C13.8579 8.28995 13.6233 8.52546 13.3336 8.52563H6.66663C6.37678 8.52563 6.1414 8.29005 6.14124 8.00024C6.14124 7.71029 6.37668 7.47485 6.66663 7.47485H13.3336Z\" fill=\"currentColor\"/> <path d=\"M3.07874 6.98267C3.50002 6.98267 3.84226 7.32411 3.84241 7.74536C3.84241 8.16674 3.50011 8.50903 3.07874 8.50903C2.65748 8.50888 2.31604 8.16665 2.31604 7.74536C2.31619 7.3242 2.65758 6.98281 3.07874 6.98267Z\" fill=\"currentColor\"/> <path d=\"M13.3336 3.47485C13.6234 3.47503 13.858 3.7104 13.858 4.00024C13.8579 4.28995 13.6233 4.52546 13.3336 4.52563H6.66663C6.37678 4.52563 6.1414 4.29005 6.14124 4.00024C6.14124 3.71029 6.37668 3.47485 6.66663 3.47485H13.3336Z\" fill=\"currentColor\"/> <path d=\"M3.07874 2.98267C3.50002 2.98267 3.84226 3.32411 3.84241 3.74536C3.84241 4.16674 3.50011 4.50903 3.07874 4.50903C2.65748 4.50888 2.31604 4.16665 2.31604 3.74536C2.31619 3.3242 2.65758 2.98281 3.07874 2.98267Z\" fill=\"currentColor\"/></svg>";
|
|
2
4
|
const deviceGlyphs = {"local": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"none\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M12.6667 2.47461C13.6928 2.47479 14.525 3.30694 14.5251 4.33301V10.1445C15.0248 10.184 15.3787 10.6177 15.3787 11.0928V11.9355C15.3785 12.9036 14.6274 13.7373 13.6433 13.7373H2.34644C1.36254 13.7371 0.611273 12.9035 0.611084 11.9355V11.0928C0.611084 10.6149 0.970003 10.1796 1.47437 10.1445V4.33301C1.47454 3.30702 2.3068 2.47492 3.33276 2.47461H12.6667ZM1.66187 11.9355C1.66205 12.3765 1.99397 12.6863 2.34644 12.6865H13.6433C13.9959 12.6865 14.3287 12.3767 14.3289 11.9355V11.1924H1.66187V11.9355ZM3.33276 3.52539C2.8867 3.5257 2.52532 3.88692 2.52515 4.33301V10.1416H13.4744V4.33301C13.4742 3.88684 13.1129 3.52557 12.6667 3.52539H3.33276Z\" fill=\"currentColor\"/></svg>", "remote": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"none\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M11.5005 1.1416C12.4346 1.14184 13.1919 1.89985 13.1919 2.83398V6.55859H13.3335C13.8259 6.55859 14.1917 6.9676 14.1919 7.42969V8.2959C14.1914 9.14015 13.5269 9.85938 12.6665 9.85938H3.3335C2.47306 9.85938 1.80856 9.14015 1.80811 8.2959V7.42969C1.80834 6.9677 2.17428 6.55877 2.6665 6.55859H2.80811V2.83398C2.80811 1.8997 3.56621 1.1416 4.50049 1.1416H11.5005ZM2.85889 8.2959C2.85933 8.59786 3.08989 8.80859 3.3335 8.80859H12.6665C12.9101 8.80859 13.1416 8.59786 13.1421 8.2959V7.60938H2.85889V8.2959ZM4.50049 2.19238C4.14611 2.19238 3.85889 2.4796 3.85889 2.83398V6.55859H12.1421V2.83398C12.1421 2.47975 11.8547 2.19262 11.5005 2.19238H4.50049Z\" fill=\"currentColor\"/> <path d=\"M2.8595 13.4307C3.28863 13.4308 3.63684 13.7788 3.63684 14.208C3.63677 14.6371 3.28859 14.9852 2.8595 14.9854C2.43029 14.9854 2.08222 14.6372 2.08215 14.208C2.08215 13.7788 2.43025 13.4307 2.8595 13.4307Z\" fill=\"currentColor\"/> <path d=\"M5.42981 13.4307C5.859 13.4307 6.20715 13.7788 6.20715 14.208C6.20709 14.6372 5.85896 14.9853 5.42981 14.9854C5.0006 14.9854 4.65253 14.6372 4.65247 14.208C4.65247 13.7788 5.00056 13.4307 5.42981 13.4307Z\" fill=\"currentColor\"/> <path d=\"M8.00012 13.4307C8.42937 13.4307 8.77747 13.7788 8.77747 14.208C8.7774 14.6372 8.42933 14.9854 8.00012 14.9854C7.57092 14.9854 7.22284 14.6372 7.22278 14.208C7.22278 13.7788 7.57087 13.4307 8.00012 13.4307Z\" fill=\"currentColor\"/> <path d=\"M10.5704 13.4307C10.9997 13.4307 11.3478 13.7788 11.3478 14.208C11.3477 14.6372 10.9996 14.9854 10.5704 14.9854C10.1414 14.9852 9.79413 14.6371 9.79407 14.208C9.79407 13.7789 10.1414 13.4309 10.5704 13.4307Z\" fill=\"currentColor\"/> <path d=\"M13.1417 13.4307C13.5707 13.4309 13.9181 13.7789 13.9181 14.208C13.918 14.637 13.5707 14.9851 13.1417 14.9854C12.7125 14.9854 12.3644 14.6372 12.3644 14.208C12.3644 13.7788 12.7125 13.4307 13.1417 13.4307Z\" fill=\"currentColor\"/> <path d=\"M8.00012 10.8809C8.42925 10.8809 8.77727 11.2281 8.77747 11.6572C8.77747 12.0865 8.42937 12.4346 8.00012 12.4346C7.57087 12.4346 7.22278 12.0865 7.22278 11.6572C7.22298 11.2281 7.571 10.8809 8.00012 10.8809Z\" fill=\"currentColor\"/></svg>"};
|
|
3
5
|
const text = (en, cn) => (document.documentElement.lang || navigator.language || "en").startsWith("zh") ? cn : en;
|
|
@@ -9,27 +11,32 @@ style.textContent = `
|
|
|
9
11
|
[data-minecodex-task-title] { pointer-events:auto; -webkit-app-region:no-drag; cursor:pointer; border-radius:var(--radius-lg); min-height:32px; padding-inline:6px; }
|
|
10
12
|
[data-minecodex-task-title]:hover { background:var(--color-background-primary-ghost-hover); }
|
|
11
13
|
[data-minecodex-task-title]:focus-visible { outline:2px solid var(--color-ring); outline-offset:-2px; }
|
|
12
|
-
[data-minecodex-task-badge] { display:inline-flex; flex-shrink:0; align-items:center; gap:4px; color:var(--color-text-secondary); font-family:var(--font-sans-default); font-size:var(--text-xs); font-variant-numeric:tabular-nums; }
|
|
14
|
+
[data-minecodex-task-badge] { display:inline-flex; flex-shrink:0; align-items:center; gap:4px; color:var(--color-text-secondary); font-family:var(--font-sans-default); font-size:var(--text-xs); font-variant-numeric:tabular-nums; position:relative; }
|
|
15
|
+
[data-minecodex-task-badge][data-notification]::after { content:""; position:absolute; width:6px; height:6px; right:-2px; top:-1px; border-radius:50%; background:var(--blue-400,#3A83F7); box-shadow:0 0 0 1.5px var(--color-token-dropdown-background); }
|
|
16
|
+
[data-minecodex-task-badge][data-notification=red]::after { background:#FF3B30; }
|
|
17
|
+
[data-minecodex-sidebar-original] { display:none !important; }
|
|
13
18
|
[data-minecodex-unread=trigger] { -webkit-app-region:no-drag; }
|
|
14
|
-
[${marker}=
|
|
15
|
-
[${marker}=scrim][hidden] { display:none; }
|
|
16
|
-
[${marker}=panel] { position:fixed; z-index:2147483000; box-sizing:border-box; width:min(320px,calc(100vw - 16px)); max-height:min(660px,calc(100vh - 64px)); display:flex; flex-direction:column; overflow:hidden; padding:var(--app-menu-gutter,4px); padding-bottom:calc(var(--spacing,4px) * 3); border:1px solid var(--color-border); border-radius:var(--radius-2xl); background:var(--color-token-dropdown-background); color:var(--color-text-primary); box-shadow:var(--shadow-xl-spread); font-family:var(--font-sans-default); font-size:var(--text-sm); }
|
|
19
|
+
[${marker}=panel] { position:fixed; z-index:2147483000; box-sizing:border-box; width:min(320px,calc(100vw - 16px)); max-height:min(440px,calc(100vh - 64px)); display:flex; flex-direction:column; overflow:hidden; padding:6px 0; border:1px solid var(--color-border); border-radius:16px; background:var(--color-token-dropdown-background); color:var(--color-text-primary); box-shadow:var(--shadow-md,0 4px 16px rgba(0,0,0,.12)); font-family:var(--font-sans-default); font-size:var(--text-sm); }
|
|
17
20
|
[${marker}=panel][hidden] { display:none; }
|
|
18
|
-
[${marker}=panel] .mcx-thread-list { min-height:0; overflow-y:auto; overscroll-behavior:contain; }
|
|
19
|
-
[${marker}=panel]
|
|
20
|
-
[${marker}=panel]
|
|
21
|
-
[${marker}=panel] [data-unread-row]
|
|
21
|
+
[${marker}=panel] .mcx-thread-list { min-height:0; overflow-y:auto; overscroll-behavior:contain; padding:0 6px; scrollbar-width:thin; scrollbar-color:var(--color-border) transparent; }
|
|
22
|
+
[${marker}=panel] .mcx-thread-list:hover, [${marker}=panel] .mcx-thread-list:active { scrollbar-color:var(--color-border-strong) transparent; }
|
|
23
|
+
[${marker}=panel] h2 { flex-shrink:0; margin:8px 16px; font-size:var(--text-sm); font-weight:500; color:var(--color-text-tertiary); }
|
|
24
|
+
[${marker}=panel] [data-unread-row] { display:flex; flex-direction:column; gap:4px; width:100%; padding:10px 12px; box-sizing:border-box; border:0; border-radius:10px; background:transparent; text-align:start; color:var(--color-text-primary); font:inherit; cursor:pointer; }
|
|
25
|
+
[${marker}=panel] [data-unread-row][aria-current=page] .mcx-thread-title::before { content:""; display:inline-block; width:5px; height:5px; margin-inline-end:6px; border-radius:50%; background:currentColor; vertical-align:middle; }
|
|
22
26
|
[${marker}=panel] [data-unread-row]:hover { background:var(--color-background-primary-ghost-hover); }
|
|
23
27
|
[${marker}=panel] [data-unread-row]:focus-visible { outline:2px solid var(--color-ring); outline-offset:-2px; }
|
|
24
28
|
[${marker}=panel] .mcx-thread-header { display:flex; align-items:center; gap:8px; width:100%; min-width:0; }
|
|
25
|
-
[${marker}=panel] .mcx-thread-title { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; font-weight:500; }
|
|
29
|
+
[${marker}=panel] .mcx-thread-title { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; font-size:13px; line-height:18px; font-weight:500; }
|
|
26
30
|
[${marker}=panel] .mcx-device { display:flex; flex-shrink:0; color:var(--color-text-tertiary); }
|
|
27
31
|
[${marker}=panel] .mcx-loading { margin-inline-start:auto; flex-shrink:0; display:flex; align-items:center; color:var(--color-text-tertiary); }
|
|
28
32
|
[${marker}=panel] time { margin-inline-start:auto; flex-shrink:0; font-size:var(--text-xs); color:var(--color-text-tertiary); }
|
|
29
|
-
[${marker}=panel] .mcx-summary { display
|
|
30
|
-
[${marker}=panel] .mcx-project { display:flex; align-items:center; gap:6px; padding:0; min-width:0; font-size:
|
|
33
|
+
[${marker}=panel] .mcx-summary { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:12px; line-height:16px; color:var(--color-text-tertiary); }
|
|
34
|
+
[${marker}=panel] .mcx-project { display:flex; align-items:center; gap:6px; padding:0; min-width:0; font-size:11px; line-height:16px; color:var(--color-text-tertiary); font-weight:400; }
|
|
31
35
|
[${marker}=panel] .mcx-project > span:last-child { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
32
|
-
[${marker}=panel] .mcx-project svg { width:
|
|
36
|
+
[${marker}=panel] .mcx-project svg, [${marker}=panel] .mcx-project-icon { width:12px; height:12px; flex:none; }
|
|
37
|
+
[${marker}=panel] .mcx-project-icon > * { width:12px; height:12px; }
|
|
38
|
+
[${marker}=panel] .mcx-attention { margin-inline-start:auto; flex:none; max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; border-radius:999px; padding:2px 10px 2px 8px; font-size:12px; line-height:16px; color:var(--color-chart-blue); background:color-mix(in srgb,var(--color-chart-blue) 15%,transparent); }
|
|
39
|
+
[${marker}=panel] .mcx-attention[data-attention="approval"] { color:var(--color-chart-green); background:color-mix(in srgb,var(--color-chart-green) 20%,transparent); }
|
|
33
40
|
[${marker}=panel] .mcx-empty { margin:20px 12px; color:var(--color-text-tertiary); }
|
|
34
41
|
`;
|
|
35
42
|
document.head.append(style);
|
|
@@ -39,14 +46,23 @@ panel.id = "minecodex-unread-panel";
|
|
|
39
46
|
panel.setAttribute("role", "dialog");
|
|
40
47
|
panel.hidden = true;
|
|
41
48
|
document.body.append(panel);
|
|
42
|
-
const scrim = document.createElement("div");
|
|
43
|
-
scrim.setAttribute(marker, "scrim");
|
|
44
|
-
scrim.setAttribute("aria-hidden", "true");
|
|
45
|
-
scrim.hidden = true;
|
|
46
|
-
document.body.append(scrim);
|
|
47
|
-
scrim.addEventListener("click", () => close(true), { signal });
|
|
48
49
|
let trigger = null, badge = null, binding = null, standalone = false;
|
|
49
50
|
let activityFrame = null, activityTimer = null;
|
|
51
|
+
let hoverOpenTimer = null, hoverCloseTimer = null;
|
|
52
|
+
function cancelHoverTimers() {
|
|
53
|
+
clearTimeout(hoverOpenTimer); clearTimeout(hoverCloseTimer);
|
|
54
|
+
}
|
|
55
|
+
function scheduleHoverOpen() {
|
|
56
|
+
cancelHoverTimers();
|
|
57
|
+
if (panel.hidden) hoverOpenTimer = setTimeout(open, 150);
|
|
58
|
+
}
|
|
59
|
+
function scheduleHoverClose() {
|
|
60
|
+
cancelHoverTimers();
|
|
61
|
+
// 留出跨越入口与浮层间隙的时间。
|
|
62
|
+
hoverCloseTimer = setTimeout(() => close(), 250);
|
|
63
|
+
}
|
|
64
|
+
panel.addEventListener("pointerenter", cancelHoverTimers, { signal });
|
|
65
|
+
panel.addEventListener("pointerleave", scheduleHoverClose, { signal });
|
|
50
66
|
const recentRows = new Map();
|
|
51
67
|
const openedAt = new Map();
|
|
52
68
|
const messageUpdates = new Map();
|
|
@@ -80,10 +96,14 @@ function threadRecord(row) {
|
|
|
80
96
|
}
|
|
81
97
|
return { ...threadDetails(row), key:row.dataset.appActionSidebarThreadId, unread:isUnread(row), activate, spinner:row.querySelector?.('[class~="motion-safe:animate-spin"]')?.outerHTML ?? "" };
|
|
82
98
|
}
|
|
83
|
-
function
|
|
99
|
+
function notificationColor(records) {
|
|
100
|
+
return records.some(item => item.attention) ? "red" : records.some(item => item.unread) ? "blue" : null;
|
|
101
|
+
}
|
|
102
|
+
function selectThreads(records, recent, now = Date.now(), currentKey = null) {
|
|
103
|
+
const rank = item => item.attention === "input" ? 0 : item.attention === "approval" ? 1 : item.unread ? 2 : item.working ? 3 : 4;
|
|
84
104
|
return [...new Map(records.map(item => [item.key, item])).values()]
|
|
85
|
-
.filter(item => item.working || item.unread || now - (recent.get(item.key) ?? -Infinity) < 3600000)
|
|
86
|
-
.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
105
|
+
.filter(item => item.key !== currentKey && (item.attention || item.working || item.unread || now - (recent.get(item.key) ?? -Infinity) < 3600000))
|
|
106
|
+
.sort((a, b) => rank(a) - rank(b) || (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
87
107
|
}
|
|
88
108
|
function captureCurrentRow() {
|
|
89
109
|
const selected = document.querySelector('#app-shell-sidebar [data-app-action-sidebar-thread-selected="true"]');
|
|
@@ -104,6 +124,16 @@ function projectForRow(row) {
|
|
|
104
124
|
}
|
|
105
125
|
return null;
|
|
106
126
|
}
|
|
127
|
+
function attentionState(row) {
|
|
128
|
+
let fiber = row[Object.keys(row).find(key => key.startsWith("__reactFiber"))];
|
|
129
|
+
for (let depth = 0; fiber && depth < 35; depth++, fiber = fiber.return) {
|
|
130
|
+
const props = fiber.memoizedProps;
|
|
131
|
+
// 复用原生状态契约,不从摘要文案推断待确认状态。
|
|
132
|
+
if (props?.chips?.some(chip => chip.id === "awaiting-approval")) return "approval";
|
|
133
|
+
if (props?.statusPill?.label?.props?.id === "codex.localTaskRow.needsInput") return "input";
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
107
137
|
function threadDetails(row) {
|
|
108
138
|
let fiber = row[Object.keys(row).find(key => key.startsWith("__reactFiber"))];
|
|
109
139
|
let summary, hostConfig;
|
|
@@ -129,6 +159,7 @@ function threadDetails(row) {
|
|
|
129
159
|
hostId, hostName:hostConfig?.display_name || hostId,
|
|
130
160
|
project:projectForRow(row),
|
|
131
161
|
working:isWorking(row),
|
|
162
|
+
attention:attentionState(row),
|
|
132
163
|
};
|
|
133
164
|
}
|
|
134
165
|
function relativeTime(timestamp, now = Date.now()) {
|
|
@@ -146,10 +177,10 @@ function cacheProjectIcons() {
|
|
|
146
177
|
}
|
|
147
178
|
}
|
|
148
179
|
function close(restoreFocus = false) {
|
|
180
|
+
cancelHoverTimers();
|
|
149
181
|
cancelAnimationFrame(activityFrame); clearTimeout(activityTimer);
|
|
150
182
|
activityFrame = null; activityTimer = null;
|
|
151
183
|
panel.hidden = true;
|
|
152
|
-
scrim.hidden = true;
|
|
153
184
|
if (trigger?.getAttribute("aria-expanded") !== "false") trigger?.setAttribute("aria-expanded", "false");
|
|
154
185
|
if (restoreActivity) {
|
|
155
186
|
restoreActivity = false;
|
|
@@ -165,24 +196,33 @@ function position() {
|
|
|
165
196
|
panel.style.left = `${Math.max(8, Math.min(rect.left, innerWidth - width - 8))}px`;
|
|
166
197
|
panel.style.top = `${rect.bottom + 8}px`;
|
|
167
198
|
panel.style.maxWidth = `${width}px`;
|
|
168
|
-
panel.style.maxHeight = `${Math.max(0, Math.min(
|
|
199
|
+
panel.style.maxHeight = `${Math.max(0, Math.min(440, innerHeight - rect.bottom - 24))}px`;
|
|
169
200
|
}
|
|
170
|
-
function
|
|
201
|
+
function activityRecords() {
|
|
171
202
|
captureCurrentRow();
|
|
172
203
|
const records = [...document.querySelectorAll('#app-shell-sidebar [data-app-action-sidebar-thread-row]')].map(threadRecord);
|
|
173
204
|
const mounted = new Set(records.map(item => item.key));
|
|
174
205
|
for (const item of records) if (openedAt.has(item.key)) recentRows.set(item.key, item);
|
|
175
|
-
for (const [key, item] of recentRows) if (!mounted.has(key)) records.push({ ...item, working:false, unread:false });
|
|
176
|
-
|
|
177
|
-
|
|
206
|
+
for (const [key, item] of recentRows) if (!mounted.has(key)) records.push({ ...item, working:false, unread:false, attention:null });
|
|
207
|
+
return selectThreads(records, openedAt, Date.now(), selectedKey);
|
|
208
|
+
}
|
|
209
|
+
function syncNotification(details) {
|
|
210
|
+
const color = notificationColor(details);
|
|
211
|
+
if (color) { if (badge?.dataset.notification !== color) badge?.setAttribute("data-notification", color); }
|
|
212
|
+
else badge?.removeAttribute("data-notification");
|
|
213
|
+
}
|
|
214
|
+
function renderRows(details = activityRecords()) {
|
|
215
|
+
syncNotification(details);
|
|
216
|
+
if (!details.length) { close(); return; }
|
|
217
|
+
const next = JSON.stringify([text("Recent activity", "最近活动"), details, Math.floor(Date.now() / 60000)]);
|
|
178
218
|
if (signature === next) return;
|
|
179
219
|
signature = next;
|
|
180
220
|
const scrollTop = panel.querySelector(".mcx-thread-list")?.scrollTop ?? 0;
|
|
181
221
|
const focusedKey = panel.contains(document.activeElement) ? document.activeElement?.dataset.threadKey : null;
|
|
182
222
|
panel.replaceChildren();
|
|
183
|
-
panel.setAttribute("aria-label", text("
|
|
223
|
+
panel.setAttribute("aria-label", text("Recent activity", "最近活动"));
|
|
184
224
|
const heading = document.createElement("h2");
|
|
185
|
-
heading.textContent = text("
|
|
225
|
+
heading.textContent = text("Recent activity", "最近活动");
|
|
186
226
|
panel.append(heading);
|
|
187
227
|
const list = document.createElement("div");
|
|
188
228
|
list.className = "mcx-thread-list";
|
|
@@ -202,11 +242,18 @@ function renderRows() {
|
|
|
202
242
|
device.className = "mcx-device";
|
|
203
243
|
const remote = item.hostId !== "local";
|
|
204
244
|
device.innerHTML = deviceGlyphs[remote ? "remote" : "local"];
|
|
205
|
-
device.title =
|
|
245
|
+
device.title = text("Remote", "远程");
|
|
206
246
|
device.setAttribute("aria-label", device.title);
|
|
207
|
-
header.append(title
|
|
247
|
+
header.append(title);
|
|
248
|
+
if (remote) header.append(device);
|
|
208
249
|
const elapsed = relativeTime(item.timestamp);
|
|
209
|
-
if (item.
|
|
250
|
+
if (item.attention) {
|
|
251
|
+
const label = document.createElement("span");
|
|
252
|
+
label.className = "mcx-attention";
|
|
253
|
+
label.dataset.attention = item.attention;
|
|
254
|
+
label.textContent = item.attention === "approval" ? text("Awaiting approval", "等待批准") : text("Needs input", "需要回复");
|
|
255
|
+
header.append(label);
|
|
256
|
+
} else if (item.working) {
|
|
210
257
|
const loading = document.createElement("span");
|
|
211
258
|
loading.className = "mcx-loading";
|
|
212
259
|
loading.setAttribute("role", "status");
|
|
@@ -231,9 +278,12 @@ function renderRows() {
|
|
|
231
278
|
if (item.project) {
|
|
232
279
|
const project = document.createElement("span");
|
|
233
280
|
project.className = "mcx-project";
|
|
281
|
+
const icon = document.createElement("span");
|
|
282
|
+
icon.className = "mcx-project-icon";
|
|
234
283
|
const glyph = projectIcons.get(item.project.id)?.cloneNode(true);
|
|
235
|
-
if (glyph)
|
|
236
|
-
else
|
|
284
|
+
if (glyph) icon.append(glyph);
|
|
285
|
+
else icon.innerHTML = folderGlyph;
|
|
286
|
+
project.append(icon);
|
|
237
287
|
const name = document.createElement("span");
|
|
238
288
|
name.textContent = item.project.name;
|
|
239
289
|
project.append(name);
|
|
@@ -266,10 +316,12 @@ function open() {
|
|
|
266
316
|
captureCurrentRow();
|
|
267
317
|
const native = activityButton();
|
|
268
318
|
if (!native) return;
|
|
319
|
+
const details = activityRecords();
|
|
320
|
+
syncNotification(details);
|
|
321
|
+
if (!details.length) return;
|
|
269
322
|
panel.hidden = false;
|
|
270
|
-
scrim.hidden = false;
|
|
271
323
|
trigger.setAttribute("aria-expanded", "true");
|
|
272
|
-
renderRows(); position();
|
|
324
|
+
renderRows(details); position();
|
|
273
325
|
// 先呈现浮层,下一帧之后再更新原生 Activity,避免同步 React 切换阻塞点击反馈。
|
|
274
326
|
if (native.getAttribute("aria-pressed") !== "true") {
|
|
275
327
|
activityFrame = requestAnimationFrame(() => {
|
|
@@ -288,7 +340,36 @@ function open() {
|
|
|
288
340
|
function visible(element) {
|
|
289
341
|
return element && !element.closest('[aria-hidden="true"], [inert]') && element.checkVisibility();
|
|
290
342
|
}
|
|
343
|
+
const nativeSidebarIcons = new Map();
|
|
344
|
+
function restoreSidebarIcons() {
|
|
345
|
+
for (const [original, replacement] of nativeSidebarIcons) {
|
|
346
|
+
original.removeAttribute("data-minecodex-sidebar-original"); replacement.remove();
|
|
347
|
+
}
|
|
348
|
+
nativeSidebarIcons.clear();
|
|
349
|
+
}
|
|
350
|
+
function syncSidebarIcons(toggle) {
|
|
351
|
+
for (const [original, replacement] of nativeSidebarIcons) {
|
|
352
|
+
if (!original.isConnected || !toggle.contains(original)) {
|
|
353
|
+
original.removeAttribute("data-minecodex-sidebar-original"); replacement.remove(); nativeSidebarIcons.delete(original);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
for (const original of toggle.querySelectorAll('svg:not([data-minecodex-sidebar-replacement])')) {
|
|
357
|
+
if (nativeSidebarIcons.has(original)) continue;
|
|
358
|
+
const glyph = sidebarGlyphs[original.getAttribute("width")];
|
|
359
|
+
if (!glyph) continue;
|
|
360
|
+
const container = document.createElement("span");
|
|
361
|
+
container.innerHTML = glyph;
|
|
362
|
+
const replacement = container.firstElementChild;
|
|
363
|
+
replacement.setAttribute("class", original.getAttribute("class") || "");
|
|
364
|
+
replacement.setAttribute("data-minecodex-sidebar-replacement", "");
|
|
365
|
+
replacement.setAttribute("data-no-autosize", "true");
|
|
366
|
+
// 不移除 React 所有的 SVG,只切换显示并插入独立资源。
|
|
367
|
+
original.setAttribute("data-minecodex-sidebar-original", "");
|
|
368
|
+
original.after(replacement); nativeSidebarIcons.set(original, replacement);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
291
371
|
function detachTrigger() {
|
|
372
|
+
restoreSidebarIcons();
|
|
292
373
|
binding?.abort(); binding = null;
|
|
293
374
|
if (standalone) trigger?.remove();
|
|
294
375
|
else if (trigger) {
|
|
@@ -332,7 +413,9 @@ function sync() {
|
|
|
332
413
|
badge.setAttribute("data-minecodex-task-badge", "");
|
|
333
414
|
badge.innerHTML = taskGlyph;
|
|
334
415
|
trigger.prepend(badge);
|
|
335
|
-
trigger.addEventListener("click", event => { event.stopPropagation(); panel.hidden
|
|
416
|
+
trigger.addEventListener("click", event => { event.stopPropagation(); if (panel.hidden) open(); }, {signal:binding.signal});
|
|
417
|
+
trigger.addEventListener("pointerenter", scheduleHoverOpen, { signal:binding.signal });
|
|
418
|
+
trigger.addEventListener("pointerleave", scheduleHoverClose, { signal:binding.signal });
|
|
336
419
|
trigger.addEventListener("keydown", event => {
|
|
337
420
|
if (["Enter", " ", "ArrowDown"].includes(event.key)) {
|
|
338
421
|
event.preventDefault(); event.stopPropagation();
|
|
@@ -340,9 +423,12 @@ function sync() {
|
|
|
340
423
|
}
|
|
341
424
|
}, {signal:binding.signal});
|
|
342
425
|
}
|
|
343
|
-
|
|
426
|
+
syncSidebarIcons(sidebarToggle);
|
|
427
|
+
const details = activityRecords();
|
|
428
|
+
syncNotification(details);
|
|
429
|
+
const label = text("Recent activity", "最近活动");
|
|
344
430
|
if (trigger.getAttribute("aria-label") !== label) trigger.setAttribute("aria-label", label);
|
|
345
|
-
if (!panel.hidden) { renderRows(); position(); }
|
|
431
|
+
if (!panel.hidden) { renderRows(details); position(); }
|
|
346
432
|
}
|
|
347
433
|
const observer = new MutationObserver(() => {
|
|
348
434
|
if (queued) return;
|
|
@@ -350,11 +436,11 @@ const observer = new MutationObserver(() => {
|
|
|
350
436
|
queueMicrotask(() => { queued = false; sync(); });
|
|
351
437
|
});
|
|
352
438
|
observer.observe(document.documentElement, { childList:true, subtree:true, attributes:true, attributeFilter:["aria-expanded", "aria-pressed", "aria-hidden", "data-app-shell-header-layout", "data-app-action-sidebar-thread-selected", "lang"] });
|
|
353
|
-
document.addEventListener("pointerdown", event => { if (
|
|
439
|
+
document.addEventListener("pointerdown", event => { if (!trigger?.contains(event.target) && !panel.contains(event.target)) close(); }, { signal });
|
|
354
440
|
document.addEventListener("keydown", event => { if (event.key === "Escape" && !panel.hidden) { event.preventDefault(); close(true); } }, { signal });
|
|
355
441
|
window.addEventListener("resize", position, { signal });
|
|
356
|
-
const refreshTimer = setInterval(
|
|
442
|
+
const refreshTimer = setInterval(sync, 30000);
|
|
357
443
|
sync();
|
|
358
444
|
return () => {
|
|
359
|
-
disposed = true; clearInterval(refreshTimer); observer.disconnect(); close(); detachTrigger(); panel.remove();
|
|
445
|
+
disposed = true; clearInterval(refreshTimer); observer.disconnect(); close(); detachTrigger(); panel.remove(); style.remove();
|
|
360
446
|
};
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
## 数据契约
|
|
10
10
|
|
|
11
|
-
已在 2026-09-
|
|
12
|
-
`["rate-limit-status"
|
|
13
|
-
|
|
11
|
+
已在 2026-09-29 的运行中 Codex 确认:原生 QueryClient 的成功额度数据缓存键为
|
|
12
|
+
`["rate-limit-status"]`,由 Codex 自行更新。
|
|
13
|
+
插件只订阅该缓存,校验响应的 user_id/account_id 与当前原生 auth 一致,不自行请求
|
|
14
14
|
接口,不持久化账号或额度数据。切换账号、退出登录、额度数据缺失时隐藏旧值。
|
|
15
15
|
|
|
16
16
|
窗口按 `limit_window_seconds` 识别:604800 为周、18000 为五小时;不依赖
|
|
@@ -42,7 +42,7 @@ function nativeContext(rail) {
|
|
|
42
42
|
function currentWindows(context) {
|
|
43
43
|
const { auth, client } = context;
|
|
44
44
|
if (auth?.isLoading || !auth?.userId || !auth?.accountId || !["chatgpt", "chatgptAuthTokens"].includes(auth.authMethod)) return null;
|
|
45
|
-
const data = client?.getQueryData(["rate-limit-status"
|
|
45
|
+
const data = client?.getQueryData(["rate-limit-status"]);
|
|
46
46
|
// 账号与缓存必须同时一致;旧账号的最后一次响应不能留在导航栏。
|
|
47
47
|
if (!data || data.user_id !== auth.userId || data.account_id !== auth.accountId) return null;
|
|
48
48
|
return remainingWindows(data);
|