minecodex 2.0.3 → 2.0.4

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.
@@ -14,7 +14,9 @@
14
14
  | 主要文字 | `--color-text-primary` | 弹层标题和数值 |
15
15
  | 分类进度 | Codex `--app-block-accent-purple`、`--app-block-accent-blue`、`--app-block-accent-green`,以及 `--color-token-border-default` | 各弹层的分类比例与未分类段 |
16
16
 
17
- 状态行和输入框共用内容宽度,单行高度为 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 和点击外部关闭。遵守系统减少动态效果设置,不添加持续动画。
17
+ 状态行和输入框共用内容宽度,单行高度为 24px,顶部留 2px 内边距;每个状态按钮高 22px,内边距为上下 3px、左右 6px。按钮之间 gap 为 4px,两组可见内容间距为 16px(6 + 4 + 6);按钮内图标与文字 gap 为 6px。状态行展开且可见时,在包含它的原生 `[data-thread-scroll-footer="true"]` 容器上将 `--thread-composer-bottom-inset` 设为 4px;默认主聊天底部 padding 从 16px 收为 4px,收起、隐藏或卸载后自动恢复。原生 panel 的 `pb-3` 与 compact 分支不消费该变量,保留其独立留白。展开的状态行使用 -4px 顶部外边距,将输入框下方原生 8px gap 的实际距离收为4px,收起时恢复。宽度不足时自然换行,不覆盖输入框或悬浮控件。统计图标采用 Lucide Gauge,用量图标采用 Phosphor Database 单路径版本,两者以 14px 绘制;上下文使用与 Codex 原生相同参数的 12px 百分环。弹层由按钮锚定,键盘可聚焦,Escape 和点击外部关闭。遵守系统减少动态效果设置,不添加持续动画。
18
+
19
+ 挂载依据:2026-10-02,Codex 26.930.21537 的主聊天实测 DOM 与 `thread-scroll-layout` / `app-primary` 源码。状态行仍作为 `[data-codex-composer-root]` 的紧邻后续节点;中间的 `display:contents` 包装不改变它与输入框在外层 flex 中的顺序和宽度。折叠入口放在 reasoning 控件最接近 `items-center` 控制组的包装节点之前;原生 Context 环仍为带 `aria-label`、`role=img` 的 span,包含 SVG circle。新版默认 footer 使用 `pb-(--thread-composer-bottom-inset)`,不再匹配旧 `.pb-4` 规则。panel/compact 分支来自源码,未据此宣称侧边聊天的手动验收已完成。
18
20
 
19
21
  状态行以当前输入框所在的会话为准,不依赖侧栏中可能因项目折叠而卸载的 Thread 行。新聊天没有会话 ID 时隐藏旧数据;首条消息建立会话后默认展开详细状态行;若用户曾收起,则沿用其选择。尚未取得的指标用 `—` 占位,不等待日志或 Token 用量;切换会话时立即清空旧数值,详情展开状态保持不变。
20
22
 
@@ -2,10 +2,17 @@
2
2
  export function createContextUsageAdapter(signal, codex, onChange, loadFamily = null) {
3
3
  let family=null, thread=null, composer=null, unsubscribe=null;
4
4
  const empty = () => ({ contextUsedTokens:null, contextWindow:null, contextPercent:null });
5
+ let published = null;
6
+ function publish(value) {
7
+ // 未就绪时重复连接不能重绘,否则 DOM 监听会在微任务中自循环。
8
+ if (published && Object.keys(value).every(key => published[key] === value[key])) return;
9
+ published = value;
10
+ onChange(value);
11
+ }
5
12
  function disconnect() { unsubscribe?.(); unsubscribe=null; }
6
13
  function connect(id, element) {
7
- if (thread === id && unsubscribe) return;
8
- disconnect(); thread=id; composer=element; onChange(empty());
14
+ if (thread === id && composer === element && unsubscribe) return;
15
+ disconnect(); thread=id; composer=element; publish(empty());
9
16
  if (!id || !family || !element || signal.aborted) return;
10
17
  const props=codex.props(element, p => p.value instanceof Map && [...p.value.values()].some(node => node?.token?.__scopeBrand === 'AppScope'));
11
18
  const chain=props?.value, node=chain && [...chain.values()].find(node => node?.token?.__scopeBrand === 'AppScope');
@@ -13,11 +20,11 @@ export function createContextUsageAdapter(signal, codex, onChange, loadFamily =
13
20
  try {
14
21
  const atom=family.resolve(node,chain,id).resolve(node,chain);
15
22
  const update=()=>{
16
- if (signal.aborted || thread !== id) return;
23
+ if (signal.aborted || thread !== id || composer !== element) return;
17
24
  const usage=node.store.get(atom), window=usage?.modelContextWindow, total=usage?.last?.totalTokens;
18
25
  const valid=Number.isFinite(window) && window > 0 && Number.isFinite(total) && total >= 0;
19
26
  const used=valid ? Math.min(total,window) : null;
20
- onChange(valid ? {contextUsedTokens:used,contextWindow:window,contextPercent:Math.round(used/window*100)} : empty());
27
+ publish(valid ? {contextUsedTokens:used,contextWindow:window,contextPercent:Math.round(used/window*100)} : empty());
21
28
  };
22
29
  unsubscribe=node.store.sub(atom,update); update(); codex.report("context-usage",true);
23
30
  } catch { codex.report("context-usage",false,"atom-contract-unavailable"); }
@@ -43,7 +43,7 @@ style.textContent = `
43
43
  [data-minecodex-session-reveal]:not([data-expanded]) { pointer-events:none; }
44
44
  [data-minecodex-session-reveal][hidden] { display:none; }
45
45
  /* 仅展开状态行时缩减原生底部留白;收起或卸载后自动恢复。 */
46
- .bottom-0.pb-4:has([data-minecodex-session-reveal][data-expanded]:not([hidden])) { padding-bottom:4px; }
46
+ [data-thread-scroll-footer="true"]:has([data-minecodex-session-reveal][data-expanded]:not([hidden])) { --thread-composer-bottom-inset:4px; }
47
47
  [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(--mcx-radius-full); background:transparent; color:var(--mcx-color-text-tertiary); cursor:pointer; }
48
48
  [data-minecodex-context-trigger][hidden] { display:none; }
49
49
  [data-context-progress] { transition:stroke-dashoffset 120ms ease-out; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecodex",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Lightweight, local-first plugins for the Codex desktop app.",
5
5
  "license": "MIT",
6
6
  "type": "module",