codex-overleaf-link 2.6.0 → 2.6.5
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 +9 -9
- package/README.zh-CN.md +9 -9
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +70 -0
- package/extension/src/content/composerAttachments.js +35 -8
- package/extension/src/content/contentRuntime.js +200 -51
- package/extension/src/content/diagnosticsController.js +77 -34
- package/extension/src/content/diagnosticsExport.js +1 -1
- package/extension/src/content/diagnosticsPanel.js +129 -42
- package/extension/src/content/generated/content.bundle.js +1283 -331
- package/extension/src/content/generated/content.bundle.meta.json +3 -3
- package/extension/src/content/localSkillsPanel.js +4 -2
- package/extension/src/content/markdownText.js +11 -9
- package/extension/src/content/modelPicker.js +28 -19
- package/extension/src/content/nativeCompatibilityController.js +23 -9
- package/extension/src/content/pageBridgeClient.js +5 -5
- package/extension/src/content/panelMaintenance.js +4 -0
- package/extension/src/content/panelRenderer.js +4 -1
- package/extension/src/content/providerSettingsDialog.js +26 -18
- package/extension/src/content/recentProjects.js +26 -16
- package/extension/src/content/runQueueScheduler.js +19 -9
- package/extension/src/content/runResultActions.js +51 -4
- package/extension/src/content/runReviewController.js +9 -3
- package/extension/src/content/scopedPersistencePanelState.js +34 -25
- package/extension/src/content/selectionContextView.js +30 -3
- package/extension/src/content/sharedSessionViewSync.js +9 -1
- package/extension/src/content/writebackOrchestrator.js +266 -50
- package/extension/src/content/writingStyleSettings.js +70 -17
- package/extension/src/page/overleafEditor.js +12 -3
- package/extension/src/page/overleafProjectSnapshot.js +56 -3
- package/extension/src/page/snapshotRouter.js +75 -66
- package/extension/src/page/treeOperations.js +200 -19
- package/extension/src/page/writebackRouter.js +152 -40
- package/extension/src/pageBridge.js +17 -7
- package/extension/src/shared/agentTranscript.js +23 -2
- package/extension/src/shared/auditRecords.js +1 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/composerAttachmentDraft.js +22 -7
- package/extension/src/shared/failureReasons.js +15 -0
- package/extension/src/shared/i18n.js +16 -0
- package/extension/src/shared/pageRpcContract.js +1 -1
- package/extension/src/shared/projectFiles.js +2 -1
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +34 -0
- package/extension/src/shared/staleGuard.js +58 -0
- package/extension/src/shared/storageMigration.js +104 -1
- package/extension/styles/panel.css +191 -63
- package/native-host/src/codexSessionRunner.js +1 -0
- package/native-host/src/debugLog.js +27 -3
- package/native-host/src/index.js +42 -11
- package/native-host/src/localSkills.js +1 -1
- package/native-host/src/mirrorWorkspace.js +47 -3
- package/native-host/src/nativeEnvironment.js +27 -5
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/skills/reviewer-response/SKILL.md +65 -0
- package/native-host/src/taskRunnerRuntime.js +11 -3
- package/native-host/src/updateManager.js +24 -6
- package/native-host/src/writingStyleRuntime.js +1 -1
- package/package.json +1 -1
|
@@ -103,7 +103,12 @@
|
|
|
103
103
|
try {
|
|
104
104
|
result = await spec.run({ collectOnly: true });
|
|
105
105
|
} catch (error) {
|
|
106
|
-
result = projectableResult(() => ({
|
|
106
|
+
result = projectableResult(() => ({
|
|
107
|
+
status: 'failed',
|
|
108
|
+
summary: tr('diagnosticsCheckErrored'),
|
|
109
|
+
nextStep: tr('runOutcomeNext'),
|
|
110
|
+
technical: error?.message || String(error)
|
|
111
|
+
}));
|
|
107
112
|
}
|
|
108
113
|
const bucket = diagnosticsHealthBucket(result?.status);
|
|
109
114
|
if (bucket === 'fail') {
|
|
@@ -118,7 +123,9 @@
|
|
|
118
123
|
status: result?.status || 'info',
|
|
119
124
|
title: tr(spec.label),
|
|
120
125
|
summary: translated?.summary || translated?.subtitle || '',
|
|
121
|
-
nextStep: bucket === 'ok' ? '' : (translated?.nextStep || '')
|
|
126
|
+
nextStep: bucket === 'ok' ? '' : (translated?.nextStep || ''),
|
|
127
|
+
installCommand: translated?.installCommand,
|
|
128
|
+
technical: translated?.technical
|
|
122
129
|
};
|
|
123
130
|
});
|
|
124
131
|
}
|
|
@@ -211,13 +218,15 @@
|
|
|
211
218
|
const codexOk = environment.codex?.ok === true;
|
|
212
219
|
const latexTools = environment.latex?.available || [];
|
|
213
220
|
const latexOk = latexTools.length > 0;
|
|
214
|
-
const
|
|
221
|
+
const facts = [
|
|
215
222
|
codexOk
|
|
216
|
-
?
|
|
217
|
-
|
|
223
|
+
? (environment.codex.path
|
|
224
|
+
? { label: 'Codex', value: environment.codex.path, kind: 'path' }
|
|
225
|
+
: { label: 'Codex', value: tx('Found on PATH', '已在 PATH 中找到') })
|
|
226
|
+
: { label: 'Codex', value: tx('Not found', '未找到'), tone: 'fail' },
|
|
218
227
|
latexOk
|
|
219
|
-
?
|
|
220
|
-
:
|
|
228
|
+
? { label: 'LaTeX', value: latexTools.join(', ') }
|
|
229
|
+
: { label: 'LaTeX', value: tx('Not found', '未找到'), tone: 'warn' }
|
|
221
230
|
];
|
|
222
231
|
if (codexOk && latexOk) {
|
|
223
232
|
return {
|
|
@@ -225,7 +234,7 @@
|
|
|
225
234
|
subtitle: tx('Codex and LaTeX are available.', 'Codex 和 LaTeX 都可以使用。'),
|
|
226
235
|
status: 'completed',
|
|
227
236
|
summary: tx('Codex is connected and local LaTeX tools are available. Codex can read, edit, and locally check this Overleaf project.', 'Codex 已连接,本机 LaTeX 工具可用。你可以让 Codex 读取、修改并本地检查 Overleaf 项目。'),
|
|
228
|
-
|
|
237
|
+
facts,
|
|
229
238
|
technical: JSON.stringify(environment, null, 2)
|
|
230
239
|
};
|
|
231
240
|
}
|
|
@@ -236,7 +245,7 @@
|
|
|
236
245
|
summary: codexOk
|
|
237
246
|
? tx('Local Codex can run, but LaTeX compile tools were not found. File edits are unaffected, but compile verification is limited.', '本机 Codex 可以运行,但没有找到 LaTeX 编译工具;修改文件不受影响,编译验证会受限。')
|
|
238
247
|
: tx('The extension reached the native host, but local Codex was not found, so tasks cannot start.', '插件已经连上 native host,但没有找到本机 Codex,所以不能启动任务。'),
|
|
239
|
-
|
|
248
|
+
facts,
|
|
240
249
|
nextStep: codexOk
|
|
241
250
|
? tx('For compile verification, make sure latexmk or pdflatex is available on your Terminal PATH.', '如需编译验证,请确认 latexmk 或 pdflatex 在终端 PATH 中可用。')
|
|
242
251
|
: tx('Make sure codex works in Terminal, then reload the extension.', '请确认终端里可以运行 codex,然后重新加载扩展。'),
|
|
@@ -281,7 +290,7 @@
|
|
|
281
290
|
subtitle: messages.subtitle,
|
|
282
291
|
status: 'failed',
|
|
283
292
|
summary: messages.summary,
|
|
284
|
-
|
|
293
|
+
facts: formatNativeCompatibilityFacts(compatibility, statusValue),
|
|
285
294
|
nextStep: messages.nextStep,
|
|
286
295
|
installCommand: compatibility.installCommand,
|
|
287
296
|
technical: formatNativeCompatibilityTechnicalDetails(compatibility, response)
|
|
@@ -320,14 +329,24 @@
|
|
|
320
329
|
}
|
|
321
330
|
}
|
|
322
331
|
|
|
323
|
-
function
|
|
332
|
+
function formatNativeCompatibilityFacts(compatibility = {}, statusValue = '') {
|
|
324
333
|
const native = compatibility.native || {};
|
|
325
334
|
return [
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
335
|
+
{
|
|
336
|
+
label: tx('Extension version', '扩展版本'),
|
|
337
|
+
value: compatibility.extensionVersion || 'unknown',
|
|
338
|
+
tone: statusValue === 'extension_too_old' ? 'fail' : undefined
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
label: tx('Native version', 'Native 版本'),
|
|
342
|
+
value: native.version || 'unknown',
|
|
343
|
+
tone: statusValue === 'native_too_old' ? 'fail' : undefined
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
label: tx('Native protocol', 'Native 协议'),
|
|
347
|
+
value: formatNativeProtocolVersion(native),
|
|
348
|
+
tone: statusValue === 'protocol_unsupported' ? 'fail' : undefined
|
|
349
|
+
}
|
|
331
350
|
];
|
|
332
351
|
}
|
|
333
352
|
|
|
@@ -397,8 +416,13 @@
|
|
|
397
416
|
const failure = mirrorState.failureActive && mirrorState.lastFailure ? { ...mirrorState.lastFailure } : null;
|
|
398
417
|
const disabledResult = projectableResult(() => {
|
|
399
418
|
const result = {
|
|
400
|
-
title: tr('diagnosticsOtTitle'),
|
|
401
|
-
|
|
419
|
+
title: tr('diagnosticsOtTitle'),
|
|
420
|
+
subtitle: tr('diagnosticsOtSubtitle'),
|
|
421
|
+
status: 'completed',
|
|
422
|
+
summary: tr('diagnosticsOtSummaryDisabled'),
|
|
423
|
+
facts: [],
|
|
424
|
+
nextStep: '',
|
|
425
|
+
technical: ''
|
|
402
426
|
};
|
|
403
427
|
if (failure) {
|
|
404
428
|
result.status = 'warning';
|
|
@@ -491,10 +515,14 @@
|
|
|
491
515
|
status: enabled && !fallback && !bridgeFailed ? 'completed' : (enabled || bridgeFailed ? 'warning' : 'completed'),
|
|
492
516
|
summary: tr(failureActive ? otWarmMirrorController.getFailureMessageKey(warmMirrorState.lastErrorCode)
|
|
493
517
|
: enabled ? 'diagnosticsOtSummaryEnabled' : 'diagnosticsOtSummaryDisabled'),
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
518
|
+
facts: [
|
|
519
|
+
{ label: tr('otStatus'), value: formatOtStatusLabel(statusValue) },
|
|
520
|
+
{ label: tr('otFreshFiles'), value: String(otFreshFileCount) },
|
|
521
|
+
{
|
|
522
|
+
label: tr('otFallback'),
|
|
523
|
+
value: fallback ? tr('yes') : tr('no'),
|
|
524
|
+
tone: enabled && fallback ? 'warn' : undefined
|
|
525
|
+
}
|
|
498
526
|
],
|
|
499
527
|
nextStep: fallback ? tr('diagnosticsOtNextStep') : '',
|
|
500
528
|
technical: formatOtDiagnosticsTechnicalDetails({
|
|
@@ -563,10 +591,23 @@
|
|
|
563
591
|
const reviewingOk = probe?.reviewing?.ok === true;
|
|
564
592
|
const writable = probe?.capabilities?.editor?.write !== false;
|
|
565
593
|
const readable = probe?.editor?.ok === true;
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
594
|
+
const reviewingNeeded = mode !== 'ask';
|
|
595
|
+
const facts = [
|
|
596
|
+
{
|
|
597
|
+
label: tx('Current file', '当前文件'),
|
|
598
|
+
value: readable ? tx('Read', '已读取') : tx('Not verified', '未确认'),
|
|
599
|
+
tone: readable ? undefined : 'warn'
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
label: tx('Write access', '写入'),
|
|
603
|
+
value: writable ? tx('Checked again before writing', '写入前会再次验证') : tx('Not writable yet', '暂不可写'),
|
|
604
|
+
tone: writable ? undefined : 'warn'
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
label: tx('Track changes', '留痕'),
|
|
608
|
+
value: reviewingOk ? tx('Verified', '已确认') : tx('Not verified', '未确认'),
|
|
609
|
+
tone: reviewingOk || !reviewingNeeded ? undefined : 'warn'
|
|
610
|
+
}
|
|
570
611
|
];
|
|
571
612
|
|
|
572
613
|
if (readable && writable) {
|
|
@@ -575,7 +616,7 @@
|
|
|
575
616
|
subtitle: formatProbeStatusBar(probe),
|
|
576
617
|
status: reviewingOk || mode === 'ask' ? 'completed' : 'warning',
|
|
577
618
|
summary: tx('The current Overleaf file can be read and written. The extension still verifies before writing to avoid overwriting new user or collaborator edits.', '当前 Overleaf 文件可以读取和写入。写入前插件仍会再次验证,避免覆盖用户或协作者的新改动。'),
|
|
578
|
-
|
|
619
|
+
facts,
|
|
579
620
|
nextStep: reviewingOk || mode === 'ask' ? '' : tx('If you need tracked writes, turn on Overleaf Reviewing/Track Changes first.', '如果需要留痕写入,请先在 Overleaf 开启 Reviewing/Track Changes。'),
|
|
580
621
|
technical: formatPageDiagnosticsTechnicalDetails(probe)
|
|
581
622
|
};
|
|
@@ -588,7 +629,7 @@
|
|
|
588
629
|
summary: readable
|
|
589
630
|
? tx('The extension read the current Overleaf file, but has not verified that the current editor can be written.', '插件已读到当前 Overleaf 文件,但还没有确认当前编辑器可以写入。')
|
|
590
631
|
: tx('The extension has not verified that the current editor can be written. Usually this means no .tex file is open yet, or Overleaf is still loading.', '插件没有确认当前编辑器可以写入。通常是还没点开 .tex 文件,或者 Overleaf 页面仍在加载。'),
|
|
591
|
-
|
|
632
|
+
facts,
|
|
592
633
|
nextStep: tx('Open the target .tex file from the left Overleaf file tree, wait for the editor to load, then check again.', '在 Overleaf 左侧文件树点开要处理的 .tex 文件,等编辑器加载完成后重新检测。'),
|
|
593
634
|
technical: formatPageDiagnosticsTechnicalDetails(probe)
|
|
594
635
|
};
|
|
@@ -602,10 +643,10 @@
|
|
|
602
643
|
const warnings = getProjectSnapshotWarnings(project);
|
|
603
644
|
const warningText = [...warnings.blocking, ...warnings.nonBlocking].map(formatProjectSnapshotWarning);
|
|
604
645
|
const source = project?.capabilities?.method || 'unknown';
|
|
605
|
-
const
|
|
606
|
-
tx(
|
|
607
|
-
tx(
|
|
608
|
-
|
|
646
|
+
const facts = [
|
|
647
|
+
{ label: tx('Text files', '文本文件'), value: tx(String(textCount), `${textCount} 个`) },
|
|
648
|
+
{ label: tx('Asset files', '资源文件'), value: tx(String(binaryCount), `${binaryCount} 个`) },
|
|
649
|
+
{ label: tx('Current file', '当前文件'), value: project?.activePath || tx('Not detected', '未识别') }
|
|
609
650
|
];
|
|
610
651
|
if (fullProject && files.length) {
|
|
611
652
|
return {
|
|
@@ -616,7 +657,8 @@
|
|
|
616
657
|
`The extension read the full Overleaf project: ${textCount} text file(s)${binaryCount ? `, ${binaryCount} asset file(s)` : ''}.`,
|
|
617
658
|
`插件已读到完整 Overleaf 项目:${textCount} 个文本文件${binaryCount ? `,${binaryCount} 个资源文件` : ''}。`
|
|
618
659
|
),
|
|
619
|
-
|
|
660
|
+
facts,
|
|
661
|
+
notes: warningText,
|
|
620
662
|
technical: formatProjectSnapshotLog(project)
|
|
621
663
|
};
|
|
622
664
|
}
|
|
@@ -630,7 +672,8 @@
|
|
|
630
672
|
`没有读到完整的 Overleaf 项目。当前只读到 ${textCount} 个文本文件${binaryCount ? `,${binaryCount} 个资源文件` : ''}。`
|
|
631
673
|
)
|
|
632
674
|
: tx('The full Overleaf project was not read, and no usable file content was available.', '没有读到完整的 Overleaf 项目,也没有拿到可用文件内容。'),
|
|
633
|
-
|
|
675
|
+
facts,
|
|
676
|
+
notes: warningText,
|
|
634
677
|
nextStep: tx('Reload Overleaf and wait for the left file tree to load, then retry. If you only want one file, use + to add @file context.', '刷新 Overleaf 页面,等左侧文件树加载完成后重试;如果只想处理一个文件,可以用 + 添加 @file 上下文。'),
|
|
635
678
|
technical: formatProjectSnapshotLog(project)
|
|
636
679
|
};
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
},
|
|
165
165
|
platform: nativeDiagnostics.platform,
|
|
166
166
|
nativeEnvironment: nativeDiagnostics.nativeEnvironment,
|
|
167
|
-
mirror,
|
|
167
|
+
mirror: mirror ?? { status: 'unavailable' },
|
|
168
168
|
auditLogs,
|
|
169
169
|
run: activeRecord?.runProjectId === projectId && matchesAccount(activeRecord, currentRunView?.runAccountScopeId)
|
|
170
170
|
? activeRecord : recentRuns[0] || {},
|
|
@@ -182,11 +182,18 @@
|
|
|
182
182
|
const details = root.querySelector('[data-diagnostics-result-details]');
|
|
183
183
|
const scroll = root.querySelector('.codex-diagnostics-result-scroll');
|
|
184
184
|
const open = details?.open, scrollTop = scroll?.scrollTop;
|
|
185
|
-
const checks = Array.from(root.querySelectorAll('.codex-diagnostics-check'), node =>
|
|
185
|
+
const checks = Array.from(root.querySelectorAll('.codex-diagnostics-check'), node => ({
|
|
186
|
+
open: node.open,
|
|
187
|
+
technicalOpen: node.querySelector('.codex-diagnostics-technical')?.open
|
|
188
|
+
}));
|
|
186
189
|
showResult(instance, result);
|
|
187
190
|
if (details) details.open = open;
|
|
188
191
|
root.querySelectorAll('.codex-diagnostics-check').forEach((node, index) => {
|
|
189
|
-
if (index < checks.length)
|
|
192
|
+
if (index < checks.length) {
|
|
193
|
+
node.open = checks[index].open;
|
|
194
|
+
const technical = node.querySelector('.codex-diagnostics-technical');
|
|
195
|
+
if (technical) technical.open = checks[index].technicalOpen;
|
|
196
|
+
}
|
|
190
197
|
});
|
|
191
198
|
if (scroll) scroll.scrollTop = scrollTop;
|
|
192
199
|
}
|
|
@@ -203,45 +210,48 @@
|
|
|
203
210
|
root.hidden = false;
|
|
204
211
|
root.dataset.status = result.status || 'info';
|
|
205
212
|
root.setAttribute('aria-label', result.title || t(instance, 'diagnosticsResult'));
|
|
206
|
-
root.querySelector('[data-diagnostics-result-title]')
|
|
213
|
+
const titleNode = root.querySelector('[data-diagnostics-result-title]');
|
|
214
|
+
const dot = document.createElement('span');
|
|
215
|
+
dot.className = 'codex-diagnostics-status-dot';
|
|
216
|
+
dot.setAttribute('aria-hidden', 'true');
|
|
217
|
+
const titleText = document.createElement('span');
|
|
218
|
+
titleText.className = 'codex-diagnostics-result-title-text';
|
|
219
|
+
titleText.textContent = result.title || t(instance, 'diagnosticsResult');
|
|
220
|
+
titleNode.replaceChildren(dot, titleText);
|
|
207
221
|
root.querySelector('[data-diagnostics-result-subtitle]').textContent = result.subtitle || '';
|
|
208
222
|
|
|
209
223
|
const body = root.querySelector('[data-diagnostics-result-body]');
|
|
210
224
|
body.textContent = '';
|
|
211
|
-
|
|
212
|
-
|
|
225
|
+
const facts = Array.isArray(result.facts) ? result.facts.filter(fact => fact?.label) : [];
|
|
226
|
+
const notes = [
|
|
227
|
+
...(Array.isArray(result.notes) ? result.notes : []),
|
|
228
|
+
...(Array.isArray(result.bullets) ? result.bullets : [])
|
|
229
|
+
].filter(Boolean);
|
|
230
|
+
const hasChecks = Array.isArray(result.checks) && result.checks.length > 0;
|
|
231
|
+
if (result.summary && !(result.status === 'completed' && facts.length)) {
|
|
232
|
+
appendParagraph(body, result.summary, 'codex-diagnostics-summary');
|
|
213
233
|
}
|
|
214
234
|
// Aggregated health report: one status row per check (Run all checks).
|
|
215
|
-
if (
|
|
216
|
-
renderCheckRows(body, result.checks);
|
|
217
|
-
} else if (!result.summary) {
|
|
235
|
+
if (hasChecks) {
|
|
236
|
+
renderCheckRows(instance, body, result.checks);
|
|
237
|
+
} else if (!result.summary && !facts.length && !notes.length) {
|
|
218
238
|
appendParagraph(body, t(instance, 'diagnosticsNoResult'));
|
|
219
239
|
}
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
list.append(li);
|
|
226
|
-
}
|
|
227
|
-
body.append(list);
|
|
240
|
+
if (facts.length) {
|
|
241
|
+
renderFacts(body, facts);
|
|
242
|
+
}
|
|
243
|
+
if (notes.length) {
|
|
244
|
+
renderNotes(body, notes);
|
|
228
245
|
}
|
|
229
246
|
if (result.nextStep) {
|
|
230
|
-
|
|
231
|
-
next.className = 'codex-diagnostics-next-step';
|
|
232
|
-
next.textContent = `${t(instance, 'nextStepPrefix')}${result.nextStep}`;
|
|
233
|
-
body.append(next);
|
|
247
|
+
appendNextStep(instance, body, result.nextStep, 'p', 'codex-diagnostics-next-step');
|
|
234
248
|
}
|
|
235
249
|
if (result.installCommand) {
|
|
236
250
|
renderInstallCommand(instance, body, result.installCommand);
|
|
237
251
|
}
|
|
238
252
|
|
|
239
253
|
const details = root.querySelector('[data-diagnostics-result-details]');
|
|
240
|
-
|
|
241
|
-
const technicalText = String(result.technical || '').trim();
|
|
242
|
-
details.open = false;
|
|
243
|
-
details.hidden = !technicalText;
|
|
244
|
-
technical.textContent = technicalText;
|
|
254
|
+
renderTechnicalDetails(details, result.technical);
|
|
245
255
|
closeMenu(instance);
|
|
246
256
|
openFloating(instance, root, 400);
|
|
247
257
|
if (wasHidden) root.querySelector('[data-diagnostics-result-close]')?.focus({ preventScroll: true });
|
|
@@ -324,14 +334,14 @@
|
|
|
324
334
|
const wrap = document.createElement('div');
|
|
325
335
|
wrap.className = 'codex-install-command';
|
|
326
336
|
|
|
327
|
-
const
|
|
337
|
+
const head = document.createElement('div');
|
|
338
|
+
head.className = 'codex-install-command-head';
|
|
339
|
+
const label = document.createElement('span');
|
|
328
340
|
label.className = 'codex-install-command-label';
|
|
329
341
|
label.textContent = t(instance, 'runInTerminal');
|
|
330
|
-
wrap.append(label);
|
|
331
342
|
|
|
332
343
|
const code = document.createElement('code');
|
|
333
344
|
code.textContent = command;
|
|
334
|
-
wrap.append(code);
|
|
335
345
|
|
|
336
346
|
const copyButton = document.createElement('button');
|
|
337
347
|
copyButton.type = 'button';
|
|
@@ -343,18 +353,86 @@
|
|
|
343
353
|
copyButton.textContent = t(instance, 'copyInstallCommand');
|
|
344
354
|
}, 1400);
|
|
345
355
|
});
|
|
346
|
-
|
|
356
|
+
head.append(label, copyButton);
|
|
357
|
+
wrap.append(head, code);
|
|
347
358
|
|
|
348
359
|
container.append(wrap);
|
|
349
360
|
}
|
|
350
361
|
|
|
351
|
-
function appendParagraph(container, text) {
|
|
362
|
+
function appendParagraph(container, text, className) {
|
|
352
363
|
const paragraph = document.createElement('p');
|
|
364
|
+
if (className) {
|
|
365
|
+
paragraph.className = className;
|
|
366
|
+
}
|
|
353
367
|
paragraph.textContent = text;
|
|
354
368
|
container.append(paragraph);
|
|
355
369
|
}
|
|
356
370
|
|
|
357
|
-
|
|
371
|
+
function appendNextStep(instance, container, text, tagName, className) {
|
|
372
|
+
const next = document.createElement(tagName);
|
|
373
|
+
next.className = className;
|
|
374
|
+
const label = document.createElement('span');
|
|
375
|
+
label.className = 'codex-diagnostics-next-label';
|
|
376
|
+
label.textContent = t(instance, 'nextStepPrefix');
|
|
377
|
+
next.append(label, document.createTextNode(text));
|
|
378
|
+
container.append(next);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Facts pair a label and value, with optional tone and path presentation.
|
|
382
|
+
function renderFacts(container, facts) {
|
|
383
|
+
const list = document.createElement('dl');
|
|
384
|
+
list.className = 'codex-diagnostics-facts';
|
|
385
|
+
for (const fact of facts) {
|
|
386
|
+
const term = document.createElement('dt');
|
|
387
|
+
term.textContent = fact.label;
|
|
388
|
+
const value = document.createElement('dd');
|
|
389
|
+
const text = String(fact.value ?? '');
|
|
390
|
+
if (fact.kind === 'path') {
|
|
391
|
+
value.textContent = shortenPath(text);
|
|
392
|
+
value.title = text;
|
|
393
|
+
} else {
|
|
394
|
+
value.textContent = text;
|
|
395
|
+
}
|
|
396
|
+
if (fact.tone) {
|
|
397
|
+
value.dataset.tone = fact.tone;
|
|
398
|
+
}
|
|
399
|
+
list.append(term, value);
|
|
400
|
+
}
|
|
401
|
+
container.append(list);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Shorten only the display; the full path stays in title and technical details.
|
|
405
|
+
function shortenPath(path) {
|
|
406
|
+
if (path.length <= 36) {
|
|
407
|
+
return path;
|
|
408
|
+
}
|
|
409
|
+
const parts = path.split('/');
|
|
410
|
+
if (parts.length < 5) {
|
|
411
|
+
return path;
|
|
412
|
+
}
|
|
413
|
+
const short = `${parts.slice(0, 3).join('/')}/…/${parts[parts.length - 1]}`;
|
|
414
|
+
return short.length < path.length ? short : path;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function renderNotes(container, notes) {
|
|
418
|
+
const list = document.createElement('ul');
|
|
419
|
+
list.className = 'codex-diagnostics-notes';
|
|
420
|
+
for (const note of notes) {
|
|
421
|
+
const item = document.createElement('li');
|
|
422
|
+
item.textContent = note;
|
|
423
|
+
list.append(item);
|
|
424
|
+
}
|
|
425
|
+
container.append(list);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function renderTechnicalDetails(details, value) {
|
|
429
|
+
const text = String(value || '').trim();
|
|
430
|
+
details.open = false;
|
|
431
|
+
details.hidden = !text;
|
|
432
|
+
details.querySelector('pre').textContent = text;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Map a result status to a coarse health bucket used for the row dot + the
|
|
358
436
|
// overall dot. 'completed' reads as a pass; anything not-ok is attention.
|
|
359
437
|
function healthBucket(status) {
|
|
360
438
|
if (status === 'completed' || status === 'ok') return 'ok';
|
|
@@ -363,10 +441,10 @@
|
|
|
363
441
|
return 'info';
|
|
364
442
|
}
|
|
365
443
|
|
|
366
|
-
// Render the aggregated report as one scannable row per check: a status
|
|
444
|
+
// Render the aggregated report as one scannable row per check: a status dot
|
|
367
445
|
// (via CSS [data-status]), a plain-language title + summary, and an optional
|
|
368
|
-
// actionable next step.
|
|
369
|
-
function renderCheckRows(container, checks) {
|
|
446
|
+
// actionable next step, install command and technical evidence.
|
|
447
|
+
function renderCheckRows(instance, container, checks) {
|
|
370
448
|
const list = document.createElement('div');
|
|
371
449
|
list.className = 'codex-diagnostics-checks';
|
|
372
450
|
for (const check of checks) {
|
|
@@ -377,16 +455,16 @@
|
|
|
377
455
|
row.open = row.dataset.status === 'warn' || row.dataset.status === 'fail';
|
|
378
456
|
const heading = document.createElement('summary');
|
|
379
457
|
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
458
|
+
const dot = document.createElement('span');
|
|
459
|
+
dot.className = 'codex-diagnostics-check-dot';
|
|
460
|
+
dot.setAttribute('aria-hidden', 'true');
|
|
383
461
|
|
|
384
462
|
const textWrap = document.createElement('div');
|
|
385
463
|
textWrap.className = 'codex-diagnostics-check-text';
|
|
386
464
|
const title = document.createElement('div');
|
|
387
465
|
title.className = 'codex-diagnostics-check-title';
|
|
388
466
|
title.textContent = check.title || '';
|
|
389
|
-
heading.append(
|
|
467
|
+
heading.append(dot, title);
|
|
390
468
|
if (check.summary) {
|
|
391
469
|
const summary = document.createElement('div');
|
|
392
470
|
summary.className = 'codex-diagnostics-check-summary';
|
|
@@ -394,10 +472,19 @@
|
|
|
394
472
|
textWrap.append(summary);
|
|
395
473
|
}
|
|
396
474
|
if (check.nextStep) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
textWrap.
|
|
475
|
+
appendNextStep(instance, textWrap, check.nextStep, 'div', 'codex-diagnostics-check-next');
|
|
476
|
+
}
|
|
477
|
+
if (check.installCommand) {
|
|
478
|
+
renderInstallCommand(instance, textWrap, check.installCommand);
|
|
479
|
+
}
|
|
480
|
+
if (check.technical) {
|
|
481
|
+
const details = document.createElement('details');
|
|
482
|
+
details.className = 'codex-diagnostics-technical';
|
|
483
|
+
const summary = document.createElement('summary');
|
|
484
|
+
summary.textContent = t(instance, 'technicalDetails');
|
|
485
|
+
details.append(summary, document.createElement('pre'));
|
|
486
|
+
renderTechnicalDetails(details, check.technical);
|
|
487
|
+
textWrap.append(details);
|
|
401
488
|
}
|
|
402
489
|
|
|
403
490
|
row.append(heading, textWrap);
|