codex-overleaf-link 2.0.0 → 2.1.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/README.md +24 -23
- package/extension/bootstrap/manifest.template.json +2 -1
- package/extension/runtime-manifest.json +12 -6
- package/extension/src/background.js +7 -0
- package/extension/src/content/composerPanel.js +13 -3
- package/extension/src/content/contentRuntime.js +129 -64
- package/extension/src/content/contextTray.js +173 -9
- package/extension/src/content/markdownText.js +16 -1
- package/extension/src/content/mathText.js +233 -0
- package/extension/src/content/modelPicker.js +117 -106
- package/extension/src/content/modelPickerSupport.js +115 -0
- package/extension/src/content/nativeChannel.js +20 -1
- package/extension/src/content/panelMaintenance.js +2 -0
- package/extension/src/content/projectSettingsCoordinator.js +2 -0
- package/extension/src/content/providerSettingsCoordinator.js +413 -0
- package/extension/src/content/providerSettingsDialog.js +1006 -0
- package/extension/src/content/runTimelineView.js +24 -5
- package/extension/src/content/settingsPanel.js +33 -2
- package/extension/src/content/writebackOrchestrator.js +3 -1
- package/extension/src/page/overleafProjectSnapshot.js +29 -33
- package/extension/src/page/snapshotRouter.js +75 -8
- package/extension/src/page/writebackRouter.js +20 -33
- package/extension/src/pageBridge.js +1 -1
- package/extension/src/shared/agentTranscript.js +130 -0
- package/extension/src/shared/compatibility.js +3 -2
- package/extension/src/shared/failureReasons.js +75 -0
- package/extension/src/shared/i18n.js +22 -0
- package/extension/src/shared/models.js +1 -0
- package/extension/src/shared/providerProfiles.js +283 -0
- package/extension/src/shared/sessionState.js +12 -2
- package/extension/src/shared/storageDb.js +1 -1
- package/extension/styles/panel.css +689 -1
- package/extension/vendor/katex/LICENSE +21 -0
- package/extension/vendor/katex/VERSION +2 -0
- package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/extension/vendor/katex/katex.min.css +1 -0
- package/extension/vendor/katex/katex.min.js +1 -0
- package/native-host/src/anthropicBridgeRequest.js +435 -0
- package/native-host/src/anthropicBridgeResponse.js +431 -0
- package/native-host/src/anthropicMessagesBridge.js +281 -0
- package/native-host/src/chatBridgeRequest.js +274 -0
- package/native-host/src/chatBridgeResponse.js +594 -0
- package/native-host/src/chatCompletionsBridge.js +485 -0
- package/native-host/src/codexCommand.js +25 -0
- package/native-host/src/codexPromptAssembly.js +9 -0
- package/native-host/src/codexProviderLaunch.js +213 -0
- package/native-host/src/codexProviderTest.js +196 -0
- package/native-host/src/codexSessionRunner.js +29 -25
- package/native-host/src/managedInstall.js +1 -0
- package/native-host/src/providerBridgeRoutes.js +11 -0
- package/native-host/src/providerModelCatalog.js +107 -0
- package/native-host/src/providerProfile.js +454 -0
- package/native-host/src/providerReasoning.js +235 -0
- package/native-host/src/providerRedaction.js +18 -0
- package/native-host/src/providerRuntime.js +365 -0
- package/native-host/src/providerStore.js +691 -0
- package/native-host/src/readProgressGuard.js +360 -0
- package/native-host/src/taskRunnerRuntime.js +39 -3
- package/native-host/src/updateArchive.js +1 -0
- package/package.json +2 -1
|
@@ -62,6 +62,21 @@
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
function buildMarkdownInlineNodes(value) {
|
|
65
|
+
const source = String(value || '');
|
|
66
|
+
const mathText = typeof window !== 'undefined'
|
|
67
|
+
? window.CodexOverleafMathText
|
|
68
|
+
: null;
|
|
69
|
+
if (!mathText?.buildMathNodes) {
|
|
70
|
+
return buildMarkdownInlineNodesWithoutMath(source);
|
|
71
|
+
}
|
|
72
|
+
return mathText.buildMathNodes(source, {
|
|
73
|
+
document,
|
|
74
|
+
katex: window.katex,
|
|
75
|
+
renderText: buildMarkdownInlineNodesWithoutMath
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function buildMarkdownInlineNodesWithoutMath(value) {
|
|
65
80
|
const source = String(value || '');
|
|
66
81
|
const nodes = [];
|
|
67
82
|
let index = 0;
|
|
@@ -587,7 +602,7 @@
|
|
|
587
602
|
}
|
|
588
603
|
|
|
589
604
|
function isMarkdownHeadingLine(line) {
|
|
590
|
-
return /^\s*(#{1,6}\s+\S
|
|
605
|
+
return /^\s*(#{1,6}\s+\S.*|\*\*[^*]+\*\*:?\s*)$/.test(line);
|
|
591
606
|
}
|
|
592
607
|
|
|
593
608
|
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
(function initCodexOverleafMathText(root) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const DELIMITERS = Object.freeze([
|
|
5
|
+
Object.freeze({ open: '$$', close: '$$', display: true, singleDollar: false }),
|
|
6
|
+
Object.freeze({ open: '\\[', close: '\\]', display: true, singleDollar: false }),
|
|
7
|
+
Object.freeze({ open: '\\(', close: '\\)', display: false, singleDollar: false }),
|
|
8
|
+
Object.freeze({ open: '$', close: '$', display: false, singleDollar: true })
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function parseMathSegments(value) {
|
|
12
|
+
const source = String(value || '');
|
|
13
|
+
if (!source) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const codeRanges = collectInlineCodeRanges(source);
|
|
17
|
+
const segments = [];
|
|
18
|
+
let cursor = 0;
|
|
19
|
+
while (cursor < source.length) {
|
|
20
|
+
const match = findNextMath(source, cursor, codeRanges);
|
|
21
|
+
if (!match) {
|
|
22
|
+
segments.push({ type: 'text', value: source.slice(cursor) });
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
if (match.start > cursor) {
|
|
26
|
+
segments.push({ type: 'text', value: source.slice(cursor, match.start) });
|
|
27
|
+
}
|
|
28
|
+
segments.push({
|
|
29
|
+
type: 'math',
|
|
30
|
+
value: match.value,
|
|
31
|
+
raw: source.slice(match.start, match.end),
|
|
32
|
+
display: match.display
|
|
33
|
+
});
|
|
34
|
+
cursor = match.end;
|
|
35
|
+
}
|
|
36
|
+
return segments;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function findNextMath(source, fromIndex, codeRanges) {
|
|
40
|
+
let best = null;
|
|
41
|
+
for (const delimiter of DELIMITERS) {
|
|
42
|
+
const match = findNextDelimitedMath(source, fromIndex, delimiter, codeRanges);
|
|
43
|
+
if (!match) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!best || match.start < best.start
|
|
47
|
+
|| (match.start === best.start && delimiter.open.length > best.delimiter.open.length)) {
|
|
48
|
+
best = { ...match, delimiter };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return best;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function findNextDelimitedMath(source, fromIndex, delimiter, codeRanges) {
|
|
55
|
+
let searchFrom = fromIndex;
|
|
56
|
+
while (searchFrom < source.length) {
|
|
57
|
+
const start = source.indexOf(delimiter.open, searchFrom);
|
|
58
|
+
if (start < 0) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (!isValidOpening(source, start, delimiter, codeRanges)) {
|
|
62
|
+
searchFrom = start + delimiter.open.length;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const closeStart = findClosingDelimiter(
|
|
66
|
+
source,
|
|
67
|
+
start + delimiter.open.length,
|
|
68
|
+
delimiter,
|
|
69
|
+
codeRanges
|
|
70
|
+
);
|
|
71
|
+
if (closeStart < 0) {
|
|
72
|
+
searchFrom = start + delimiter.open.length;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const value = source.slice(start + delimiter.open.length, closeStart);
|
|
76
|
+
const end = closeStart + delimiter.close.length;
|
|
77
|
+
if (!isValidMathValue(value, delimiter) || overlapsCodeRange(start, end, codeRanges)) {
|
|
78
|
+
searchFrom = start + delimiter.open.length;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
return { start, end, value, display: delimiter.display };
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isValidOpening(source, index, delimiter, codeRanges) {
|
|
87
|
+
if (isEscaped(source, index) || isInsideRange(index, codeRanges)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (!delimiter.singleDollar) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
const previous = source[index - 1] || '';
|
|
94
|
+
const next = source[index + 1] || '';
|
|
95
|
+
return previous !== '$' && next !== '$' && next !== '' && !/\s/.test(next);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function findClosingDelimiter(source, fromIndex, delimiter, codeRanges) {
|
|
99
|
+
let searchFrom = fromIndex;
|
|
100
|
+
while (searchFrom < source.length) {
|
|
101
|
+
const index = source.indexOf(delimiter.close, searchFrom);
|
|
102
|
+
if (index < 0) {
|
|
103
|
+
return -1;
|
|
104
|
+
}
|
|
105
|
+
const previous = source[index - 1] || '';
|
|
106
|
+
const next = source[index + delimiter.close.length] || '';
|
|
107
|
+
const invalidSingleDollar = delimiter.singleDollar
|
|
108
|
+
&& (previous === '$' || next === '$' || /\s/.test(previous));
|
|
109
|
+
if (!isEscaped(source, index) && !isInsideRange(index, codeRanges) && !invalidSingleDollar) {
|
|
110
|
+
return index;
|
|
111
|
+
}
|
|
112
|
+
searchFrom = index + delimiter.close.length;
|
|
113
|
+
}
|
|
114
|
+
return -1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isValidMathValue(value, delimiter) {
|
|
118
|
+
const source = String(value || '');
|
|
119
|
+
if (!source.trim()) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return delimiter.display || !/[\r\n]/.test(source);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function collectInlineCodeRanges(source) {
|
|
126
|
+
const ranges = [];
|
|
127
|
+
let index = 0;
|
|
128
|
+
while (index < source.length) {
|
|
129
|
+
if (source[index] !== '`' || isEscaped(source, index)) {
|
|
130
|
+
index++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
let runLength = 1;
|
|
134
|
+
while (source[index + runLength] === '`') {
|
|
135
|
+
runLength++;
|
|
136
|
+
}
|
|
137
|
+
const marker = '`'.repeat(runLength);
|
|
138
|
+
const closeStart = source.indexOf(marker, index + runLength);
|
|
139
|
+
if (closeStart < 0) {
|
|
140
|
+
index += runLength;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
ranges.push([index, closeStart + runLength]);
|
|
144
|
+
index = closeStart + runLength;
|
|
145
|
+
}
|
|
146
|
+
return ranges;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isEscaped(source, index) {
|
|
150
|
+
let slashCount = 0;
|
|
151
|
+
for (let cursor = index - 1; cursor >= 0 && source[cursor] === '\\'; cursor--) {
|
|
152
|
+
slashCount++;
|
|
153
|
+
}
|
|
154
|
+
return slashCount % 2 === 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isInsideRange(index, ranges) {
|
|
158
|
+
return ranges.some(([start, end]) => index >= start && index < end);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function overlapsCodeRange(start, end, ranges) {
|
|
162
|
+
return ranges.some(([rangeStart, rangeEnd]) => rangeStart < end && rangeEnd > start);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildMathNodes(value, options = {}) {
|
|
166
|
+
const documentRef = options.document || root.document;
|
|
167
|
+
const renderText = typeof options.renderText === 'function'
|
|
168
|
+
? options.renderText
|
|
169
|
+
: text => [documentRef.createTextNode(text)];
|
|
170
|
+
const nodes = [];
|
|
171
|
+
for (const segment of parseMathSegments(value)) {
|
|
172
|
+
if (segment.type === 'text') {
|
|
173
|
+
if (segment.value) {
|
|
174
|
+
nodes.push(...toNodeArray(renderText(segment.value)));
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
nodes.push(createMathNode(segment, {
|
|
179
|
+
document: documentRef,
|
|
180
|
+
katex: options.katex || root.katex
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
return nodes;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function createMathNode(segment, options = {}) {
|
|
187
|
+
const documentRef = options.document || root.document;
|
|
188
|
+
const node = documentRef.createElement('span');
|
|
189
|
+
node.className = `run-math ${segment.display ? 'run-math--display' : 'run-math--inline'}`;
|
|
190
|
+
node.dataset.mathDisplay = segment.display ? 'block' : 'inline';
|
|
191
|
+
try {
|
|
192
|
+
if (typeof options.katex?.render !== 'function') {
|
|
193
|
+
throw new Error('KaTeX is unavailable');
|
|
194
|
+
}
|
|
195
|
+
options.katex.render(segment.value, node, {
|
|
196
|
+
displayMode: segment.display,
|
|
197
|
+
throwOnError: true,
|
|
198
|
+
trust: false,
|
|
199
|
+
strict: 'warn',
|
|
200
|
+
maxExpand: 200,
|
|
201
|
+
maxSize: 20,
|
|
202
|
+
output: 'htmlAndMathml'
|
|
203
|
+
});
|
|
204
|
+
node.dataset.mathRendered = 'true';
|
|
205
|
+
return node;
|
|
206
|
+
} catch (_error) {
|
|
207
|
+
node.classList.add('run-math--fallback');
|
|
208
|
+
node.dataset.mathRendered = 'false';
|
|
209
|
+
node.textContent = segment.raw;
|
|
210
|
+
return node;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function toNodeArray(value) {
|
|
215
|
+
if (Array.isArray(value)) {
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
if (value === undefined || value === null) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
return [value];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const api = {
|
|
225
|
+
buildMathNodes,
|
|
226
|
+
createMathNode,
|
|
227
|
+
parseMathSegments
|
|
228
|
+
};
|
|
229
|
+
root.CodexOverleafMathText = api;
|
|
230
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
231
|
+
module.exports = api;
|
|
232
|
+
}
|
|
233
|
+
})(typeof window !== 'undefined' ? window : globalThis);
|
|
@@ -23,6 +23,14 @@
|
|
|
23
23
|
getPanel,
|
|
24
24
|
getState
|
|
25
25
|
} = deps;
|
|
26
|
+
const Support = window.CodexOverleafModelPickerSupport;
|
|
27
|
+
const {
|
|
28
|
+
formatCompactModelLabel,
|
|
29
|
+
normalizeModelOptionId,
|
|
30
|
+
normalizeReasoningEffortsForSelect,
|
|
31
|
+
normalizeSpeedTiersForSelect,
|
|
32
|
+
retainSelectedModel
|
|
33
|
+
} = Support;
|
|
26
34
|
|
|
27
35
|
let modelDiscovery = { status: 'fallback', source: 'fallback', fetchedAt: '' };
|
|
28
36
|
|
|
@@ -78,6 +86,7 @@
|
|
|
78
86
|
reasoningSelect.value = choice.dataset.reasoningChoice;
|
|
79
87
|
} else if (choice.dataset.modelChoice && modelSelect) {
|
|
80
88
|
modelSelect.value = choice.dataset.modelChoice;
|
|
89
|
+
renderReasoningOptions(getRenderedModelEntries());
|
|
81
90
|
renderSpeedOptions(getRenderedModelEntries());
|
|
82
91
|
} else if (choice.dataset.speedChoice && speedSelect) {
|
|
83
92
|
speedSelect.value = choice.dataset.speedChoice;
|
|
@@ -101,9 +110,18 @@
|
|
|
101
110
|
const hasDiscoveredModels = response?.ok
|
|
102
111
|
&& Array.isArray(response.result?.models)
|
|
103
112
|
&& response.result.models.length > 0;
|
|
113
|
+
if (!hasDiscoveredModels && !Support.shouldUseBuiltInFallback(response)) {
|
|
114
|
+
applyUnavailableModelOptions(response?.error);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
104
117
|
const sourceModels = hasDiscoveredModels ? response.result.models : fallbackModels;
|
|
105
|
-
const
|
|
106
|
-
|
|
118
|
+
const retainedSelectedModel = retainSelectedModel(sourceModels, currentSelectedModel);
|
|
119
|
+
const normalized = modelCatalog.normalizeDiscoveredModels({ models: sourceModels, selectedModel: retainedSelectedModel });
|
|
120
|
+
if (normalized.usedFallback && response.result?.providerId && response.result.providerId !== 'builtin') {
|
|
121
|
+
applyUnavailableModelOptions({ code: 'provider_model_catalog_invalid', message: 'The active provider returned no usable models.' });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
renderModelOptions(normalized.models, retainedSelectedModel);
|
|
107
125
|
modelDiscovery = {
|
|
108
126
|
status: hasDiscoveredModels && !normalized.usedFallback ? 'discovered' : 'fallback',
|
|
109
127
|
source: hasDiscoveredModels ? response.result?.source || 'unknown' : 'fallback',
|
|
@@ -113,16 +131,43 @@
|
|
|
113
131
|
};
|
|
114
132
|
updateModelDisplay();
|
|
115
133
|
} catch (error) {
|
|
116
|
-
|
|
134
|
+
applyUnavailableModelOptions(error);
|
|
117
135
|
}
|
|
118
136
|
}
|
|
119
137
|
|
|
138
|
+
function applyUnavailableModelOptions(error) {
|
|
139
|
+
const modelSelect = getPanel()?.querySelector('[data-model]');
|
|
140
|
+
if (modelSelect) {
|
|
141
|
+
modelSelect.textContent = '';
|
|
142
|
+
const option = document.createElement('option');
|
|
143
|
+
option.value = '';
|
|
144
|
+
option.textContent = tx('Models unavailable', '模型不可用');
|
|
145
|
+
option.disabled = true;
|
|
146
|
+
modelSelect.append(option);
|
|
147
|
+
modelSelect.value = '';
|
|
148
|
+
modelSelect.disabled = true;
|
|
149
|
+
}
|
|
150
|
+
if (getState()) getState().model = '';
|
|
151
|
+
modelDiscovery = {
|
|
152
|
+
status: 'unavailable',
|
|
153
|
+
source: 'custom-provider',
|
|
154
|
+
fetchedAt: '',
|
|
155
|
+
errorCode: error?.code || '',
|
|
156
|
+
errorMessage: error?.message || (error ? String(error) : '')
|
|
157
|
+
};
|
|
158
|
+
renderReasoningOptions([]);
|
|
159
|
+
renderSpeedOptions([]);
|
|
160
|
+
renderModelConfigChoices();
|
|
161
|
+
updateModelDisplay();
|
|
162
|
+
}
|
|
163
|
+
|
|
120
164
|
function applyFallbackModelOptions(selectedModel, error) {
|
|
121
165
|
const modelCatalog = getModelCatalog();
|
|
122
166
|
const fallbackModels = modelCatalog.FALLBACK_MODELS;
|
|
123
167
|
const sourceModels = fallbackModels;
|
|
124
|
-
const
|
|
125
|
-
|
|
168
|
+
const retainedSelectedModel = retainSelectedModel(sourceModels, selectedModel);
|
|
169
|
+
const normalized = modelCatalog.normalizeDiscoveredModels({ models: sourceModels, selectedModel: retainedSelectedModel });
|
|
170
|
+
renderModelOptions(normalized.models, retainedSelectedModel);
|
|
126
171
|
modelDiscovery = {
|
|
127
172
|
status: 'fallback',
|
|
128
173
|
source: 'fallback',
|
|
@@ -134,86 +179,7 @@
|
|
|
134
179
|
}
|
|
135
180
|
|
|
136
181
|
function getModelCatalog() {
|
|
137
|
-
|
|
138
|
-
if (Array.isArray(shared?.FALLBACK_MODELS) && typeof shared?.normalizeDiscoveredModels === 'function') {
|
|
139
|
-
return shared;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
FALLBACK_MODELS: buildDomModelCatalogFallback(),
|
|
144
|
-
normalizeDiscoveredModels: normalizeDiscoveredModelsFallback
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function buildDomModelCatalogFallback() {
|
|
149
|
-
const modelSelect = getPanel()?.querySelector('[data-model]');
|
|
150
|
-
const domModels = Array.from(modelSelect?.options || [])
|
|
151
|
-
.map(option => ({
|
|
152
|
-
id: normalizeModelOptionId(option.value),
|
|
153
|
-
label: option.textContent || option.value
|
|
154
|
-
}))
|
|
155
|
-
.filter(model => model.id);
|
|
156
|
-
|
|
157
|
-
return domModels.length ? domModels : [
|
|
158
|
-
{ id: 'gpt-5.5', label: 'GPT-5.5' },
|
|
159
|
-
{ id: 'gpt-5.4', label: 'GPT-5.4' },
|
|
160
|
-
{ id: 'gpt-5.4-mini', label: 'GPT-5.4 Mini' },
|
|
161
|
-
{ id: 'gpt-5.3-codex', label: 'GPT-5.3 Codex' },
|
|
162
|
-
{ id: 'gpt-5.3-codex-spark', label: 'GPT-5.3 Codex Spark' },
|
|
163
|
-
{ id: 'gpt-5.2', label: 'GPT-5.2' }
|
|
164
|
-
];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function normalizeDiscoveredModelsFallback({ models, selectedModel } = {}) {
|
|
168
|
-
const normalized = normalizeModelCatalogEntries(models);
|
|
169
|
-
const usedFallback = normalized.length === 0;
|
|
170
|
-
const resultModels = usedFallback ? buildDomModelCatalogFallback().map(model => ({ ...model })) : normalized;
|
|
171
|
-
const selectedId = normalizeModelOptionId(selectedModel);
|
|
172
|
-
|
|
173
|
-
if (selectedId && !resultModels.some(model => model.id === selectedId)) {
|
|
174
|
-
resultModels.push({
|
|
175
|
-
id: selectedId,
|
|
176
|
-
label: `${selectedId} (custom)`,
|
|
177
|
-
unverified: true
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
models: resultModels,
|
|
183
|
-
usedFallback
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function normalizeModelCatalogEntries(models) {
|
|
188
|
-
if (!Array.isArray(models)) {
|
|
189
|
-
return [];
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const seen = new Set();
|
|
193
|
-
const result = [];
|
|
194
|
-
|
|
195
|
-
for (const model of models) {
|
|
196
|
-
const id = normalizeModelOptionId(typeof model === 'string' ? model : model?.id);
|
|
197
|
-
if (!id || seen.has(id)) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
seen.add(id);
|
|
201
|
-
const normalized = {
|
|
202
|
-
id,
|
|
203
|
-
label: typeof model?.label === 'string' && model.label.length > 0 ? model.label : id,
|
|
204
|
-
reasoningEfforts: Array.isArray(model?.reasoningEfforts) ? model.reasoningEfforts.slice() : [],
|
|
205
|
-
speedTiers: normalizeSpeedTiersForSelect(model?.speedTiers)
|
|
206
|
-
};
|
|
207
|
-
if (Object.prototype.hasOwnProperty.call(Object(model), 'defaultReasoningEffort')) {
|
|
208
|
-
normalized.defaultReasoningEffort = model.defaultReasoningEffort;
|
|
209
|
-
}
|
|
210
|
-
if (Object.prototype.hasOwnProperty.call(Object(model), 'defaultSpeedTier')) {
|
|
211
|
-
normalized.defaultSpeedTier = model.defaultSpeedTier;
|
|
212
|
-
}
|
|
213
|
-
result.push(normalized);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return result;
|
|
182
|
+
return Support.getModelCatalog({ getPanel });
|
|
217
183
|
}
|
|
218
184
|
|
|
219
185
|
function renderModelOptions(models, selectedModel) {
|
|
@@ -224,6 +190,7 @@
|
|
|
224
190
|
|
|
225
191
|
const selectedId = normalizeModelOptionId(selectedModel);
|
|
226
192
|
modelSelect.textContent = '';
|
|
193
|
+
modelSelect.disabled = false;
|
|
227
194
|
let renderedSelected = false;
|
|
228
195
|
let firstModelId = '';
|
|
229
196
|
|
|
@@ -238,6 +205,9 @@
|
|
|
238
205
|
const option = document.createElement('option');
|
|
239
206
|
option.value = id;
|
|
240
207
|
option.textContent = model.label;
|
|
208
|
+
option.dataset.reasoningEfforts = normalizeReasoningEffortsForSelect(model.reasoningEfforts).join(',');
|
|
209
|
+
option.dataset.defaultReasoningEffort = model.defaultReasoningEffort || '';
|
|
210
|
+
option.dataset.reasoningPresentation = model.reasoningPresentation || '';
|
|
241
211
|
option.dataset.speedTiers = normalizeSpeedTiersForSelect(model.speedTiers).join(',');
|
|
242
212
|
option.dataset.defaultSpeedTier = model.defaultSpeedTier || 'standard';
|
|
243
213
|
if (model.unverified) {
|
|
@@ -253,6 +223,9 @@
|
|
|
253
223
|
const option = document.createElement('option');
|
|
254
224
|
option.value = selectedId;
|
|
255
225
|
option.textContent = `${selectedId} (custom)`;
|
|
226
|
+
option.dataset.reasoningEfforts = '';
|
|
227
|
+
option.dataset.defaultReasoningEffort = '';
|
|
228
|
+
option.dataset.reasoningPresentation = 'none';
|
|
256
229
|
option.dataset.speedTiers = 'standard';
|
|
257
230
|
option.dataset.defaultSpeedTier = 'standard';
|
|
258
231
|
option.dataset.unverified = 'true';
|
|
@@ -265,6 +238,7 @@
|
|
|
265
238
|
} else if (firstModelId) {
|
|
266
239
|
modelSelect.value = firstModelId;
|
|
267
240
|
}
|
|
241
|
+
renderReasoningOptions(models);
|
|
268
242
|
renderSpeedOptions(models);
|
|
269
243
|
renderModelConfigChoices();
|
|
270
244
|
updateModelDisplay();
|
|
@@ -296,6 +270,37 @@
|
|
|
296
270
|
renderModelConfigChoices();
|
|
297
271
|
}
|
|
298
272
|
|
|
273
|
+
function renderReasoningOptions(models) {
|
|
274
|
+
const reasoningSelect = getPanel()?.querySelector('[data-reasoning]');
|
|
275
|
+
const modelSelect = getPanel()?.querySelector('[data-model]');
|
|
276
|
+
if (!reasoningSelect || !modelSelect) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const selectedModel = normalizeModelOptionId(modelSelect.value);
|
|
281
|
+
const model = (Array.isArray(models) ? models : []).find(item => normalizeModelOptionId(item?.id) === selectedModel);
|
|
282
|
+
const presentation = model?.reasoningPresentation || 'effort';
|
|
283
|
+
let efforts = normalizeReasoningEffortsForSelect(model?.reasoningEfforts);
|
|
284
|
+
if (!efforts.length) {
|
|
285
|
+
efforts = presentation === 'none' ? ['none'] : ['low', 'medium', 'high', 'xhigh'];
|
|
286
|
+
}
|
|
287
|
+
const current = reasoningSelect.value || getState()?.reasoningEffort || '';
|
|
288
|
+
const fallback = efforts.includes(model?.defaultReasoningEffort)
|
|
289
|
+
? model.defaultReasoningEffort
|
|
290
|
+
: efforts.includes('high') ? 'high' : efforts[0];
|
|
291
|
+
|
|
292
|
+
reasoningSelect.textContent = '';
|
|
293
|
+
for (const effort of efforts) {
|
|
294
|
+
const option = document.createElement('option');
|
|
295
|
+
option.value = effort;
|
|
296
|
+
option.textContent = formatReasoningEffortLabel(effort, presentation);
|
|
297
|
+
reasoningSelect.append(option);
|
|
298
|
+
}
|
|
299
|
+
reasoningSelect.value = efforts.includes(current) ? current : fallback;
|
|
300
|
+
reasoningSelect.disabled = presentation === 'none';
|
|
301
|
+
reasoningSelect.dataset.presentation = presentation;
|
|
302
|
+
}
|
|
303
|
+
|
|
299
304
|
function renderModelConfigChoices() {
|
|
300
305
|
renderReasoningChoices();
|
|
301
306
|
renderModelChoices();
|
|
@@ -310,10 +315,15 @@
|
|
|
310
315
|
return;
|
|
311
316
|
}
|
|
312
317
|
list.textContent = '';
|
|
318
|
+
const hidden = reasoningSelect.dataset.presentation === 'none';
|
|
319
|
+
list.hidden = hidden;
|
|
320
|
+
if (list.nextElementSibling?.classList?.contains('codex-model-config-divider')) {
|
|
321
|
+
list.nextElementSibling.hidden = hidden;
|
|
322
|
+
}
|
|
313
323
|
for (const option of Array.from(reasoningSelect.options || [])) {
|
|
314
324
|
list.append(createModelConfigChoice({
|
|
315
325
|
value: option.value,
|
|
316
|
-
label: formatReasoningEffortLabel(option.value),
|
|
326
|
+
label: option.textContent || formatReasoningEffortLabel(option.value, reasoningSelect.dataset.presentation),
|
|
317
327
|
datasetName: 'reasoningChoice'
|
|
318
328
|
}));
|
|
319
329
|
}
|
|
@@ -394,39 +404,33 @@
|
|
|
394
404
|
}
|
|
395
405
|
}
|
|
396
406
|
|
|
397
|
-
function normalizeSpeedTiersForSelect(speedTiers) {
|
|
398
|
-
const tiers = Array.isArray(speedTiers)
|
|
399
|
-
? speedTiers.map(tier => normalizeModelOptionId(tier)).filter(Boolean)
|
|
400
|
-
: ['standard'];
|
|
401
|
-
return tiers.includes('standard') ? tiers : ['standard', ...tiers];
|
|
402
|
-
}
|
|
403
|
-
|
|
404
407
|
function formatSpeedTierLabel(tier) {
|
|
405
408
|
return tier === 'fast' ? tx('Fast', '快速') : tx('Standard', '标准');
|
|
406
409
|
}
|
|
407
410
|
|
|
408
|
-
function formatReasoningEffortLabel(effort) {
|
|
411
|
+
function formatReasoningEffortLabel(effort, presentation = '') {
|
|
412
|
+
if (presentation === 'toggle') {
|
|
413
|
+
return effort === 'none' ? tx('Off', '关闭') : tx('On', '开启');
|
|
414
|
+
}
|
|
415
|
+
if (presentation === 'deepseek') {
|
|
416
|
+
if (effort === 'none') return tx('Off', '关闭');
|
|
417
|
+
if (effort === 'xhigh') return 'Max';
|
|
418
|
+
}
|
|
409
419
|
const labels = getLocale() === 'zh'
|
|
410
|
-
? { low: '低', medium: '中', high: '高', xhigh: '超高' }
|
|
411
|
-
: { low: 'Low', medium: 'Medium', high: 'High', xhigh: 'XHigh' };
|
|
420
|
+
? { none: '关闭', minimal: '最小', low: '低', medium: '中', high: '高', xhigh: '超高' }
|
|
421
|
+
: { none: 'Off', minimal: 'Minimal', low: 'Low', medium: 'Medium', high: 'High', xhigh: 'XHigh' };
|
|
412
422
|
return labels[effort] || effort || '';
|
|
413
423
|
}
|
|
414
424
|
|
|
415
|
-
function formatCompactModelLabel(label) {
|
|
416
|
-
return String(label || '').replace(/^gpt[-\s]*/i, '');
|
|
417
|
-
}
|
|
418
|
-
|
|
419
425
|
function resolveSelectedModel() {
|
|
420
426
|
return getPanel()?.querySelector('[data-model]')?.value || getState()?.model || '';
|
|
421
427
|
}
|
|
422
428
|
|
|
423
|
-
function normalizeModelOptionId(id) {
|
|
424
|
-
return typeof id === 'string' ? id.trim() : '';
|
|
425
|
-
}
|
|
426
|
-
|
|
427
429
|
function getModelDiscoverySourceLabel() {
|
|
428
430
|
if (modelDiscovery.errorCode || modelDiscovery.errorMessage) {
|
|
429
|
-
return
|
|
431
|
+
return modelDiscovery.status === 'fallback'
|
|
432
|
+
? `${tr('modelSourceFailed')} (${tr('modelSourceFallback')})`
|
|
433
|
+
: tr('modelSourceFailed');
|
|
430
434
|
}
|
|
431
435
|
if (modelDiscovery.source === 'fallback') {
|
|
432
436
|
return tr('modelSourceFallback');
|
|
@@ -454,7 +458,12 @@
|
|
|
454
458
|
});
|
|
455
459
|
modelDisplay.title = sourceTitle;
|
|
456
460
|
if (reasoningDisplay) {
|
|
457
|
-
|
|
461
|
+
const reasoningSelect = getPanel()?.querySelector('[data-reasoning]');
|
|
462
|
+
const presentation = reasoningSelect?.dataset.presentation || '';
|
|
463
|
+
reasoningDisplay.hidden = presentation === 'none';
|
|
464
|
+
reasoningDisplay.textContent = presentation === 'none'
|
|
465
|
+
? ''
|
|
466
|
+
: formatReasoningEffortLabel(reasoningSelect?.value || getState()?.reasoningEffort || '', presentation);
|
|
458
467
|
}
|
|
459
468
|
if (speedIndicator) {
|
|
460
469
|
speedIndicator.hidden = readSelectedSpeedInput() !== 'fast';
|
|
@@ -475,8 +484,10 @@
|
|
|
475
484
|
handleModelConfigChoiceClick,
|
|
476
485
|
loadModelOptions,
|
|
477
486
|
applyFallbackModelOptions,
|
|
487
|
+
applyUnavailableModelOptions,
|
|
478
488
|
getModelCatalog,
|
|
479
489
|
renderModelOptions,
|
|
490
|
+
renderReasoningOptions,
|
|
480
491
|
renderSpeedOptions,
|
|
481
492
|
renderModelConfigChoices,
|
|
482
493
|
resolveSelectedModel,
|