codex-overleaf-link 2.5.0 → 2.5.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 +38 -26
- package/README.zh-CN.md +39 -27
- package/extension/bootstrap/background.js +52 -9
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/bootstrap/popup.html +1 -0
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/src/background.js +206 -53
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +74 -14
- package/extension/src/backgroundUpdateCoordinator.js +118 -21
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +8 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +72 -14
- package/extension/src/content/composerAttachments.js +93 -45
- package/extension/src/content/composerPanel.js +18 -0
- package/extension/src/content/contentRuntime.js +661 -676
- package/extension/src/content/diagnosticsController.js +122 -96
- package/extension/src/content/diagnosticsExport.js +19 -5
- package/extension/src/content/diagnosticsPanel.js +30 -0
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +6879 -2786
- package/extension/src/content/generated/content.bundle.meta.json +11 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +6 -0
- package/extension/src/content/localSkillsPanel.js +102 -12
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +7 -4
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +15 -2
- package/extension/src/content/panelMaintenance.js +40 -12
- package/extension/src/content/panelRenderer.js +6 -0
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +46 -8
- package/extension/src/content/providerSettingsDialog.js +71 -13
- package/extension/src/content/recentProjects.js +11 -5
- package/extension/src/content/runActivitySummary.js +40 -6
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +37 -14
- package/extension/src/content/runFailureNotice.js +21 -12
- package/extension/src/content/runGuidanceController.js +72 -27
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runQueueScheduler.js +148 -42
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +8 -125
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +146 -153
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +1 -1
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/settingsPanel.js +7 -3
- package/extension/src/content/settingsWorkbench.js +5 -0
- package/extension/src/content/sharedSessionViewSync.js +45 -8
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +60 -8
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +53 -4
- package/extension/src/content/writebackOrchestrator.js +82 -73
- package/extension/src/content/writingStyleSettings.js +35 -11
- package/extension/src/page/binaryAssetUploader.js +136 -14
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +1 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +6 -4
- package/extension/src/page/snapshotRouter.js +84 -39
- package/extension/src/page/textFileCreator.js +105 -25
- package/extension/src/page/trackedChangeCapture.js +12 -2
- package/extension/src/page/trackedChangeOwnership.js +19 -1
- package/extension/src/page/trackedChangeReplay.js +41 -21
- package/extension/src/page/trackedChangesLifecycle.js +217 -199
- package/extension/src/page/treeOperations.js +7 -10
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +2 -0
- package/extension/src/page/writebackRouter.js +80 -79
- package/extension/src/pageBridge.js +19 -3
- package/extension/src/popup.js +62 -10
- package/extension/src/shared/agentTranscript.js +23 -0
- package/extension/src/shared/auditRecords.js +2 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +79 -57
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +309 -3
- package/extension/src/shared/globalPreferences.js +30 -13
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +298 -2
- package/extension/src/shared/managedUpdateProjection.js +2 -2
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +5 -4
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +5 -3
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/selectionContext.js +12 -3
- package/extension/src/shared/sessionState.js +72 -27
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +37 -14
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +58 -26
- package/extension/src/shared/storageMigration.js +71 -111
- package/extension/src/shared/storageRunActions.js +209 -26
- package/extension/src/shared/subagentActivityModel.js +1 -1
- package/extension/src/shared/undoOperations.js +136 -13
- package/extension/src/shared/updateConsent.js +6 -4
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +16 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +38 -38
- package/extension/styles/panel.css +19 -5
- package/native-host/src/anthropicBridgeRequest.js +4 -2
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexPromptAssembly.js +31 -12
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexSessionRunner.js +110 -183
- package/native-host/src/codexSessionTiming.js +18 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +110 -35
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/processStopBarrier.js +42 -7
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerProfile.js +4 -2
- package/native-host/src/providerReasoning.js +2 -7
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +72 -14
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +36 -8
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +79 -66
- package/native-host/src/subagentTelemetry.js +19 -10
- package/native-host/src/subagentWorkspacePath.js +5 -2
- package/native-host/src/taskRunnerRuntime.js +77 -54
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/updateManager.js +297 -39
- package/native-host/src/writingStyleRuntime.js +57 -29
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
(function initCodexOverleafSensitivePreflight(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafSensitivePreflight = factory();
|
|
6
|
+
}
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function sensitivePreflightFactory() {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
function create({
|
|
11
|
+
SensitiveScan, normalizeGovernanceRules, isTextSnapshotFile, sendBackgroundNative,
|
|
12
|
+
getCurrentProjectId, appendRunEvent, appendCompletionReport, showPluginConfirm, tr, tx
|
|
13
|
+
}) {
|
|
14
|
+
async function runSensitivePreflight(options) {
|
|
15
|
+
options = options || {};
|
|
16
|
+
const { task, project, rules, useExistingMirror = false, guidance = false } = options;
|
|
17
|
+
const normalizedRules = normalizeGovernanceRules(rules);
|
|
18
|
+
if (normalizedRules.sensitiveCheckEnabled === false) {
|
|
19
|
+
return { blocked: false, findings: [] };
|
|
20
|
+
}
|
|
21
|
+
if (!SensitiveScan?.scanSensitiveInputs && !useExistingMirror) {
|
|
22
|
+
return { blocked: false, findings: [] };
|
|
23
|
+
}
|
|
24
|
+
let findings = SensitiveScan?.scanSensitiveInputs
|
|
25
|
+
? SensitiveScan.scanSensitiveInputs({
|
|
26
|
+
task,
|
|
27
|
+
files: guidance ? [] : (project?.files || []).filter(isTextSnapshotFile)
|
|
28
|
+
})
|
|
29
|
+
: [];
|
|
30
|
+
if (useExistingMirror && !guidance) {
|
|
31
|
+
findings = dedupeSensitiveFindings([
|
|
32
|
+
...findings,
|
|
33
|
+
...await scanNativeMirrorSensitiveFindings()
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
if (!findings.length) {
|
|
37
|
+
return { blocked: false, findings: [] };
|
|
38
|
+
}
|
|
39
|
+
options.assertCurrent?.();
|
|
40
|
+
appendRunEvent({
|
|
41
|
+
title: tx(
|
|
42
|
+
`Sensitive content check found ${findings.length} item(s).`,
|
|
43
|
+
`敏感内容检查发现 ${findings.length} 项。`
|
|
44
|
+
),
|
|
45
|
+
status: 'failed',
|
|
46
|
+
detail: findings.slice(0, 8).map(finding => ({
|
|
47
|
+
[tr('detailFile')]: finding.path || finding.source || 'task',
|
|
48
|
+
[tr('detailReason')]: finding.detectorId,
|
|
49
|
+
[tx('Preview', '预览')]: finding.preview || '[REDACTED]'
|
|
50
|
+
}))
|
|
51
|
+
});
|
|
52
|
+
if (normalizedRules.sensitiveConfirmAllowed === true) {
|
|
53
|
+
const approved = await showPluginConfirm({
|
|
54
|
+
title: tr('sensitiveConfirmTitle'),
|
|
55
|
+
message: formatSensitiveConfirmMessage(findings),
|
|
56
|
+
confirmLabel: guidance ? tx('Send guidance', '发送指导') : tr('sensitiveConfirmRun'),
|
|
57
|
+
cancelLabel: tr('confirmDefaultCancel'),
|
|
58
|
+
destructive: true
|
|
59
|
+
});
|
|
60
|
+
options.assertCurrent?.();
|
|
61
|
+
return { blocked: !approved, findings };
|
|
62
|
+
}
|
|
63
|
+
// Blocking an extra message must not finish the task already in progress.
|
|
64
|
+
if (guidance) return { blocked: true, findings };
|
|
65
|
+
appendCompletionReport({
|
|
66
|
+
conclusion: tx(
|
|
67
|
+
'This run was blocked before Codex received project context because sensitive content was detected.',
|
|
68
|
+
'检测到敏感内容,本轮已在向 Codex 发送项目上下文前阻止。'
|
|
69
|
+
),
|
|
70
|
+
status: 'blocked',
|
|
71
|
+
operations: [],
|
|
72
|
+
applyResults: [],
|
|
73
|
+
nextStep: tx(
|
|
74
|
+
'Remove the sensitive content, narrow @context, or enable explicit sensitive confirmation in Project Settings.',
|
|
75
|
+
'请移除敏感内容、缩小 @context,或在项目设置里允许敏感内容显式确认。'
|
|
76
|
+
)
|
|
77
|
+
});
|
|
78
|
+
return { blocked: true, findings };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function formatSensitiveConfirmMessage(findings = []) {
|
|
82
|
+
const visible = findings.slice(0, 8).map(finding => {
|
|
83
|
+
const path = finding.path || finding.source || 'task';
|
|
84
|
+
return `${path}: ${finding.detectorId || 'sensitive'} (${finding.preview || '[REDACTED]'})`;
|
|
85
|
+
});
|
|
86
|
+
return [
|
|
87
|
+
tr('sensitiveConfirmMessage'),
|
|
88
|
+
'',
|
|
89
|
+
...visible,
|
|
90
|
+
findings.length > visible.length ? tx(`${findings.length - visible.length} more finding(s).`, `另有 ${findings.length - visible.length} 项。`) : ''
|
|
91
|
+
].filter(Boolean).join('\n');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function scanNativeMirrorSensitiveFindings() {
|
|
95
|
+
try {
|
|
96
|
+
const response = await sendBackgroundNative({
|
|
97
|
+
method: 'mirror.scanSensitive',
|
|
98
|
+
params: { projectId: getCurrentProjectId() }
|
|
99
|
+
});
|
|
100
|
+
if (response?.ok) {
|
|
101
|
+
return Array.isArray(response.result?.findings) ? response.result.findings : [];
|
|
102
|
+
}
|
|
103
|
+
return [{
|
|
104
|
+
detectorId: 'mirror-sensitive-scan-unavailable',
|
|
105
|
+
source: 'local-mirror',
|
|
106
|
+
preview: response?.error?.message || 'Could not scan the local mirror before Codex runs.'
|
|
107
|
+
}];
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return [{
|
|
110
|
+
detectorId: 'mirror-sensitive-scan-unavailable',
|
|
111
|
+
source: 'local-mirror',
|
|
112
|
+
preview: error?.message || 'Could not scan the local mirror before Codex runs.'
|
|
113
|
+
}];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function dedupeSensitiveFindings(findings = []) {
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
const deduped = [];
|
|
120
|
+
for (const finding of findings || []) {
|
|
121
|
+
const key = [
|
|
122
|
+
finding?.detectorId || '',
|
|
123
|
+
finding?.source || '',
|
|
124
|
+
finding?.path || '',
|
|
125
|
+
finding?.preview || ''
|
|
126
|
+
].join('\u0000');
|
|
127
|
+
if (seen.has(key)) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
seen.add(key);
|
|
131
|
+
deduped.push(finding);
|
|
132
|
+
}
|
|
133
|
+
return deduped;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { run: runSensitivePreflight };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { create };
|
|
140
|
+
});
|
|
@@ -7,15 +7,32 @@
|
|
|
7
7
|
|
|
8
8
|
function create(deps = {}) {
|
|
9
9
|
const {
|
|
10
|
-
SessionState, getState, setState, sendBackgroundNative, saveState,
|
|
10
|
+
SessionState, getState, setState, getScope, sendBackgroundNative, saveState,
|
|
11
11
|
applyStateToPanel, showPluginToast, tr
|
|
12
12
|
} = deps;
|
|
13
13
|
|
|
14
14
|
async function forkRunFromNode(runId) {
|
|
15
15
|
const previousState = getState();
|
|
16
|
+
const skillPreferences = {
|
|
17
|
+
loadCodexLocalSkills: previousState.loadCodexLocalSkills !== false,
|
|
18
|
+
loadCodexOverleafSkills: previousState.loadCodexOverleafSkills !== false,
|
|
19
|
+
codexLocalSkillEnabled: { ...previousState.codexLocalSkillEnabled }
|
|
20
|
+
};
|
|
16
21
|
const source = findSource(previousState, runId);
|
|
17
22
|
if (!source) return failFork('session_fork_source_missing', tr('forkRunUnavailable'));
|
|
18
|
-
const
|
|
23
|
+
const scope = getScope ? { ...getScope() } : null;
|
|
24
|
+
const requestedActiveId = previousState.activeSessionId;
|
|
25
|
+
// The Native request and copied history must describe the same origin,
|
|
26
|
+
// even when the source session is edited while the request is pending.
|
|
27
|
+
const { session, run, runIndex } = JSON.parse(JSON.stringify(source));
|
|
28
|
+
const sameScope = () => {
|
|
29
|
+
const current = getScope?.();
|
|
30
|
+
return !scope || ['accountScopeId', 'projectId', 'generation'].every(key => current?.[key] === scope[key]);
|
|
31
|
+
};
|
|
32
|
+
const currentSource = () => {
|
|
33
|
+
const current = findSource(getState(), runId);
|
|
34
|
+
return current?.session.id === session.id && current.run.codexTurnId === run.codexTurnId;
|
|
35
|
+
};
|
|
19
36
|
const draftThreadId = run.codexTurnId && run.interruptedDraft?.turnId === run.codexTurnId
|
|
20
37
|
? run.interruptedDraft.threadId : '';
|
|
21
38
|
// Provider switches replace the session thread, not the selected node's origin.
|
|
@@ -25,25 +42,39 @@
|
|
|
25
42
|
}
|
|
26
43
|
let response;
|
|
27
44
|
try {
|
|
45
|
+
const resolveLocalSkills = deps.captureLocalSkillIdsForFork || deps.getEnabledCodexLocalSkillIds;
|
|
46
|
+
const enabledCodexLocalSkillIds = skillPreferences.loadCodexLocalSkills
|
|
47
|
+
? captureLocalSkillIds(await resolveLocalSkills?.(skillPreferences)) : [];
|
|
48
|
+
if (!sameScope() || !currentSource()) return null;
|
|
49
|
+
if (resolveLocalSkills && !Array.isArray(enabledCodexLocalSkillIds)) {
|
|
50
|
+
throw codedError('codex_local_skill_unavailable', tr('forkRunFailed'));
|
|
51
|
+
}
|
|
28
52
|
response = await sendBackgroundNative({
|
|
29
53
|
method: 'codex.thread.fork',
|
|
30
54
|
params: {
|
|
31
55
|
threadId: sourceThreadId,
|
|
32
56
|
lastTurnId: run.codexTurnId,
|
|
33
57
|
speedTier: session.speedTier,
|
|
34
|
-
loadCodexLocalSkills:
|
|
35
|
-
loadCodexOverleafSkills:
|
|
58
|
+
loadCodexLocalSkills: skillPreferences.loadCodexLocalSkills,
|
|
59
|
+
loadCodexOverleafSkills: skillPreferences.loadCodexOverleafSkills,
|
|
60
|
+
enabledCodexLocalSkillIds
|
|
36
61
|
}
|
|
37
62
|
});
|
|
38
63
|
} catch (error) {
|
|
64
|
+
if (!sameScope() || !currentSource()) return null;
|
|
39
65
|
return failFork(error?.code || 'session_fork_failed', error?.message || tr('forkRunFailed'));
|
|
40
66
|
}
|
|
67
|
+
if (!sameScope() || !currentSource()) {
|
|
68
|
+
if (response?.ok && response.result?.threadId) await clearFork(response.result.threadId);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
41
71
|
if (!response?.ok || !response.result?.threadId) {
|
|
42
72
|
return failFork(response?.error?.code || 'session_fork_failed', response?.error?.message || tr('forkRunFailed'));
|
|
43
73
|
}
|
|
44
74
|
const copiedRuns = session.runs.slice(0, runIndex + 1).map(copyRunSnapshot);
|
|
45
75
|
const titleBase = String(session.title || copiedRuns[0]?.task || tr('newSessionFallback')).trim();
|
|
46
|
-
const
|
|
76
|
+
const mergeBase = getState();
|
|
77
|
+
const forkTitle = buildUniqueForkTitle(mergeBase.sessions, titleBase);
|
|
47
78
|
const forked = SessionState.createSession({
|
|
48
79
|
title: forkTitle,
|
|
49
80
|
titleSource: 'manual',
|
|
@@ -62,27 +93,55 @@
|
|
|
62
93
|
forkedFromSessionId: session.id,
|
|
63
94
|
forkedFromRunId: run.id
|
|
64
95
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})
|
|
96
|
+
const activeId = mergeBase.activeSessionId === requestedActiveId
|
|
97
|
+
? forked.id : mergeBase.activeSessionId;
|
|
98
|
+
const installed = SessionState.setActiveSession({
|
|
99
|
+
...mergeBase, sessions: [...(mergeBase.sessions || []), forked]
|
|
100
|
+
}, activeId);
|
|
101
|
+
const installedSnapshot = JSON.stringify(installed);
|
|
102
|
+
const forkSnapshot = JSON.stringify(forked);
|
|
103
|
+
setState(installed);
|
|
70
104
|
try {
|
|
105
|
+
// Keep the visible composer and active-session owner aligned during save.
|
|
106
|
+
applyStateToPanel();
|
|
71
107
|
await saveState();
|
|
72
108
|
} catch (error) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
109
|
+
if (sameScope()) {
|
|
110
|
+
const current = getState();
|
|
111
|
+
const currentFork = current.sessions?.find(item => item.id === forked.id);
|
|
112
|
+
// Never erase work entered into the fork after its optimistic insert.
|
|
113
|
+
if (!currentFork || JSON.stringify(currentFork) === forkSnapshot) {
|
|
114
|
+
if (currentFork) {
|
|
115
|
+
if (current === installed && JSON.stringify(current) === installedSnapshot) {
|
|
116
|
+
setState(mergeBase);
|
|
117
|
+
} else {
|
|
118
|
+
const remaining = current.sessions.filter(item => item.id !== forked.id);
|
|
119
|
+
const fallbackId = remaining.some(item => item.id === mergeBase.activeSessionId)
|
|
120
|
+
? mergeBase.activeSessionId : remaining[remaining.length - 1]?.id;
|
|
121
|
+
setState(SessionState.setActiveSession({ ...current, sessions: remaining },
|
|
122
|
+
current.activeSessionId === forked.id ? fallbackId : current.activeSessionId));
|
|
123
|
+
}
|
|
124
|
+
try { applyStateToPanel(); } catch (_renderError) { /* Preserve the original failure and finish cleanup. */ }
|
|
125
|
+
}
|
|
126
|
+
await clearFork(response.result.threadId, forked.id);
|
|
127
|
+
}
|
|
128
|
+
if (sameScope()) showPluginToast(error.message || tr('forkRunFailed'), { status: 'failed', sticky: true });
|
|
129
|
+
}
|
|
79
130
|
throw error;
|
|
80
131
|
}
|
|
81
|
-
|
|
132
|
+
// Persistence owns its captured scope. A late result cannot render a new
|
|
133
|
+
// view or delete a thread that may already have been saved in the old one.
|
|
134
|
+
if (!sameScope() || !getState().sessions?.some(item => item.id === forked.id)) return null;
|
|
82
135
|
showPluginToast(tr('forkRunDone'), { status: 'completed' });
|
|
83
136
|
return forked;
|
|
84
137
|
}
|
|
85
138
|
|
|
139
|
+
async function clearFork(threadId, sessionId) {
|
|
140
|
+
try {
|
|
141
|
+
await sendBackgroundNative({ method: 'codex.history.clearPlugin', params: { sessionId, threadId } });
|
|
142
|
+
} catch (_error) { /* Best-effort cleanup must not restore a stale view. */ }
|
|
143
|
+
}
|
|
144
|
+
|
|
86
145
|
function failFork(code, message) {
|
|
87
146
|
showPluginToast(message, { status: 'failed', sticky: true });
|
|
88
147
|
throw codedError(code, message);
|
|
@@ -91,6 +150,10 @@
|
|
|
91
150
|
return { forkRunFromNode };
|
|
92
151
|
}
|
|
93
152
|
|
|
153
|
+
function captureLocalSkillIds(value) {
|
|
154
|
+
return Array.isArray(value) ? [...value] : undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
94
157
|
function buildUniqueForkTitle(sessions = [], sourceTitle = '') {
|
|
95
158
|
const fallback = String(sourceTitle || 'New Session').trim() || 'New Session';
|
|
96
159
|
const baseTitle = fallback
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
}
|
|
202
202
|
});
|
|
203
203
|
if (!response?.ok) {
|
|
204
|
-
showPluginToast(tr('deleteSessionHistoryFailedToast', { message: response?.error?.message || '
|
|
204
|
+
showPluginToast(tr('deleteSessionHistoryFailedToast', { message: response?.error?.message || tr('deleteSessionNativeFailureDetail') }), { status: 'warning', sticky: true });
|
|
205
205
|
} else if (response.result?.skipped) {
|
|
206
206
|
showPluginToast(tr('deleteSessionNoThreadToast'), { status: 'info' });
|
|
207
207
|
} else {
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
</label>
|
|
243
243
|
<p class="codex-set-row-help" data-i18n="loadCodexLocalSkillsHelp">Pull skills from your local Codex installation.</p>
|
|
244
244
|
<button type="button" class="codex-skills-entry" data-skills-entry>
|
|
245
|
-
<span class="codex-skills-entry-label" data-i18n="
|
|
245
|
+
<span class="codex-skills-entry-label" data-i18n="skillsManageEntry">Manage skills</span>
|
|
246
246
|
<span class="codex-skills-entry-summary" data-skills-entry-summary></span>
|
|
247
247
|
<span class="codex-skills-entry-chevron" aria-hidden="true">›</span>
|
|
248
248
|
</button>
|
|
@@ -252,9 +252,9 @@
|
|
|
252
252
|
</section>
|
|
253
253
|
<section class="codex-custom-instructions-panel codex-skills-panel" data-skills-screen>
|
|
254
254
|
<div class="codex-custom-instructions-head">
|
|
255
|
-
<button type="button" data-skills-back title="Back" aria-label="Back">‹</button>
|
|
255
|
+
<button type="button" data-skills-back data-i18n-title="settingsBack" data-i18n-aria-label="settingsBack" title="Back" aria-label="Back">‹</button>
|
|
256
256
|
<div>
|
|
257
|
-
<div class="codex-custom-instructions-title" data-i18n="
|
|
257
|
+
<div class="codex-custom-instructions-title" data-i18n="localSkillsTitle">Skills</div>
|
|
258
258
|
</div>
|
|
259
259
|
<span class="codex-set-saved" data-set-saved hidden><svg class="codex-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="m3.5 8 3 3 6-6"/></svg><span data-i18n="settingsSaved">Saved</span></span>
|
|
260
260
|
</div>
|
|
@@ -266,6 +266,9 @@
|
|
|
266
266
|
</div>
|
|
267
267
|
<div class="codex-skills-divider"></div>
|
|
268
268
|
<div class="codex-local-skill-list" data-local-skill-list></div>
|
|
269
|
+
<div class="codex-skills-divider"></div>
|
|
270
|
+
<div class="codex-wb-skills-list-title codex-wb-skills-caption" data-i18n="codexLocalSkillsTitle">Local Codex skills</div>
|
|
271
|
+
<div class="codex-local-skill-list" data-codex-local-skill-list></div>
|
|
269
272
|
</section>
|
|
270
273
|
`;
|
|
271
274
|
|
|
@@ -338,6 +341,7 @@
|
|
|
338
341
|
setSkillsSummary: text => setSkillsSummary(instance, text),
|
|
339
342
|
setProviderSummary: summary => setProviderSummary(instance, summary),
|
|
340
343
|
refreshNotes: () => updateGovernanceNotes(instance),
|
|
344
|
+
refreshLocale: () => instance.workbench?.refresh(),
|
|
341
345
|
destroy: () => destroy(instance),
|
|
342
346
|
_instance: instance
|
|
343
347
|
};
|
|
@@ -243,6 +243,11 @@
|
|
|
243
243
|
projectFilter.setAttribute('aria-pressed', String(filter === 'project'));
|
|
244
244
|
closeButton.title = t('settingsWorkbenchClose');
|
|
245
245
|
closeButton.setAttribute('aria-label', t('settingsWorkbenchClose'));
|
|
246
|
+
const skillsBack = skills?.querySelector('[data-skills-back]');
|
|
247
|
+
if (skillsBack) {
|
|
248
|
+
skillsBack.title = t('settingsBack');
|
|
249
|
+
skillsBack.setAttribute('aria-label', t('settingsBack'));
|
|
250
|
+
}
|
|
246
251
|
for (const [id, tab] of navButtons) {
|
|
247
252
|
tab.hidden = !availablePages.has(id);
|
|
248
253
|
tab.setAttribute('aria-selected', String(id === active));
|
|
@@ -4,11 +4,39 @@
|
|
|
4
4
|
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
const sameScope = (a, b) => a.accountScopeId === b.accountScopeId
|
|
8
|
+
&& a.projectId === b.projectId && a.generation === b.generation;
|
|
9
|
+
|
|
10
|
+
function applySnapshot(options, sessions) {
|
|
11
|
+
const previous = options.getState(), scope = options.getScope();
|
|
12
|
+
const next = options.normalize({ ...previous, sessions }, { restoreRunningRuns: false });
|
|
13
|
+
const unchangedSnapshot = JSON.stringify(next);
|
|
14
|
+
options.setState(next);
|
|
15
|
+
try {
|
|
16
|
+
options.render();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
// A callback may replace state, edit it in place or navigate. Restore only
|
|
19
|
+
// the unchanged view owned by this failed passive application.
|
|
20
|
+
let unchanged = false;
|
|
21
|
+
try {
|
|
22
|
+
unchanged = options.getState() === next && sameScope(scope, options.getScope())
|
|
23
|
+
&& JSON.stringify(options.getState()) === unchangedSnapshot;
|
|
24
|
+
} catch (_error) { /* Unknown state must not overwrite a newer edit. */ }
|
|
25
|
+
if (unchanged) {
|
|
26
|
+
options.setState(previous);
|
|
27
|
+
// A second render can also fail. The old merge base still protects
|
|
28
|
+
// subsequent saves, including values from partially restored controls.
|
|
29
|
+
try { options.render(); } catch (_error) { /* Report the first failure. */ }
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
function install(options) {
|
|
8
|
-
const { Bridge, StorageDb, getScope, getState, isBusy,
|
|
36
|
+
const { Bridge, StorageDb, getScope, getState, isBusy, notice } = options;
|
|
37
|
+
const apply = options.apply || (sessions => applySnapshot(options, sessions));
|
|
9
38
|
let saving = 0, version = 0, pending = false, timer = null, refreshing = false, stopped = false;
|
|
10
|
-
|
|
11
|
-
&& a.projectId === b.projectId && a.generation === b.generation;
|
|
39
|
+
let retryCount = 0;
|
|
12
40
|
const signature = state => JSON.stringify([state.activeSessionId,
|
|
13
41
|
(state.sessions || []).map(session => [session.id, session.updatedAt, session.task])]);
|
|
14
42
|
function schedule(delay = 250) {
|
|
@@ -37,12 +65,21 @@
|
|
|
37
65
|
const ids = new Set(records.map(record => record.id));
|
|
38
66
|
const newLocal = (local.sessions || []).filter(session =>
|
|
39
67
|
!ids.has(session.id) && !StorageDb.wasSharedSessionStored(session.id));
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
StorageDb.adoptSharedSessionBaseline(records);
|
|
68
|
+
// Rendering may fail after only part of the composer was updated.
|
|
69
|
+
// Keep the prior merge base until the complete view accepts the data.
|
|
43
70
|
apply([...records, ...newLocal]);
|
|
71
|
+
if (stopped || !sameScope(scope, getScope())) return;
|
|
72
|
+
if (saving || version !== capturedVersion) { pending = true; return; }
|
|
73
|
+
StorageDb.adoptSharedSessionBaseline(records);
|
|
74
|
+
retryCount = 0;
|
|
44
75
|
} catch (error) {
|
|
45
76
|
notice?.(error);
|
|
77
|
+
// One retry can recover transient DOM failures. Local edits, navigation
|
|
78
|
+
// and persistent failures stop it; another notification starts afresh.
|
|
79
|
+
if (!stopped && sameScope(scope, getScope()) && version === capturedVersion
|
|
80
|
+
&& signature(getState()) === capturedSignature && retryCount < 1) {
|
|
81
|
+
retryCount++; pending = true;
|
|
82
|
+
}
|
|
46
83
|
} finally {
|
|
47
84
|
refreshing = false;
|
|
48
85
|
schedule(1000);
|
|
@@ -51,7 +88,7 @@
|
|
|
51
88
|
const unsubscribe = Bridge.subscribe(change => {
|
|
52
89
|
const scope = getScope();
|
|
53
90
|
if (!change.projectId || change.projectId === scope.projectId) {
|
|
54
|
-
pending = true; schedule();
|
|
91
|
+
retryCount = 0; pending = true; schedule();
|
|
55
92
|
}
|
|
56
93
|
});
|
|
57
94
|
return {
|
|
@@ -68,5 +105,5 @@
|
|
|
68
105
|
}
|
|
69
106
|
};
|
|
70
107
|
}
|
|
71
|
-
return { install };
|
|
108
|
+
return { install, applySnapshot };
|
|
72
109
|
});
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
let lastUserActivityAt = Date.now();
|
|
6
6
|
let stableSavedSince = 0;
|
|
7
7
|
let applying = false;
|
|
8
|
+
let saveIdentity;
|
|
8
9
|
const documentRef = options.document || root.document;
|
|
9
10
|
const noteActivity = event => {
|
|
10
|
-
if (event?.target?.closest?.('#codex-overleaf-panel')) return;
|
|
11
|
+
if (!options.includePanelActivity && event?.target?.closest?.('#codex-overleaf-panel')) return;
|
|
11
12
|
lastUserActivityAt = Date.now();
|
|
12
13
|
stableSavedSince = 0;
|
|
13
14
|
};
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
saveResult = { ok: false, state: 'unavailable' };
|
|
43
44
|
}
|
|
44
45
|
const saved = saveResult?.ok === true && ['verified_saved', 'verified_quiet'].includes(saveResult?.state);
|
|
46
|
+
if (saveResult?.identity !== saveIdentity) { stableSavedSince = 0; saveIdentity = saveResult?.identity; }
|
|
45
47
|
if (saved) {
|
|
46
48
|
stableSavedSince = stableSavedSince || Date.now();
|
|
47
49
|
} else {
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
|
|
63
65
|
return {
|
|
64
66
|
buildIdleProbe,
|
|
67
|
+
noteActivity,
|
|
65
68
|
handleMessage,
|
|
66
69
|
isApplying: () => applying
|
|
67
70
|
};
|
|
@@ -245,6 +245,7 @@
|
|
|
245
245
|
if (!currentView) return;
|
|
246
246
|
renderSettings();
|
|
247
247
|
if (!notice) return;
|
|
248
|
+
notice.setAttribute('aria-label', tx('Stable update', '稳定版更新'));
|
|
248
249
|
if (!currentView.showPanel) {
|
|
249
250
|
notice.hidden = true;
|
|
250
251
|
notice.replaceChildren();
|
|
@@ -355,7 +356,14 @@
|
|
|
355
356
|
let statusText = '';
|
|
356
357
|
let buttonText = tx('Check for updates', '检查更新');
|
|
357
358
|
|
|
358
|
-
if (
|
|
359
|
+
if (stateName === 'idle' && state.code === 'update_cooldown') {
|
|
360
|
+
const until = Number(state.cooldownUntil || 0);
|
|
361
|
+
const time = until > 0 && Number.isFinite(until) ? new Date(until).toLocaleString(getLocale() || undefined) : '';
|
|
362
|
+
statusText = tx(
|
|
363
|
+
`Update v${latestVersion} is paused after a failed installation. ${time ? `Check again after ${time}.` : 'Check again after its cooldown ends.'}`,
|
|
364
|
+
`v${latestVersion} 安装失败后已暂停更新。${time ? `请在 ${time} 后再次检查。` : '请在冷却期结束后再次检查。'}`
|
|
365
|
+
);
|
|
366
|
+
} else if (['reload_required', 'reload_tabs_required'].includes(stateName)) {
|
|
359
367
|
summaryText = tx(`Installed v${latestVersion}; running extension v${currentVersion}.`, `已安装 v${latestVersion};扩展当前运行 v${currentVersion}。`);
|
|
360
368
|
statusText = getCopy(state).detail;
|
|
361
369
|
buttonText = stateName === 'reload_required' ? tx('Reload extension', '重新加载扩展') : tx('Refresh Overleaf tabs', '刷新 Overleaf 标签页');
|
|
@@ -373,7 +381,7 @@
|
|
|
373
381
|
statusText = getCopy(state).detail;
|
|
374
382
|
buttonText = tx('Update in progress', '更新进行中');
|
|
375
383
|
} else if (stateName === 'failed' || stateName === 'rolled_back') {
|
|
376
|
-
statusText = state
|
|
384
|
+
statusText = formatUpdateMessage(state) || getCopy(state).detail;
|
|
377
385
|
buttonText = tx('Retry', '重试');
|
|
378
386
|
} else if (stateName === 'committed') {
|
|
379
387
|
summaryText = tx(`Current v${currentVersion}. Update completed successfully.`, `当前版本 v${currentVersion}。更新已成功完成。`);
|
|
@@ -443,11 +451,11 @@
|
|
|
443
451
|
const target = state.latestVersion ? 'v' + state.latestVersion : '';
|
|
444
452
|
if (state.cancelRequested) return {
|
|
445
453
|
eyebrow: tx('Cancelling update', '正在取消更新'), title: target,
|
|
446
|
-
detail: state
|
|
454
|
+
detail: formatUpdateMessage(state) || tx('Stopping this update and clearing its authorization.', '正在停止本次更新并清理更新授权。')
|
|
447
455
|
};
|
|
448
456
|
if (state.recoveryPending) return {
|
|
449
457
|
eyebrow: tx('Checking update recovery', '正在确认更新恢复状态'), title: target,
|
|
450
|
-
detail: state
|
|
458
|
+
detail: formatUpdateMessage(state) || tx('Waiting for the installed version to be confirmed.', '正在确认实际安装的版本。')
|
|
451
459
|
};
|
|
452
460
|
const blocker = blockersCopy(state.blockers?.length ? state.blockers : [state.blocker]);
|
|
453
461
|
return {
|
|
@@ -455,11 +463,11 @@
|
|
|
455
463
|
detail: tx('Reading the latest signed release.', '正在读取最新签名版本。') },
|
|
456
464
|
reload_required: {
|
|
457
465
|
eyebrow: tx('Installed, awaiting reload', '已安装,等待加载'), title: target,
|
|
458
|
-
detail: state
|
|
466
|
+
detail: formatUpdateMessage(state) || tx('The new files are installed. Reload the extension when Overleaf is saved and idle.', '新版文件已安装,请在 Overleaf 保存并空闲后重新加载扩展。')
|
|
459
467
|
},
|
|
460
468
|
reload_tabs_required: {
|
|
461
469
|
eyebrow: tx('Updated runtime is ready', '新版运行组件已就绪'), title: target,
|
|
462
|
-
detail: state
|
|
470
|
+
detail: formatUpdateMessage(state) || tx('Refresh the saved, idle Overleaf tabs to load the updated panel.', '刷新已保存且空闲的 Overleaf 标签页以加载新版面板。')
|
|
463
471
|
},
|
|
464
472
|
manual_install_required: {
|
|
465
473
|
eyebrow: tx('Reinstall needed for this update', '这次更新需要重新安装'),
|
|
@@ -509,7 +517,7 @@
|
|
|
509
517
|
failed: {
|
|
510
518
|
eyebrow: tx('Update could not continue', '更新无法继续'),
|
|
511
519
|
title: target,
|
|
512
|
-
detail: state
|
|
520
|
+
detail: formatUpdateMessage(state) || tx('Review the update details and retry.', '请查看更新详情后重试。')
|
|
513
521
|
}
|
|
514
522
|
}[state.state] || { eyebrow: '', title: '', detail: '' };
|
|
515
523
|
}
|
|
@@ -521,6 +529,45 @@
|
|
|
521
529
|
return `npm exec --yes codex-overleaf-link@${version} -- install-managed`;
|
|
522
530
|
}
|
|
523
531
|
|
|
532
|
+
function formatUpdateMessage(state = {}) {
|
|
533
|
+
const code = state.code || '';
|
|
534
|
+
const messages = {
|
|
535
|
+
update_phase_timeout: ['The update timed out and its download was stopped. Retry when the connection is available.', '更新超时,下载已停止。连接恢复后请重试。'],
|
|
536
|
+
update_not_applied: ['The update was stopped before installation. Retry when ready.', '更新已在安装前停止,可准备就绪后重试。'],
|
|
537
|
+
update_recovery_pending: ['The update result is being checked. Recovery has not been confirmed.', '正在确认更新结果,恢复状态尚未确认。'],
|
|
538
|
+
update_rolled_back: ['The previous version has been restored.', '已恢复上一版本。'],
|
|
539
|
+
update_not_managed: ['Run install-managed once to enable coordinated automatic updates.', '请先运行一次 install-managed,以启用扩展与本机服务的协调更新。'],
|
|
540
|
+
update_native_busy: ['Native Host is still processing a Codex task.', 'Native Host 仍在处理 Codex 任务。'],
|
|
541
|
+
update_revoke_too_late: ['The update is already being installed. Wait for its result.', '更新已进入安装,请等待结果。'],
|
|
542
|
+
update_cancel_unconfirmed: ['Update cancellation is unconfirmed. Check recovery before retrying.', '尚未确认更新已取消,请先检查恢复状态。'],
|
|
543
|
+
update_apply_unconfirmed: ['Update installation is unconfirmed. Check recovery before retrying.', '尚未确认更新安装结果,请先检查恢复状态。'],
|
|
544
|
+
update_rollback_unconfirmed: ['Restoring the previous version is unconfirmed. Check recovery before retrying.', '尚未确认旧版本已恢复,请先检查恢复状态。'],
|
|
545
|
+
update_reload_unavailable: ['No verified installed update is awaiting reload.', '当前没有已验证且等待重载的更新。'],
|
|
546
|
+
update_network_cancelled: ['The update download was cancelled.', '更新下载已取消。'],
|
|
547
|
+
update_apply_failed: ['The update could not be installed. Check recovery and retry.', '无法安装更新,请检查恢复状态后重试。']
|
|
548
|
+
};
|
|
549
|
+
if (messages[code]) return tx(...messages[code]);
|
|
550
|
+
if (['update_health_failed', 'update_request_failed', 'update_failed'].includes(code)) return tx('The updated components could not be confirmed. Check recovery before retrying.', '尚未确认更新后的组件正常,请先检查恢复状态。');
|
|
551
|
+
if (['update_bootstrap_health_timeout', 'update_bootstrap_reload_failed'].includes(code)) return tx('The updated startup components did not finish restarting and confirming. Check recovery before retrying.', '更新后的启动组件未能完成重启和确认,请先检查恢复状态。');
|
|
552
|
+
if (['update_bootstrap_source_mismatch', 'update_bootstrap_source_version_mismatch'].includes(code)) return tx('The installed startup files do not match a supported update source. Check recovery before retrying.', '已安装的启动文件与支持的升级来源不一致,请先检查恢复状态。');
|
|
553
|
+
if (['update_bootstrap_record_missing', 'update_bootstrap_record_invalid', 'update_bootstrap_backup_mismatch', 'update_bootstrap_already_restoring'].includes(code)) return tx('The startup recovery material could not be confirmed. The update result remains unconfirmed.', '尚未确认启动组件的恢复材料有效,更新结果仍待确认。');
|
|
554
|
+
if (['update_bootstrap_confirmation_pending', 'update_bootstrap_native_restart_required'].includes(code)) return tx('Waiting for the updated startup components and Native Host to finish restarting and confirming.', '正在等待更新后的启动组件和本机服务完成重启及确认。');
|
|
555
|
+
if (code === 'update_bootstrap_monitor_unavailable') return tx('The update recovery monitor could not start. Check recovery before retrying.', '更新恢复监护程序未能启动,请先检查恢复状态。');
|
|
556
|
+
if (['update_bootstrap_transaction_mismatch', 'update_bootstrap_transaction_invalid'].includes(code)) return tx('The startup update transaction could not be matched. Check recovery before retrying.', '未能匹配启动组件的更新事务,请先检查恢复状态。');
|
|
557
|
+
if (code.startsWith('update_bootstrap_')) return tx('The startup update package or installed files could not be verified. Check recovery before retrying.', '无法验证启动组件更新包或已安装文件,请先检查恢复状态。');
|
|
558
|
+
if (['update_reload_required', 'update_reload_busy'].includes(code)) {
|
|
559
|
+
const match = /^Reload is waiting for Overleaf to be saved and idle \(([^)]+)\)\.$/.exec(state.message || '');
|
|
560
|
+
if (match) return blockersCopy(match[1].split(', '));
|
|
561
|
+
}
|
|
562
|
+
if (['update_check_timeout', 'update_activation_timeout', 'update_status_timeout'].includes(code)) return tx('The update operation timed out. Check recovery or retry when the connection is available.', '更新操作超时,请检查恢复状态,或在连接恢复后重试。');
|
|
563
|
+
if (['update_candidate_missing', 'update_candidate_stale', 'update_not_staged', 'update_staged_archive_invalid', 'update_recovery_superseded'].includes(code)) return tx('No current verified update is ready. Check for updates again.', '当前没有已验证且可安装的更新,请重新检查更新。');
|
|
564
|
+
if (['update_authorization_in_progress', 'update_transaction_in_progress', 'update_transaction_locked'].includes(code)) return tx('Another update operation is in progress. Wait for it to finish.', '另一项更新操作正在进行,请等待其完成。');
|
|
565
|
+
if (['update_authorization_invalid', 'update_consent_required', 'update_consent_mismatch', 'update_transaction_mismatch', 'update_transaction_state_changed'].includes(code)) return tx('The update authorization or transaction changed. Check for updates and authorize the current candidate again.', '更新授权或事务已变化,请重新检查更新并确认当前候选版本。');
|
|
566
|
+
if (['update_archive_checksum_invalid', 'update_archive_compressed_limit', 'update_archive_duplicate_path', 'update_archive_file_count_limit', 'update_archive_file_limit', 'update_archive_invalid', 'update_archive_path_forbidden', 'update_archive_path_too_long', 'update_archive_required_file_missing', 'update_archive_total_limit', 'update_archive_truncated', 'update_archive_type_forbidden', 'update_staging_not_empty', 'update_operation_invalid', 'update_bundle_hash_invalid', 'update_bundle_name_invalid', 'update_bundle_size_invalid', 'update_manifest_artifacts_invalid', 'update_manifest_commit_invalid', 'update_manifest_invalid', 'update_manifest_manual_install_invalid', 'update_manifest_origin_mismatch', 'update_manifest_protocol_invalid', 'update_manifest_schema_unsupported', 'update_manifest_timestamp_invalid', 'update_manifest_too_large', 'update_manifest_version_invalid', 'update_signature_algorithm_unsupported', 'update_signature_invalid', 'update_signature_key_invalid', 'update_signature_key_unknown', 'update_signature_too_large', 'update_version_invalid'].includes(code)) return tx('The signed update or archive could not be verified. Check the release details and retry.', '无法验证更新签名或压缩包,请检查发布详情后重试。');
|
|
567
|
+
if (['update_github_http_error', 'update_github_response_invalid', 'update_asset_missing', 'update_asset_url_forbidden', 'update_release_asset_invalid', 'update_release_manifest_mismatch', 'update_bundle_size_mismatch', 'update_bundle_hash_mismatch', 'update_native_version_mismatch', 'update_extension_version_mismatch', 'update_runtime_manifest_invalid', 'update_runtime_asset_missing', 'update_bootstrap_manifest_invalid', 'update_managed_layout_invalid', 'update_marker_invalid', 'update_current_version_invalid', 'update_health_version_mismatch', 'update_confirmation_unexpected', 'update_rollback_unavailable'].includes(code)) return tx('The update or installed files could not be verified. Check the update details and repair or retry.', '无法验证更新或已安装文件,请检查更新详情后修复或重试。');
|
|
568
|
+
return state.message || '';
|
|
569
|
+
}
|
|
570
|
+
|
|
524
571
|
function blockerCopy(value) {
|
|
525
572
|
return {
|
|
526
573
|
unsaved: tx('Overleaf has not confirmed that this document is saved.', 'Overleaf 尚未确认当前文档已保存。'),
|
|
@@ -541,6 +588,7 @@
|
|
|
541
588
|
tab_unavailable: tx('An Overleaf tab is unavailable for the safety check.', '某个 Overleaf 标签页当前无法完成安全检查。'),
|
|
542
589
|
idle_probe_failed: tx('The Overleaf safety check failed. Reload the affected tab and retry.', 'Overleaf 安全检查失败;请刷新受影响的标签页后重试。'),
|
|
543
590
|
native_project_locked: tx('Native Host still holds a project lock.', 'Native Host 仍持有项目锁。'),
|
|
591
|
+
native_process_stop_unconfirmed: tx('A previous Codex process has not yet confirmed its exit.', '前一次 Codex 进程尚未确认退出。'),
|
|
544
592
|
native_run_active: tx('Native Host is still processing a task.', 'Native Host 仍在处理任务。'),
|
|
545
593
|
busy: tx('A component is still busy.', '某个组件仍处于忙碌状态。')
|
|
546
594
|
}[value] || tx('Waiting until every Overleaf tab is saved and idle.', '正在等待所有 Overleaf 标签页完成保存并进入空闲状态。');
|
|
@@ -555,6 +603,10 @@
|
|
|
555
603
|
}
|
|
556
604
|
|
|
557
605
|
function manualReasonText(state = {}) {
|
|
606
|
+
if (state.code === 'prerelease_promotion_requires_manual_update') {
|
|
607
|
+
return tx('The installed release candidate has the same version number as the stable release. Reinstall to use the stable release.',
|
|
608
|
+
'已安装的候选版与稳定版使用相同版本号。请重新安装以使用稳定版。');
|
|
609
|
+
}
|
|
558
610
|
const reason = state.manualReason || {};
|
|
559
611
|
return tx(reason.en || reason.zh || '', reason.zh || reason.en || '');
|
|
560
612
|
}
|
|
@@ -564,5 +616,5 @@
|
|
|
564
616
|
return /^zh\b/i.test(locale) ? chinese : english;
|
|
565
617
|
}
|
|
566
618
|
|
|
567
|
-
return Object.freeze({ mount });
|
|
619
|
+
return Object.freeze({ mount, refreshLocale: render });
|
|
568
620
|
});
|