codex-overleaf-link 1.9.5 → 1.9.7

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.
@@ -303,12 +303,32 @@
303
303
  return section;
304
304
  }
305
305
 
306
- async function sendConsentAction(action) {
307
- const type = {
306
+ function openUpdateCenter(action = '') {
307
+ const url = new URL(chrome.runtime.getURL('bootstrap/update.html'));
308
+ if (action) url.searchParams.set('action', action);
309
+ const opened = window.open(
310
+ url.href,
311
+ 'codex-overleaf-update-center',
312
+ 'popup,width=420,height=540,resizable=yes,scrollbars=yes'
313
+ );
314
+ if (opened) {
315
+ opened.focus();
316
+ return;
317
+ }
318
+ window.location.assign(url.href);
319
+ }
320
+
321
+ async function sendConsentAction(action) {
322
+ if (action === 'install' || action === 'retry' || action === 'center') {
323
+ openUpdateCenter(action === 'center' ? '' : action);
324
+ action = 'get';
325
+ }
326
+ const type = {
308
327
  get: 'codex-overleaf/consent-update-get-state',
309
328
  check: 'codex-overleaf/consent-update-check',
310
329
  install: 'codex-overleaf/consent-update-install',
311
330
  later: 'codex-overleaf/consent-update-later',
331
+ dismiss: 'codex-overleaf/consent-update-dismiss',
312
332
  retry: 'codex-overleaf/consent-update-check'
313
333
  }[action];
314
334
  const response = await chrome.runtime.sendMessage({ type });
@@ -339,8 +359,19 @@
339
359
  const state = view?.state || { state: 'idle' };
340
360
  const progress = view?.progress || { value: 0, determinate: true, phase: 'idle' };
341
361
  const copy = getUpdateCopy(state, view);
362
+ const stateName = state.state || 'idle';
363
+ const focusMode = Boolean(
364
+ options.busyAction === 'install' ||
365
+ view.execution ||
366
+ ['committed', 'rolled_back', 'failed'].includes(stateName)
367
+ );
368
+ section.classList.toggle('is-quiet', stateName === 'idle');
369
+ section.classList.toggle('is-offer', stateName === 'update_available');
370
+ section.classList.toggle('is-focus', focusMode);
371
+ section.classList.toggle('is-result', ['committed', 'rolled_back', 'failed'].includes(stateName));
372
+ document.body.classList.toggle('consent-update-focus', focusMode);
342
373
  const card = document.createElement('div');
343
- card.className = 'consent-update-card state-' + (state.state || 'idle');
374
+ card.className = 'consent-update-card state-' + stateName;
344
375
 
345
376
  const head = document.createElement('div');
346
377
  head.className = 'consent-update-head';
@@ -350,6 +381,14 @@
350
381
  version.textContent = copy.version;
351
382
  head.append(title, version);
352
383
 
384
+ const route = document.createElement('div');
385
+ route.className = 'consent-update-route';
386
+ const currentVersion = state.currentVersion ? 'v' + state.currentVersion : '';
387
+ const latestVersion = state.latestVersion ? 'v' + state.latestVersion : '';
388
+ route.textContent = currentVersion && latestVersion && currentVersion !== latestVersion
389
+ ? currentVersion + ' → ' + latestVersion
390
+ : latestVersion || currentVersion;
391
+
353
392
  const detail = document.createElement('p');
354
393
  detail.className = 'consent-update-detail';
355
394
  detail.textContent = copy.detail;
@@ -369,6 +408,9 @@
369
408
  const phase = document.createElement('div');
370
409
  phase.className = 'consent-update-phase';
371
410
  phase.textContent = copy.phase;
411
+ const showPhaseText = Boolean(copy.phase && copy.phase !== copy.detail);
412
+ const steps = createUpdateSteps(stateName);
413
+ const footnote = createUpdateFootnote(stateName);
372
414
 
373
415
  const actions = document.createElement('div');
374
416
  actions.className = 'consent-update-actions';
@@ -387,10 +429,15 @@
387
429
  live.setAttribute('aria-live', 'polite');
388
430
  live.textContent = ['committed', 'rolled_back', 'failed'].includes(state.state) ? copy.phase : '';
389
431
 
390
- card.append(head, detail);
432
+ card.append(head);
433
+ if ((focusMode || stateName === 'update_available') && route.textContent) card.append(route);
434
+ card.append(detail);
391
435
  if (!['idle', 'update_available'].includes(state.state) || options.busyAction === 'check') {
392
- card.append(progressEl, phase);
436
+ card.append(progressEl);
437
+ if (showPhaseText) card.append(phase);
393
438
  }
439
+ if (steps) card.append(steps);
440
+ if (footnote) card.append(footnote);
394
441
  card.append(actions, live);
395
442
  if (state.state === 'failed' && (state.code || state.message)) {
396
443
  const details = document.createElement('details');
@@ -407,6 +454,57 @@
407
454
  }
408
455
  }
409
456
 
457
+ function createUpdateSteps(state) {
458
+ const activeIndex = {
459
+ downloading: 0,
460
+ staged: 1,
461
+ waiting_for_idle: 1,
462
+ applying: 2,
463
+ awaiting_health: 2,
464
+ committed: 3,
465
+ rolled_back: 2
466
+ }[state];
467
+ if (!Number.isInteger(activeIndex)) return null;
468
+ const labels = [
469
+ utx('Download and verify', '下载并验证'),
470
+ utx('Wait for a safe point', '等待安全时机'),
471
+ utx('Restart and health check', '重启并健康检查')
472
+ ];
473
+ const list = document.createElement('ol');
474
+ list.className = 'consent-update-steps';
475
+ labels.forEach((label, index) => {
476
+ const item = document.createElement('li');
477
+ const status = state === 'committed'
478
+ ? 'done'
479
+ : state === 'rolled_back' && index === 2
480
+ ? 'error'
481
+ : index < activeIndex
482
+ ? 'done'
483
+ : index === activeIndex
484
+ ? 'active'
485
+ : 'pending';
486
+ item.className = 'is-' + status;
487
+ const marker = document.createElement('span');
488
+ marker.className = 'consent-update-step-marker';
489
+ marker.textContent = String(index + 1);
490
+ const text = document.createElement('span');
491
+ text.textContent = label;
492
+ item.append(marker, text);
493
+ list.append(item);
494
+ });
495
+ return list;
496
+ }
497
+
498
+ function createUpdateFootnote(state) {
499
+ if (!['downloading', 'staged', 'waiting_for_idle', 'applying', 'awaiting_health'].includes(state)) return null;
500
+ const note = document.createElement('p');
501
+ note.className = 'consent-update-footnote';
502
+ note.textContent = ['applying', 'awaiting_health'].includes(state)
503
+ ? utx('This popup may close briefly while the extension restarts.', '扩展重启时此窗口可能会短暂关闭。')
504
+ : utx('You can close this popup. The update continues in the background.', '可以关闭此窗口,更新会在后台继续。');
505
+ return note;
506
+ }
507
+
410
508
  function getVisibleActions(view = {}, busyAction = '') {
411
509
  const state = view.state?.state || 'idle';
412
510
  if (busyAction) {
@@ -422,6 +520,9 @@
422
520
  return [{ id: 'later', label: utx('Later', '稍后提醒'), primary: false }];
423
521
  }
424
522
  if (['downloading', 'applying', 'awaiting_health', 'checking'].includes(state)) return [];
523
+ if (state === 'committed') {
524
+ return [{ id: 'dismiss', label: utx('Done', '完成'), primary: true }];
525
+ }
425
526
  if (['failed', 'rolled_back'].includes(state)) {
426
527
  return [{ id: 'retry', label: utx('Retry', '重试'), primary: true }];
427
528
  }
@@ -434,6 +535,7 @@
434
535
  retry: utx('Checking...', '正在检查...'),
435
536
  install: utx('Starting update...', '正在启动更新...'),
436
537
  later: utx('Saving...', '正在保存...')
538
+ , dismiss: utx('Closing...', '正在关闭...')
437
539
  }[action] || utx('Working...', '处理中...');
438
540
  }
439
541
 
@@ -442,7 +544,7 @@
442
544
  const latest = state.latestVersion ? 'v' + state.latestVersion : '';
443
545
  const blocker = formatBlocker(state.blocker);
444
546
  const map = {
445
- idle: [utx('Stable updates', '稳定版更新'), utx('Up to date', '已是最新版'), utx('Automatically checks signed stable releases.', '自动检查经过签名验证的稳定版本。')],
547
+ idle: [utx('Updates', '更新'), utx('Up to date', '已是最新版'), utx('Signed stable channel.', '签名稳定版通道。')],
446
548
  checking: [utx('Checking for updates', '正在检查更新'), '', utx('Checking the latest signed stable release', '正在检查最新签名稳定版本')],
447
549
  update_available: [utx('Update available', '发现新版本'), latest, view.snoozed ? utx('Reminder paused. Update remains ready here.', '提醒已暂停,仍可随时从这里更新。') : utx('Signed stable update for the extension and Native Host.', '扩展与 Native Host 的签名稳定更新。')],
448
550
  downloading: [utx('Downloading update', '正在下载更新'), latest, utx('Downloading and verifying the coordinated update', '正在下载并验证协调更新')],
@@ -450,7 +552,7 @@
450
552
  waiting_for_idle: [utx('Waiting for a safe point', '正在等待安全时机'), latest, blocker],
451
553
  applying: [utx('Installing update', '正在安装更新'), latest, utx('Installing extension and Native Host', '正在安装扩展与 Native Host')],
452
554
  awaiting_health: [utx('Checking updated components', '正在检查更新后的组件'), latest, utx('Restarting and checking both components', '正在重启并检查两个组件')],
453
- committed: [utx('Update complete', '更新完成'), latest || current, utx('Extension and Native Host were updated together.', '扩展与 Native Host 已同步更新。')],
555
+ committed: [utx('Update complete', '更新完成'), latest || current, utx('Extension and Native Host are healthy.', '扩展与 Native Host 均运行正常。')],
454
556
  rolled_back: [utx('Previous version restored', '已恢复上一版本'), current, utx('Health check failed, so the previous coordinated pair was restored.', '健康检查失败,已恢复上一组协调版本。')],
455
557
  failed: [utx('Update could not continue', '更新无法继续'), latest, state.message || utx('Check the details and retry.', '请查看详情后重试。')]
456
558
  };
@@ -482,6 +584,7 @@
482
584
  const style = document.createElement('style');
483
585
  style.id = 'codex-overleaf-consent-update-styles';
484
586
  style.textContent = [
587
+ 'html { overflow:hidden; background:#171918; }',
485
588
  'body { width:344px; padding:16px; background:#171918; color:#eceeec; font-family:"Avenir Next","Segoe UI",sans-serif; }',
486
589
  'h1 { font-size:16px; letter-spacing:-.01em; }',
487
590
  '.version-status { color:#a8ada9; }',
@@ -504,6 +607,31 @@
504
607
  '.consent-update-actions button:focus-visible { outline:2px solid #79b8f2; outline-offset:2px; }',
505
608
  '.consent-update-live { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }',
506
609
  '.consent-update-card details { margin-top:10px; color:#a9aea9; font-size:11px; } .consent-update-card details code { background:#202320; color:#c9ceca; }',
610
+ 'body .consent-updates { margin-top:16px; padding:13px 0 0; border:0; border-top:1px solid #303733; border-radius:0; background:transparent; }',
611
+ 'body .consent-update-card { border:0; padding:0; background:transparent; box-shadow:none; }',
612
+ 'body .consent-updates.is-quiet .consent-update-card { display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; gap:6px 12px; }',
613
+ 'body .consent-updates.is-quiet .consent-update-detail { display:none; }',
614
+ 'body .consent-updates.is-quiet .consent-update-actions { grid-column:2; grid-row:1; margin:0; }',
615
+ 'body .consent-updates.is-quiet .consent-update-actions button { width:auto; min-width:0; padding:5px 9px; border-color:transparent; background:transparent; color:#aeb7b1; }',
616
+ 'body .consent-updates.is-offer, body .consent-updates.is-focus { padding-top:0; border-top:0; }',
617
+ 'body .consent-updates.is-offer .consent-update-card { padding:15px; border:1px solid #34433b; border-radius:12px; background:linear-gradient(145deg,#202622,#1b201d); box-shadow:0 12px 32px rgba(0,0,0,.16); }',
618
+ 'body.consent-update-focus { width:340px; min-height:340px; padding:20px; }',
619
+ 'body.consent-update-focus > .version-status, body.consent-update-focus > #open-panel, body.consent-update-focus > #status, body.consent-update-focus > .install { display:none !important; }',
620
+ 'body.consent-update-focus > h1 { margin-bottom:18px; }',
621
+ 'body.consent-update-focus .consent-updates { margin-top:0; padding:0; border:0; }',
622
+ 'body.consent-update-focus .consent-update-card { min-height:274px; padding:18px; border:1px solid #35443d; border-radius:14px; background:radial-gradient(circle at 95% 0,rgba(79,158,232,.12),transparent 42%),linear-gradient(150deg,#202522,#1a1e1c); box-shadow:0 18px 42px rgba(0,0,0,.24); }',
623
+ '.consent-update-route { margin:9px 0 0; color:#d9e6dd; font:600 13px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace; letter-spacing:-.02em; }',
624
+ '.consent-update-steps { display:grid; gap:10px; margin:16px 0 0; padding:0; list-style:none; }',
625
+ '.consent-update-steps li { display:grid; grid-template-columns:24px minmax(0,1fr); align-items:center; gap:9px; color:#8f9892; font-size:12px; }',
626
+ '.consent-update-step-marker { display:inline-flex; width:22px; height:22px; align-items:center; justify-content:center; border:1px solid #3a413d; border-radius:999px; color:#89918c; font:700 10px/1 ui-monospace,monospace; }',
627
+ '.consent-update-steps li.is-done { color:#aeb9b2; }',
628
+ '.consent-update-steps li.is-done .consent-update-step-marker { border-color:#42694f; color:#8bd79e; background:#21392a; }',
629
+ '.consent-update-steps li.is-active { color:#edf2ef; }',
630
+ '.consent-update-steps li.is-active .consent-update-step-marker { border-color:#68a8e2; color:#eef7ff; background:#2d669a; box-shadow:0 0 0 3px rgba(98,168,232,.12); }',
631
+ '.consent-update-steps li.is-error .consent-update-step-marker { border-color:#9d554f; color:#ffd9d5; background:#542b28; }',
632
+ '.consent-update-footnote { margin:15px 0 0; padding-top:12px; border-top:1px solid #303733; color:#929b95; font-size:11px; }',
633
+ 'body .consent-updates.is-result .consent-update-progress { margin-top:18px; }',
634
+ 'body .consent-updates.is-focus .consent-update-actions { margin-top:18px; }',
507
635
  '@keyframes consent-progress { 0% { transform:translateX(-110%); } 100% { transform:translateX(240%); } }',
508
636
  '@media (prefers-reduced-motion:reduce) { .consent-update-progress.is-indeterminate span { animation:none; width:65% !important; } }'
509
637
  ].join('\n');
@@ -12,7 +12,7 @@
12
12
  const MIN_NATIVE_VERSION = '1.0.0';
13
13
  const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
14
14
  const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
15
- const BUILD_TARGET_VERSION = '1.9.5';
15
+ const BUILD_TARGET_VERSION = '1.9.7';
16
16
  const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
17
17
  const REQUIRED_CAPABILITIES = Object.freeze([
18
18
  'bridgePing',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "1.9.5",
3
+ "version": "1.9.7",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",