minecodex 2.0.44 → 2.0.46
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/README.md +0 -1
- package/apps/dashboard/app.js +1 -1
- package/apps/dashboard/assets/preview-content.js +4 -3
- package/features/file-navigation/src/adapters/codex/modules.mjs +10 -11
- package/features/files-auto-refresh/assets/folder-sync.svg +7 -0
- package/features/files-auto-refresh/codex-feature.json +17 -0
- package/features/files-auto-refresh/src/adapters/codex/directories.mjs +75 -0
- package/features/files-auto-refresh/src/entry.mjs +58 -0
- package/features/files-auto-refresh/src/injection-source.mjs +8 -0
- package/features/files-auto-refresh/src/refresh.mjs +32 -0
- package/features/files-auto-refresh/vendor/lucide-LICENSE +43 -0
- package/features/images/src/adapters/codex/native-image-modules.mjs +11 -4
- package/features/images/src/frontend/local-image-source.mjs +11 -1
- package/features/markdown-reading/src/adapters/codex/table-actions.mjs +16 -17
- package/features/markdown-reading/src/adapters/codex/tables.mjs +18 -4
- package/features/markdown-reading/src/injection-source.mjs +2 -1
- package/features/mermaid-diagrams/src/adapters/codex/native.mjs +14 -6
- package/features/model-slider/src/frontend/install.mjs +11 -2
- package/features/model-slider/src/injection-source.mjs +2 -1
- package/features/quick-chat/assets/close.svg +15 -0
- package/features/quick-chat/codex-feature.json +2 -1
- package/features/quick-chat/src/adapters/codex/lifecycle.mjs +27 -0
- package/features/quick-chat/src/adapters/codex/renderer.mjs +8 -5
- package/features/quick-chat/src/adapters/codex/shortcut.mjs +38 -14
- package/features/quick-chat/src/frontend/entry.mjs +33 -18
- package/features/quick-chat/src/injection-source.mjs +2 -1
- package/features/usage-meter/src/adapters/codex/renderer.mjs +14 -4
- package/features/usage-meter/src/usage-meter.mjs +24 -19
- package/package.json +6 -4
- package/packages/cli/src/config.mjs +4 -2
- package/packages/cli/src/control-server.mjs +1 -1
- package/packages/ui/src/native/tokens.mjs +2 -0
- package/features/hide-upsell-banner/codex-feature.json +0 -24
- package/features/hide-upsell-banner/src/hide-upsell-banner.mjs +0 -94
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createModelControllerAdapter } from "./adapters/codex/controller.mjs";
|
|
2
2
|
import { createModelRules } from "./domain/model-rules.mjs";
|
|
3
|
-
import { installModelSlider } from "./frontend/install.mjs";
|
|
3
|
+
import { installModelSlider, replaceModelSliderShell } from "./frontend/install.mjs";
|
|
4
4
|
|
|
5
5
|
// 两部分均显式序列化,Renderer 不捕获 Node imports。
|
|
6
6
|
export function createModelSliderInstallerSource() {
|
|
7
7
|
return `function installModelSliderFeature(signal, context) {
|
|
8
|
+
const replaceModelSliderShell = ${replaceModelSliderShell.toString()};
|
|
8
9
|
return (${installModelSlider.toString()})(signal, context, (${createModelRules.toString()})(), ${createModelControllerAdapter.toString()});
|
|
9
10
|
}`;
|
|
10
11
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!-- @license lucide-static v0.468.0 - ISC; source: lucide-icons/lucide icons/x.svg -->
|
|
2
|
+
<svg
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
|
+
fill="none"
|
|
8
|
+
stroke="currentColor"
|
|
9
|
+
stroke-width="2"
|
|
10
|
+
stroke-linecap="round"
|
|
11
|
+
stroke-linejoin="round"
|
|
12
|
+
>
|
|
13
|
+
<path d="M18 6 6 18" />
|
|
14
|
+
<path d="m6 6 12 12" />
|
|
15
|
+
</svg>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 从原生“丢弃 Quick Chat”动作中仅取关闭和草稿清理;不调用会话销毁,保留后台生成。
|
|
2
|
+
export function quickChatLifecycleActions(source) {
|
|
3
|
+
const match = source.match(/let ([\w$]+)=([\w$]+)\.get\(([\w$]+)\);([\w$]+)\(\2\),\2\.set\(\3,null\),\1!=null&&\(([\w$]+)\(\2,\{draftKey:\1\.conversationId,content:null\}\),([\w$]+)\(\2,\1\.conversationId,[`"'][`"']\),\2\.set\(([\w$]+),\1\.conversationId,\[\]\)/);
|
|
4
|
+
if (!match) throw new Error('quick-chat-close-contract-unavailable');
|
|
5
|
+
const exports = source.slice(source.lastIndexOf('export{'));
|
|
6
|
+
const exported = local => {
|
|
7
|
+
const escaped = local.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');
|
|
8
|
+
const name = exports.match(new RegExp(`(?:\\{|,)${escaped} as ([\\w$]+)(?:,|\\})`))?.[1];
|
|
9
|
+
if (!name) throw new Error('quick-chat-close-export-unavailable');
|
|
10
|
+
return name;
|
|
11
|
+
};
|
|
12
|
+
const roles = {session:exported(match[3]),close:exported(match[4]),draft:exported(match[5]),text:exported(match[6]),attachments:exported(match[7])};
|
|
13
|
+
const mode = source.match(/([\w$]+)=[\w$]+\([\w$]+,\(\{get:[\w$]+\}\)=>\{[\s\S]{0,700}?conversationDetailMode[\s\S]{0,700}?[`"']STEPS_PROSE[`"'][^{}]*\?[`"']work[`"']:[`"']codex[`"']/)?.[1];
|
|
14
|
+
if (mode) roles.mode=exported(mode);
|
|
15
|
+
return roles;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function quickChatScope(codex, element) {
|
|
19
|
+
for (let fiber=codex.currentFiber(element),depth=0;fiber && depth++<200;fiber=fiber.return) {
|
|
20
|
+
const values=[];
|
|
21
|
+
for (let hook=fiber.memoizedState,count=0;hook && count++<150;hook=hook.next) values.push(hook.memoizedState?.current);
|
|
22
|
+
for (const row of fiber.updateQueue?.memoCache?.data ?? []) values.push(...row);
|
|
23
|
+
const scope=values.find(value=>typeof value?.get==='function' && typeof value.set==='function' && value.scope?.__scopeBrand==='AppScope');
|
|
24
|
+
if (scope) return scope;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
@@ -18,10 +18,14 @@ export function createQuickChatAdapter(codex) {
|
|
|
18
18
|
if (!history) return null;
|
|
19
19
|
const state = codex.props(editable, props => typeof props.submitDisabled === "boolean" && props.conversationId);
|
|
20
20
|
const controller = codex.props(editable, props => props.composerController?.eventEmitter)?.composerController;
|
|
21
|
+
const expanded = Boolean(floating.presentation === "expanded" || floating.forceExpanded);
|
|
21
22
|
const minimize = [...surface.querySelectorAll('button')].find(button => {
|
|
22
|
-
const props = codex.props(button, props => props.standalone === true && typeof props.onMinimize === "function" && typeof props.isCompact === "boolean"
|
|
23
|
-
|
|
23
|
+
const props = codex.props(button, props => (props.standalone === true || expanded) && typeof props.onMinimize === "function" && typeof props.isCompact === "boolean"
|
|
24
|
+
&& Object.keys(props).every(key => ["standalone","isCompact","onMinimize"].includes(key)), 5);
|
|
25
|
+
return Boolean(props && button.checkVisibility({visibilityProperty:true}) && !button.hasAttribute('data-mcx-quick-chat-history') && !button.hasAttribute('data-mcx-quick-chat-close'));
|
|
24
26
|
});
|
|
27
|
+
const drag = expanded && [...surface.querySelectorAll('header button[data-quick-chat-drag-handle]')]
|
|
28
|
+
.find(button => button.checkVisibility({visibilityProperty:true}));
|
|
25
29
|
let receipt = null;
|
|
26
30
|
for (let fiber = codex.currentFiber(surface), depth = 0; fiber && depth < 80; depth++, fiber = fiber.return) {
|
|
27
31
|
if (fiber.memoizedProps?.session?.conversationId !== session.conversationId) continue;
|
|
@@ -41,13 +45,12 @@ export function createQuickChatAdapter(codex) {
|
|
|
41
45
|
const project = [...surface.querySelectorAll('[data-composer-rail-item] button')].find(button =>
|
|
42
46
|
codex.props(button, props => props.variant === "composer" && typeof props.onProjectChange === "function"));
|
|
43
47
|
const projectRail = project?.closest('[data-composer-rail-item]') ?? null;
|
|
44
|
-
const expanded = Boolean(floating.presentation === "expanded" || floating.forceExpanded);
|
|
45
48
|
const emitter = controller?.eventEmitter;
|
|
46
49
|
const subscribable = typeof emitter?.addListener === "function" && typeof emitter?.removeListener === "function";
|
|
47
50
|
if (subscribable && !subscriptions.has(emitter)) subscriptions.set(emitter, nextSubscription++);
|
|
48
|
-
const snapshot = {surface, editable, minimize, projectRail, background:!session.hasConversation && floating.forceExpanded ? background : null, sessionId:session.conversationId, hasConversation:session.hasConversation,
|
|
51
|
+
const snapshot = {surface, editable, minimize, closeAnchor:drag || minimize, projectRail, background:!session.hasConversation && floating.forceExpanded ? background : null, sessionId:session.conversationId, hasConversation:session.hasConversation,
|
|
49
52
|
canSubmit:Boolean(state && !state.submitDisabled && !state.submitLoading), receiptId:receipt ? receipts.get(receipt) : 0, receiptPresentation:receipt?.presentation ?? null, subscriptionId:subscribable ? subscriptions.get(emitter) : 0,
|
|
50
|
-
expanded, showHistory:Boolean(minimize && !expanded), historyVisible:quickChatHistoryVisible(floating.renderTranscript?.()),
|
|
53
|
+
expanded, standalone:Boolean(minimize && codex.props(minimize,props=>props.standalone===true,5)), showHistory:Boolean(minimize && !expanded), showClose:Boolean(minimize && expanded), historyVisible:quickChatHistoryVisible(floating.renderTranscript?.()),
|
|
51
54
|
expand:() => floating.onPresentationChange("expanded"),
|
|
52
55
|
// 历史动作通过 forceExpanded 临时展开;不要覆盖最小化后的恢复形态。
|
|
53
56
|
history:() => history.onSelect()};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {quickChatLifecycleActions, quickChatScope} from "./lifecycle.mjs";
|
|
1
2
|
// 通过原生能力检查与已有会话分支定位打开动作,不依赖构建哈希或压缩名称。
|
|
2
3
|
export function quickChatOpenExport(source) {
|
|
3
4
|
const match = source.match(/function ([\w$]+)\(([\w$]+),[\w$]+=\{\}\)\{if\(!\2\.get\([\w$]+,\{name:[`"']chatgpt\.quick-chat[`"']\}\)\.isCapable\)return;let [\w$]+=\2\.get\([\w$]+\),[\w$]+=\2\.get\([\w$]+\);if\(/);
|
|
@@ -83,13 +84,13 @@ export function installQuickChatShortcut(signal, codex, discover = null) {
|
|
|
83
84
|
const picker = codex.props(button, p => typeof p.onModeSelect === 'function' && typeof p.mode === 'string', 80);
|
|
84
85
|
// 原生 ChatGPT 也使用 work 路由,具体产品由 workModeAccess 区分。
|
|
85
86
|
if (picker?.mode !== 'codex' && !(picker?.mode === 'work' && picker.workModeAccess === 'chatgpt')) continue;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
const scope=quickChatScope(codex,button);
|
|
88
|
+
if (scope) return scope;
|
|
89
|
+
}
|
|
90
|
+
// 侧栏可能卸载;从仍挂载的窗口内容取当前 scope,模式由原生状态判定。
|
|
91
|
+
if (bindings?.mode) for (const anchor of document.querySelectorAll('[data-app-navigation-rail], [data-codex-composer-root], [data-chatgpt-composer], main, #root > *, #app > *')) {
|
|
92
|
+
const scope=quickChatScope(codex,anchor);
|
|
93
|
+
if (scope && bindings.mode(scope)==='codex') return scope;
|
|
93
94
|
}
|
|
94
95
|
return null;
|
|
95
96
|
}
|
|
@@ -120,12 +121,29 @@ export function installQuickChatShortcut(signal, codex, discover = null) {
|
|
|
120
121
|
if (!response.ok) throw error;
|
|
121
122
|
native=quickChatNativeCommand(await response.text());
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
const bindings={native};
|
|
125
|
+
if (!native) {
|
|
126
|
+
const roles = quickChatLifecycleExports(source);
|
|
127
|
+
if (!module[roles.openState] && typeof module[roles.initialize] === 'function') module[roles.initialize]();
|
|
128
|
+
const open = module[roles.open], create = module[roles.create], openState = module[roles.openState];
|
|
129
|
+
if (typeof open !== 'function' || typeof create !== 'function' || !openState) throw new Error('quick-chat-open-action-unavailable');
|
|
130
|
+
Object.assign(bindings,{open,create,isOpen:scope=>scope.get(openState)});
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const actions=quickChatLifecycleActions(source);
|
|
134
|
+
if (actions.mode && module[actions.mode]) bindings.mode=scope=>scope.get(module[actions.mode]);
|
|
135
|
+
if (['close','draft','text'].every(key=>typeof module[actions[key]]==='function') && module[actions.session] && module[actions.attachments]) {
|
|
136
|
+
bindings.close=(scope,id)=>{
|
|
137
|
+
if (scope.get(module[actions.session])?.conversationId!==id) return false;
|
|
138
|
+
module[actions.draft](scope,{draftKey:id,content:null});
|
|
139
|
+
module[actions.text](scope,id,'');
|
|
140
|
+
scope.set(module[actions.attachments],id,[]);
|
|
141
|
+
module[actions.close](scope);
|
|
142
|
+
return true;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
} catch { codex.report('quick-chat-close',false,'quick-chat-close-contract-unavailable'); }
|
|
146
|
+
return bindings;
|
|
129
147
|
}
|
|
130
148
|
const ready = (discover ?? load)().then(value => {
|
|
131
149
|
if (disposed || signal.aborted) return;
|
|
@@ -146,6 +164,12 @@ export function installQuickChatShortcut(signal, codex, discover = null) {
|
|
|
146
164
|
}
|
|
147
165
|
return true;
|
|
148
166
|
}
|
|
167
|
+
function close(surface,id) {
|
|
168
|
+
const scope=quickChatScope(codex,surface);
|
|
169
|
+
if (!scope || !bindings?.close || disposed || signal.aborted) return false;
|
|
170
|
+
pendingFocus=null;
|
|
171
|
+
return bindings.close(scope,id);
|
|
172
|
+
}
|
|
149
173
|
function keydown(event) {
|
|
150
174
|
const primaryModifier = windows ? event.ctrlKey && !event.metaKey : event.metaKey && !event.ctrlKey;
|
|
151
175
|
if (event.defaultPrevented || event.repeat || event.isComposing
|
|
@@ -156,5 +180,5 @@ export function installQuickChatShortcut(signal, codex, discover = null) {
|
|
|
156
180
|
document.addEventListener('keydown', keydown, {signal});
|
|
157
181
|
function dispose() { disposed = true; stopWaiting?.(); document.removeEventListener('keydown', keydown); bindings = null; pendingFocus=null; }
|
|
158
182
|
signal.addEventListener('abort', dispose, {once:true});
|
|
159
|
-
return {ready, dispose, sync:focusNew, activate, available:() => Boolean(bindings && !bindings.native && !disposed && !signal.aborted)};
|
|
183
|
+
return {ready, dispose, sync:focusNew, activate, close, canClose:() => Boolean(bindings?.close && !disposed && !signal.aborted), available:() => Boolean(bindings && !bindings.native && !disposed && !signal.aborted)};
|
|
160
184
|
}
|
|
@@ -6,13 +6,17 @@ let scheduled = false, disposed = false;
|
|
|
6
6
|
const text = (en,zh) => (document.documentElement.lang || navigator.language).startsWith('zh') ? zh : en;
|
|
7
7
|
const style = document.createElement('style');
|
|
8
8
|
style.textContent = `
|
|
9
|
-
[data-mcx-quick-chat-history]{margin-inline-end:8px;flex-shrink:0}
|
|
10
|
-
[data-mcx-quick-chat-history]>svg{width:16px;height:16px}
|
|
9
|
+
[data-mcx-quick-chat-history],[data-mcx-quick-chat-close]{margin-inline-end:8px;flex-shrink:0}
|
|
10
|
+
[data-mcx-quick-chat-history]>svg,[data-mcx-quick-chat-close]>svg{width:16px;height:16px}
|
|
11
|
+
[data-mcx-quick-chat-close]{margin-inline-end:0}
|
|
11
12
|
[data-mcx-quick-chat-minimize]{border-radius:calc(infinity * 1px)}
|
|
13
|
+
/* 单行输入沿用控件中线,消除原生编辑区换行布局留下的底部间距。 */
|
|
14
|
+
[data-mcx-quick-chat-compact] [data-composer-input]:has(>[data-composer-input-layout="single-line"]){margin-block:0;align-self:center}
|
|
15
|
+
[data-mcx-quick-chat-compact] [data-composer-input-layout="single-line"]{margin-block:0;align-self:center}
|
|
12
16
|
[data-mcx-quick-chat-project-rail]{display:none!important}
|
|
13
17
|
/* 隐藏唯一的项目栏时,一并收起原生 rail 的圆角留白与负 margin。 */
|
|
14
18
|
[data-composer-rail]:has(>[data-mcx-quick-chat-project-rail]):not(:has(>:not([data-mcx-quick-chat-project-rail]):not(:empty))){display:none!important}
|
|
15
|
-
[data-floating-chat-surface]>:has(>[data-mcx-quick-chat-history]){display:flex;align-items:center}
|
|
19
|
+
[data-floating-chat-surface]>:has(>[data-mcx-quick-chat-history],>[data-mcx-quick-chat-close]){display:flex;align-items:center}
|
|
16
20
|
`;
|
|
17
21
|
document.head.append(style);
|
|
18
22
|
function releaseBackground(instance) {
|
|
@@ -21,7 +25,7 @@ function releaseBackground(instance) {
|
|
|
21
25
|
instance.background = null;
|
|
22
26
|
}
|
|
23
27
|
function releaseProjectRail(instance) { instance.projectRail?.removeAttribute('data-mcx-quick-chat-project-rail'); instance.projectRail = null; }
|
|
24
|
-
function remove(instance) { releaseBackground(instance); releaseProjectRail(instance); instance.minimize?.removeAttribute('data-mcx-quick-chat-minimize'); instance.unsubscribe(); instance.button?.remove(); instance.tracker.clear(); instance.historyTracker.clear(); }
|
|
28
|
+
function remove(instance) { instance.surface?.removeAttribute('data-mcx-quick-chat-compact'); releaseBackground(instance); releaseProjectRail(instance); instance.minimize?.removeAttribute('data-mcx-quick-chat-minimize'); instance.unsubscribe(); instance.button?.remove(); instance.tracker.clear(); instance.historyTracker.clear(); }
|
|
25
29
|
function sync() {
|
|
26
30
|
if (disposed) return;
|
|
27
31
|
shortcut.sync();
|
|
@@ -32,13 +36,14 @@ function sync() {
|
|
|
32
36
|
const selected = instance?.historyTracker.selected(snapshot);
|
|
33
37
|
if (!instance || instance.sessionId !== snapshot.sessionId || instance.subscriptionId !== snapshot.subscriptionId) {
|
|
34
38
|
if (instance) remove(instance);
|
|
35
|
-
instance = {sessionId:snapshot.sessionId, subscriptionId:snapshot.subscriptionId, tracker:createSubmissionTracker(), historyTracker:createHistoryTracker(), button:null};
|
|
39
|
+
instance = {surface:snapshot.surface, sessionId:snapshot.sessionId, subscriptionId:snapshot.subscriptionId, tracker:createSubmissionTracker(), historyTracker:createHistoryTracker(), button:null};
|
|
36
40
|
const begin = () => { const current = native.read(snapshot.surface); if (current) instance.tracker.begin(current); };
|
|
37
41
|
instance.unsubscribe = native.subscribe(snapshot, begin);
|
|
38
42
|
instances.set(snapshot.surface, instance);
|
|
39
43
|
}
|
|
40
44
|
if (selected || instance.tracker.accepted(snapshot)) snapshot.expand();
|
|
41
45
|
instance.historyTracker.open(snapshot);
|
|
46
|
+
snapshot.surface.toggleAttribute('data-mcx-quick-chat-compact', !snapshot.expanded);
|
|
42
47
|
if (instance.projectRail !== snapshot.projectRail) {
|
|
43
48
|
releaseProjectRail(instance);
|
|
44
49
|
instance.projectRail = snapshot.projectRail;
|
|
@@ -54,25 +59,34 @@ function sync() {
|
|
|
54
59
|
const minimize = snapshot.minimize;
|
|
55
60
|
if (instance.minimize !== minimize) {
|
|
56
61
|
instance.minimize?.removeAttribute('data-mcx-quick-chat-minimize'); instance.minimize = minimize;
|
|
57
|
-
minimize?.setAttribute('data-mcx-quick-chat-minimize','');
|
|
62
|
+
if (snapshot.standalone) minimize?.setAttribute('data-mcx-quick-chat-minimize','');
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
const kind=snapshot.showHistory ? 'history' : snapshot.showClose && shortcut.canClose() ? 'close' : null;
|
|
65
|
+
const anchor=kind==='close' ? snapshot.closeAnchor : minimize;
|
|
66
|
+
if (!minimize || !anchor || !kind) { instance.button?.remove(); instance.button = null; continue; }
|
|
67
|
+
if (!instance.button?.isConnected || instance.button.parentElement !== anchor.parentElement || instance.button.nextElementSibling!==anchor || instance.buttonKind!==kind) {
|
|
61
68
|
instance.button?.remove();
|
|
62
69
|
const button = minimize.cloneNode(false);
|
|
63
70
|
for (const attr of ['id','disabled','aria-disabled','aria-describedby','aria-expanded','title']) button.removeAttribute(attr);
|
|
64
71
|
button.removeAttribute('data-mcx-quick-chat-minimize');
|
|
65
72
|
button.type = 'button';
|
|
66
|
-
button.setAttribute(
|
|
67
|
-
button.innerHTML = config.resources
|
|
73
|
+
button.setAttribute(`data-mcx-quick-chat-${kind}`, '');
|
|
74
|
+
button.innerHTML = config.resources[kind];
|
|
68
75
|
const icon = button.querySelector('svg');
|
|
69
76
|
if (icon) { icon.setAttribute('width','16'); icon.setAttribute('height','16'); icon.setAttribute('aria-hidden','true'); }
|
|
70
|
-
button.addEventListener('pointerdown', event => event.preventDefault(), {signal});
|
|
71
|
-
button.addEventListener('click',
|
|
72
|
-
|
|
77
|
+
button.addEventListener('pointerdown', event => {event.preventDefault();event.stopPropagation();}, {signal});
|
|
78
|
+
button.addEventListener('click', event => {
|
|
79
|
+
event.preventDefault(); event.stopPropagation();
|
|
80
|
+
instance.tracker.clear(); instance.historyTracker.clear();
|
|
81
|
+
const current=native.read(snapshot.surface);
|
|
82
|
+
if (!current || current.sessionId!==instance.sessionId) return;
|
|
83
|
+
if (kind==='history') current.history();
|
|
84
|
+
else if (!shortcut.close(snapshot.surface,current.sessionId)) config.codex.report('quick-chat-close',false,'quick-chat-close-action-unavailable');
|
|
85
|
+
}, {signal});
|
|
86
|
+
anchor.before(button); instance.button = button; instance.buttonKind=kind;
|
|
73
87
|
}
|
|
74
88
|
if (instance.button.className !== minimize.className) instance.button.className = minimize.className;
|
|
75
|
-
const label = text('Chat history', '聊天历史');
|
|
89
|
+
const label = kind==='history' ? text('Chat history', '聊天历史') : text('Close Quick Chat', '关闭快捷聊天');
|
|
76
90
|
if (instance.button.getAttribute('aria-label') !== label) {
|
|
77
91
|
instance.button.setAttribute('aria-label',label); instance.button.setAttribute('data-minecodex-tooltip',label);
|
|
78
92
|
}
|
|
@@ -91,14 +105,15 @@ document.addEventListener('submit',begin,{capture:true,signal});
|
|
|
91
105
|
const observer = new MutationObserver(schedule);
|
|
92
106
|
observer.observe(document.documentElement,{subtree:true,childList:true,attributes:true,attributeFilter:['aria-hidden','inert','hidden','lang']});
|
|
93
107
|
sync();
|
|
108
|
+
void shortcut.ready.then(schedule);
|
|
94
109
|
config.registerSelfCheck?.(() => {
|
|
95
|
-
const snapshots = native.surfaces(), anchors = snapshots.filter(item => item.showHistory);
|
|
96
|
-
const
|
|
110
|
+
const snapshots = native.surfaces(), anchors = snapshots.filter(item => item.showHistory || item.showClose);
|
|
111
|
+
const controls = anchors.every(item => instances.get(item.surface)?.button?.isConnected), entry = sidebar.status();
|
|
97
112
|
const applicable = anchors.length > 0 || entry.applicable;
|
|
98
|
-
const available = (!anchors.length ||
|
|
113
|
+
const available = (!anchors.length || controls) && (!entry.applicable || entry.available);
|
|
99
114
|
return {state:applicable ? available ? 'available' : 'unavailable' : 'not-applicable',
|
|
100
115
|
reason:applicable ? available ? null : 'quick-chat-controls-unavailable' : 'no-classic-quick-chat-floating-controls',
|
|
101
|
-
checks:[...(anchors.length ? [{name:'quick-chat-
|
|
116
|
+
checks:[...(anchors.length ? [{name:'quick-chat-controls',available:controls}] : []), ...(entry.applicable ? [{name:'quick-chat-sidebar-entry',available:entry.available}] : [])],
|
|
102
117
|
scopes:snapshots.map(item=>({name:'quick-chat',element:item.surface}))};
|
|
103
118
|
});
|
|
104
119
|
return () => { disposed = true; shortcut.dispose(); sidebar.dispose(); observer.disconnect(); for (const instance of instances.values()) remove(instance); instances.clear(); style.remove(); };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {quickChatLifecycleActions, quickChatScope} from "./adapters/codex/lifecycle.mjs";
|
|
1
2
|
import {quickChatHistoryVisible} from "./adapters/codex/history.mjs";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { createQuickChatAdapter } from "./adapters/codex/renderer.mjs";
|
|
@@ -9,7 +10,7 @@ import { createHistoryTracker } from "./domain/history.mjs";
|
|
|
9
10
|
export async function createPageScriptSource() {
|
|
10
11
|
const source = await readFile(new URL("./frontend/entry.mjs", import.meta.url), "utf8");
|
|
11
12
|
const functions = [quickChatHistoryVisible, loadQuickChatSidebarIcon, createQuickChatSidebarAdapter, installQuickChatSidebar,
|
|
12
|
-
quickChatOpenExport, quickChatLifecycleExports, quickChatNativeCommand, installQuickChatShortcut, createQuickChatAdapter,
|
|
13
|
+
quickChatLifecycleActions, quickChatScope, quickChatOpenExport, quickChatLifecycleExports, quickChatNativeCommand, installQuickChatShortcut, createQuickChatAdapter,
|
|
13
14
|
createSubmissionTracker, createHistoryTracker];
|
|
14
15
|
return functions.map(fn => `const ${fn.name}=${fn.toString()};`).join('\n') + '\n' + source;
|
|
15
16
|
}
|
|
@@ -44,6 +44,13 @@ function findAvatar(rail) {
|
|
|
44
44
|
?? buttons.findLast(button => button.querySelector('img[alt], img[src], [data-avatar]'))
|
|
45
45
|
?? null;
|
|
46
46
|
}
|
|
47
|
+
function findUsage(rail) {
|
|
48
|
+
// 原生图表的轨道 token 是语义锚点,避免依赖构建生成的 class 或界面语言。
|
|
49
|
+
return [...(rail?.querySelectorAll("button") ?? [])].find(button =>
|
|
50
|
+
!button.hasAttribute?.("data-minecodex-usage-meter") &&
|
|
51
|
+
(button.querySelector?.('[stroke="var(--color-usage-meter-track)"]') ||
|
|
52
|
+
/^(Usage|用量|使用量|使用情况|使用情況)$/.test(button.getAttribute("aria-label") ?? ""))) ?? null;
|
|
53
|
+
}
|
|
47
54
|
function avatarAnchor(avatar, rail) {
|
|
48
55
|
// 头像内部是横向行;在最近的原生纵向容器中插入独立一项。
|
|
49
56
|
let anchor = avatar;
|
|
@@ -58,13 +65,16 @@ function avatarAnchor(avatar, rail) {
|
|
|
58
65
|
unsubscribe?.(); client=context.client;
|
|
59
66
|
unsubscribe=client?.getQueryCache().subscribe(event=>{ if (event.query?.queryKey?.[0] === "rate-limit-status") invalidate(); });
|
|
60
67
|
}
|
|
61
|
-
const windows=currentWindows(context), avatar=findAvatar(rail);
|
|
62
|
-
const available=Boolean(avatar && context.router && windows);
|
|
68
|
+
const windows=currentWindows(context), avatar=findAvatar(rail), usage=findUsage(rail);
|
|
69
|
+
const available=Boolean((usage || avatar) && context.router && windows);
|
|
63
70
|
codex.report("usage-meter",available,available ? null : "account-quota-or-anchor-unavailable");
|
|
64
|
-
return { rail,avatar,windows,available,anchorAvailable:Boolean(avatar && context.router),
|
|
71
|
+
return { rail,avatar,usage,windows,available,anchorAvailable:Boolean((usage || avatar) && context.router),
|
|
65
72
|
applicable:context.auth?.isLoading ? null : context.auth ? ["chatgpt","chatgptAuthTokens"].includes(context.auth.authMethod) : null };
|
|
66
73
|
}
|
|
67
74
|
function open(rail) { const {router}=nativeContext(rail); return router?.navigate("/settings/usage?tab=overview",{state:router.state?.location?.state}); }
|
|
68
75
|
signal.addEventListener("abort",()=>unsubscribe?.(),{once:true});
|
|
69
|
-
return { read,open,anchor:avatarAnchor
|
|
76
|
+
return { read,open,anchor:avatarAnchor,
|
|
77
|
+
conceal: usage => usage?.setAttribute("data-minecodex-native-usage", ""),
|
|
78
|
+
reveal: usage => usage?.removeAttribute("data-minecodex-native-usage") };
|
|
79
|
+
|
|
70
80
|
}
|
|
@@ -17,9 +17,10 @@ let hoverTimer = null;
|
|
|
17
17
|
const style = document.createElement("style");
|
|
18
18
|
style.id = STYLE_ID;
|
|
19
19
|
style.textContent = `
|
|
20
|
-
[${MARKER}] { position:relative; display:flex; align-items:center; justify-content:center; flex:none; color:var(--mcx-color-text-tertiary); -webkit-app-region:no-drag; }
|
|
20
|
+
[${MARKER}] { position:relative; display:flex; align-items:center; justify-content:center; flex:none; color:var(--mcx-color-usage-meter-fill,var(--mcx-color-text-tertiary)); -webkit-app-region:no-drag; }
|
|
21
|
+
[data-minecodex-native-usage] { display:none !important; }
|
|
21
22
|
[${MARKER}] .mcx-usage-ring { display:block; width:24px; height:24px; overflow:visible; }
|
|
22
|
-
[${MARKER}] .mcx-usage-number { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:
|
|
23
|
+
[${MARKER}] .mcx-usage-number { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:var(--mcx-color-usage-meter-fill,var(--mcx-color-text-tertiary)); font-family:var(--mcx-font-sans-default); font-size:calc(var(--mcx-text-xs) * 2 / 3); line-height:1; font-weight:var(--font-weight-medium,500); letter-spacing:0; font-variant-numeric:tabular-nums; pointer-events:none; }
|
|
23
24
|
#${TIP_ID} { width:max-content; max-width:calc(100vw - 16px); }
|
|
24
25
|
#${TIP_ID}[hidden] { display:none; }
|
|
25
26
|
#${TIP_ID} dl { display:grid; grid-template-columns:auto auto; gap:2px 16px; margin:0; }
|
|
@@ -31,13 +32,14 @@ style.textContent = `
|
|
|
31
32
|
document.head.append(style);
|
|
32
33
|
|
|
33
34
|
function svgNode(name, attributes) {
|
|
35
|
+
if (name === "path" || name === "circle") attributes = { ...attributes, "vector-effect": "non-scaling-stroke" };
|
|
34
36
|
const node = document.createElementNS("http://www.w3.org/2000/svg", name);
|
|
35
37
|
for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, String(value));
|
|
36
38
|
return node;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
function quotaArc(start, end, clockwise) {
|
|
40
|
-
const radius =
|
|
42
|
+
const radius = 44 / 3;
|
|
41
43
|
const point = angle => {
|
|
42
44
|
const radians = (angle - 90) * Math.PI / 180;
|
|
43
45
|
return [16 + radius * Math.cos(radians), 16 + radius * Math.sin(radians)];
|
|
@@ -52,8 +54,8 @@ function draw(windows) {
|
|
|
52
54
|
if (windows.fiveHour != null) {
|
|
53
55
|
// 左下、右下各留 24° 圆头中心间距;两段都从左下切口扣减。
|
|
54
56
|
for (const [key, start, end, clockwise] of [["fiveHour", 237, 483, true], ["weekly", 213, 147, false]]) {
|
|
55
|
-
const group = svgNode("g", { "data-window": key, fill: "none", stroke: "currentColor", "stroke-width":
|
|
56
|
-
group.append(svgNode("path", { d: quotaArc(start, end, clockwise),
|
|
57
|
+
const group = svgNode("g", { "data-window": key, fill: "none", stroke: "currentColor", "stroke-width": 2, "stroke-linecap": "round" });
|
|
58
|
+
group.append(svgNode("path", { d: quotaArc(start, end, clockwise), stroke: "var(--mcx-color-usage-meter-track,color-mix(in srgb,currentColor 18%,transparent))" }));
|
|
57
59
|
if (windows[key] > 0) {
|
|
58
60
|
const remainingStart = start + (end - start) * (1 - windows[key] / 100);
|
|
59
61
|
group.append(svgNode("path", { d: quotaArc(remainingStart, end, clockwise) }));
|
|
@@ -61,9 +63,9 @@ function draw(windows) {
|
|
|
61
63
|
svg.append(group);
|
|
62
64
|
}
|
|
63
65
|
} else {
|
|
64
|
-
const key = "weekly", radius =
|
|
65
|
-
const group = svgNode("g", { "data-window": key, fill: "none", stroke: "currentColor", "stroke-width":
|
|
66
|
-
group.append(svgNode("circle", { cx: 16, cy: 16, r: radius,
|
|
66
|
+
const key = "weekly", radius = 44 / 3;
|
|
67
|
+
const group = svgNode("g", { "data-window": key, fill: "none", stroke: "currentColor", "stroke-width": 2 });
|
|
68
|
+
group.append(svgNode("circle", { cx: 16, cy: 16, r: radius, stroke: "var(--mcx-color-usage-meter-track,color-mix(in srgb,currentColor 18%,transparent))" }));
|
|
67
69
|
if (windows[key] > 0) group.append(svgNode("circle", {
|
|
68
70
|
cx: 16, cy: 16, r: radius, pathLength: 100,
|
|
69
71
|
"stroke-dasharray": `${windows[key]} 100`, "stroke-linecap": "round", transform: "rotate(-90 16 16)",
|
|
@@ -127,6 +129,7 @@ function hideTooltip() {
|
|
|
127
129
|
}
|
|
128
130
|
function removeControl() {
|
|
129
131
|
hideTooltip();
|
|
132
|
+
nativeUsage.reveal(record?.usage);
|
|
130
133
|
record?.button.remove();
|
|
131
134
|
record?.tooltip.remove();
|
|
132
135
|
record = null;
|
|
@@ -136,21 +139,23 @@ function removeControl() {
|
|
|
136
139
|
|
|
137
140
|
|
|
138
141
|
|
|
139
|
-
function mount(avatar, rail) {
|
|
140
|
-
//
|
|
141
|
-
const button = avatar.cloneNode(false);
|
|
142
|
+
function mount(avatar, rail, usage) {
|
|
143
|
+
// 复用原生 Usage 热区;旧版宿主才使用 Avatar 模板。
|
|
144
|
+
const button = (usage || avatar).cloneNode(false);
|
|
142
145
|
for (const attribute of [...button.attributes]) {
|
|
143
|
-
if (attribute.name.startsWith("aria-") || ["id", "title", "data-state", "data-sidebar-destination", "data-testid"].includes(attribute.name)) button.removeAttribute(attribute.name);
|
|
146
|
+
if (attribute.name.startsWith("aria-") || ["id", "title", "data-state", "data-sidebar-destination", "data-testid", "data-minecodex-native-usage"].includes(attribute.name)) button.removeAttribute(attribute.name);
|
|
144
147
|
}
|
|
145
148
|
button.setAttribute(MARKER, "");
|
|
149
|
+
config.ui.theme.bindScope(button, ["--color-usage-meter-fill", "--color-usage-meter-track"]);
|
|
146
150
|
const tooltip = document.createElement("div");
|
|
147
151
|
tooltip.id = TIP_ID;
|
|
148
152
|
config.ui.tooltipSurface(tooltip);
|
|
149
153
|
tooltip.setAttribute("role", "tooltip");
|
|
150
154
|
tooltip.hidden = true;
|
|
151
|
-
nativeUsage.anchor(avatar, rail).before(button);
|
|
155
|
+
(usage || nativeUsage.anchor(avatar, rail)).before(button);
|
|
156
|
+
nativeUsage.conceal(usage);
|
|
152
157
|
document.body.append(tooltip);
|
|
153
|
-
record = { button, tooltip, rail, avatar, signature: null };
|
|
158
|
+
record = { button, tooltip, rail, avatar, usage, signature: null };
|
|
154
159
|
button.addEventListener("pointerenter", () => { hoverTimer = setTimeout(showTooltip, 350); });
|
|
155
160
|
button.addEventListener("pointerleave", hideTooltip);
|
|
156
161
|
button.addEventListener("focus", showTooltip);
|
|
@@ -165,10 +170,10 @@ function mount(avatar, rail) {
|
|
|
165
170
|
function sync() {
|
|
166
171
|
queued = false;
|
|
167
172
|
if (signal.aborted) return;
|
|
168
|
-
const { rail,avatar,windows,available } = nativeUsage.read();
|
|
173
|
+
const { rail,avatar,usage,windows,available } = nativeUsage.read();
|
|
169
174
|
if (!available) { removeControl(); return; }
|
|
170
|
-
if (record?.avatar !== avatar || !record?.button.isConnected) { removeControl(); mount(avatar, rail); }
|
|
171
|
-
else if (record.button.nextElementSibling !== nativeUsage.anchor(avatar, rail)) nativeUsage.anchor(avatar, rail).before(record.button);
|
|
175
|
+
if (record?.avatar !== avatar || record?.usage !== usage || !record?.button.isConnected) { removeControl(); mount(avatar, rail, usage); }
|
|
176
|
+
else if (record.button.nextElementSibling !== (usage || nativeUsage.anchor(avatar, rail))) (usage || nativeUsage.anchor(avatar, rail)).before(record.button);
|
|
172
177
|
const signature = JSON.stringify(windows);
|
|
173
178
|
if (record.signature !== signature) { record.signature = signature; draw(windows); }
|
|
174
179
|
}
|
|
@@ -196,8 +201,8 @@ config.registerSelfCheck?.(() => {
|
|
|
196
201
|
const snapshot=nativeUsage.read();
|
|
197
202
|
const available=Boolean(snapshot.available && record?.button.isConnected);
|
|
198
203
|
return {state:available ? "available" : snapshot.applicable === false ? "not-applicable" : !snapshot.anchorAvailable && snapshot.rail ? "unavailable" : "unknown",reason:available ? null : "quota-data-or-anchor-unavailable",
|
|
199
|
-
checks:[{name:"
|
|
200
|
-
scopes:[{name:"account",element:snapshot.avatar}]};
|
|
204
|
+
checks:[{name:"usage-anchor",available:Boolean(snapshot.usage || snapshot.avatar)},{name:"account-quota",available:Boolean(snapshot.windows)},{name:"usage-meter-mount",available:Boolean(record?.button.isConnected)}],
|
|
205
|
+
scopes:[{name:"account",element:snapshot.usage || snapshot.avatar}]};
|
|
201
206
|
});
|
|
202
207
|
|
|
203
208
|
return cleanup;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minecodex",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.46",
|
|
4
4
|
"description": "Lightweight, local-first plugins for the Codex desktop app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,8 +30,6 @@
|
|
|
30
30
|
"features/images/src",
|
|
31
31
|
"features/images/web",
|
|
32
32
|
"features/images/codex-feature.json",
|
|
33
|
-
"features/hide-upsell-banner/src",
|
|
34
|
-
"features/hide-upsell-banner/codex-feature.json",
|
|
35
33
|
"features/session-status/src",
|
|
36
34
|
"features/session-status/codex-feature.json",
|
|
37
35
|
"features/session-status/vendor/lucide-LICENSE",
|
|
@@ -73,6 +71,10 @@
|
|
|
73
71
|
"features/markdown-reading/codex-feature.json",
|
|
74
72
|
"features/markdown-reading/vendor/lucide-LICENSE",
|
|
75
73
|
"features/markdown-reading/assets",
|
|
74
|
+
"features/files-auto-refresh/src",
|
|
75
|
+
"features/files-auto-refresh/codex-feature.json",
|
|
76
|
+
"features/files-auto-refresh/assets",
|
|
77
|
+
"features/files-auto-refresh/vendor/lucide-LICENSE",
|
|
76
78
|
"features/file-navigation/src",
|
|
77
79
|
"features/file-navigation/codex-feature.json",
|
|
78
80
|
"features/file-navigation/assets",
|
|
@@ -94,7 +96,7 @@
|
|
|
94
96
|
},
|
|
95
97
|
"scripts": {
|
|
96
98
|
"start": "node packages/cli/bin/mcx.mjs serve",
|
|
97
|
-
"test": "node --test packages/cli/test/*.test.mjs packages/runtime-host/test/*.test.mjs features/model-slider/test/*.test.mjs features/notes/test/*.test.mjs features/images/test/*.test.mjs features/
|
|
99
|
+
"test": "node --test packages/cli/test/*.test.mjs packages/runtime-host/test/*.test.mjs features/model-slider/test/*.test.mjs features/notes/test/*.test.mjs features/images/test/*.test.mjs features/session-status/test/*.test.mjs features/usage-meter/test/*.test.mjs apps/dashboard/test/*.test.mjs features/unread-inbox/test/*.test.mjs features/quick-chat/test/*.test.mjs features/composer-expand/test/*.test.mjs features/sidebar-focus/test/*.test.mjs features/sites/test/*.test.mjs packages/ui/test/*.test.mjs packages/codex-adapter/test/*.test.mjs features/markdown-reading/test/*.test.mjs features/file-navigation/test/*.test.mjs features/files-auto-refresh/test/*.test.mjs packages/plugin/test/*.test.mjs features/mermaid-diagrams/test/*.test.mjs",
|
|
98
100
|
"test:cli": "node --test packages/cli/test/*.test.mjs",
|
|
99
101
|
"test:runtime": "node --test packages/runtime-host/test/*.test.mjs",
|
|
100
102
|
"check": "node scripts/check-syntax.mjs",
|
|
@@ -2,14 +2,13 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
export const FEATURE_IDS = Object.freeze(["model-slider", "notes", "images", "
|
|
5
|
+
export const FEATURE_IDS = Object.freeze(["model-slider", "notes", "images", "session-status", "usage-meter", "sidebar-focus", "sites", "markdown-reading", "mermaid-diagrams", "file-navigation", "files-auto-refresh", "quick-chat", "composer-expand"]);
|
|
6
6
|
export const DEFAULT_CONFIG = Object.freeze({
|
|
7
7
|
schemaVersion: 1,
|
|
8
8
|
features: Object.freeze({
|
|
9
9
|
"model-slider": true,
|
|
10
10
|
notes: true,
|
|
11
11
|
images: true,
|
|
12
|
-
"hide-upsell-banner": true,
|
|
13
12
|
"session-status": true,
|
|
14
13
|
"usage-meter": true,
|
|
15
14
|
"sidebar-focus": true,
|
|
@@ -17,6 +16,7 @@ export const DEFAULT_CONFIG = Object.freeze({
|
|
|
17
16
|
"markdown-reading": true,
|
|
18
17
|
"mermaid-diagrams": false,
|
|
19
18
|
"file-navigation": true,
|
|
19
|
+
"files-auto-refresh": true,
|
|
20
20
|
"quick-chat": true,
|
|
21
21
|
"composer-expand": true,
|
|
22
22
|
}),
|
|
@@ -44,6 +44,8 @@ export function normalizeConfig(value = {}) {
|
|
|
44
44
|
};
|
|
45
45
|
// 暂停的 Task 通知不继承旧配置,也不再提供开启入口。
|
|
46
46
|
delete features["unread-inbox"];
|
|
47
|
+
// 已移除的额度推广隐藏功能不继承旧启用项。
|
|
48
|
+
delete features["hide-upsell-banner"];
|
|
47
49
|
// 清理已退役的独立 Quick Chat 修复开关。
|
|
48
50
|
delete features["quick-chat-repair"];
|
|
49
51
|
return {
|
|
@@ -14,7 +14,7 @@ import { createDataTransfer, MAX_TRANSFER_BYTES } from "./data-transfer/service.
|
|
|
14
14
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
15
15
|
const packageMetadata = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
|
|
16
16
|
const MAX_BODY_BYTES = 64 * 1024;
|
|
17
|
-
const DASHBOARD_FEATURE_ORDER = ["model-slider", "notes", "usage-meter", "session-status", "markdown-reading", "mermaid-diagrams", "file-navigation", "images", "sites", "quick-chat", "composer-expand", "sidebar-focus"
|
|
17
|
+
const DASHBOARD_FEATURE_ORDER = ["model-slider", "notes", "usage-meter", "session-status", "markdown-reading", "mermaid-diagrams", "file-navigation", "files-auto-refresh", "images", "sites", "quick-chat", "composer-expand", "sidebar-focus"];
|
|
18
18
|
const CONTENT_TYPES = new Map([
|
|
19
19
|
[".css", "text/css; charset=utf-8"],
|
|
20
20
|
[".html", "text/html; charset=utf-8"],
|
|
@@ -102,6 +102,8 @@ export const CODEX_THEME_TOKENS = Object.freeze([
|
|
|
102
102
|
"--color-text-primary",
|
|
103
103
|
"--color-text-secondary",
|
|
104
104
|
"--color-text-tertiary",
|
|
105
|
+
"--color-usage-meter-fill",
|
|
106
|
+
"--color-usage-meter-track",
|
|
105
107
|
"--color-border-overlay",
|
|
106
108
|
"--color-ring",
|
|
107
109
|
"--color-dialog-overlay",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
|
-
"id": "hide-upsell-banner",
|
|
4
|
-
"version": "0.1.11",
|
|
5
|
-
"label": {
|
|
6
|
-
"en": "Hide Usage Promotions",
|
|
7
|
-
"zh-CN": "隐藏额度推广"
|
|
8
|
-
},
|
|
9
|
-
"description": {
|
|
10
|
-
"en": "Hide plan upgrade banners above the message box for a cleaner chat.",
|
|
11
|
-
"zh-CN": "隐藏输入框上方的套餐升级推广,让对话更清爽。"
|
|
12
|
-
},
|
|
13
|
-
"entry": {
|
|
14
|
-
"kind": "page-script",
|
|
15
|
-
"script": "src/hide-upsell-banner.mjs"
|
|
16
|
-
},
|
|
17
|
-
"icon": {
|
|
18
|
-
"viewBox": "0 0 24 24",
|
|
19
|
-
"markup": "<rect width=\"18\" height=\"12\" x=\"3\" y=\"6\" rx=\"2.5\"></rect><path d=\"m5 5 14 14\"></path>"
|
|
20
|
-
},
|
|
21
|
-
"placement": {
|
|
22
|
-
"order": 40
|
|
23
|
-
}
|
|
24
|
-
}
|