codex-overleaf-link 1.2.0 → 1.3.0

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  <h1>Codex Overleaf Link</h1>
4
4
  <p><strong>Empower Overleaf with Codex.</strong></p>
5
5
  <p>
6
- <img src="https://img.shields.io/badge/version-1.2.0-blue" alt="version">
6
+ <img src="https://img.shields.io/badge/version-1.3.0-blue" alt="version">
7
7
  <img src="https://img.shields.io/badge/platform-macOS%20%2F%20Windows%20%2F%20Linux-lightgrey" alt="platform">
8
8
  <img src="https://img.shields.io/badge/chrome-MV3-green" alt="chrome manifest v3">
9
9
  <img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="node version">
@@ -34,10 +34,10 @@ The recommended release path is npm-first for the native host, plus Chrome's req
34
34
  1. Install or update the native host with the pinned npm package:
35
35
 
36
36
  ```bash
37
- npm exec --yes codex-overleaf-link@1.2.0 -- install-native
37
+ npm exec --yes codex-overleaf-link@1.3.0 -- install-native
38
38
  ```
39
39
 
40
- 2. Download `codex-overleaf-link-extension-v1.2.0.zip` from the [v1.2.0 GitHub Release](https://github.com/Ghqqqq/codex-overleaf-link/releases/tag/v1.2.0), then unzip it to a stable local folder.
40
+ 2. Download `codex-overleaf-link-extension-v1.3.0.zip` from the [v1.3.0 GitHub Release](https://github.com/Ghqqqq/codex-overleaf-link/releases/tag/v1.3.0), then unzip it to a stable local folder.
41
41
 
42
42
  3. Open `chrome://extensions`, enable **Developer mode**, click **Load unpacked**, and select the unzipped extension folder.
43
43
 
@@ -48,14 +48,14 @@ If you modify the extension key or load a custom build that gets a different id,
48
48
  Source installer fallback for macOS / Linux, mainly for development or source checkout installs:
49
49
 
50
50
  ```bash
51
- CODEX_OVERLEAF_REF=v1.2.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.sh)"
51
+ CODEX_OVERLEAF_REF=v1.3.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.sh)"
52
52
  ```
53
53
 
54
54
  Source installer fallback for Windows from PowerShell:
55
55
 
56
56
  ```powershell
57
- iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.ps1 -OutFile install.ps1
58
- $env:CODEX_OVERLEAF_REF='v1.2.0'
57
+ iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.ps1 -OutFile install.ps1
58
+ $env:CODEX_OVERLEAF_REF='v1.3.0'
59
59
  powershell -ExecutionPolicy Bypass -File install.ps1
60
60
  ```
61
61
 
@@ -66,19 +66,19 @@ npm installs, updates, uninstalls, and diagnoses the native host only. npm does
66
66
  Install or update the native host for the official release extension id:
67
67
 
68
68
  ```bash
69
- npm exec --yes codex-overleaf-link@1.2.0 -- install-native
69
+ npm exec --yes codex-overleaf-link@1.3.0 -- install-native
70
70
  ```
71
71
 
72
72
  Diagnose the registered native host:
73
73
 
74
74
  ```bash
75
- npm exec --yes codex-overleaf-link@1.2.0 -- doctor
75
+ npm exec --yes codex-overleaf-link@1.3.0 -- doctor
76
76
  ```
77
77
 
78
78
  Uninstall the native host:
79
79
 
80
80
  ```bash
81
- npm exec --yes codex-overleaf-link@1.2.0 -- uninstall-native
81
+ npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native
82
82
  ```
83
83
 
84
84
  Use `--extension-id <chrome-extension-id>` only for a custom/dev unpacked extension id that differs from the official bundled id.
@@ -116,10 +116,10 @@ If Chrome assigns a different extension id, rerun `npm run install:native -- --e
116
116
  <details>
117
117
  <summary><strong>Update</strong></summary>
118
118
 
119
- For a deterministic v1.2.0 update, run the pinned npm command. This is also the native mismatch recovery command shown by the popup and panel when they report **Native host update required**.
119
+ For a deterministic v1.3.0 update, run the pinned npm command. This is also the native mismatch recovery command shown by the popup and panel when they report **Native host update required**.
120
120
 
121
121
  ```bash
122
- npm exec --yes codex-overleaf-link@1.2.0 -- install-native
122
+ npm exec --yes codex-overleaf-link@1.3.0 -- install-native
123
123
  ```
124
124
 
125
125
  If npm is unavailable, use the GitHub Release script fallback for your platform.
@@ -127,14 +127,14 @@ If npm is unavailable, use the GitHub Release script fallback for your platform.
127
127
  macOS / Linux:
128
128
 
129
129
  ```bash
130
- CODEX_OVERLEAF_REF=v1.2.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.sh)"
130
+ CODEX_OVERLEAF_REF=v1.3.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.sh)"
131
131
  ```
132
132
 
133
133
  Windows PowerShell:
134
134
 
135
135
  ```powershell
136
- iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.ps1 -OutFile install.ps1
137
- $env:CODEX_OVERLEAF_REF='v1.2.0'
136
+ iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.ps1 -OutFile install.ps1
137
+ $env:CODEX_OVERLEAF_REF='v1.3.0'
138
138
  powershell -ExecutionPolicy Bypass -File install.ps1
139
139
  ```
140
140
 
@@ -144,13 +144,13 @@ Then reload the extension in `chrome://extensions` and refresh the Overleaf page
144
144
 
145
145
  ## GitHub Release Artifacts
146
146
 
147
- The v1.2.0 GitHub Release contains:
147
+ The v1.3.0 GitHub Release contains:
148
148
 
149
- - `codex-overleaf-link-extension-v1.2.0.zip`: loadable Chrome extension package for manual unpacked installation.
150
- - `codex-overleaf-native-host-v1.2.0.tar.gz`: native host runtime files used by the installer and release verification.
151
- - `codex-overleaf-link-1.2.0.tgz`: npm native host CLI package for pinned install, doctor, and uninstall flows.
152
- - `install.sh`: release-pinned macOS / Linux installer that defaults to `v1.2.0` when run directly from the release artifact.
153
- - `install.ps1`: release-pinned Windows PowerShell installer that defaults to `v1.2.0` when run directly from the release artifact.
149
+ - `codex-overleaf-link-extension-v1.3.0.zip`: loadable Chrome extension package for manual unpacked installation.
150
+ - `codex-overleaf-native-host-v1.3.0.tar.gz`: native host runtime files used by the installer and release verification.
151
+ - `codex-overleaf-link-1.3.0.tgz`: npm native host CLI package for pinned install, doctor, and uninstall flows.
152
+ - `install.sh`: release-pinned macOS / Linux installer that defaults to `v1.3.0` when run directly from the release artifact.
153
+ - `install.ps1`: release-pinned Windows PowerShell installer that defaults to `v1.3.0` when run directly from the release artifact.
154
154
  - `uninstall-native-host.mjs`: native host uninstaller that removes the Chrome Native Messaging manifest, bridge executable, and runtime copy.
155
155
  - `nativeHostPlatform.js`, `manifest.js`, `runtimeInstaller.js`: helper files required by the loose uninstaller asset.
156
156
  - `SHA256SUMS` and `release-manifest.json`: checksum and artifact metadata for release verification.
@@ -161,16 +161,16 @@ The v1.2.0 GitHub Release contains:
161
161
  macOS / Linux:
162
162
 
163
163
  ```bash
164
- node ~/.codex-overleaf/source/scripts/uninstall-native-host.mjs
164
+ npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native
165
165
  ```
166
166
 
167
167
  Windows PowerShell:
168
168
 
169
169
  ```powershell
170
- node $env:LOCALAPPDATA\CodexOverleaf\source\scripts\uninstall-native-host.mjs
170
+ npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native
171
171
  ```
172
172
 
173
- If you installed from a manual checkout, you can also run `npm run uninstall:native` inside the repo.
173
+ If you installed from a manual checkout or source installer, you can also run `npm run uninstall:native` inside the repo, use `node ~/.codex-overleaf/source/scripts/uninstall-native-host.mjs` on macOS / Linux, or use `node $env:LOCALAPPDATA\CodexOverleaf\source\scripts\uninstall-native-host.mjs` on Windows PowerShell.
174
174
 
175
175
  Remove the extension from `chrome://extensions`. Optionally delete `~/.codex-overleaf` on macOS / Linux to remove local mirrors, native runtime files, and plugin history. On Windows, `%LOCALAPPDATA%\CodexOverleaf` contains the native source, runtime, bridge, and native log, while `%USERPROFILE%\.codex-overleaf` contains project mirrors, plugin Codex home/history, and Codex Overleaf skills. Full Windows cleanup requires deleting both roots, or following [Local Data And Cleanup](#local-data-and-cleanup).
176
176
 
@@ -202,13 +202,13 @@ The uninstaller removes the Native Messaging registration, bridge executable, an
202
202
  Linux Chromium install or update:
203
203
 
204
204
  ```bash
205
- CODEX_OVERLEAF_REF=v1.2.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.sh)" -- --browser chromium
205
+ CODEX_OVERLEAF_REF=v1.3.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.sh)" -- --browser chromium
206
206
  ```
207
207
 
208
208
  Linux Chromium uninstall:
209
209
 
210
210
  ```bash
211
- node ~/.codex-overleaf/source/scripts/uninstall-native-host.mjs --browser chromium
211
+ npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native --browser chromium
212
212
  ```
213
213
 
214
214
  ## Features
@@ -335,7 +335,7 @@ Full uninstall and data deletion:
335
335
  - macOS/Linux: `rm -rf ~/.codex-overleaf ~/Codex\ Overleaf\ Link\ Extension`
336
336
  - Windows PowerShell: `Remove-Item -Recurse -Force "$env:LOCALAPPDATA\CodexOverleaf", "$env:USERPROFILE\.codex-overleaf" -ErrorAction SilentlyContinue`
337
337
 
338
- Composer attachments are turn-scoped Codex context. Limits are 8 attachments per run and 12 MiB per attachment. Attachments are staged under `.codex-overleaf-attachments` inside the mirror workspace and are ignored during writeback.
338
+ Composer attachments are turn-scoped Codex context. Limits are 8 attachments per run, 12 MiB per attachment, and 32 MiB total raw attachment size per run. Attachments are staged under `.codex-overleaf-attachments` inside the mirror workspace and are ignored during writeback.
339
339
 
340
340
  ## FAQ And Troubleshooting
341
341
 
@@ -344,7 +344,7 @@ Composer attachments are turn-scoped Codex context. Limits are 8 attachments per
344
344
  Run the pinned npm native-host installer, reload the extension in `chrome://extensions`, then refresh the Overleaf tab. This also fixes extension/native version mismatch and native protocol mismatch.
345
345
 
346
346
  ```bash
347
- npm exec --yes codex-overleaf-link@1.2.0 -- install-native
347
+ npm exec --yes codex-overleaf-link@1.3.0 -- install-native
348
348
  ```
349
349
 
350
350
  If npm is unavailable, use the GitHub Release script fallback for your platform.
@@ -352,14 +352,14 @@ If npm is unavailable, use the GitHub Release script fallback for your platform.
352
352
  macOS/Linux:
353
353
 
354
354
  ```bash
355
- CODEX_OVERLEAF_REF=v1.2.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.sh)"
355
+ CODEX_OVERLEAF_REF=v1.3.0 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.sh)"
356
356
  ```
357
357
 
358
358
  Windows PowerShell:
359
359
 
360
360
  ```powershell
361
- iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.2.0/install.ps1 -OutFile install.ps1
362
- $env:CODEX_OVERLEAF_REF='v1.2.0'
361
+ iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.0/install.ps1 -OutFile install.ps1
362
+ $env:CODEX_OVERLEAF_REF='v1.3.0'
363
363
  powershell -ExecutionPolicy Bypass -File install.ps1
364
364
  ```
365
365
 
@@ -418,8 +418,8 @@ Use this matrix for release-candidate signoff and compatibility reports. Record
418
418
  | Browser/channel/version | Google Chrome channel and version. | Google Chrome channel and version. | Google Chrome channel and version. | Chromium channel/package and version. |
419
419
  | Install mode | Manual unpacked extension from GitHub Release zip or checkout. | Manual unpacked extension from GitHub Release zip or checkout. | Manual unpacked extension from GitHub Release zip or checkout. | Manual unpacked extension from GitHub Release zip or checkout; native host installed with `--browser chromium`. |
420
420
  | Extension id | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. |
421
- | Installer/update command | `npm exec --yes codex-overleaf-link@1.2.0 -- install-native` | `npm exec --yes codex-overleaf-link@1.2.0 -- install-native` | `npm exec --yes codex-overleaf-link@1.2.0 -- install-native` | `npm exec --yes codex-overleaf-link@1.2.0 -- install-native --browser chromium` |
422
- | Uninstall command | `npm exec --yes codex-overleaf-link@1.2.0 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.2.0 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.2.0 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.2.0 -- uninstall-native --browser chromium` |
421
+ | Installer/update command | `npm exec --yes codex-overleaf-link@1.3.0 -- install-native` | `npm exec --yes codex-overleaf-link@1.3.0 -- install-native` | `npm exec --yes codex-overleaf-link@1.3.0 -- install-native` | `npm exec --yes codex-overleaf-link@1.3.0 -- install-native --browser chromium` |
422
+ | Uninstall command | `npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.3.0 -- uninstall-native --browser chromium` |
423
423
  | Manifest/registry path | `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.codex.overleaf.json` | `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.codex.overleaf` -> `%LOCALAPPDATA%\CodexOverleaf\native-host-runtime\com.codex.overleaf.json` | `~/.config/google-chrome/NativeMessagingHosts/com.codex.overleaf.json` | `~/.config/chromium/NativeMessagingHosts/com.codex.overleaf.json` |
424
424
  | Bridge/runtime/source path | Bridge `~/.codex-overleaf/codex-overleaf-bridge`; runtime `~/.codex-overleaf/native-host-runtime`; source `~/.codex-overleaf/source`. | Bridge `%LOCALAPPDATA%\CodexOverleaf\codex-overleaf-bridge.cmd`; runtime `%LOCALAPPDATA%\CodexOverleaf\native-host-runtime`; source `%LOCALAPPDATA%\CodexOverleaf\source`. | Bridge `~/.codex-overleaf/codex-overleaf-bridge`; runtime `~/.codex-overleaf/native-host-runtime`; source `~/.codex-overleaf/source`. | Bridge `~/.codex-overleaf/codex-overleaf-bridge`; runtime `~/.codex-overleaf/native-host-runtime`; source `~/.codex-overleaf/source`. |
425
425
  | Node/Git/Codex/TeX | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. |
@@ -1046,56 +1046,57 @@
1046
1046
  const key = result.reasonKey || '';
1047
1047
  const code = result.code || '';
1048
1048
  const filePath = result.reasonParams?.filePath || operation?.path || operation?.from || operation?.to || '';
1049
+ const withDebug = reason => appendSkippedReasonDebug(reason, result);
1049
1050
  if (key === 'missingBaseFile' || code === 'missing_base_file') {
1050
1051
  const target = filePath || textFor(locale, '这个文件', 'this file');
1051
- return textFor(
1052
+ return withDebug(textFor(
1052
1053
  locale,
1053
1054
  `${target} 在任务开始时没有被 Codex 读到。Codex 没有覆盖它;请刷新项目内容后重试。`,
1054
1055
  `${target} was not read when the task started. Codex did not overwrite it; refresh the project content and retry.`
1055
- );
1056
+ ));
1056
1057
  }
1057
1058
  if (key === 'staleSnapshot' || code === 'stale_snapshot') {
1058
1059
  const target = filePath || textFor(locale, '这个文件', 'this file');
1059
- return textFor(
1060
+ return withDebug(textFor(
1060
1061
  locale,
1061
1062
  `${target} 在任务执行期间被你或协作者改过,Codex 没有覆盖它。请查看差异后重试。`,
1062
1063
  `${target} changed while Codex was working, so Codex did not overwrite it. Review the diff and retry.`
1063
- );
1064
+ ));
1064
1065
  }
1065
1066
  if (key === 'stalePatchLocation') {
1066
- return textFor(
1067
+ return withDebug(textFor(
1067
1068
  locale,
1068
1069
  'Codex 要修改的位置已经无法和当前 Overleaf 内容对齐,所以没有写入。请重新运行任务。',
1069
1070
  'The edit location no longer matches the current Overleaf content, so nothing was written. Rerun the task.'
1070
- );
1071
+ ));
1071
1072
  }
1072
1073
  if (key === 'stalePatchConflict' || code === 'stale_patch_range') {
1073
- return textFor(
1074
+ return withDebug(textFor(
1074
1075
  locale,
1075
1076
  'Codex 要修改的具体位置已经被你或协作者改过,所以没有覆盖它。请查看差异后重试。',
1076
1077
  'The exact edit location was changed by you or a collaborator, so Codex did not overwrite it. Review the diff and retry.'
1077
- );
1078
+ ));
1078
1079
  }
1079
1080
  if (code === 'stale_patch') {
1080
- return textFor(
1081
+ return withDebug(textFor(
1081
1082
  locale,
1082
1083
  '这处内容已经和 Codex 读取时不同,所以没有写入。请重新运行,让 Codex 先读取你的最新 Overleaf 内容。',
1083
1084
  'This exact text changed since Codex read it, so nothing was written. Rerun after Codex reads the latest Overleaf content.'
1084
- );
1085
+ ));
1085
1086
  }
1086
1087
  if (code === 'invalid_patch') {
1087
- return textFor(
1088
+ return withDebug(textFor(
1088
1089
  locale,
1089
1090
  'Codex 生成的局部写入范围无效,所以没有写入。',
1090
1091
  'Codex produced an invalid local edit range, so nothing was written.'
1091
- );
1092
+ ));
1092
1093
  }
1093
1094
  if (code === 'write_verification_failed') {
1094
- return textFor(
1095
+ return withDebug(textFor(
1095
1096
  locale,
1096
1097
  '写入后读回内容和 Codex 预期不一致,已停止把这次操作标记为成功。请刷新 Overleaf 后重试。',
1097
1098
  'After writing, the editor content did not match Codex\'s expected result, so the write was not marked successful. Reload Overleaf and retry.'
1098
- );
1099
+ ));
1099
1100
  }
1100
1101
  if (code === 'file_tree_verification_failed') {
1101
1102
  return textFor(
@@ -1120,7 +1121,42 @@
1120
1121
  `${target} was created by you or a collaborator while Codex was working, so Codex did not overwrite it. Review the diff and retry.`
1121
1122
  );
1122
1123
  }
1123
- return localizeVisibleReason(result.reason || result.error || result.code || textFor(locale, '未知原因', 'unknown reason'), locale);
1124
+ return withDebug(localizeVisibleReason(result.reason || result.error || result.code || textFor(locale, '未知原因', 'unknown reason'), locale));
1125
+ }
1126
+
1127
+ function appendSkippedReasonDebug(reason, result = {}) {
1128
+ const debug = result.debug || result.diagnostics;
1129
+ const text = formatSkippedReasonDebug(debug);
1130
+ return text ? `${reason} [debug: ${text}]` : reason;
1131
+ }
1132
+
1133
+ function formatSkippedReasonDebug(debug) {
1134
+ if (!debug || typeof debug !== 'object') {
1135
+ return '';
1136
+ }
1137
+ const parts = [];
1138
+ const add = (key, value) => {
1139
+ if (value === undefined || value === null || value === '') {
1140
+ return;
1141
+ }
1142
+ parts.push(`${key}=${String(value)}`);
1143
+ };
1144
+ add('stage', debug.stage);
1145
+ add('rev', debug.revision);
1146
+ add('op', debug.operationPath);
1147
+ add('active', debug.activePath);
1148
+ add('initial', debug.initialActivePath);
1149
+ add('last', debug.lastActivePath);
1150
+ add('currentLen', debug.current?.length);
1151
+ add('currentNorm', debug.current?.normalizedLength);
1152
+ add('currentHash', debug.current?.hash);
1153
+ add('baseKnown', debug.baseKnown);
1154
+ add('baseLen', debug.base?.length);
1155
+ add('baseNorm', debug.base?.normalizedLength);
1156
+ add('baseHash', debug.base?.hash);
1157
+ add('elapsed', debug.elapsedMs);
1158
+ add('opened', debug.openedMethod);
1159
+ return parts.join(', ');
1124
1160
  }
1125
1161
 
1126
1162
  function localizeVisibleReason(reason, locale = 'zh') {
@@ -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.2.0';
15
+ const BUILD_TARGET_VERSION = '1.3.0';
16
16
  const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
17
17
  const REQUIRED_CAPABILITIES = Object.freeze([
18
18
  'bridgePing',
@@ -15,7 +15,6 @@
15
15
  const TEXT_EXTENSION_PATTERN = '(?:tex|bib|sty|cls|bst|bbx|cbx|lbx|cfg|def|clo|ist|txt|md|latex)';
16
16
  const REFERENCE_PREFIX_PATTERN = '(^|[\\s\\[({"\'])';
17
17
  const PATH_PATTERN = `([^\\s\\[\\](){}<>"'\`,;]+?\\.${TEXT_EXTENSION_PATTERN})`;
18
- const MARKDOWN_LINK_PATTERN = /\[([^\]]*)\]\(([^)]*)\)/g;
19
18
  const BARE_LOCAL_PATH_PATTERN = /(?:file:\/\/\/?[^\s)\]]+|[A-Za-z]:[\\/][^\s)\]]+|\/(?:Users|home|private|var|tmp)\/[^\s)\]]+|[^\s)\]]*[\\/]\.codex-overleaf[\\/]projects[\\/][^\s)\]]+)/gi;
20
19
 
21
20
  function parseLineReferencesFromText({ text, mode }) {
@@ -53,7 +52,7 @@
53
52
  return '';
54
53
  }
55
54
 
56
- let sanitized = text.replace(MARKDOWN_LINK_PATTERN, (rawMarkdown, label, target) => {
55
+ let sanitized = replaceMarkdownLinks(text, (_rawMarkdown, label, target) => {
57
56
  const sanitizedLabel = sanitizeLocalReferenceText(label, {
58
57
  projectFiles,
59
58
  placeholderBrackets: false
@@ -83,6 +82,63 @@
83
82
  return sanitized;
84
83
  }
85
84
 
85
+ function replaceMarkdownLinks(text, replacer) {
86
+ const source = String(text || '');
87
+ let output = '';
88
+ let cursor = 0;
89
+
90
+ for (let index = 0; index < source.length; index += 1) {
91
+ if (source[index] !== '[') {
92
+ continue;
93
+ }
94
+ const closeLabel = source.indexOf(']', index + 1);
95
+ if (closeLabel === -1 || source[closeLabel + 1] !== '(') {
96
+ continue;
97
+ }
98
+ const targetStart = closeLabel + 2;
99
+ const targetEnd = findMarkdownTargetEnd(source, targetStart);
100
+ if (targetEnd === -1) {
101
+ continue;
102
+ }
103
+ output += source.slice(cursor, index);
104
+ output += replacer(
105
+ source.slice(index, targetEnd + 1),
106
+ source.slice(index + 1, closeLabel),
107
+ unescapeMarkdownTarget(source.slice(targetStart, targetEnd))
108
+ );
109
+ cursor = targetEnd + 1;
110
+ index = targetEnd;
111
+ }
112
+
113
+ return output + source.slice(cursor);
114
+ }
115
+
116
+ function findMarkdownTargetEnd(text, start) {
117
+ let depth = 0;
118
+ for (let index = start; index < text.length; index += 1) {
119
+ const char = text[index];
120
+ if (char === '\\') {
121
+ index += 1;
122
+ continue;
123
+ }
124
+ if (char === '(') {
125
+ depth += 1;
126
+ continue;
127
+ }
128
+ if (char === ')') {
129
+ if (depth === 0) {
130
+ return index;
131
+ }
132
+ depth -= 1;
133
+ }
134
+ }
135
+ return -1;
136
+ }
137
+
138
+ function unescapeMarkdownTarget(value) {
139
+ return String(value || '').replace(/\\([()\\])/g, '$1');
140
+ }
141
+
86
142
  function isLocalPathLike(value) {
87
143
  if (typeof value !== 'string') {
88
144
  return false;
@@ -114,6 +170,8 @@
114
170
  return '';
115
171
  }
116
172
 
173
+ normalized = stripReferenceQueryAndHash(normalized);
174
+
117
175
  if (/^file:\/\//i.test(normalized)) {
118
176
  normalized = normalized.replace(/^file:\/\/\/?/i, '/');
119
177
  }
@@ -147,6 +205,9 @@
147
205
  }
148
206
 
149
207
  const refs = [];
208
+ if (mode === 'markdown-link-target') {
209
+ collectMarkdownTargetReferences(text, mode, refs);
210
+ }
150
211
  collectColonReferences(text, mode, refs);
151
212
  collectLineWordReferences(text, mode, refs);
152
213
  collectChineseLineReferences(text, mode, refs);
@@ -163,6 +224,28 @@
163
224
  }, []);
164
225
  }
165
226
 
227
+ function collectMarkdownTargetReferences(text, mode, refs) {
228
+ const normalizedText = stripReferenceQueryAndHash(String(text || '').trim());
229
+ const match = normalizedText.match(new RegExp(`^(.+?\\.${TEXT_EXTENSION_PATTERN}):(\\d+)(?::(\\d+))?$`, 'i'));
230
+ if (!match) {
231
+ return;
232
+ }
233
+ const line = parsePositiveInteger(match[2]);
234
+ const column = match[3] ? parsePositiveInteger(match[3]) : null;
235
+ if (!line || (match[3] && !column)) {
236
+ return;
237
+ }
238
+ refs.push({
239
+ rawText: text,
240
+ displayText: text,
241
+ rawPath: match[1],
242
+ line,
243
+ column,
244
+ source: mode,
245
+ index: 0
246
+ });
247
+ }
248
+
166
249
  function collectColonReferences(text, mode, refs) {
167
250
  const pattern = new RegExp(`${REFERENCE_PREFIX_PATTERN}${PATH_PATTERN}:(\\d+)(?::(\\d+))?(?![-:\\d])`, 'gi');
168
251
  let match;
@@ -298,6 +381,10 @@
298
381
  .some(ref => isLocalPathLike(ref.rawPath));
299
382
  }
300
383
 
384
+ function stripReferenceQueryAndHash(value) {
385
+ return String(value || '').replace(/([:.]\d+(?::\d+)?)(?:[?#].*)$/, '$1');
386
+ }
387
+
301
388
  function collectTextProjectFiles(projectFiles) {
302
389
  if (!Array.isArray(projectFiles)) {
303
390
  return [];
@@ -41,8 +41,20 @@
41
41
  }
42
42
 
43
43
  const baseContent = baseFileLookup.get(filePath);
44
+ if (!baseContent || baseContent.trim().length === 0) {
45
+ if (typeof console !== 'undefined') {
46
+ console.warn('[codex-overleaf] staleGuard: base empty for', filePath, '→ skipping check');
47
+ }
48
+ return { ok: true };
49
+ }
44
50
  const current = normalizeText(currentContent);
45
51
  if (current !== baseContent) {
52
+ if (typeof console !== 'undefined') {
53
+ console.warn('[codex-overleaf] staleGuard: STALE', filePath,
54
+ 'base.length:', baseContent.length, 'current.length:', current.length,
55
+ 'base[0..80]:', JSON.stringify(baseContent.slice(0, 80)),
56
+ 'current[0..80]:', JSON.stringify(current.slice(0, 80)));
57
+ }
46
58
  const patchRangeFreshness = checkPatchRangeFreshness(operation, current);
47
59
  if (patchRangeFreshness) {
48
60
  return patchRangeFreshness;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -19,6 +19,7 @@
19
19
  "benchmark:large": "node scripts/benchmark-large-project.mjs --gate",
20
20
  "smoke:extension": "node scripts/smoke-extension.mjs",
21
21
  "verify:release": "node scripts/verify-release.mjs",
22
+ "verify:release-artifacts": "node scripts/verify-release-artifacts.mjs",
22
23
  "build:release": "node scripts/build-release.mjs",
23
24
  "install:native": "node scripts/install-native-host.mjs",
24
25
  "uninstall:native": "node scripts/uninstall-native-host.mjs",