codex-overleaf-link 2.2.1 → 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 +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +8 -6
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +17 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
(function initCodexOverleafModuleRegistry(root, factory) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const api = factory(root);
|
|
5
|
+
if (typeof module === 'object' && module.exports) {
|
|
6
|
+
module.exports = api;
|
|
7
|
+
}
|
|
8
|
+
if (root) {
|
|
9
|
+
root.CodexOverleafModuleRegistry = api;
|
|
10
|
+
}
|
|
11
|
+
})(typeof window !== 'undefined' ? window : globalThis, function moduleRegistryFactory(root) {
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const definitions = new Map();
|
|
15
|
+
const instances = new Map();
|
|
16
|
+
|
|
17
|
+
function define(name, dependencies, factory) {
|
|
18
|
+
name = normalizeName(name);
|
|
19
|
+
if (definitions.has(name) || instances.has(name)) {
|
|
20
|
+
throw new Error(`Codex Overleaf module is already defined: ${name}`);
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(dependencies) || typeof factory !== 'function') {
|
|
23
|
+
throw new TypeError(`Codex Overleaf module ${name} has an invalid definition.`);
|
|
24
|
+
}
|
|
25
|
+
definitions.set(name, {
|
|
26
|
+
dependencies: dependencies.map(normalizeName),
|
|
27
|
+
factory
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(root, `CodexOverleaf${name}`, {
|
|
30
|
+
configurable: true,
|
|
31
|
+
enumerable: false,
|
|
32
|
+
get() {
|
|
33
|
+
return resolve(name);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolve(name, stack = []) {
|
|
39
|
+
name = normalizeName(name);
|
|
40
|
+
if (instances.has(name)) {
|
|
41
|
+
return instances.get(name);
|
|
42
|
+
}
|
|
43
|
+
const definition = definitions.get(name);
|
|
44
|
+
if (!definition) {
|
|
45
|
+
const legacyValue = readLegacyValue(name);
|
|
46
|
+
if (legacyValue) {
|
|
47
|
+
return legacyValue;
|
|
48
|
+
}
|
|
49
|
+
throw moduleError('content_dependency_missing', `Codex Overleaf module is not defined: ${name}`, {
|
|
50
|
+
moduleName: name,
|
|
51
|
+
resolutionStack: stack
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (stack.includes(name)) {
|
|
55
|
+
const cycle = [...stack.slice(stack.indexOf(name)), name];
|
|
56
|
+
throw moduleError('content_dependency_cycle', `Codex Overleaf module dependency cycle: ${cycle.join(' -> ')}`, {
|
|
57
|
+
cycle
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const nextStack = [...stack, name];
|
|
61
|
+
const args = definition.dependencies.map(dependency => resolve(dependency, nextStack));
|
|
62
|
+
const value = definition.factory(...args);
|
|
63
|
+
if (!value) {
|
|
64
|
+
throw moduleError('content_dependency_invalid_export', `Codex Overleaf module returned no export: ${name}`, {
|
|
65
|
+
moduleName: name
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
instances.set(name, value);
|
|
69
|
+
definitions.delete(name);
|
|
70
|
+
Object.defineProperty(root, `CodexOverleaf${name}`, {
|
|
71
|
+
configurable: true,
|
|
72
|
+
enumerable: false,
|
|
73
|
+
value,
|
|
74
|
+
writable: false
|
|
75
|
+
});
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveAll(names) {
|
|
80
|
+
const resolved = {};
|
|
81
|
+
const failures = [];
|
|
82
|
+
for (const name of names || []) {
|
|
83
|
+
try {
|
|
84
|
+
resolved[name] = resolve(name);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
failures.push(error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (failures.length) {
|
|
90
|
+
const error = moduleError(
|
|
91
|
+
'content_dependency_resolution_failed',
|
|
92
|
+
failures.map(item => item.message).join('; '),
|
|
93
|
+
{ failures }
|
|
94
|
+
);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
return resolved;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function readLegacyValue(name) {
|
|
101
|
+
const descriptor = Object.getOwnPropertyDescriptor(root, `CodexOverleaf${name}`);
|
|
102
|
+
if (!descriptor || typeof descriptor.get === 'function') {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return descriptor.value || null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function normalizeName(value) {
|
|
109
|
+
const name = String(value || '').trim();
|
|
110
|
+
if (!/^[A-Z][A-Za-z0-9_$]*$/.test(name)) {
|
|
111
|
+
throw new TypeError(`Invalid Codex Overleaf module name: ${name || '(empty)'}`);
|
|
112
|
+
}
|
|
113
|
+
return name;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function moduleError(code, message, detail) {
|
|
117
|
+
return Object.assign(new Error(message), { code, detail });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
define,
|
|
122
|
+
resolve,
|
|
123
|
+
resolveAll
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
(function initCodexOverleafNativeCompatibilityController(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafNativeCompatibilityController = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function nativeCompatibilityControllerFactory() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function create(options = {}) {
|
|
9
|
+
const Compatibility = options.compatibility;
|
|
10
|
+
const nativeChannel = options.nativeChannel;
|
|
11
|
+
const gatedMethods = options.gatedMethods || new Set();
|
|
12
|
+
const tx = options.tx;
|
|
13
|
+
const tr = options.tr;
|
|
14
|
+
|
|
15
|
+
function sendNative(payload) {
|
|
16
|
+
return dispatch(payload, 'sendNative');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sendBackgroundNative(payload) {
|
|
20
|
+
return dispatch(payload, 'sendBackgroundNative');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function dispatch(payload, channelMethod) {
|
|
24
|
+
const compatibilityGate = await ensureForMethod(payload?.method);
|
|
25
|
+
if (!compatibilityGate.ok) return compatibilityGate.response;
|
|
26
|
+
if (gatedMethods.has(payload?.method)) options.throwIfCancellationRequested?.();
|
|
27
|
+
return nativeChannel[channelMethod](
|
|
28
|
+
attachEvidence(payload, compatibilityGate.compatibility)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function ensureForMethod(method) {
|
|
33
|
+
const metadata = options.getExtensionCompatibilityMetadata?.() || {};
|
|
34
|
+
const params = Compatibility?.buildBridgePingParams
|
|
35
|
+
? Compatibility.buildBridgePingParams(metadata)
|
|
36
|
+
: {};
|
|
37
|
+
const response = await nativeChannel.sendBackgroundNative({
|
|
38
|
+
method: 'bridge.ping',
|
|
39
|
+
params
|
|
40
|
+
});
|
|
41
|
+
const compatibility = Compatibility?.evaluateNativeCompatibility
|
|
42
|
+
? Compatibility.evaluateNativeCompatibility(response, metadata)
|
|
43
|
+
: options.fallbackNativeCompatibility(response);
|
|
44
|
+
const allowed = !gatedMethods.has(method) ||
|
|
45
|
+
(Compatibility?.isNativeMethodAllowed
|
|
46
|
+
? Compatibility.isNativeMethodAllowed(method, compatibility)
|
|
47
|
+
: compatibility.status === 'ok');
|
|
48
|
+
if (allowed) return { ok: true, compatibility };
|
|
49
|
+
|
|
50
|
+
const message = formatBlockedMessage(method, compatibility);
|
|
51
|
+
const installCommand = options.installCommand;
|
|
52
|
+
const error = {
|
|
53
|
+
code: 'native_update_required',
|
|
54
|
+
message,
|
|
55
|
+
status: compatibility.status || 'unknown_native',
|
|
56
|
+
classification: options.getNativeCompatibilityClassification(compatibility),
|
|
57
|
+
installCommand: compatibility.installCommand || installCommand,
|
|
58
|
+
updateCommand: compatibility.updateCommand || compatibility.installCommand || installCommand,
|
|
59
|
+
currentNativeVersion: compatibility.currentNativeVersion
|
|
60
|
+
|| compatibility.nativeVersion
|
|
61
|
+
|| compatibility.native?.version
|
|
62
|
+
|| '',
|
|
63
|
+
requiredVersion: compatibility.requiredVersion || Compatibility?.BUILD_TARGET_VERSION || '',
|
|
64
|
+
releaseUrl: compatibility.releaseUrl || ''
|
|
65
|
+
};
|
|
66
|
+
notifyBlocked(error, compatibility);
|
|
67
|
+
return { ok: false, compatibility, response: { ok: false, error } };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function attachEvidence(payload = {}, compatibility) {
|
|
71
|
+
if (!compatibility || !gatedMethods.has(payload?.method)) return payload;
|
|
72
|
+
return {
|
|
73
|
+
...payload,
|
|
74
|
+
params: {
|
|
75
|
+
...(payload.params || {}),
|
|
76
|
+
nativeCompatibility: compatibility
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function notifyBlocked(error, compatibility = {}) {
|
|
82
|
+
const message = error?.message || String(error || '');
|
|
83
|
+
if (options.getCurrentRunView?.()) {
|
|
84
|
+
options.appendRunEvent?.({ title: message, status: 'failed' });
|
|
85
|
+
showUpdateGuidance(compatibility);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
showUpdateGuidance(compatibility);
|
|
89
|
+
options.showPluginToast?.(message, { status: 'failed', sticky: true });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function refreshBadge() {
|
|
93
|
+
const renderer = options.getPanelRendererInstance?.();
|
|
94
|
+
if (!renderer?.headerEl || !Compatibility?.evaluateNativeCompatibility) return;
|
|
95
|
+
try {
|
|
96
|
+
const metadata = options.getExtensionCompatibilityMetadata?.() || {};
|
|
97
|
+
const params = Compatibility.buildBridgePingParams
|
|
98
|
+
? Compatibility.buildBridgePingParams(metadata)
|
|
99
|
+
: {};
|
|
100
|
+
const response = await sendBackgroundNative({ method: 'bridge.ping', params });
|
|
101
|
+
const compatibility = Compatibility.evaluateNativeCompatibility(response, metadata);
|
|
102
|
+
const classification = options.getNativeCompatibilityClassification(compatibility);
|
|
103
|
+
if (classification === 'compatible') {
|
|
104
|
+
options.setBadge?.(renderer.headerEl, { type: 'none' });
|
|
105
|
+
options.setDiagnosticsHealth?.('ok');
|
|
106
|
+
try {
|
|
107
|
+
options.localStorage?.setItem('codexOverleafNativeEverOk', 'true');
|
|
108
|
+
} catch (_storageError) {}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
options.setDiagnosticsHealth?.(classification === 'update-available' ? 'warn' : 'fail');
|
|
112
|
+
options.setBadge?.(renderer.headerEl, {
|
|
113
|
+
type: 'update',
|
|
114
|
+
tooltip: tx('Native host update available', 'Native host 可更新'),
|
|
115
|
+
onClick: () => showUpdateGuidance(compatibility)
|
|
116
|
+
});
|
|
117
|
+
if (!compatibility?.native?.version) maybePromptFirstRunSetup();
|
|
118
|
+
} catch (_error) {
|
|
119
|
+
options.setDiagnosticsHealth?.('fail');
|
|
120
|
+
options.setBadge?.(renderer.headerEl, {
|
|
121
|
+
type: 'update',
|
|
122
|
+
tooltip: tx(
|
|
123
|
+
'Native host is not responding — click for setup steps',
|
|
124
|
+
'Native host 未响应——点击查看安装步骤'
|
|
125
|
+
),
|
|
126
|
+
onClick: () => showUpdateGuidance(options.fallbackNativeCompatibility({ ok: false }))
|
|
127
|
+
});
|
|
128
|
+
maybePromptFirstRunSetup();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function maybePromptFirstRunSetup() {
|
|
133
|
+
const storage = options.localStorage;
|
|
134
|
+
try {
|
|
135
|
+
if (!storage || storage.getItem('codexOverleafSetupPromptShown') === 'true') return;
|
|
136
|
+
if (storage.getItem('codexOverleafNativeEverOk') === 'true') return;
|
|
137
|
+
storage.setItem('codexOverleafSetupPromptShown', 'true');
|
|
138
|
+
} catch (_storageError) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
showUpdateGuidance(options.fallbackNativeCompatibility({ ok: false }));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function showUpdateGuidance(compatibility = {}) {
|
|
145
|
+
let panel = options.getPanel?.();
|
|
146
|
+
if (!panel) {
|
|
147
|
+
options.ensurePanelOpen?.();
|
|
148
|
+
panel = options.getPanel?.();
|
|
149
|
+
}
|
|
150
|
+
if (!panel) return;
|
|
151
|
+
panel.querySelector('[data-native-update-guidance]')?.remove();
|
|
152
|
+
const native = compatibility.native || {};
|
|
153
|
+
const extensionVersion = compatibility.extensionVersion || Compatibility?.BUILD_TARGET_VERSION || '';
|
|
154
|
+
const nativeVersion = compatibility.currentNativeVersion
|
|
155
|
+
|| compatibility.nativeVersion
|
|
156
|
+
|| native.version
|
|
157
|
+
|| tx('missing', '未安装');
|
|
158
|
+
const command = compatibility.updateCommand
|
|
159
|
+
|| compatibility.installCommand
|
|
160
|
+
|| options.installCommand;
|
|
161
|
+
const releaseUrl = compatibility.releaseUrl || '';
|
|
162
|
+
const overlay = options.document.createElement('div');
|
|
163
|
+
overlay.className = 'codex-plugin-confirm';
|
|
164
|
+
overlay.dataset.nativeUpdateGuidance = 'true';
|
|
165
|
+
overlay.setAttribute('role', 'dialog');
|
|
166
|
+
overlay.setAttribute('aria-modal', 'true');
|
|
167
|
+
const nativeMissing = !native.version;
|
|
168
|
+
overlay.setAttribute(
|
|
169
|
+
'aria-label',
|
|
170
|
+
nativeMissing
|
|
171
|
+
? tx('Set up the native host', '安装 Native host')
|
|
172
|
+
: tx('Native host update available', 'Native host 可更新')
|
|
173
|
+
);
|
|
174
|
+
const card = options.document.createElement('section');
|
|
175
|
+
card.className = 'codex-plugin-confirm-card';
|
|
176
|
+
const title = options.document.createElement('div');
|
|
177
|
+
title.className = 'codex-plugin-confirm-title';
|
|
178
|
+
title.textContent = nativeMissing
|
|
179
|
+
? tx('One step left: install the native host', '还差一步:安装 Native host')
|
|
180
|
+
: tx('Native host update available', 'Native host 可更新');
|
|
181
|
+
const body = options.document.createElement('div');
|
|
182
|
+
body.className = 'codex-plugin-confirm-body';
|
|
183
|
+
body.textContent = [
|
|
184
|
+
nativeMissing
|
|
185
|
+
? tx(
|
|
186
|
+
`Extension v${extensionVersion} is ready; Codex still needs its local bridge to read and edit this project.`,
|
|
187
|
+
`扩展 v${extensionVersion} 已就绪;Codex 还需要本地桥接程序才能读写这个项目。`
|
|
188
|
+
)
|
|
189
|
+
: tx(
|
|
190
|
+
`Extension v${extensionVersion} / Native v${nativeVersion}`,
|
|
191
|
+
`扩展 v${extensionVersion} / Native v${nativeVersion}`
|
|
192
|
+
),
|
|
193
|
+
tx(
|
|
194
|
+
'Run the platform-specific command below, reload the extension, then refresh Overleaf.',
|
|
195
|
+
'运行下面的平台命令,重新加载扩展,然后刷新 Overleaf。'
|
|
196
|
+
)
|
|
197
|
+
].join('\n\n');
|
|
198
|
+
const commandCode = options.document.createElement('code');
|
|
199
|
+
commandCode.textContent = command;
|
|
200
|
+
Object.assign(commandCode.style, {
|
|
201
|
+
display: 'block',
|
|
202
|
+
whiteSpace: 'pre-wrap',
|
|
203
|
+
marginTop: '0.75rem'
|
|
204
|
+
});
|
|
205
|
+
body.append(commandCode);
|
|
206
|
+
if (releaseUrl) {
|
|
207
|
+
const link = options.document.createElement('a');
|
|
208
|
+
link.href = releaseUrl;
|
|
209
|
+
link.target = '_blank';
|
|
210
|
+
link.rel = 'noopener noreferrer';
|
|
211
|
+
link.textContent = tx('Open GitHub Release', '打开 GitHub Release');
|
|
212
|
+
Object.assign(link.style, { display: 'block', marginTop: '0.75rem' });
|
|
213
|
+
body.append(link);
|
|
214
|
+
}
|
|
215
|
+
const actions = options.document.createElement('div');
|
|
216
|
+
actions.className = 'codex-plugin-confirm-actions';
|
|
217
|
+
const copy = options.document.createElement('button');
|
|
218
|
+
copy.type = 'button';
|
|
219
|
+
copy.className = 'codex-plugin-confirm-confirm';
|
|
220
|
+
copy.textContent = tx('Copy command', '复制命令');
|
|
221
|
+
copy.addEventListener('click', async () => {
|
|
222
|
+
await options.navigator.clipboard.writeText(command);
|
|
223
|
+
copy.textContent = tx('Copied', '已复制');
|
|
224
|
+
});
|
|
225
|
+
const close = options.document.createElement('button');
|
|
226
|
+
close.type = 'button';
|
|
227
|
+
close.className = 'codex-plugin-confirm-cancel';
|
|
228
|
+
close.textContent = tx('Close', '关闭');
|
|
229
|
+
close.addEventListener('click', () => overlay.remove());
|
|
230
|
+
actions.append(close, copy);
|
|
231
|
+
card.append(title, body, actions);
|
|
232
|
+
overlay.append(card);
|
|
233
|
+
overlay.addEventListener('click', event => {
|
|
234
|
+
if (event.target === overlay) overlay.remove();
|
|
235
|
+
});
|
|
236
|
+
panel.append(overlay);
|
|
237
|
+
copy.focus();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function scheduleFirstUseOnboardingTip() {
|
|
241
|
+
options.setTimeout(() => {
|
|
242
|
+
maybeShowFirstUseOnboardingTip().catch(() => {});
|
|
243
|
+
}, 250);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function maybeShowFirstUseOnboardingTip() {
|
|
247
|
+
const panel = options.getPanel?.();
|
|
248
|
+
if (!panel || (options.getState?.()?.sessions || []).length > 0) return;
|
|
249
|
+
const key = options.onboardingTipStorageKey;
|
|
250
|
+
const stored = await options.chromeApi.storage.local.get([key]);
|
|
251
|
+
if (stored?.[key]) return;
|
|
252
|
+
const modeSelect = panel.querySelector('[data-mode]');
|
|
253
|
+
if (!modeSelect) return;
|
|
254
|
+
const tip = options.document.createElement('div');
|
|
255
|
+
tip.className = 'codex-onboarding-tip';
|
|
256
|
+
tip.dataset.onboardingTip = 'true';
|
|
257
|
+
tip.setAttribute('role', 'status');
|
|
258
|
+
tip.textContent = tx(
|
|
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
|
+
);
|
|
262
|
+
Object.assign(tip.style, {
|
|
263
|
+
position: 'absolute',
|
|
264
|
+
zIndex: '2147483647',
|
|
265
|
+
maxWidth: '280px'
|
|
266
|
+
});
|
|
267
|
+
panel.append(tip);
|
|
268
|
+
const panelRect = panel.getBoundingClientRect();
|
|
269
|
+
const modeRect = modeSelect.getBoundingClientRect();
|
|
270
|
+
tip.style.left = `${Math.max(12, modeRect.left - panelRect.left)}px`;
|
|
271
|
+
tip.style.bottom = `${Math.max(72, panelRect.bottom - modeRect.top + 8)}px`;
|
|
272
|
+
const dismiss = () => {
|
|
273
|
+
tip.remove();
|
|
274
|
+
options.chromeApi.storage.local.set({ [key]: true }).catch?.(() => {});
|
|
275
|
+
panel.removeEventListener('click', dismiss, true);
|
|
276
|
+
panel.removeEventListener('keydown', dismiss, true);
|
|
277
|
+
};
|
|
278
|
+
panel.addEventListener('click', dismiss, true);
|
|
279
|
+
panel.addEventListener('keydown', dismiss, true);
|
|
280
|
+
options.setTimeout(dismiss, 8000);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function formatBlockedMessage(method, compatibility = {}) {
|
|
284
|
+
const params = { method: method || 'native request' };
|
|
285
|
+
switch (compatibility.status || 'unknown_native') {
|
|
286
|
+
case 'native_too_old':
|
|
287
|
+
return tr('nativeCompatibilityBlockedNativeTooOld', params);
|
|
288
|
+
case 'extension_too_old':
|
|
289
|
+
return tr('nativeCompatibilityBlockedExtensionTooOld', params);
|
|
290
|
+
case 'protocol_unsupported':
|
|
291
|
+
return tr('nativeCompatibilityBlockedProtocol', params);
|
|
292
|
+
case 'native_unhealthy':
|
|
293
|
+
return tr('nativeCompatibilityBlockedUnhealthy', params);
|
|
294
|
+
case 'native_missing':
|
|
295
|
+
return tr('nativeCompatibilityBlockedMissing', params);
|
|
296
|
+
default:
|
|
297
|
+
return tr('nativeCompatibilityBlockedGeneric', params);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return Object.freeze({
|
|
302
|
+
attachEvidence,
|
|
303
|
+
ensureForMethod,
|
|
304
|
+
refreshBadge,
|
|
305
|
+
scheduleFirstUseOnboardingTip,
|
|
306
|
+
sendBackgroundNative,
|
|
307
|
+
sendNative,
|
|
308
|
+
showUpdateGuidance
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return Object.freeze({ create });
|
|
313
|
+
});
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
(function initCodexOverleafOtWarmMirror() {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const otWarmMirrorController = window.CodexOverleafOtWarmMirrorController;
|
|
5
|
-
const runController = window.CodexOverleafRunController;
|
|
6
|
-
const mirrorHealth = window.CodexOverleafMirrorHealth;
|
|
7
|
-
|
|
8
4
|
// Experimental OT warm-mirror glue carved out of contentRuntime.js (v1.4.9
|
|
9
5
|
// structural-debt phase 5): the per-project enable toggle flow, the
|
|
10
6
|
// poll/flush timers and patch queue, mirror prefetch, warm-start
|
|
@@ -15,6 +11,12 @@
|
|
|
15
11
|
// runtime reads it through the exported accessors and clears it on project
|
|
16
12
|
// navigation via clearMirrorPrefetchTimer()/releaseOtWarmMirrorProject().
|
|
17
13
|
function create(deps = {}) {
|
|
14
|
+
const {
|
|
15
|
+
otWarmMirrorController,
|
|
16
|
+
runController,
|
|
17
|
+
mirrorHealth,
|
|
18
|
+
projectFiles
|
|
19
|
+
} = deps;
|
|
18
20
|
const {
|
|
19
21
|
tr,
|
|
20
22
|
tx,
|
|
@@ -853,7 +855,7 @@
|
|
|
853
855
|
project,
|
|
854
856
|
snapshotWarnings,
|
|
855
857
|
focusFiles,
|
|
856
|
-
isUsableProjectFileContent:
|
|
858
|
+
isUsableProjectFileContent: projectFiles.isUsableProjectFileContent
|
|
857
859
|
});
|
|
858
860
|
if (snapshotWarnings.blocking.length && !focusedPartialSnapshot) {
|
|
859
861
|
for (const warning of snapshotWarnings.blocking) {
|
|
@@ -866,7 +868,7 @@
|
|
|
866
868
|
applyResults: [],
|
|
867
869
|
nextStep: tr('warmMirrorStaleBlockedNextStep')
|
|
868
870
|
});
|
|
869
|
-
finishRunView(tr('warmMirrorStaleBlockedTitle'), 'failed');
|
|
871
|
+
await finishRunView(tr('warmMirrorStaleBlockedTitle'), 'failed');
|
|
870
872
|
return { ok: false };
|
|
871
873
|
}
|
|
872
874
|
if (focusedPartialSnapshot) {
|