codex-overleaf-link 2.4.0 → 2.5.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.
Files changed (327) hide show
  1. package/README.md +272 -311
  2. package/README.zh-CN.md +287 -326
  3. package/extension/bootstrap/background.js +47 -10
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +10 -16
  6. package/extension/bootstrap/update.js +25 -2
  7. package/extension/runtime-manifest.json +3 -1
  8. package/extension/src/background.js +28 -3
  9. package/extension/src/backgroundSharedSessions.js +380 -0
  10. package/extension/src/backgroundUpdateCoordinator.js +442 -379
  11. package/extension/src/content/activeTurnControl.js +8 -0
  12. package/extension/src/content/assetTransferBroker.js +186 -12
  13. package/extension/src/content/changeHistoryView.js +175 -0
  14. package/extension/src/content/composerAttachments.js +24 -22
  15. package/extension/src/content/composerPanel.js +23 -8
  16. package/extension/src/content/contentRuntime.js +572 -571
  17. package/extension/src/content/diagnosticsController.js +1 -2
  18. package/extension/src/content/diagnosticsExport.js +222 -0
  19. package/extension/src/content/diagnosticsPanel.js +48 -19
  20. package/extension/src/content/diffReviewPanel.js +8 -5
  21. package/extension/src/content/generated/content.bundle.js +8974 -1781
  22. package/extension/src/content/generated/content.bundle.meta.json +22 -3
  23. package/extension/src/content/legacyGlobalRegistry.js +19 -1
  24. package/extension/src/content/localSkillsPanel.js +42 -7
  25. package/extension/src/content/markdownDomRenderer.js +9 -4
  26. package/extension/src/content/modelPicker.js +1 -1
  27. package/extension/src/content/pageBridgeClient.js +18 -3
  28. package/extension/src/content/panelMaintenance.js +51 -120
  29. package/extension/src/content/panelRenderer.js +4 -0
  30. package/extension/src/content/pendingInputView.js +109 -76
  31. package/extension/src/content/providerSettingsCoordinator.js +17 -6
  32. package/extension/src/content/providerSettingsDialog.js +661 -108
  33. package/extension/src/content/recentProjects.js +364 -356
  34. package/extension/src/content/runActivitySummary.js +631 -0
  35. package/extension/src/content/runController.js +3 -1
  36. package/extension/src/content/runFailureNotice.js +14 -1
  37. package/extension/src/content/runGuidanceController.js +4 -3
  38. package/extension/src/content/runPresence.js +306 -0
  39. package/extension/src/content/runQueueScheduler.js +2 -1
  40. package/extension/src/content/runResultActions.js +123 -3
  41. package/extension/src/content/runTimelineView.js +194 -164
  42. package/extension/src/content/scopedPersistenceCoordinator.js +2 -1
  43. package/extension/src/content/scopedPersistencePanelState.js +45 -2
  44. package/extension/src/content/selectionContextView.js +300 -0
  45. package/extension/src/content/sessionPersistence.js +10 -2
  46. package/extension/src/content/settingsPanel.js +93 -54
  47. package/extension/src/content/settingsWorkbench.js +374 -0
  48. package/extension/src/content/sharedSessionViewSync.js +72 -0
  49. package/extension/src/content/subagentActivityView.js +195 -0
  50. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  51. package/extension/src/content/updateNotice.js +121 -50
  52. package/extension/src/content/writebackController.js +257 -0
  53. package/extension/src/content/writebackOrchestrator.js +146 -141
  54. package/extension/src/content/writebackPlan.js +231 -0
  55. package/extension/src/content/writebackRecovery.js +122 -0
  56. package/extension/src/content/writingStyleSettings.js +576 -0
  57. package/extension/src/content/writtenChangesView.js +152 -0
  58. package/extension/src/page/binaryAssetUploader.js +97 -19
  59. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  60. package/extension/src/page/referenceProjects.js +148 -0
  61. package/extension/src/page/saveState.js +58 -3
  62. package/extension/src/page/snapshotRouter.js +44 -13
  63. package/extension/src/page/textCoordinates.js +102 -0
  64. package/extension/src/page/textFileCreator.js +350 -59
  65. package/extension/src/page/trackedChangeCapture.js +42 -56
  66. package/extension/src/page/trackedChangeOwnership.js +198 -0
  67. package/extension/src/page/trackedChangeReplay.js +164 -0
  68. package/extension/src/page/trackedChangesLifecycle.js +139 -150
  69. package/extension/src/page/treeOperations.js +36 -29
  70. package/extension/src/page/writebackReceiptJournal.js +112 -0
  71. package/extension/src/page/writebackRouter.js +176 -136
  72. package/extension/src/pageBridge.js +59 -101
  73. package/extension/src/popup.js +203 -59
  74. package/extension/src/shared/agentTranscript.js +18 -4
  75. package/extension/src/shared/auditRecords.js +71 -0
  76. package/extension/src/shared/changeHistoryModel.js +94 -0
  77. package/extension/src/shared/compatibility.js +1 -1
  78. package/extension/src/shared/compileAdapter.js +42 -0
  79. package/extension/src/shared/failureReasons.js +20 -0
  80. package/extension/src/shared/globalPreferences.js +1 -1
  81. package/extension/src/shared/i18n.js +187 -65
  82. package/extension/src/shared/managedUpdateProjection.js +6 -3
  83. package/extension/src/shared/pageRpcContract.js +7 -1
  84. package/extension/src/shared/providerProfiles.js +109 -0
  85. package/extension/src/shared/runActivityModel.js +361 -0
  86. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  87. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  88. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  89. package/extension/src/shared/selectionContext.js +28 -0
  90. package/extension/src/shared/sessionState.js +97 -6
  91. package/extension/src/shared/sharedSessionBridge.js +288 -0
  92. package/extension/src/shared/storageDb.js +23 -55
  93. package/extension/src/shared/storageMigration.js +3 -0
  94. package/extension/src/shared/storageRunActions.js +54 -4
  95. package/extension/src/shared/storageValuePruning.js +40 -0
  96. package/extension/src/shared/subagentActivityModel.js +174 -0
  97. package/extension/src/shared/undoOperations.js +87 -0
  98. package/extension/src/shared/updateConsent.js +37 -8
  99. package/extension/src/shared/writebackIntent.js +62 -0
  100. package/extension/src/shared/writebackSettlement.js +29 -10
  101. package/extension/src/shared/writingStyle.js +33 -0
  102. package/extension/styles/panel.css +2136 -744
  103. package/extension/styles/popup.css +110 -0
  104. package/native-host/src/anthropicBridgeRequest.js +2 -1
  105. package/native-host/src/codexModelRecovery.js +107 -0
  106. package/native-host/src/codexPromptAssembly.js +2 -0
  107. package/native-host/src/codexProviderLaunch.js +3 -3
  108. package/native-host/src/codexRuntimeIdentity.js +1 -0
  109. package/native-host/src/codexSessionRunner.js +146 -177
  110. package/native-host/src/codexSessionTiming.js +71 -0
  111. package/native-host/src/managedInstall.js +5 -4
  112. package/native-host/src/nativeEnvironment.js +70 -0
  113. package/native-host/src/nativeQuotas.js +15 -8
  114. package/native-host/src/overleafWorkspaceRules.js +16 -0
  115. package/native-host/src/processStopBarrier.js +80 -0
  116. package/native-host/src/providerModelCatalog.js +4 -3
  117. package/native-host/src/providerProfile.js +13 -1
  118. package/native-host/src/providerReasoning.js +9 -5
  119. package/native-host/src/providerStore.js +4 -0
  120. package/native-host/src/readProgressGuard.js +84 -17
  121. package/native-host/src/releaseTransport.js +14 -5
  122. package/native-host/src/selectionScope.js +58 -0
  123. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  124. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  125. package/native-host/src/subagentBroker.js +102 -98
  126. package/native-host/src/subagentTelemetry.js +316 -0
  127. package/native-host/src/subagentWorkspace.js +103 -0
  128. package/native-host/src/subagentWorkspacePath.js +31 -0
  129. package/native-host/src/taskRunnerRuntime.js +55 -5
  130. package/native-host/src/turnAttachments.js +113 -0
  131. package/native-host/src/updateManager.js +122 -12
  132. package/native-host/src/updateNetworkControl.js +75 -0
  133. package/native-host/src/updateTrust.js +21 -2
  134. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  135. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  136. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  137. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  138. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  139. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  140. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  141. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  142. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  143. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  144. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  145. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  146. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  147. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  148. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  149. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  150. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  151. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  152. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  153. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  154. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  155. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  156. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  157. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  158. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  159. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  160. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  161. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  162. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  163. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  164. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  165. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  166. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  167. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  168. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  169. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  170. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  171. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  172. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  173. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  174. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  175. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  176. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  177. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  178. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  179. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  180. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  181. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  182. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  183. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  184. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  185. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  186. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  187. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  188. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  189. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  190. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  191. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  192. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  193. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  194. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  195. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  196. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  197. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  198. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  199. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  200. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  201. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  202. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  203. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  303. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  304. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  305. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  306. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  307. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  308. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  309. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  310. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  311. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  312. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  313. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  314. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  315. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  316. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  317. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  318. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  319. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  320. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  321. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  322. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  323. package/native-host/src/writingStyleEvidence.js +102 -0
  324. package/native-host/src/writingStylePdfWorker.js +35 -0
  325. package/native-host/src/writingStyleProfile.js +165 -0
  326. package/native-host/src/writingStyleRuntime.js +447 -0
  327. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
1
  (function initCodexOverleafTrackedChangesLifecycle(root, factory) {
2
2
  if (typeof module === 'object' && module.exports) {
3
- module.exports = factory();
3
+ module.exports = factory(require('./trackedChangeReplay'));
4
4
  } else {
5
- root.CodexOverleafTrackedChangesLifecycle = factory();
5
+ root.CodexOverleafTrackedChangesLifecycle = factory(root.CodexOverleafTrackedChangeReplay);
6
6
  }
7
- })(typeof window !== 'undefined' ? window : globalThis, function trackedChangesLifecycleFactory() {
7
+ })(typeof window !== 'undefined' ? window : globalThis, function trackedChangesLifecycleFactory(Replay) {
8
8
  'use strict';
9
9
 
10
10
  // Tracked-changes lifecycle — carved out of writebackRouter.js in v1.8.0
@@ -49,6 +49,9 @@
49
49
  verifyActiveEditorText,
50
50
  collectElements,
51
51
  } = deps;
52
+ const { buildAcceptReplayOperations, rebaseCheckpointPair } = Replay.create({
53
+ normalizeSafeProjectPath, applyTextPatches
54
+ });
52
55
 
53
56
  function nativeReviewBlocked(path, code, applied = [], requestStarted = false) {
54
57
  const reason = requestStarted
@@ -104,11 +107,118 @@
104
107
  return nativeReviewBlocked(path, 'native_review_file_unavailable');
105
108
  const scope = await waitForNativeReview(path, refs, untracked);
106
109
  if (!scope.ok) return nativeReviewBlocked(path, scope.reason || 'native_review_unavailable');
110
+ if (scope.merged) {
111
+ const before = (params.expectedFiles || []).filter(file => file.path === path);
112
+ const post = (params.postFiles || []).filter(file => file.path === path);
113
+ if (scope.merged.runProjectId !== params.runProjectId || before.length !== 1 || post.length !== 1
114
+ || before[0].content !== scope.merged.preContent || post[0].content !== scope.merged.postContent)
115
+ return nativeReviewBlocked(path, 'native_merged_review_checkpoint_mismatch');
116
+ }
107
117
  files.push({ path, nativeDocId: scope.nativeDocId, scope });
108
118
  }
109
119
  return { ok: true, files, refs, deadline };
110
120
  }
111
121
 
122
+ function mergedReviewContextMatches(params, file, content) {
123
+ const projectId = typeof deps.getProjectId === 'function' ? deps.getProjectId()
124
+ : /\/project\/([^/?#]+)/.exec(window?.location?.pathname || '')?.[1];
125
+ return Boolean(params.runProjectId && projectId === params.runProjectId
126
+ && !checkWritebackRunProjectId(params) && getActiveFilePath() === file.path
127
+ && readActiveEditorText() === content);
128
+ }
129
+
130
+ async function reviewMergedInsertion(params, file, action) {
131
+ const path = file.path, accepting = action === 'accept';
132
+ let scope = prepareTrackedChangeReview(path, file.scope.targets);
133
+ if (!scope.ok || !scope.merged || scope.nativeDocId !== file.nativeDocId
134
+ || !mergedReviewContextMatches(params, file, scope.merged.postContent))
135
+ return nativeReviewBlocked(path, 'native_merged_review_scope_changed');
136
+ const before = (params.expectedFiles || []).filter(item => item.path === path);
137
+ const post = (params.postFiles || []).filter(item => item.path === path);
138
+ if (before.length !== 1 || post.length !== 1
139
+ || before[0].content !== scope.merged.preContent || post[0].content !== scope.merged.postContent)
140
+ return nativeReviewBlocked(path, 'native_merged_review_checkpoint_mismatch');
141
+ const view = deps.getCodeMirrorEditorView?.();
142
+ if (typeof view?.dispatch !== 'function' || view.state?.doc?.toString() !== post[0].content)
143
+ return nativeReviewBlocked(path, 'native_merged_review_editor_unavailable');
144
+
145
+ const editing = await forceEditingForAcceptReplay();
146
+ if (!editing.ok) return nativeReviewBlocked(path, 'native_merged_review_editing_unavailable');
147
+ let attempted = false;
148
+ try {
149
+ const stable = await waitForStableEditingForAcceptReplay({ waitMs: 1400, intervalMs: 160 });
150
+ scope = prepareTrackedChangeReview(path, file.scope.targets);
151
+ if (!stable.ok || !scope.ok || !scope.merged || scope.nativeDocId !== file.nativeDocId
152
+ || !mergedReviewContextMatches(params, file, post[0].content)
153
+ || deps.getCodeMirrorEditorView?.() !== view || view.state.doc.toString() !== post[0].content)
154
+ return nativeReviewBlocked(path, 'native_merged_review_scope_changed');
155
+
156
+ // A same-text replacement is deliberate: plain Editing removes tracking
157
+ // only from these exact inserted slices, without a transient text rollback.
158
+ // Never send a merged parent's ID to the whole-change Accept endpoint.
159
+ const changes = scope.merged.ranges.map(range => ({
160
+ from: range.start, to: range.end,
161
+ insert: accepting ? post[0].content.slice(range.start, range.end) : ''
162
+ }));
163
+ const expectedText = accepting ? post[0].content : before[0].content;
164
+ const expectedKeys = accepting ? scope.merged.preservedKeys : scope.merged.beforeKeys;
165
+ attempted = true;
166
+ view.dispatch({ changes });
167
+ compileBridge.markSourceEdited();
168
+
169
+ const until = Math.min(file.deadline || Date.now() + 8000, Date.now() + 8000);
170
+ let matchingSince = null;
171
+ while (Date.now() <= until) {
172
+ if (!mergedReviewContextMatches(params, file, expectedText))
173
+ return nativeReviewBlocked(path, 'native_merged_review_content_or_context_changed', [], true);
174
+ const after = prepareTrackedChangeCapture(path);
175
+ const matches = after.ready && after.source === 'native' && after.nativeDocId === file.nativeDocId
176
+ && JSON.stringify(after.refs.map(ref => ref.key).sort()) === JSON.stringify(expectedKeys);
177
+ if (matches) {
178
+ if (matchingSince === null) matchingSince = Date.now();
179
+ if (Date.now() - matchingSince >= 500) {
180
+ return { ok: true, verified: true, applied: scope.targets.map(trackedChange => ({
181
+ trackedChange, result: { ok: true, verified: true, verifiedContent: expectedText,
182
+ method: accepting ? 'overleaf-accept-untracked-replay' : 'overleaf-merged-insertion-undo',
183
+ preservedTrackedChanges: true }
184
+ })), skipped: [] };
185
+ }
186
+ } else matchingSince = null;
187
+ await delay(150);
188
+ }
189
+ return nativeReviewBlocked(path, 'native_merged_review_not_verified', [], true);
190
+ } catch (_error) {
191
+ return nativeReviewBlocked(path, 'native_merged_review_interrupted', [], attempted);
192
+ } finally {
193
+ // Match the existing no-trace Accept policy: do not re-enable tracking
194
+ // while an untracked operation can still be awaiting its server flush.
195
+ if (!attempted && editing.activated) await setReviewingEnabled(true, { waitMs: 1800 });
196
+ }
197
+ }
198
+
199
+ async function rejectPlanWithMergedInsertions(params, plan) {
200
+ const mixed = plan.files.find(file => !file.scope.merged && file.scope.unrelated.length);
201
+ if (mixed) return nativeReviewBlocked(mixed.path, 'native_reject_mixed_changes');
202
+ const applied = [];
203
+ for (const file of plan.files) {
204
+ if (Date.now() >= plan.deadline || checkWritebackRunProjectId(params))
205
+ return nativeReviewBlocked(file.path, 'native_review_context_changed', applied, applied.length > 0);
206
+ if (getActiveFilePath() !== file.path && !(await openFileByPath(file.path)).ok)
207
+ return nativeReviewBlocked(file.path, 'native_review_file_unavailable', applied, applied.length > 0);
208
+ const scoped = { ...params,
209
+ trackedChanges: plan.refs.filter(ref => ref.path === file.path),
210
+ expectedFiles: (params.expectedFiles || []).filter(item => item.path === file.path),
211
+ postFiles: (params.postFiles || []).filter(item => item.path === file.path)
212
+ };
213
+ const result = file.scope.merged
214
+ ? await reviewMergedInsertion(scoped, { ...file, deadline: plan.deadline }, 'reject')
215
+ : await rejectTrackedChanges(scoped);
216
+ applied.push(...(result.applied || []));
217
+ if (!result.ok) return { ...result, applied };
218
+ }
219
+ return { ok: true, applied, skipped: [] };
220
+ }
221
+
112
222
  async function acceptNativeReview(params, plan) {
113
223
  const applied = [];
114
224
  let token = window?.document?.querySelector?.('meta[name="ol-csrfToken"]')?.getAttribute('content');
@@ -126,6 +236,12 @@
126
236
  return nativeReviewBlocked(path, 'native_review_context_changed', applied, applied.length > 0);
127
237
  if (getActiveFilePath() !== path && !(await openFileByPath(path)).ok)
128
238
  return nativeReviewBlocked(path, 'native_review_file_unavailable', applied, applied.length > 0);
239
+ if (file.scope.merged) {
240
+ const result = await reviewMergedInsertion(params, { ...file, deadline: plan.deadline }, 'accept');
241
+ applied.push(...(result.applied || []));
242
+ if (!result.ok) return { ...result, applied };
243
+ continue;
244
+ }
129
245
  await waitForNativeReview(path, plan.refs);
130
246
  const scope = prepareTrackedChangeReview(path, plan.refs), text = readActiveEditorText();
131
247
  if (!scope.ok || !scope.acceptByIdSupported || scope.nativeDocId !== file.nativeDocId
@@ -195,8 +311,24 @@
195
311
 
196
312
  const nativeReview = await prepareNativeReview(params, true);
197
313
  if (nativeReview && !nativeReview.ok) return nativeReview;
314
+ if (nativeReview?.files.some(file => file.scope.merged))
315
+ return rejectPlanWithMergedInsertions(params, nativeReview);
198
316
  const mixedFile = nativeReview?.files.find(file => file.scope.unrelated.length);
199
317
  if (mixedFile) return nativeReviewBlocked(mixedFile.path, 'native_reject_mixed_changes');
318
+ if (params.untrackedUndo === true && !trackedChanges.length) {
319
+ if (!nativeReview) return nativeReviewBlocked('', 'native_review_unavailable');
320
+ // An ordinary write has no owned editor-history entry. Restore its
321
+ // checkpoint directly; global Undo may belong to an earlier user edit.
322
+ const restored = await restoreExpectedFilesWithNoTraceUndo(expectedFiles, postFiles, {
323
+ runProjectId: params.runProjectId
324
+ });
325
+ if (restored.applied.length) compileBridge.markSourceEdited();
326
+ return { ...restored, ok: restored.attempted && restored.ok,
327
+ skipped: restored.attempted ? restored.skipped : [{ trackedChange: null, result: {
328
+ ok: false, code: restored.code || 'snapshot_undo_unavailable',
329
+ reason: restored.reason || 'The saved checkpoint is unavailable for this undo.'
330
+ } }] };
331
+ }
200
332
  const editorUndo = await rejectTrackedChangesViaEditorUndo(expectedFiles, postFiles, applied);
201
333
  if (editorUndo.ok) {
202
334
  if (applied.length > 0) {
@@ -933,125 +1065,9 @@
933
1065
  return { ok: true, activated: true };
934
1066
  }
935
1067
 
936
- // Builds the minimal replay operations for Accept All. The replay must write
937
- // only the changed fragments, never a whole-file replaceAll.
938
- //
939
- // Preferred source: the run's own original forward writeback operations
940
- // (carrying their `patches`). After the editor-undo the document is back at
941
- // the pre-write content, so those patches' `expected` slices match and
942
- // re-apply cleanly.
943
- //
944
- // Fallback: when a path has no usable original patch operation, compute a
945
- // minimal pre->post diff (trim the common prefix/suffix to a single targeted
946
- // {from,to,insert} patch).
947
- function buildAcceptReplayOperations(paths, expectedByPath, postByPath, appliedOperations) {
948
- const patchesByPath = collectAppliedEditPatchesByPath(appliedOperations);
949
- const operations = [];
950
- for (const path of paths) {
951
- const preContent = expectedByPath.get(path);
952
- const postContent = postByPath.get(path);
953
- if (typeof preContent !== 'string' || typeof postContent !== 'string') {
954
- return {
955
- ok: false,
956
- code: 'accept_missing_run_content',
957
- reason: `${path} 缺少写入前/写入后内容;Codex 没有重放本轮改动。`
958
- };
959
- }
960
-
961
- // Preferred: re-apply the run's original forward patches verbatim.
962
- const originalPatches = patchesByPath.get(path);
963
- if (originalPatches && originalPatches.length && patchesApplyCleanly(preContent, originalPatches)) {
964
- operations.push({
965
- type: 'edit',
966
- path,
967
- patches: originalPatches,
968
- reason: 'Accept tracked edit (replay untracked)'
969
- });
970
- continue;
971
- }
972
-
973
- // Fallback: minimal pre->post diff trimmed to a single targeted patch.
974
- const diffPatch = buildMinimalDiffPatch(preContent, postContent);
975
- if (!diffPatch) {
976
- // pre === post: nothing to replay for this file.
977
- continue;
978
- }
979
- operations.push({
980
- type: 'edit',
981
- path,
982
- patches: [diffPatch],
983
- reason: 'Accept tracked edit (replay untracked)'
984
- });
985
- }
986
- return { ok: true, operations };
987
- }
988
1068
 
989
- // Collects the per-path `patches` arrays from the run's applied edit
990
- // operations. Only edit operations that actually carried `patches` are
991
- // usable; whole-file replaceAll / verifiedContent operations are skipped so
992
- // the replay never falls back to a whole-file write.
993
- function collectAppliedEditPatchesByPath(appliedOperations) {
994
- const patchesByPath = new Map();
995
- for (const rawOperation of Array.isArray(appliedOperations) ? appliedOperations : []) {
996
- if (!rawOperation || rawOperation.type !== 'edit') {
997
- continue;
998
- }
999
- const path = typeof rawOperation.path === 'string'
1000
- ? normalizeSafeProjectPath(rawOperation.path)
1001
- : '';
1002
- if (!path || !Array.isArray(rawOperation.patches) || !rawOperation.patches.length) {
1003
- continue;
1004
- }
1005
- const normalized = rawOperation.patches.map(patch => ({
1006
- from: Number(patch?.from),
1007
- to: Number(patch?.to),
1008
- expected: String(patch?.expected ?? ''),
1009
- insert: String(patch?.insert ?? '')
1010
- }));
1011
- const existing = patchesByPath.get(path) || [];
1012
- patchesByPath.set(path, existing.concat(normalized));
1013
- }
1014
- return patchesByPath;
1015
- }
1016
1069
 
1017
- // Confirms a patch set re-applies cleanly against the given (pre-write) text:
1018
- // every patch range is valid and its `expected` slice matches.
1019
- function patchesApplyCleanly(text, patches) {
1020
- const applied = applyTextPatches(text, patches);
1021
- return applied.ok === true;
1022
- }
1023
1070
 
1024
- // Computes a single targeted {from,to,insert} patch describing the pre->post
1025
- // change by trimming the common prefix and suffix. Returns null when the two
1026
- // strings are identical.
1027
- function buildMinimalDiffPatch(preContent, postContent) {
1028
- if (preContent === postContent) {
1029
- return null;
1030
- }
1031
- const preLen = preContent.length;
1032
- const postLen = postContent.length;
1033
- let prefix = 0;
1034
- const maxPrefix = Math.min(preLen, postLen);
1035
- while (prefix < maxPrefix && preContent[prefix] === postContent[prefix]) {
1036
- prefix += 1;
1037
- }
1038
- let suffix = 0;
1039
- const maxSuffix = Math.min(preLen, postLen) - prefix;
1040
- while (
1041
- suffix < maxSuffix
1042
- && preContent[preLen - 1 - suffix] === postContent[postLen - 1 - suffix]
1043
- ) {
1044
- suffix += 1;
1045
- }
1046
- const from = prefix;
1047
- const to = preLen - suffix;
1048
- return {
1049
- from,
1050
- to,
1051
- expected: preContent.slice(from, to),
1052
- insert: postContent.slice(from, postLen - suffix)
1053
- };
1054
- }
1055
1071
 
1056
1072
  async function rejectTrackedChangesViaEditorUndo(expectedFiles, postFiles, applied) {
1057
1073
  const expectedByPath = new Map((expectedFiles || [])
@@ -1249,36 +1265,6 @@
1249
1265
  };
1250
1266
  }
1251
1267
 
1252
- function rebaseCheckpointPair(actualContent, expectedContent, postContent) {
1253
- const actual = String(actualContent ?? '');
1254
- const expected = String(expectedContent ?? '');
1255
- const post = String(postContent ?? '');
1256
- if (actual === post) {
1257
- return {
1258
- ok: true,
1259
- expectedContent: expected,
1260
- postContent: post,
1261
- prefixLength: 0,
1262
- suffixLength: 0
1263
- };
1264
- }
1265
- if (!post) {
1266
- return { ok: false };
1267
- }
1268
- const matchIndex = actual.indexOf(post);
1269
- if (matchIndex < 0 || actual.indexOf(post, matchIndex + 1) >= 0) {
1270
- return { ok: false };
1271
- }
1272
- const prefix = actual.slice(0, matchIndex);
1273
- const suffix = actual.slice(matchIndex + post.length);
1274
- return {
1275
- ok: true,
1276
- expectedContent: prefix + expected + suffix,
1277
- postContent: actual,
1278
- prefixLength: prefix.length,
1279
- suffixLength: suffix.length
1280
- };
1281
- }
1282
1268
 
1283
1269
  async function waitForActiveEditorExpectedText(filePath, expectedContent, timeoutMs) {
1284
1270
  const deadline = Date.now() + Math.max(0, Number(timeoutMs) || 0);
@@ -1630,7 +1616,10 @@
1630
1616
  }
1631
1617
  return {
1632
1618
  acceptTrackedChanges,
1633
- rejectTrackedChanges,
1619
+ rejectTrackedChanges: async params => {
1620
+ const result = await rejectTrackedChanges(params);
1621
+ return deps.confirmReviewWriteback ? deps.confirmReviewWriteback(params, result) : result;
1622
+ },
1634
1623
  collectTrackedChangeRefsForPaths,
1635
1624
  prepareTrackedChangeCapture, getTrackedChangeCaptureStatus,
1636
1625
  captureTrackedWrite,
@@ -246,7 +246,7 @@
246
246
  if (node) {
247
247
  dispatchFileTreeOpenClick(node);
248
248
  await delay(250);
249
- const active = await waitForActiveFile(normalizedPath, 5000);
249
+ const active = await waitForActiveFile(normalizedPath, options.activeWaitMs ?? 5000);
250
250
  diagnostics.domClickActivePath = getActiveFilePath();
251
251
  if (active) {
252
252
  return {
@@ -254,6 +254,20 @@
254
254
  method: 'dom-click'
255
255
  };
256
256
  }
257
+ // Overleaf can drop a click while the previous document is still
258
+ // settling (e.g. right after a multi-file write). Re-resolve the row,
259
+ // activate the row itself plus a keyboard Enter, and wait once more.
260
+ const retryNode = findFileTreeNode(normalizedPath, { invalidateCache: true }) || node;
261
+ dispatchActivationSequence(retryNode);
262
+ dispatchKeyboardOpen(findFileTreeOpenClickTarget(retryNode) || retryNode);
263
+ const retried = await waitForActiveFile(normalizedPath, options.retryWaitMs ?? 4000);
264
+ diagnostics.domRetryActivePath = getActiveFilePath() || 'unknown';
265
+ if (retried) {
266
+ return {
267
+ ok: true,
268
+ method: 'dom-click-retry'
269
+ };
270
+ }
257
271
  }
258
272
 
259
273
  const manager = findFileTreeManager();
@@ -322,7 +336,7 @@
322
336
  .map(item => `${item.method}/${item.argType}:ok=${item.ok ? 'yes' : 'no'},active=${item.activePath || 'unknown'}${item.error ? ',error=' + item.error : ''}`)
323
337
  .slice(0, 8)
324
338
  .join('|') || 'none';
325
- return `open diagnostics initial=${diagnostics.initialActivePath || 'unknown'}, initialNode=${diagnostics.initialDomNodeFound ? 'yes' : 'no'}, folders=${folders}, postExpandNode=${diagnostics.postExpandDomNodeFound ? 'yes' : 'no'}, domClickActive=${diagnostics.domClickActivePath || 'unknown'}, manager=${managers}`;
339
+ return `open diagnostics initial=${diagnostics.initialActivePath || 'unknown'}, initialNode=${diagnostics.initialDomNodeFound ? 'yes' : 'no'}, folders=${folders}, postExpandNode=${diagnostics.postExpandDomNodeFound ? 'yes' : 'no'}, domClickActive=${diagnostics.domClickActivePath || 'unknown'}, domRetryActive=${diagnostics.domRetryActivePath || 'none'}, manager=${managers}`;
326
340
  }
327
341
 
328
342
  function dispatchFileTreeOpenClick(node) {
@@ -361,6 +375,16 @@
361
375
  }
362
376
  }
363
377
 
378
+ function dispatchKeyboardOpen(node) {
379
+ for (const type of ['keydown', 'keyup']) {
380
+ try {
381
+ node?.dispatchEvent(new KeyboardEvent(type, { key: 'Enter', code: 'Enter', bubbles: true, cancelable: true }));
382
+ } catch (_error) {
383
+ // KeyboardEvent may be unavailable in synthetic harnesses.
384
+ }
385
+ }
386
+ }
387
+
364
388
  function findFileTreeOpenClickTarget(node) {
365
389
  const selectors = [
366
390
  '.file-tree-entity-details',
@@ -1052,7 +1076,7 @@
1052
1076
  return labelPath;
1053
1077
  }
1054
1078
 
1055
- const textPath = normalizeProjectPathCandidate(node.textContent || '');
1079
+ const textPath = normalizeProjectPathCandidate(readFileTreeNodeText(node));
1056
1080
  if (!textPath) {
1057
1081
  const docIdPath = resolveProjectPathFromDomDocId(node);
1058
1082
  const domTreePath = resolveProjectPathFromDomTree(node);
@@ -1181,7 +1205,7 @@
1181
1205
  node.getAttribute?.('data-name'),
1182
1206
  node.getAttribute?.('aria-label'),
1183
1207
  node.getAttribute?.('title'),
1184
- node.textContent || ''
1208
+ readFileTreeNodeText(node)
1185
1209
  ];
1186
1210
  for (const value of candidates) {
1187
1211
  const path = normalizeProjectPathCandidate(value);
@@ -1244,21 +1268,12 @@
1244
1268
  }
1245
1269
 
1246
1270
  function normalizeProjectPathCandidate(value) {
1247
- const text = normalizeDomText(value);
1271
+ const text = String(value || '').trim();
1248
1272
  if (!text || hasMultipleTextPathExtensions(text)) {
1249
1273
  return '';
1250
1274
  }
1251
- const candidates = [
1252
- stripOverleafIconAffixes(text),
1253
- text
1254
- ];
1255
- for (const candidate of candidates) {
1256
- const normalized = normalizeSafeProjectPath(candidate);
1257
- if (normalized && window.CodexOverleafProjectFiles.isTextProjectPath(normalized)) {
1258
- return normalized;
1259
- }
1260
- }
1261
- return '';
1275
+ const normalized = normalizeSafeProjectPath(text);
1276
+ return normalized && window.CodexOverleafProjectFiles.isTextProjectPath(normalized) ? normalized : '';
1262
1277
  }
1263
1278
 
1264
1279
  function normalizeDomText(value) {
@@ -1275,19 +1290,11 @@
1275
1290
  return false;
1276
1291
  }
1277
1292
 
1278
- function stripOverleafIconAffixes(value) {
1279
- let text = normalizeDomText(value);
1280
- for (let index = 0; index < 4; index += 1) {
1281
- const next = text
1282
- .replace(/^(?:description|article|book_5|insert_drive_file|draft|text_snippet|note|chevron_right|expand_more|folder_open|folder)+/i, '')
1283
- .replace(/(?:more_vert\s*Menu|more_vert|Menu)$/i, '')
1284
- .trim();
1285
- if (next === text) {
1286
- break;
1287
- }
1288
- text = next;
1289
- }
1290
- return text;
1293
+ function readFileTreeNodeText(node) {
1294
+ // Icon ligatures can be valid filename prefixes; use the actual label.
1295
+ const names = Array.from(node.querySelectorAll?.('.item-name') || []);
1296
+ if (names.length > 1) return '';
1297
+ return names.length === 1 ? names[0].textContent || '' : node.textContent || '';
1291
1298
  }
1292
1299
 
1293
1300
  function inferPathFromTreeAncestors(node, fileName) {
@@ -0,0 +1,112 @@
1
+ (function (root, factory) {
2
+ const api = factory();
3
+ if (typeof module === 'object' && module.exports) module.exports = api;
4
+ if (root) root.CodexOverleafWritebackReceiptJournal = api;
5
+ })(typeof window !== 'undefined' ? window : globalThis, function () {
6
+ 'use strict';
7
+
8
+ function create({ getProjectId, prepareEditor, applyOperations, now = Date.now,
9
+ retentionMs = 600000, maxReceipts = 16 }) {
10
+ const receipts = new Map();
11
+ let active = null;
12
+ const clone = value => JSON.parse(JSON.stringify(value));
13
+ const empty = () => ({ applied: [], skipped: [], trackedChanges: [], trackedChangeCaptures: [] });
14
+ function failure(code, error) { return { ok: false, code, error, changedDocument: false }; }
15
+ function deadlineFailure(params) {
16
+ return params.deadlineAt != null && (!Number.isFinite(params.deadlineAt) || now() >= params.deadlineAt)
17
+ ? failure('writeback_deadline_exceeded', 'The operation deadline expired before any new write could start.') : null;
18
+ }
19
+ function prune() {
20
+ for (const [id, receipt] of receipts) {
21
+ if (receipt.state === 'completed' && now() - receipt.updatedAt > retentionMs) receipts.delete(id);
22
+ }
23
+ while (receipts.size >= maxReceipts) {
24
+ const oldest = Array.from(receipts).find(([, value]) => value.state === 'completed');
25
+ if (!oldest) break;
26
+ receipts.delete(oldest[0]);
27
+ }
28
+ }
29
+ function snapshot(receipt) {
30
+ const result = receipt.result || {
31
+ ...receipt.partial,
32
+ ok: receipt.partial.skipped.length === 0,
33
+ trackedChanges: receipt.partial.trackedChangeCaptures.some(value => value.state !== 'observed')
34
+ ? [] : receipt.partial.trackedChanges
35
+ };
36
+ return clone({ ok: true, requestId: receipt.id, runProjectId: receipt.projectId,
37
+ state: receipt.state, updatedAt: receipt.updatedAt, result });
38
+ }
39
+ function get(params = {}) {
40
+ if (!params.runProjectId || params.runProjectId !== getProjectId(params.runProjectId)) {
41
+ return failure('writeback_receipt_scope_changed', 'The writeback project is no longer active.');
42
+ }
43
+ const receipt = receipts.get(params.requestId);
44
+ if (!receipt || receipt.projectId !== params.runProjectId) {
45
+ return failure('writeback_receipt_missing', 'The original writeback receipt is unavailable. Do not replay the write.');
46
+ }
47
+ return snapshot(receipt);
48
+ }
49
+ async function apply(params = {}) {
50
+ const id = params.writebackRequestId, projectId = params.runProjectId;
51
+ if (typeof id !== 'string' || !id || id.length > 160 || !projectId || projectId !== getProjectId(projectId)) {
52
+ return failure('writeback_receipt_identity_invalid', 'Writeback request identity could not be confirmed.');
53
+ }
54
+ const fingerprint = JSON.stringify(params.operations || []);
55
+ const previous = receipts.get(id);
56
+ if (previous) {
57
+ if (previous.projectId !== projectId || previous.fingerprint !== fingerprint) {
58
+ return failure('writeback_receipt_conflict', 'The writeback request ID was reused with different operations.');
59
+ }
60
+ return previous.promise; // Duplicate delivery retrieves the original work; it never executes it again.
61
+ }
62
+ const expired = deadlineFailure(params);
63
+ if (expired) return expired;
64
+ if (active) return failure('writeback_in_progress', 'An earlier writeback is still running. Wait for its result before writing again.');
65
+ prune();
66
+ const receipt = { id, projectId, fingerprint, state: 'running', updatedAt: now(), partial: empty() };
67
+ receipts.set(id, receipt);
68
+ active = receipt;
69
+ const onOperationResult = ({ operation, result }) => {
70
+ const entry = clone({ operation, result });
71
+ (result.ok ? receipt.partial.applied : receipt.partial.skipped).push(entry);
72
+ if (operation.type === 'edit' && result.ok && Array.isArray(result.trackedChanges)) {
73
+ receipt.partial.trackedChanges.push(...clone(result.trackedChanges));
74
+ }
75
+ if (result.trackedChangeCapture) receipt.partial.trackedChangeCaptures.push(clone(result.trackedChangeCapture));
76
+ receipt.updatedAt = now();
77
+ };
78
+ // Defer execution until the promise is assigned, including for immediately completed operations.
79
+ receipt.promise = Promise.resolve().then(async () => {
80
+ if (deadlineFailure(params)) return deadlineFailure(params);
81
+ if (params.reviewingPolicy === 'no-trace-undo') {
82
+ const prepared = await prepareEditor(params);
83
+ if (prepared.ok !== true) return { applied: [], changedDocument: false,
84
+ skipped: (params.operations || []).map(operation => ({ operation, result: prepared })) };
85
+ }
86
+ if (deadlineFailure(params)) return deadlineFailure(params);
87
+ return applyOperations(params.operations || [], {
88
+ baseFiles: params.baseFiles || null,
89
+ reviewingPolicy: params.reviewingPolicy || '',
90
+ requireReviewing: params.requireReviewing === true,
91
+ requireEditing: params.requireEditing === true,
92
+ runProjectId: projectId,
93
+ ...(params.deadlineAt == null ? {} : { deadlineAt: params.deadlineAt }),
94
+ onOperationResult
95
+ });
96
+ }).catch(error => ({
97
+ ...snapshot(receipt).result, ok: false, code: error?.code || 'page_bridge_dispatch_failed',
98
+ error: error?.message || String(error), requestId: id, changedDocument: true
99
+ })).then(result => {
100
+ receipt.result = clone(result && typeof result === 'object' ? result
101
+ : { ok: false, code: 'writeback_result_unconfirmed', error: 'The page returned no writeback result.' });
102
+ receipt.state = 'completed';
103
+ receipt.updatedAt = now();
104
+ if (active === receipt) active = null;
105
+ return receipt.result;
106
+ });
107
+ return receipt.promise;
108
+ }
109
+ return Object.freeze({ apply, get });
110
+ }
111
+ return { create };
112
+ });