codex-overleaf-link 2.3.0 → 2.3.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 +23 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +20 -0
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +108 -110
- package/extension/src/content/generated/content.bundle.js +566 -331
- package/extension/src/content/generated/content.bundle.meta.json +4 -3
- package/extension/src/content/markdownText.js +12 -10
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/nativeCompatibilityController.js +2 -2
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/runTimelineView.js +6 -0
- package/extension/src/content/writebackOrchestrator.js +2 -49
- package/extension/src/page/trackedChangesLifecycle.js +10 -4
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/shared/agentTranscript.js +10 -3
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/pageRpcContract.js +2 -2
- package/extension/src/shared/runExecutionSnapshot.js +14 -4
- package/extension/src/shared/runExecutionSnapshotCodec.js +5 -2
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +20 -1
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +50 -47
- package/extension/src/shared/sessionState.js +46 -11
- package/extension/src/shared/storageDb.js +6 -5
- package/extension/src/shared/writebackOperationEvidence.js +16 -6
- package/extension/src/shared/writebackSettlement.js +28 -6
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/taskRunnerRuntime.js +11 -81
- package/native-host/src/updateManager.js +36 -1
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"entry": "extension/entries/content-entry.mjs",
|
|
4
4
|
"output": "extension/src/content/generated/content.bundle.js",
|
|
5
|
-
"sourceDigest": "
|
|
6
|
-
"outputDigest": "
|
|
7
|
-
"byteLength":
|
|
5
|
+
"sourceDigest": "ddbc07ac8d34d864aa3648c53211c5858e0312a73df815a0392bc52ec5eac1cc",
|
|
6
|
+
"outputDigest": "42051c8624bdfca3094c8bef2430094e55b7e1ec49d475ac27f6df14629bf313",
|
|
7
|
+
"byteLength": 2285557,
|
|
8
8
|
"inputs": [
|
|
9
9
|
"extension/entries/content-entry.mjs",
|
|
10
10
|
"extension/src/content/activeTurnControl.js",
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"extension/src/shared/runInputQueue.js",
|
|
82
82
|
"extension/src/shared/scopedPersistenceBrowserAdapter.js",
|
|
83
83
|
"extension/src/shared/scopedPersistenceQueuePolicy.js",
|
|
84
|
+
"extension/src/shared/scopedPersistenceReceipt.js",
|
|
84
85
|
"extension/src/shared/scopedPersistenceTransaction.js",
|
|
85
86
|
"extension/src/shared/sensitiveScan.js",
|
|
86
87
|
"extension/src/shared/sessionState.js",
|
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
tx,
|
|
14
14
|
callPageBridge, getCurrentProjectId,
|
|
15
15
|
getCurrentProjectReferenceFiles,
|
|
16
|
+
getCurrentProjectMathSources,
|
|
16
17
|
showPluginToast
|
|
17
18
|
} = deps;
|
|
18
19
|
|
|
19
20
|
function renderMarkdownInlineText(target, value) {
|
|
20
21
|
target.replaceChildren(...buildMarkdownInlineNodes(value));
|
|
21
22
|
}
|
|
23
|
+
function getMathMacros() { return MathText?.buildMathMacros?.(getCurrentProjectMathSources?.() || []) || {}; }
|
|
22
24
|
|
|
23
25
|
function sanitizeAssistantVisibleText(value) {
|
|
24
26
|
if (typeof value !== 'string' || !value) {
|
|
@@ -67,11 +69,8 @@
|
|
|
67
69
|
if (!mathText?.buildMathNodes) {
|
|
68
70
|
return buildMarkdownInlineNodesWithoutMath(source);
|
|
69
71
|
}
|
|
70
|
-
return mathText.buildMathNodes(source, {
|
|
71
|
-
|
|
72
|
-
katex: window.katex,
|
|
73
|
-
renderText: buildMarkdownInlineNodesWithoutMath
|
|
74
|
-
});
|
|
72
|
+
return mathText.buildMathNodes(source, { document, katex: window.katex,
|
|
73
|
+
macros: getMathMacros(), renderText: buildMarkdownInlineNodesWithoutMath });
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
function buildMarkdownInlineNodesWithoutMath(value) {
|
|
@@ -109,6 +108,10 @@
|
|
|
109
108
|
function buildInlineCodeNodes(value) {
|
|
110
109
|
const source = String(value || '');
|
|
111
110
|
const trimmed = source.trim();
|
|
111
|
+
const standaloneMath = MathText?.parseStandaloneMath?.(trimmed);
|
|
112
|
+
if (standaloneMath && MathText?.createMathNode) return [MathText.createMathNode(
|
|
113
|
+
standaloneMath, { document, katex: window.katex, macros: getMathMacros() }
|
|
114
|
+
)];
|
|
112
115
|
const refs = trimmed ? parseRuntimeLineReferences(trimmed, 'plain-text-token') : [];
|
|
113
116
|
const ref = refs.length === 1 && refs[0]?.rawText === trimmed ? refs[0] : null;
|
|
114
117
|
if (ref && !isRuntimeLocalPathLike(ref.rawPath)) {
|
|
@@ -492,12 +495,11 @@
|
|
|
492
495
|
`[${safeLabel.replace(/]/g, '\\]')}](${target.replace(/\)/g, '\\)')})`
|
|
493
496
|
);
|
|
494
497
|
},
|
|
495
|
-
renderCodeNodes: text =>
|
|
498
|
+
renderCodeNodes: text => buildInlineCodeNodes(text),
|
|
496
499
|
sanitizeText: sanitizeAssistantVisibleText,
|
|
497
|
-
createMathNode: segment => runtimeRoot.CodexOverleafMathText.createMathNode(segment,
|
|
498
|
-
document, katex: runtimeRoot.katex,
|
|
499
|
-
|
|
500
|
-
}),
|
|
500
|
+
createMathNode: segment => runtimeRoot.CodexOverleafMathText.createMathNode(segment,
|
|
501
|
+
{ document, katex: runtimeRoot.katex, macros: getMathMacros(),
|
|
502
|
+
renderText: text => buildMarkdownInlineNodes(text) }),
|
|
501
503
|
recordDiagnostic: recordRenderingDiagnostic
|
|
502
504
|
});
|
|
503
505
|
return;
|
|
@@ -9,6 +9,19 @@
|
|
|
9
9
|
]);
|
|
10
10
|
const LONG_INLINE_MATH_CHARS = 120;
|
|
11
11
|
const COMPLEX_INLINE_MATH_CHARS = 80;
|
|
12
|
+
const MAX_PROJECT_MACRO_SOURCES = 12;
|
|
13
|
+
const MAX_PROJECT_MACRO_SOURCE_CHARS = 256 * 1024;
|
|
14
|
+
const MAX_PROJECT_MACROS = 64;
|
|
15
|
+
const MAX_MACRO_EXPANSION_CHARS = 512;
|
|
16
|
+
const DEFAULT_MATH_MACROS = Object.freeze({
|
|
17
|
+
'\\E': '\\mathbb{E}',
|
|
18
|
+
'\\Var': '\\operatorname{Var}',
|
|
19
|
+
'\\Cov': '\\operatorname{Cov}',
|
|
20
|
+
'\\R': '\\mathbb{R}',
|
|
21
|
+
'\\N': '\\mathbb{N}',
|
|
22
|
+
'\\Z': '\\mathbb{Z}'
|
|
23
|
+
});
|
|
24
|
+
const UNSAFE_MACRO_PATTERN = /\\(?:href|url|includegraphics|input|include|write|openout|read|html\w*|class|style|data|def|gdef|edef|xdef|newcommand|renewcommand|providecommand|DeclareMathOperator)\b/i;
|
|
12
25
|
|
|
13
26
|
function parseMathSegments(value) {
|
|
14
27
|
const source = String(value || '');
|
|
@@ -74,9 +87,10 @@
|
|
|
74
87
|
searchFrom = start + delimiter.open.length;
|
|
75
88
|
continue;
|
|
76
89
|
}
|
|
77
|
-
const
|
|
90
|
+
const rawValue = source.slice(start + delimiter.open.length, closeStart);
|
|
91
|
+
const value = rawValue.trim();
|
|
78
92
|
const end = closeStart + delimiter.close.length;
|
|
79
|
-
if (!isValidMathValue(
|
|
93
|
+
if (!isValidMathValue(rawValue, delimiter) || overlapsCodeRange(start, end, codeRanges)) {
|
|
80
94
|
searchFrom = start + delimiter.open.length;
|
|
81
95
|
continue;
|
|
82
96
|
}
|
|
@@ -94,7 +108,7 @@
|
|
|
94
108
|
}
|
|
95
109
|
const previous = source[index - 1] || '';
|
|
96
110
|
const next = source[index + 1] || '';
|
|
97
|
-
return previous !== '$' && next !== '$' && next !== ''
|
|
111
|
+
return previous !== '$' && next !== '$' && next !== '';
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
function findClosingDelimiter(source, fromIndex, delimiter, codeRanges) {
|
|
@@ -107,7 +121,7 @@
|
|
|
107
121
|
const previous = source[index - 1] || '';
|
|
108
122
|
const next = source[index + delimiter.close.length] || '';
|
|
109
123
|
const invalidSingleDollar = delimiter.singleDollar
|
|
110
|
-
&& (previous === '$' || next === '$'
|
|
124
|
+
&& (previous === '$' || next === '$');
|
|
111
125
|
if (!isEscaped(source, index) && !isInsideRange(index, codeRanges) && !invalidSingleDollar) {
|
|
112
126
|
return index;
|
|
113
127
|
}
|
|
@@ -118,10 +132,24 @@
|
|
|
118
132
|
|
|
119
133
|
function isValidMathValue(value, delimiter) {
|
|
120
134
|
const source = String(value || '');
|
|
121
|
-
|
|
135
|
+
const trimmed = source.trim();
|
|
136
|
+
if (!trimmed) {
|
|
122
137
|
return false;
|
|
123
138
|
}
|
|
124
|
-
|
|
139
|
+
if (!delimiter.display && /[\r\n]/.test(source)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (!delimiter.singleDollar || (!/^\s/.test(source) && !/\s$/.test(source))) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
return looksLikeWhitespaceWrappedMath(trimmed);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function looksLikeWhitespaceWrappedMath(value) {
|
|
149
|
+
const source = String(value || '').trim();
|
|
150
|
+
return /\\[A-Za-z]+/.test(source)
|
|
151
|
+
|| /[_^=<>+\-*/()[\]{}|]/.test(source)
|
|
152
|
+
|| /^[A-Za-z](?:_[A-Za-z0-9{}]+|\^[A-Za-z0-9{}]+)?$/.test(source);
|
|
125
153
|
}
|
|
126
154
|
|
|
127
155
|
function collectInlineCodeRanges(source) {
|
|
@@ -179,7 +207,8 @@
|
|
|
179
207
|
}
|
|
180
208
|
nodes.push(createMathNode(segment, {
|
|
181
209
|
document: documentRef,
|
|
182
|
-
katex: options.katex || root.katex
|
|
210
|
+
katex: options.katex || root.katex,
|
|
211
|
+
macros: options.macros || buildMathMacros(options.projectSources)
|
|
183
212
|
}));
|
|
184
213
|
}
|
|
185
214
|
return nodes;
|
|
@@ -206,6 +235,7 @@
|
|
|
206
235
|
strict: 'warn',
|
|
207
236
|
maxExpand: 200,
|
|
208
237
|
maxSize: 20,
|
|
238
|
+
macros: options.macros || buildMathMacros(),
|
|
209
239
|
output: 'htmlAndMathml'
|
|
210
240
|
});
|
|
211
241
|
node.dataset.mathRendered = 'true';
|
|
@@ -213,7 +243,7 @@
|
|
|
213
243
|
} catch (_error) {
|
|
214
244
|
node.classList.add('run-math--fallback');
|
|
215
245
|
node.dataset.mathRendered = 'false';
|
|
216
|
-
node.textContent =
|
|
246
|
+
node.textContent = source;
|
|
217
247
|
return node;
|
|
218
248
|
}
|
|
219
249
|
}
|
|
@@ -235,6 +265,120 @@
|
|
|
235
265
|
return /(?:=|\\(?:le|ge|approx|sim|to|Rightarrow|Longrightarrow)\b|\\begin\{(?:aligned|gathered|split|array)\}|\\\\)/.test(source);
|
|
236
266
|
}
|
|
237
267
|
|
|
268
|
+
function parseStandaloneMath(value) {
|
|
269
|
+
const source = String(value || '').trim();
|
|
270
|
+
if (!source) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const segments = parseMathSegments(source);
|
|
274
|
+
if (segments.length !== 1 || segments[0].type !== 'math' || segments[0].raw !== source) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return segments[0];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function buildMathMacros(projectSources = []) {
|
|
281
|
+
const macros = { ...DEFAULT_MATH_MACROS };
|
|
282
|
+
let remainingChars = MAX_PROJECT_MACRO_SOURCE_CHARS;
|
|
283
|
+
let projectMacroCount = 0;
|
|
284
|
+
for (const sourceRecord of Array.isArray(projectSources)
|
|
285
|
+
? projectSources.slice(0, MAX_PROJECT_MACRO_SOURCES)
|
|
286
|
+
: []) {
|
|
287
|
+
if (projectMacroCount >= MAX_PROJECT_MACROS || remainingChars <= 0) {
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
const path = String(sourceRecord?.path || '');
|
|
291
|
+
if (!/\.(?:tex|sty|cls|ltx)$/i.test(path)) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const source = stripLatexComments(
|
|
295
|
+
String(sourceRecord?.content || '').slice(0, remainingChars)
|
|
296
|
+
);
|
|
297
|
+
remainingChars -= source.length;
|
|
298
|
+
const pattern = /\\(newcommand|renewcommand|providecommand|DeclareMathOperator)(\*)?/g;
|
|
299
|
+
let match;
|
|
300
|
+
while ((match = pattern.exec(source)) !== null && projectMacroCount < MAX_PROJECT_MACROS) {
|
|
301
|
+
let cursor = skipWhitespace(source, pattern.lastIndex);
|
|
302
|
+
const nameGroup = readBracedGroup(source, cursor);
|
|
303
|
+
if (!nameGroup) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const macroName = nameGroup.value.trim();
|
|
307
|
+
cursor = skipWhitespace(source, nameGroup.end);
|
|
308
|
+
if (source[cursor] === '[') {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const expansionGroup = readBracedGroup(source, cursor);
|
|
312
|
+
if (!expansionGroup) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
let expansion = expansionGroup.value.trim();
|
|
316
|
+
if (match[1] === 'DeclareMathOperator') {
|
|
317
|
+
expansion = `\\operatorname${match[2] ? '*' : ''}{${expansion}}`;
|
|
318
|
+
}
|
|
319
|
+
if (!isSafeMacroDefinition(macroName, expansion)) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
macros[macroName] = expansion;
|
|
323
|
+
projectMacroCount++;
|
|
324
|
+
pattern.lastIndex = expansionGroup.end;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return macros;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function readBracedGroup(source, start) {
|
|
331
|
+
if (source[start] !== '{') {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
let depth = 0;
|
|
335
|
+
for (let index = start; index < source.length; index++) {
|
|
336
|
+
if (source[index] === '{' && !isEscaped(source, index)) {
|
|
337
|
+
depth++;
|
|
338
|
+
} else if (source[index] === '}' && !isEscaped(source, index)) {
|
|
339
|
+
depth--;
|
|
340
|
+
if (depth === 0) {
|
|
341
|
+
return {
|
|
342
|
+
value: source.slice(start + 1, index),
|
|
343
|
+
end: index + 1
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function skipWhitespace(source, start) {
|
|
352
|
+
let index = start;
|
|
353
|
+
while (index < source.length && /\s/.test(source[index])) {
|
|
354
|
+
index++;
|
|
355
|
+
}
|
|
356
|
+
return index;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function stripLatexComments(source) {
|
|
360
|
+
return String(source || '').split(/\r?\n/).map(line => {
|
|
361
|
+
for (let index = 0; index < line.length; index++) {
|
|
362
|
+
if (line[index] === '%' && !isEscaped(line, index)) {
|
|
363
|
+
return line.slice(0, index);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return line;
|
|
367
|
+
}).join('\n');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function isSafeMacroDefinition(name, expansion) {
|
|
371
|
+
const macroName = String(name || '');
|
|
372
|
+
const body = String(expansion || '');
|
|
373
|
+
return /^\\[A-Za-z@]+$/.test(macroName)
|
|
374
|
+
&& body.length > 0
|
|
375
|
+
&& body.length <= MAX_MACRO_EXPANSION_CHARS
|
|
376
|
+
&& !body.includes('#')
|
|
377
|
+
&& !body.includes(macroName)
|
|
378
|
+
&& !UNSAFE_MACRO_PATTERN.test(macroName)
|
|
379
|
+
&& !UNSAFE_MACRO_PATTERN.test(body);
|
|
380
|
+
}
|
|
381
|
+
|
|
238
382
|
function toNodeArray(value) {
|
|
239
383
|
if (Array.isArray(value)) {
|
|
240
384
|
return value;
|
|
@@ -265,11 +409,13 @@
|
|
|
265
409
|
}
|
|
266
410
|
|
|
267
411
|
const api = {
|
|
412
|
+
buildMathMacros,
|
|
268
413
|
buildMathNodes,
|
|
269
414
|
createMathNode,
|
|
270
415
|
matchMathAt,
|
|
271
416
|
normalizeMathForRendering,
|
|
272
|
-
parseMathSegments
|
|
417
|
+
parseMathSegments,
|
|
418
|
+
parseStandaloneMath
|
|
273
419
|
};
|
|
274
420
|
root.CodexOverleafMathText = api;
|
|
275
421
|
if (typeof module !== 'undefined' && module.exports) {
|
|
@@ -256,8 +256,8 @@
|
|
|
256
256
|
tip.dataset.onboardingTip = 'true';
|
|
257
257
|
tip.setAttribute('role', 'status');
|
|
258
258
|
tip.textContent = tx(
|
|
259
|
-
'Tip: Start with Ask mode to explore safely. Switch to
|
|
260
|
-
'提示:先用 Ask
|
|
259
|
+
'Tip: Start with Ask mode to explore safely. Switch to Auto only when you want Codex to edit files.',
|
|
260
|
+
'提示:先用 Ask 模式安全探索;只有需要 Codex 修改文件时才切换到 Auto。'
|
|
261
261
|
);
|
|
262
262
|
Object.assign(tip.style, {
|
|
263
263
|
position: 'absolute',
|
|
@@ -868,7 +868,7 @@
|
|
|
868
868
|
applyResults: [],
|
|
869
869
|
nextStep: tr('warmMirrorStaleBlockedNextStep')
|
|
870
870
|
});
|
|
871
|
-
finishRunView(tr('warmMirrorStaleBlockedTitle'), 'failed');
|
|
871
|
+
await finishRunView(tr('warmMirrorStaleBlockedTitle'), 'failed');
|
|
872
872
|
return { ok: false };
|
|
873
873
|
}
|
|
874
874
|
if (focusedPartialSnapshot) {
|
|
@@ -274,6 +274,9 @@
|
|
|
274
274
|
if (status === 'failed') {
|
|
275
275
|
return tr('processedFailed', { elapsed });
|
|
276
276
|
}
|
|
277
|
+
if (status === 'rejected' || status === 'cancelled') {
|
|
278
|
+
return tr('processedCancelled', { elapsed });
|
|
279
|
+
}
|
|
277
280
|
if (status === 'running') {
|
|
278
281
|
return tr('processing', { elapsed });
|
|
279
282
|
}
|
|
@@ -411,6 +414,9 @@
|
|
|
411
414
|
if (run.startedAt && run.finishedAt) {
|
|
412
415
|
return formatProcessedSummary(run.status || 'completed', new Date(run.finishedAt).getTime() - new Date(run.startedAt).getTime());
|
|
413
416
|
}
|
|
417
|
+
if (run.status === 'rejected' || run.status === 'cancelled') {
|
|
418
|
+
return tx('Cancelled', '已取消');
|
|
419
|
+
}
|
|
414
420
|
return run.status === 'failed' ? tx('Failed', '处理失败') : tx('Done', '已处理');
|
|
415
421
|
}
|
|
416
422
|
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
let appendOperationsPreview;
|
|
26
26
|
let appendPartialWritebackWarning;
|
|
27
27
|
let appendApplyResult;
|
|
28
|
-
let renderDiffReview;
|
|
29
28
|
let renderReadOnlyDiffReview;
|
|
30
29
|
let showPluginConfirm;
|
|
31
30
|
let callPageBridge;
|
|
@@ -96,8 +95,8 @@
|
|
|
96
95
|
applyResults: [],
|
|
97
96
|
mode: runMode,
|
|
98
97
|
nextStep: tx(
|
|
99
|
-
'Switch to
|
|
100
|
-
'只有希望 Codex
|
|
98
|
+
'Switch to Auto only when you want Codex to edit files.',
|
|
99
|
+
'只有希望 Codex 修改文件时,才需要切换到自动写入。'
|
|
101
100
|
)
|
|
102
101
|
});
|
|
103
102
|
return {
|
|
@@ -178,51 +177,6 @@
|
|
|
178
177
|
};
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
if (runMode === 'confirm') {
|
|
182
|
-
appendRunEvent({
|
|
183
|
-
title: tx(
|
|
184
|
-
`Local Codex produced ${operations.length} change(s). Review the diff before applying.`,
|
|
185
|
-
`本地 Codex 产生了 ${operations.length} 项改动,请查看差异后确认。`
|
|
186
|
-
),
|
|
187
|
-
status: 'running'
|
|
188
|
-
});
|
|
189
|
-
const accepted = await renderDiffReview(visibleSyncChanges);
|
|
190
|
-
if (!accepted.length) {
|
|
191
|
-
appendRunEvent({
|
|
192
|
-
title: tx('Sync cancelled: Overleaf was not modified.', '已取消同步:Overleaf 没有被修改。'),
|
|
193
|
-
status: 'completed'
|
|
194
|
-
});
|
|
195
|
-
appendCompletionReport({
|
|
196
|
-
conclusion: tx('You cancelled syncing. Local Codex changes were not written back to Overleaf.', '你取消了同步,本地 Codex 改动没有写回 Overleaf。'),
|
|
197
|
-
status: 'rejected',
|
|
198
|
-
operations: [],
|
|
199
|
-
applyResults: [],
|
|
200
|
-
mode: runMode,
|
|
201
|
-
nextStep: tx('Run the task again, or switch to Auto if you want changes synced directly.', '可以重新运行任务,或切换到自动写入后再同步。')
|
|
202
|
-
});
|
|
203
|
-
return {
|
|
204
|
-
summaryLine: tx('Sync cancelled', '已取消同步'),
|
|
205
|
-
hasSkippedOperations: additionalSkippedEntries.length > 0,
|
|
206
|
-
settlement: buildSettlement({
|
|
207
|
-
operations,
|
|
208
|
-
applyResult: { ok: false, applied: [], skipped: additionalSkippedEntries }
|
|
209
|
-
}),
|
|
210
|
-
audit: buildAuditSummaryFromApply({
|
|
211
|
-
operations,
|
|
212
|
-
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
213
|
-
blockedFiles: additionalSkippedEntries.map(item => summarizeOperationForAudit(item.operation, item.result, 'blocked')),
|
|
214
|
-
resultStatus: 'rejected'
|
|
215
|
-
})
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
operations = buildSyncApplyOperations(accepted, project);
|
|
219
|
-
const acceptedPathSafety = partitionUnsafeProjectPathOperations(operations);
|
|
220
|
-
if (acceptedPathSafety.skipped.length) {
|
|
221
|
-
additionalSkippedEntries.push(...acceptedPathSafety.skipped);
|
|
222
|
-
operations = acceptedPathSafety.safe;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
180
|
const deleteOperations = operations.filter(operation => operation.type === 'delete');
|
|
227
181
|
if (deleteOperations.length) {
|
|
228
182
|
const approved = await showPluginConfirm({
|
|
@@ -872,7 +826,6 @@
|
|
|
872
826
|
appendOperationsPreview,
|
|
873
827
|
appendPartialWritebackWarning,
|
|
874
828
|
appendApplyResult,
|
|
875
|
-
renderDiffReview,
|
|
876
829
|
renderReadOnlyDiffReview,
|
|
877
830
|
showPluginConfirm,
|
|
878
831
|
callPageBridge,
|
|
@@ -971,8 +971,15 @@
|
|
|
971
971
|
}
|
|
972
972
|
|
|
973
973
|
for (const path of paths) {
|
|
974
|
-
|
|
975
|
-
|
|
974
|
+
const postContent = postByPath.get(path);
|
|
975
|
+
const activePathMatches = getActiveFilePath() === path;
|
|
976
|
+
const activeContentMatches = readActiveEditorText() === postContent;
|
|
977
|
+
// Overleaf can update the selected tree path before CodeMirror swaps its
|
|
978
|
+
// document. In that state getActiveFilePath() already names `path`, while
|
|
979
|
+
// readActiveEditorText() still belongs to the previously viewed file.
|
|
980
|
+
// Force-reopen the target whenever either identity signal disagrees.
|
|
981
|
+
if (path && (!activePathMatches || !activeContentMatches)) {
|
|
982
|
+
const opened = await openFileByPath(path, { force: true });
|
|
976
983
|
if (!opened.ok) {
|
|
977
984
|
return {
|
|
978
985
|
ok: false,
|
|
@@ -983,8 +990,7 @@
|
|
|
983
990
|
}
|
|
984
991
|
}
|
|
985
992
|
|
|
986
|
-
const
|
|
987
|
-
const postReady = await waitForActiveEditorExpectedText(path, postContent, 2500);
|
|
993
|
+
const postReady = await waitForActiveEditorExpectedText(path, postContent, 9000);
|
|
988
994
|
if (!postReady.ok) {
|
|
989
995
|
return {
|
|
990
996
|
ok: false,
|
|
@@ -1667,13 +1667,17 @@
|
|
|
1667
1667
|
continue;
|
|
1668
1668
|
}
|
|
1669
1669
|
const key = typeof ref.key === 'string' ? ref.key : '';
|
|
1670
|
-
if (!key
|
|
1670
|
+
if (!key) {
|
|
1671
1671
|
continue;
|
|
1672
1672
|
}
|
|
1673
|
-
seen.add(key);
|
|
1674
1673
|
const hasPath = typeof ref.path === 'string';
|
|
1675
1674
|
const hasExplicitPath = hasPath && ref.path.trim() !== '';
|
|
1676
1675
|
const path = hasPath ? normalizeSafeProjectPath(ref.path) : '';
|
|
1676
|
+
const identity = `${path}\u0000${key}`;
|
|
1677
|
+
if (seen.has(identity)) {
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
seen.add(identity);
|
|
1677
1681
|
normalized.push({
|
|
1678
1682
|
key,
|
|
1679
1683
|
id: typeof ref.id === 'string' ? ref.id : '',
|
|
@@ -727,10 +727,17 @@
|
|
|
727
727
|
if (providerError) {
|
|
728
728
|
return providerError;
|
|
729
729
|
}
|
|
730
|
-
if (/
|
|
730
|
+
if (/suggest_mode_removed|Suggest mode has been removed/i.test(text)) {
|
|
731
|
+
return {
|
|
732
|
+
conclusion: textFor(locale, '建议修改模式已经移除。', 'Suggest mode has been removed.'),
|
|
733
|
+
nextStep: textFor(locale, '请刷新扩展,然后选择“只问不改”或“自动写入”。', 'Reload the extension, then choose Ask or Auto.'),
|
|
734
|
+
failureCode: 'suggest_mode_removed'
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
if (/Mode must be "(?:confirm|ask)" or "auto"/i.test(text)) {
|
|
731
738
|
return {
|
|
732
739
|
conclusion: textFor(locale, '这轮没有写入:当前是“只问不改”,但这个任务需要写入权限。', 'No files were written: this task needs write access, but the current mode is Ask.'),
|
|
733
|
-
nextStep: textFor(locale, '
|
|
740
|
+
nextStep: textFor(locale, '请切换到“自动写入”后重新运行。', 'Switch to Auto and run the task again.'),
|
|
734
741
|
failureCode: null // no direct catalog match — preflight / governance
|
|
735
742
|
};
|
|
736
743
|
}
|
|
@@ -797,7 +804,7 @@
|
|
|
797
804
|
if (/checkpoint/i.test(text)) {
|
|
798
805
|
return {
|
|
799
806
|
conclusion: textFor(locale, '这轮没有自动写入:Codex 没有拿到可恢复版本。', 'This run did not auto-write: Codex did not get a recoverable version.'),
|
|
800
|
-
nextStep: textFor(locale, '
|
|
807
|
+
nextStep: textFor(locale, '请确认 Overleaf Reviewing 已开启后再用“自动写入”。', 'Confirm that Overleaf Reviewing is enabled before using Auto.'),
|
|
801
808
|
failureCode: null // no current catalog code; preflight reviewing issue
|
|
802
809
|
};
|
|
803
810
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const MIN_NATIVE_VERSION = '1.0.0';
|
|
13
13
|
const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
|
|
14
14
|
const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
|
|
15
|
-
const BUILD_TARGET_VERSION = '2.3.
|
|
15
|
+
const BUILD_TARGET_VERSION = '2.3.1';
|
|
16
16
|
const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
|
|
17
17
|
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
18
18
|
'bridgePing',
|
|
@@ -266,8 +266,6 @@
|
|
|
266
266
|
writeMode: 'Write mode',
|
|
267
267
|
modeAsk: 'Ask',
|
|
268
268
|
modeAskTitle: 'Read and analyze only. Do not write to Overleaf.',
|
|
269
|
-
modeConfirm: 'Suggest',
|
|
270
|
-
modeConfirmTitle: 'Show a change plan first, then write after approval.',
|
|
271
269
|
modeAuto: 'Auto',
|
|
272
270
|
modeAutoTitle: 'Write directly after authorization. Deletes still require confirmation.',
|
|
273
271
|
addContext: 'Add @ context',
|
|
@@ -403,6 +401,7 @@
|
|
|
403
401
|
refreshProbeDone: 'Refreshed: {status}',
|
|
404
402
|
refreshProbeFailed: 'Refresh failed. Reload Overleaf and try again.',
|
|
405
403
|
processedFailed: 'Failed {elapsed}',
|
|
404
|
+
processedCancelled: 'Cancelled {elapsed}',
|
|
406
405
|
processing: 'Running {elapsed}',
|
|
407
406
|
processed: 'Done {elapsed}',
|
|
408
407
|
restoredRunStoppedTitle: 'Stopped tracking this run after a page refresh',
|
|
@@ -779,8 +778,6 @@
|
|
|
779
778
|
writeMode: '写入模式',
|
|
780
779
|
modeAsk: '只问不改',
|
|
781
780
|
modeAskTitle: '只读取和分析,不写入 Overleaf',
|
|
782
|
-
modeConfirm: '建议修改',
|
|
783
|
-
modeConfirmTitle: '先给出修改方案,确认后写入',
|
|
784
781
|
modeAuto: '自动写入',
|
|
785
782
|
modeAutoTitle: '授权后直接写入,删除仍需确认',
|
|
786
783
|
addContext: '添加 @ 上下文',
|
|
@@ -916,6 +913,7 @@
|
|
|
916
913
|
refreshProbeDone: '已重新检测:{status}',
|
|
917
914
|
refreshProbeFailed: '检测失败:请刷新 Overleaf 页面后重试',
|
|
918
915
|
processedFailed: '处理失败 {elapsed}',
|
|
916
|
+
processedCancelled: '已取消 {elapsed}',
|
|
919
917
|
processing: '处理中 {elapsed}',
|
|
920
918
|
processed: '已处理 {elapsed}',
|
|
921
919
|
restoredRunStoppedTitle: '页面刷新后已停止跟踪这轮任务',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
})(typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : this), function () {
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
var REVISION = '2026-07-
|
|
8
|
+
var REVISION = '2026-07-31-page-rpc-contract-v14';
|
|
9
9
|
var METHODS = freezeCatalog({
|
|
10
10
|
initializeCapability: entry('bridge.initialize', 'control', 'none', 'default', false, 'none'),
|
|
11
11
|
probe: entry('bridge.probe', 'read', 'none', 'default', false, 'safe'),
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
file_list: 35000,
|
|
68
68
|
compile: 75000,
|
|
69
69
|
lifecycle: 120000,
|
|
70
|
-
writeback:
|
|
70
|
+
writeback: 45000
|
|
71
71
|
};
|
|
72
72
|
var candidate = Number(overrides[timeoutClass]);
|
|
73
73
|
return Number.isFinite(candidate) && candidate > 0 ? candidate : defaults[timeoutClass];
|
|
@@ -28,7 +28,10 @@
|
|
|
28
28
|
: rawQueueItem;
|
|
29
29
|
const persisted = rawQueueItem?.executionSnapshot || rawPayload?.executionSnapshot;
|
|
30
30
|
if (persisted && typeof persisted === 'object') {
|
|
31
|
-
return Codec.normalizeSnapshot(persisted, {
|
|
31
|
+
return Codec.normalizeSnapshot(persisted, {
|
|
32
|
+
source: persisted.source || 'submitted',
|
|
33
|
+
migrateLegacyConfirm: true
|
|
34
|
+
});
|
|
32
35
|
}
|
|
33
36
|
const raw = rawPayload && typeof rawPayload === 'object' ? rawPayload : {};
|
|
34
37
|
const fallback = fallbackInput && typeof fallbackInput === 'object' ? fallbackInput : {};
|
|
@@ -48,13 +51,17 @@
|
|
|
48
51
|
inferred = true;
|
|
49
52
|
}
|
|
50
53
|
return Codec.normalizeSnapshot(captured, {
|
|
51
|
-
source: inferred ? 'legacy-inferred' : 'legacy-captured'
|
|
54
|
+
source: inferred ? 'legacy-inferred' : 'legacy-captured',
|
|
55
|
+
migrateLegacyConfirm: true
|
|
52
56
|
});
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
function resolveForExecution(value = {}, options = {}) {
|
|
56
60
|
const persisted = value.executionSnapshot || value;
|
|
57
|
-
const snapshot = Codec.normalizeSnapshot(persisted, {
|
|
61
|
+
const snapshot = Codec.normalizeSnapshot(persisted, {
|
|
62
|
+
source: persisted.source || 'submitted',
|
|
63
|
+
migrateLegacyConfirm: true
|
|
64
|
+
});
|
|
58
65
|
if (snapshot.providerId === 'builtin') return Codec.normalizeSnapshot(snapshot, { source: snapshot.source });
|
|
59
66
|
const catalog = options.catalog || {};
|
|
60
67
|
const provider = ProviderProfiles?.getProviderById
|
|
@@ -80,7 +87,10 @@
|
|
|
80
87
|
|
|
81
88
|
function snapshotOf(value = {}) {
|
|
82
89
|
const persisted = value.executionSnapshot || value;
|
|
83
|
-
return Codec.normalizeSnapshot(persisted, {
|
|
90
|
+
return Codec.normalizeSnapshot(persisted, {
|
|
91
|
+
source: persisted.source || 'submitted',
|
|
92
|
+
migrateLegacyConfirm: true
|
|
93
|
+
});
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
function cloneSnapshot(value = {}) {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const SCHEMA_VERSION = 1;
|
|
11
|
-
const VALID_MODES = new Set(['ask', '
|
|
11
|
+
const VALID_MODES = new Set(['ask', 'auto']);
|
|
12
12
|
const VALID_SOURCES = new Set(['submitted', 'legacy-captured', 'legacy-inferred']);
|
|
13
13
|
const TUPLE_FIELDS = Object.freeze([
|
|
14
14
|
'mode', 'providerId', 'providerRevision', 'model', 'reasoningEffort',
|
|
@@ -19,12 +19,15 @@
|
|
|
19
19
|
function normalizeSnapshot(value = {}, options = {}) {
|
|
20
20
|
const input = value && typeof value === 'object' ? value : {};
|
|
21
21
|
assertNoSecretFields(input);
|
|
22
|
+
if (input.mode === 'confirm' && options.migrateLegacyConfirm !== true) {
|
|
23
|
+
throw snapshotError('suggest_mode_removed', 'Suggest mode has been removed. Choose Ask or Auto.');
|
|
24
|
+
}
|
|
22
25
|
const providerId = normalizeText(input.providerId) || 'builtin';
|
|
23
26
|
const revision = normalizeRevisionResult(input.providerRevision);
|
|
24
27
|
if (!revision.ok) throw snapshotError('provider_revision_conflict', revision.error);
|
|
25
28
|
const snapshot = {
|
|
26
29
|
schemaVersion: SCHEMA_VERSION,
|
|
27
|
-
mode:
|
|
30
|
+
mode: input.mode === 'auto' ? 'auto' : 'ask',
|
|
28
31
|
providerId,
|
|
29
32
|
providerRevision: providerId === 'builtin' ? '' : revision.value,
|
|
30
33
|
model: normalizeText(input.model).slice(0, 160),
|