codex-overleaf-link 2.4.1 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/README.md +275 -305
  2. package/README.zh-CN.md +290 -320
  3. package/extension/bootstrap/background.js +99 -19
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +11 -16
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/update.js +25 -2
  8. package/extension/bootstrap/updateStatus.js +1 -0
  9. package/extension/runtime-manifest.json +3 -1
  10. package/extension/src/background.js +234 -56
  11. package/extension/src/backgroundBootstrapMigration.js +181 -0
  12. package/extension/src/backgroundSharedSessions.js +440 -0
  13. package/extension/src/backgroundUpdateCoordinator.js +541 -381
  14. package/extension/src/backgroundUpdateSafety.js +173 -0
  15. package/extension/src/content/activeTurnControl.js +16 -2
  16. package/extension/src/content/applyResultFormatters.js +46 -73
  17. package/extension/src/content/assetTransferBroker.js +244 -12
  18. package/extension/src/content/changeHistoryView.js +175 -0
  19. package/extension/src/content/composerAttachments.js +117 -67
  20. package/extension/src/content/composerPanel.js +41 -8
  21. package/extension/src/content/contentRuntime.js +1084 -1099
  22. package/extension/src/content/diagnosticsController.js +122 -97
  23. package/extension/src/content/diagnosticsExport.js +236 -0
  24. package/extension/src/content/diagnosticsPanel.js +78 -19
  25. package/extension/src/content/diffReviewPanel.js +8 -5
  26. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  27. package/extension/src/content/generated/content.bundle.js +14680 -3606
  28. package/extension/src/content/generated/content.bundle.meta.json +30 -3
  29. package/extension/src/content/globalPreferencesController.js +1 -1
  30. package/extension/src/content/legacyGlobalRegistry.js +25 -1
  31. package/extension/src/content/localSkillsPanel.js +141 -16
  32. package/extension/src/content/markdownDomRenderer.js +9 -4
  33. package/extension/src/content/markdownText.js +5 -6
  34. package/extension/src/content/modelPicker.js +8 -5
  35. package/extension/src/content/nativeCompatibilityController.js +45 -20
  36. package/extension/src/content/otWarmMirror.js +4 -5
  37. package/extension/src/content/otWarmMirrorController.js +17 -20
  38. package/extension/src/content/pageBridgeClient.js +33 -5
  39. package/extension/src/content/panelMaintenance.js +87 -132
  40. package/extension/src/content/panelRenderer.js +10 -0
  41. package/extension/src/content/pendingInputView.js +109 -76
  42. package/extension/src/content/projectProviderSelection.js +13 -4
  43. package/extension/src/content/projectSessionCleanup.js +109 -62
  44. package/extension/src/content/projectSettingsCoordinator.js +39 -2
  45. package/extension/src/content/providerSettingsCoordinator.js +63 -14
  46. package/extension/src/content/providerSettingsDialog.js +724 -113
  47. package/extension/src/content/recentProjects.js +21 -6
  48. package/extension/src/content/runActivitySummary.js +665 -0
  49. package/extension/src/content/runCompletionMeta.js +344 -0
  50. package/extension/src/content/runController.js +40 -15
  51. package/extension/src/content/runFailureNotice.js +34 -12
  52. package/extension/src/content/runGuidanceController.js +76 -30
  53. package/extension/src/content/runGuidanceView.js +59 -10
  54. package/extension/src/content/runPresence.js +306 -0
  55. package/extension/src/content/runQueueScheduler.js +150 -43
  56. package/extension/src/content/runRecoveryActions.js +146 -0
  57. package/extension/src/content/runResultActions.js +11 -8
  58. package/extension/src/content/runReviewController.js +574 -0
  59. package/extension/src/content/runScrollLayout.js +21 -2
  60. package/extension/src/content/runTimelineView.js +315 -290
  61. package/extension/src/content/scopedPersistencePanelState.js +51 -6
  62. package/extension/src/content/selectionContextView.js +300 -0
  63. package/extension/src/content/sensitivePreflight.js +140 -0
  64. package/extension/src/content/sessionForkController.js +80 -17
  65. package/extension/src/content/sessionManager.js +1 -1
  66. package/extension/src/content/sessionPersistence.js +6 -1
  67. package/extension/src/content/settingsPanel.js +84 -55
  68. package/extension/src/content/settingsWorkbench.js +379 -0
  69. package/extension/src/content/sharedSessionViewSync.js +109 -0
  70. package/extension/src/content/subagentActivityView.js +195 -0
  71. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  72. package/extension/src/content/updateIdle.js +4 -1
  73. package/extension/src/content/updateNotice.js +179 -56
  74. package/extension/src/content/updateSafetyReceiver.js +58 -0
  75. package/extension/src/content/writebackController.js +308 -2
  76. package/extension/src/content/writebackOrchestrator.js +213 -199
  77. package/extension/src/content/writebackPlan.js +231 -0
  78. package/extension/src/content/writebackRecovery.js +122 -0
  79. package/extension/src/content/writingStyleSettings.js +600 -0
  80. package/extension/src/content/writtenChangesView.js +152 -0
  81. package/extension/src/page/binaryAssetUploader.js +223 -23
  82. package/extension/src/page/fileMover.js +256 -0
  83. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  84. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  85. package/extension/src/page/referenceProjects.js +149 -0
  86. package/extension/src/page/reviewContext.js +113 -0
  87. package/extension/src/page/saveState.js +20 -6
  88. package/extension/src/page/snapshotRouter.js +124 -48
  89. package/extension/src/page/textCoordinates.js +102 -0
  90. package/extension/src/page/textFileCreator.js +442 -71
  91. package/extension/src/page/trackedChangeCapture.js +53 -57
  92. package/extension/src/page/trackedChangeOwnership.js +216 -0
  93. package/extension/src/page/trackedChangeReplay.js +184 -0
  94. package/extension/src/page/trackedChangesLifecycle.js +343 -339
  95. package/extension/src/page/treeOperations.js +33 -12
  96. package/extension/src/page/updateSafetyProbe.js +41 -0
  97. package/extension/src/page/writebackReceiptJournal.js +114 -0
  98. package/extension/src/page/writebackRouter.js +242 -201
  99. package/extension/src/pageBridge.js +73 -102
  100. package/extension/src/popup.js +261 -65
  101. package/extension/src/shared/agentTranscript.js +41 -4
  102. package/extension/src/shared/auditRecords.js +73 -0
  103. package/extension/src/shared/changeHistoryModel.js +94 -0
  104. package/extension/src/shared/compatibility.js +22 -6
  105. package/extension/src/shared/compileAdapter.js +101 -37
  106. package/extension/src/shared/composerAttachmentDraft.js +151 -0
  107. package/extension/src/shared/directoryCleanupProof.js +43 -0
  108. package/extension/src/shared/failureReasons.js +329 -3
  109. package/extension/src/shared/globalPreferences.js +31 -14
  110. package/extension/src/shared/governanceRules.js +4 -37
  111. package/extension/src/shared/i18n.js +389 -25
  112. package/extension/src/shared/managedUpdateProjection.js +8 -5
  113. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  114. package/extension/src/shared/pageRpcContract.js +9 -2
  115. package/extension/src/shared/projectFiles.js +21 -44
  116. package/extension/src/shared/providerProfiles.js +114 -3
  117. package/extension/src/shared/runActivityModel.js +361 -0
  118. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  119. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  120. package/extension/src/shared/runInputQueue.js +48 -5
  121. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  122. package/extension/src/shared/selectionContext.js +37 -0
  123. package/extension/src/shared/sessionState.js +166 -30
  124. package/extension/src/shared/settlementFacts.js +10 -2
  125. package/extension/src/shared/sharedSessionBridge.js +311 -0
  126. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  127. package/extension/src/shared/staleGuard.js +118 -13
  128. package/extension/src/shared/storageDb.js +77 -78
  129. package/extension/src/shared/storageMigration.js +74 -111
  130. package/extension/src/shared/storageRunActions.js +211 -22
  131. package/extension/src/shared/storageValuePruning.js +40 -0
  132. package/extension/src/shared/subagentActivityModel.js +174 -0
  133. package/extension/src/shared/undoOperations.js +216 -6
  134. package/extension/src/shared/updateConsent.js +42 -11
  135. package/extension/src/shared/updateRuntimeIdentity.js +1 -1
  136. package/extension/src/shared/writebackIntent.js +78 -0
  137. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  138. package/extension/src/shared/writebackSettlement.js +67 -48
  139. package/extension/src/shared/writingStyle.js +33 -0
  140. package/extension/styles/panel.css +1987 -385
  141. package/extension/styles/popup.css +110 -0
  142. package/native-host/src/anthropicBridgeRequest.js +6 -3
  143. package/native-host/src/anthropicBridgeResponse.js +6 -2
  144. package/native-host/src/anthropicMessagesBridge.js +24 -16
  145. package/native-host/src/bootstrapMigration.js +296 -0
  146. package/native-host/src/bootstrapMigrationContract.js +101 -0
  147. package/native-host/src/bootstrapUpdate.js +233 -0
  148. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  149. package/native-host/src/chatBridgeResponse.js +10 -0
  150. package/native-host/src/chatCompletionsBridge.js +21 -15
  151. package/native-host/src/codexConfigPaths.js +106 -0
  152. package/native-host/src/codexHome.js +66 -73
  153. package/native-host/src/codexHomeScope.js +124 -0
  154. package/native-host/src/codexInheritedAuth.js +90 -0
  155. package/native-host/src/codexLocalSkills.js +301 -0
  156. package/native-host/src/codexModelRecovery.js +107 -0
  157. package/native-host/src/codexPromptAssembly.js +33 -12
  158. package/native-host/src/codexProviderLaunch.js +3 -3
  159. package/native-host/src/codexProviderTest.js +24 -26
  160. package/native-host/src/codexRuntimeIdentity.js +1 -0
  161. package/native-host/src/codexSessionRunner.js +211 -318
  162. package/native-host/src/codexSessionTiming.js +21 -8
  163. package/native-host/src/codexSkillIsolation.js +170 -0
  164. package/native-host/src/codexThreadFork.js +19 -3
  165. package/native-host/src/index.js +19 -7
  166. package/native-host/src/managedInstall.js +115 -39
  167. package/native-host/src/managedInstallRecovery.js +140 -0
  168. package/native-host/src/managedRuntimeEvidence.js +57 -0
  169. package/native-host/src/mirrorWorkspace.js +92 -22
  170. package/native-host/src/nativeDoctor.js +15 -0
  171. package/native-host/src/nativeEnvironment.js +70 -0
  172. package/native-host/src/nativeHelperLifecycle.js +21 -0
  173. package/native-host/src/nativeHostRegistration.js +33 -0
  174. package/native-host/src/nativeQuotas.js +15 -8
  175. package/native-host/src/nativeRunLifecycle.js +71 -0
  176. package/native-host/src/overleafWorkspaceRules.js +16 -0
  177. package/native-host/src/processStopBarrier.js +115 -0
  178. package/native-host/src/projectPathPlatform.js +30 -0
  179. package/native-host/src/providerHttp.js +80 -0
  180. package/native-host/src/providerModelCatalog.js +4 -3
  181. package/native-host/src/providerProfile.js +17 -3
  182. package/native-host/src/providerReasoning.js +10 -11
  183. package/native-host/src/providerRuntime.js +1 -0
  184. package/native-host/src/providerStore.js +76 -14
  185. package/native-host/src/releaseTransport.js +14 -5
  186. package/native-host/src/responsesPassthroughBridge.js +266 -52
  187. package/native-host/src/runtimeInstaller.js +21 -1
  188. package/native-host/src/selectionScope.js +86 -0
  189. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  190. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  191. package/native-host/src/subagentAdmission.js +26 -0
  192. package/native-host/src/subagentBroker.js +176 -159
  193. package/native-host/src/subagentTelemetry.js +325 -0
  194. package/native-host/src/subagentWorkspace.js +103 -0
  195. package/native-host/src/subagentWorkspacePath.js +34 -0
  196. package/native-host/src/taskRunnerRuntime.js +130 -57
  197. package/native-host/src/tomlStatements.js +140 -0
  198. package/native-host/src/turnAttachments.js +113 -0
  199. package/native-host/src/updateManager.js +406 -38
  200. package/native-host/src/updateNetworkControl.js +75 -0
  201. package/native-host/src/updateTrust.js +21 -2
  202. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  203. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  303. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  304. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  305. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  306. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  307. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  308. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  309. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  310. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  311. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  312. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  313. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  314. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  315. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  316. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  317. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  318. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  319. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  320. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  321. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  322. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  323. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  324. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  325. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  326. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  327. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  328. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  329. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  330. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  331. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  332. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  333. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  334. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  335. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  336. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  337. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  338. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  339. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  340. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  341. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  342. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  343. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  344. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  345. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  346. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  347. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  348. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  349. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  350. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  351. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  352. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  353. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  354. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  355. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  356. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  357. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  358. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  359. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  360. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  361. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  362. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  363. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  364. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  365. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  366. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  367. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  368. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  369. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  370. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  371. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  372. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  373. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  374. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  375. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  376. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  377. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  378. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  379. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  380. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  381. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  382. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  383. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  384. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  385. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  386. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  387. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  388. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  389. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  390. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  391. package/native-host/src/writingStyleEvidence.js +102 -0
  392. package/native-host/src/writingStylePdfWorker.js +35 -0
  393. package/native-host/src/writingStyleProfile.js +165 -0
  394. package/native-host/src/writingStyleRuntime.js +475 -0
  395. package/native-host/src/writingStyleStore.js +73 -0
  396. package/package.json +1 -1
  397. package/scripts/install-managed.mjs +1 -0
@@ -32,12 +32,13 @@
32
32
  };
33
33
  }
34
34
  if (current.state === 'verified_saved') {
35
- return { ok: true, state: 'verified_saved' };
35
+ return { ok: true, state: 'verified_saved', signal: 'saved' };
36
36
  }
37
37
  if (current.state === 'unavailable') {
38
38
  return {
39
39
  ok: false,
40
40
  state: 'unavailable',
41
+ signal: current.signal || 'unavailable',
41
42
  reason: current.reason || 'Overleaf save state is unavailable.'
42
43
  };
43
44
  }
@@ -70,11 +71,16 @@
70
71
  return {
71
72
  ok: false,
72
73
  state: 'unknown_timeout',
74
+ signal: lastState.signal || 'unavailable',
75
+ observedState: lastState.state,
73
76
  reason: buildSaveStateTimeoutReason(lastState)
74
77
  };
75
78
  }
76
79
 
77
80
  function getOverleafSaveState() {
81
+ if (pageWindow.navigator?.onLine === false) {
82
+ return { state: 'unknown', signal: 'offline', reason: 'Overleaf is offline; saving cannot be confirmed.' };
83
+ }
78
84
  if (!document || typeof document.querySelectorAll !== 'function') {
79
85
  return {
80
86
  state: 'unavailable',
@@ -86,7 +92,9 @@
86
92
  '[class*="saving-status" i]',
87
93
  '[class*="save-status" i]',
88
94
  '[data-testid*="save" i]',
89
- '[aria-label*="save" i]'
95
+ '[aria-label*="save" i]',
96
+ '[title*="saved" i]',
97
+ '[role="status"]'
90
98
  ];
91
99
  let sawSaveCandidate = false;
92
100
  let sawVerifiedSaved = false;
@@ -125,12 +133,14 @@
125
133
  if (sawNegativeState) {
126
134
  return {
127
135
  state: 'unknown',
136
+ signal: 'unsaved',
128
137
  reason: 'Overleaf save indicator reports changes are not saved.'
129
138
  };
130
139
  }
131
140
  if (sawSavingState) {
132
141
  return {
133
142
  state: 'saving',
143
+ signal: 'saving',
134
144
  reason: 'Overleaf is still saving or syncing changes.'
135
145
  };
136
146
  }
@@ -139,6 +149,7 @@
139
149
  }
140
150
  return {
141
151
  state: 'unknown',
152
+ signal: sawSaveCandidate ? 'unrecognized' : 'missing',
142
153
  reason: sawSaveCandidate
143
154
  ? 'Overleaf save indicator was present, but did not verify that all changes are saved.'
144
155
  : 'Overleaf save indicator was not found.'
@@ -149,6 +160,7 @@
149
160
  if (!node) {
150
161
  return true;
151
162
  }
163
+ if (node.closest?.('#codex-overleaf-panel')) return true;
152
164
  if (node.hidden === true || node.inert === true) {
153
165
  return true;
154
166
  }
@@ -299,16 +311,18 @@
299
311
  expected.set(path, entry.result.verifiedContent);
300
312
  }
301
313
  }
302
- const deadline = now() + Math.max(0, timeoutMs);
314
+ const deadline = Math.min(now() + Math.max(0, timeoutMs), params.deadlineAt == null ? Infinity
315
+ : Number.isFinite(params.deadlineAt) ? params.deadlineAt : now());
303
316
  let attempts = 0;
304
317
  if (expected.size && typeof readSnapshot === 'function') {
305
318
  do {
306
- if (!params.runProjectId || getProjectId() !== params.runProjectId) break;
319
+ if (now() >= deadline || params.reviewGuard?.() || !params.runProjectId || getProjectId() !== params.runProjectId) break;
307
320
  try {
308
- const snapshot = await readSnapshot({ force: true, maxAgeMs: 0,
321
+ const read = () => readSnapshot({ force: true, maxAgeMs: 0,
309
322
  zipTimeoutMs: Math.max(1, Math.min(8000, deadline - now())) });
323
+ const snapshot = params.reviewAwait ? await params.reviewAwait(read) : await read();
310
324
  attempts++;
311
- if (getProjectId() !== params.runProjectId) break;
325
+ if (now() >= deadline || params.reviewGuard?.() || getProjectId() !== params.runProjectId) break;
312
326
  if (snapshot?.ok === true && Array.from(expected).every(([path, content]) =>
313
327
  snapshot.files?.some(file => file.path === path && file.source === 'overleaf-zip'
314
328
  && file.content === content))) {
@@ -22,6 +22,7 @@
22
22
  : () => '';
23
23
  let invalidatedAt = 0;
24
24
  let preferredZipEndpoint = '';
25
+ const writebackReads = new Set();
25
26
 
26
27
  function getProjectId() {
27
28
  return treeOperations.getProjectId?.() || null;
@@ -96,9 +97,10 @@
96
97
  };
97
98
  }),
98
99
  capabilities: {
99
- fullProjectSnapshot: true,
100
+ fullProjectSnapshot: zipSnapshot.pathIdentityComplete !== false,
101
+ pathIdentityComplete: zipSnapshot.pathIdentityComplete,
100
102
  method: 'overleaf-zip-file-list',
101
- skipped: [],
103
+ skipped: zipSnapshot.skipped || [],
102
104
  diagnostics: {
103
105
  zipEndpoint: zipSnapshot.endpoint,
104
106
  zipTiming: zipSnapshot.diagnostics,
@@ -212,7 +214,7 @@
212
214
  }
213
215
 
214
216
  async function buildProjectSnapshot(params = {}) {
215
- if (params.zipOnly) {
217
+ if (params.zipOnly || params.serverOnly === true) {
216
218
  return buildZipOnlyProjectSnapshot(params);
217
219
  }
218
220
 
@@ -242,7 +244,7 @@
242
244
  if (zipSnapshot.ok && zipSnapshot.files.length) {
243
245
  const mergedFiles = mergeSnapshotFiles(
244
246
  zipSnapshot.files,
245
- filterZipSnapshotOverlayFiles(zipSnapshot.files, lightweightSnapshot?.files || [])
247
+ filterZipSnapshotOverlayFiles(zipSnapshot.files, lightweightSnapshot?.files || [], zipSnapshot.skipped)
246
248
  );
247
249
  const files = filterRequestedSnapshotFiles(mergedFiles, params, activePath);
248
250
  const requestedPathsComplete = params.restrictToRequestedPathsOnly === true
@@ -254,7 +256,9 @@
254
256
  activePath,
255
257
  files,
256
258
  capabilities: {
257
- fullProjectSnapshot: params.restrictToRequestedPathsOnly === true ? false : true,
259
+ fullProjectSnapshot: params.restrictToRequestedPathsOnly !== true && zipSnapshot.pathIdentityComplete !== false,
260
+ pathIdentityComplete: zipSnapshot.pathIdentityComplete,
261
+ contentComplete: !(zipSnapshot.skipped || []).length,
258
262
  requestedPathsComplete,
259
263
  method: 'overleaf-zip',
260
264
  skipped: zipSnapshot.skipped || [],
@@ -315,7 +319,8 @@
315
319
  80
316
320
  );
317
321
  const files = [];
318
- const skipped = [];
322
+ const skipped = [...(zipSnapshot?.skipped || [])];
323
+ const ambiguousPaths = new Set(skipped.filter(file => file.reason === 'zip_path_duplicate').map(file => file.path));
319
324
  if (zipSnapshot && !zipSnapshot.ok) {
320
325
  skipped.push({
321
326
  path: 'project.zip',
@@ -325,6 +330,7 @@
325
330
  let previousSignature = '';
326
331
 
327
332
  for (const filePath of projectPaths) {
333
+ if (ambiguousPaths.has(filePath)) continue;
328
334
  if (filePath === snapshotActivePath) {
329
335
  const ready = await waitForActiveEditorText(filePath, 5000);
330
336
  if (!ready.ok) {
@@ -403,7 +409,7 @@
403
409
  await waitForActiveEditorText(activePath, 5000);
404
410
  }
405
411
 
406
- if (snapshotActivePath && !files.length && (!lightweightOnly || params.allowZipFallback === false)) {
412
+ if (snapshotActivePath && !ambiguousPaths.has(snapshotActivePath) && !files.length && (!lightweightOnly || params.allowZipFallback === false)) {
407
413
  files.push({
408
414
  path: snapshotActivePath,
409
415
  content: readActiveEditorText(),
@@ -418,7 +424,8 @@
418
424
  activePath: snapshotActivePath,
419
425
  files,
420
426
  capabilities: {
421
- fullProjectSnapshot: params.restrictToRequestedPathsOnly === true ? false : requestedPathsComplete,
427
+ fullProjectSnapshot: params.restrictToRequestedPathsOnly !== true && requestedPathsComplete && zipSnapshot?.pathIdentityComplete !== false,
428
+ pathIdentityComplete: zipSnapshot?.pathIdentityComplete,
422
429
  requestedPathsComplete,
423
430
  method: resolvePageSnapshotMethod(files, lightweightOnly),
424
431
  skipped,
@@ -437,15 +444,14 @@
437
444
 
438
445
  async function buildZipOnlyProjectSnapshot(params = {}) {
439
446
  const activePath = getPassiveActiveFilePath();
440
- const activeText = activePath ? readActiveEditorText() : '';
447
+ // Capture read evidence together with the path/text before awaiting ZIP.
448
+ // A later editor identity cannot prove that an earlier empty read succeeded.
449
+ const activeOverlay = params.serverOnly === true ? [] : readActiveEditorOverlay(activePath);
441
450
 
442
451
  const zipSnapshot = await fetchProjectZipSnapshot(params);
443
452
  if (zipSnapshot.ok && zipSnapshot.files.length) {
444
- const activeOverlay = buildActiveEditorOverlay(activePath, activeText, {
445
- allowedPaths: zipSnapshot.files.map(file => file.path)
446
- });
447
453
  const files = filterRequestedSnapshotFiles(
448
- mergeSnapshotFiles(zipSnapshot.files, activeOverlay),
454
+ mergeSnapshotFiles(zipSnapshot.files, filterZipSnapshotOverlayFiles(zipSnapshot.files, activeOverlay, zipSnapshot.skipped)),
449
455
  params,
450
456
  activePath
451
457
  );
@@ -458,7 +464,9 @@
458
464
  activePath,
459
465
  files,
460
466
  capabilities: {
461
- fullProjectSnapshot: params.restrictToRequestedPathsOnly === true ? false : true,
467
+ fullProjectSnapshot: params.restrictToRequestedPathsOnly !== true && zipSnapshot.pathIdentityComplete !== false,
468
+ pathIdentityComplete: zipSnapshot.pathIdentityComplete,
469
+ contentComplete: !(zipSnapshot.skipped || []).length,
462
470
  requestedPathsComplete,
463
471
  method: 'overleaf-zip',
464
472
  skipped: zipSnapshot.skipped || [],
@@ -477,28 +485,35 @@
477
485
  id: getProjectId(),
478
486
  url: window.location.href,
479
487
  activePath,
480
- files: buildActiveEditorOverlay(activePath, activeText),
488
+ files: filterZipSnapshotOverlayFiles([], activeOverlay, zipSnapshot.skipped, true),
481
489
  capabilities: {
482
490
  fullProjectSnapshot: false,
483
- method: activePath && window.CodexOverleafProjectFiles.isUsableProjectFileContent(activeText)
491
+ pathIdentityComplete: zipSnapshot.pathIdentityComplete,
492
+ method: params.serverOnly === true ? 'overleaf-zip-unavailable'
493
+ : activeOverlay.length
484
494
  ? 'active-editor-zip-only-fallback'
485
495
  : 'zip-only-fallback-empty',
486
- skipped: [{
496
+ contentComplete: false,
497
+ skipped: [...(zipSnapshot.skipped || []), {
487
498
  path: 'project.zip',
488
- reason: zipSnapshot.reason
499
+ reason: zipSnapshot.reason || 'ZIP file identities were incomplete'
489
500
  }],
490
501
  diagnostics: {
491
502
  docRecordCount: 0,
492
503
  docRecords: []
493
504
  },
494
- note: 'Only the current editor was read because the Overleaf source ZIP was unavailable; no file tree navigation was attempted.'
505
+ note: params.serverOnly === true
506
+ ? 'The server snapshot was unavailable; editor content was not used as save evidence.'
507
+ : 'Only the current editor was read because the Overleaf source ZIP was unavailable; no file tree navigation was attempted.'
495
508
  }
496
509
  };
497
510
  }
498
511
 
499
- function filterZipSnapshotOverlayFiles(zipFiles = [], overlayFiles = []) {
512
+ function filterZipSnapshotOverlayFiles(zipFiles = [], overlayFiles = [], skipped = [], allowMissing = false) {
500
513
  const zipPaths = new Set((zipFiles || []).map(file => file.path).filter(Boolean));
501
- return (overlayFiles || []).filter(file => file?.source !== 'active-editor' || zipPaths.has(file.path));
514
+ const ambiguousPaths = new Set((skipped || []).filter(file => file.reason === 'zip_path_duplicate').map(file => file.path));
515
+ return (overlayFiles || []).filter(file => !ambiguousPaths.has(file?.path)
516
+ && (allowMissing || file?.source !== 'active-editor' || zipPaths.has(file.path)));
502
517
  }
503
518
 
504
519
  function getRequestedSnapshotPaths(params = {}, activePath = '') {
@@ -516,19 +531,32 @@
516
531
  return (files || []).filter(file => requestedSet.has(file?.path));
517
532
  }
518
533
 
519
- function buildActiveEditorOverlay(activePath, activeText, options = {}) {
520
- if (!activePath || !window.CodexOverleafProjectFiles.isUsableProjectFileContent(activeText)) {
521
- return [];
522
- }
523
- if (Array.isArray(options.allowedPaths) && !options.allowedPaths.includes(activePath)) {
534
+ function readActiveEditorOverlay(activePath) {
535
+ if (!activePath) return [];
536
+ try {
537
+ const content = readActiveEditorText();
538
+ if (typeof content !== 'string') return [];
539
+ if (!content.trim()) {
540
+ // The legacy reader also returns '' when no surface exists. A readable
541
+ // identity must independently confirm blank text before it can erase ZIP.
542
+ const identity = deps.getActiveEditorIdentity?.();
543
+ let observed;
544
+ if (identity?.type === 'codemirror-view' && typeof identity.doc?.toString === 'function') {
545
+ observed = identity.doc.toString();
546
+ } else if (identity?.type === 'textarea') {
547
+ observed = identity.node?.value;
548
+ } else if (identity?.type === 'contenteditable') {
549
+ const node = identity.node;
550
+ if (typeof node?.innerText === 'string' || typeof node?.textContent === 'string') {
551
+ observed = node.innerText || node.textContent || '';
552
+ }
553
+ }
554
+ if (typeof observed !== 'string' || observed !== content) return [];
555
+ } else if (!window.CodexOverleafProjectFiles.isUsableProjectFileContent(content)) return [];
556
+ return [{ path: activePath, content, source: 'active-editor', kind: 'text' }];
557
+ } catch (_error) {
524
558
  return [];
525
559
  }
526
- return [{
527
- path: activePath,
528
- content: activeText,
529
- source: 'active-editor',
530
- kind: 'text'
531
- }];
532
560
  }
533
561
 
534
562
  function mergeSnapshotFiles(primaryFiles = [], overrideFiles = []) {
@@ -537,7 +565,7 @@
537
565
  if (!file?.path || file.kind === 'binary' || typeof file.content !== 'string') {
538
566
  continue;
539
567
  }
540
- if (!window.CodexOverleafProjectFiles.isUsableProjectFileContent(file.content)) {
568
+ if (file.content.trim() && !window.CodexOverleafProjectFiles.isUsableProjectFileContent(file.content)) {
541
569
  continue;
542
570
  }
543
571
  filesByPath.set(file.path, {
@@ -631,8 +659,13 @@
631
659
  }
632
660
  }
633
661
 
634
- async function fetchProjectZipSnapshot(params = {}) {
635
- const projectId = getProjectId();
662
+ async function fetchProjectZipSnapshot(params = {}, reference = null) {
663
+ // Cross-project reads use a separate internal argument. Normal snapshot
664
+ // RPC parameters cannot change the active project's snapshot identity.
665
+ const projectId = reference ? String(reference.projectId || '') : getProjectId();
666
+ if (reference && !/^[a-f0-9]{24}$/i.test(projectId)) {
667
+ return { ok: false, reason: 'Reference project identity is invalid' };
668
+ }
636
669
  if (!projectId) {
637
670
  return {
638
671
  ok: false,
@@ -644,7 +677,9 @@
644
677
  const endpoints = [
645
678
  `${window.location.origin}/project/${encodeURIComponent(projectId)}/download/zip`,
646
679
  `${window.location.origin}/download/project/${encodeURIComponent(projectId)}`
647
- ];
680
+ ].map(endpoint => params.serverOnly === true
681
+ ? endpoint + '?codex_save_check=' + encodeURIComponent(String(params.saveCheckId || Date.now()).slice(0, 80))
682
+ : endpoint);
648
683
  const orderedEndpoints = preferredZipEndpoint && endpoints.includes(preferredZipEndpoint)
649
684
  ? [preferredZipEndpoint, ...endpoints.filter(endpoint => endpoint !== preferredZipEndpoint)]
650
685
  : endpoints;
@@ -661,7 +696,9 @@
661
696
  }
662
697
  const attemptStartedAt = Date.now();
663
698
  try {
664
- const { response, contentType, buffer, timing } = await fetchZipEndpoint(endpoint, remainingMs);
699
+ const { response, contentType, buffer, timing } = await fetchZipEndpoint(endpoint, remainingMs,
700
+ reference?.signal || params.signal, !reference && (params.writebackVerification === true || params.serverOnly === true));
701
+ if (reference?.signal?.aborted) throw new Error('Reference reading was cancelled');
665
702
  if (!response.ok) {
666
703
  attempts.push({
667
704
  endpoint,
@@ -678,7 +715,7 @@
678
715
  });
679
716
  const extractMs = Date.now() - extractStartedAt;
680
717
  const files = extracted.files || [];
681
- if (!files.length) {
718
+ if (!files.length && extracted.pathIdentityComplete !== false) {
682
719
  attempts.push({
683
720
  endpoint,
684
721
  status: response.status,
@@ -702,6 +739,7 @@
702
739
  return {
703
740
  ok: true,
704
741
  endpoint,
742
+ pathIdentityComplete: extracted.pathIdentityComplete,
705
743
  skipped: extracted.skipped || [],
706
744
  files: files.map(file => ({
707
745
  ...file,
@@ -716,10 +754,12 @@
716
754
  } catch (error) {
717
755
  attempts.push({
718
756
  endpoint,
719
- error: error.message,
757
+ error: error.message,
758
+ ...(error.timing || {}),
720
759
  elapsedMs: Date.now() - attemptStartedAt
721
760
  });
722
761
  errors.push(`${endpoint} failed: ${error.message}`);
762
+ if (reference?.signal?.aborted) break;
723
763
  if (error?.code === 'zip_timeout') {
724
764
  errors.push(`Overleaf ZIP download exhausted its ${timeoutMs}ms total timeout budget`);
725
765
  break;
@@ -738,8 +778,15 @@
738
778
  };
739
779
  }
740
780
 
741
- function fetchZipEndpoint(endpoint, timeoutMs) {
781
+ function fetchZipEndpoint(endpoint, timeoutMs, parentSignal, writeback = false) {
742
782
  const controller = typeof AbortController === 'function' ? new AbortController() : null;
783
+ if (writeback && controller) writebackReads.add(controller);
784
+ const started = Date.now(), observedTiming = {};
785
+ const abort = () => controller?.abort();
786
+ if (controller && parentSignal) {
787
+ if (parentSignal.aborted) controller.abort();
788
+ else parentSignal.addEventListener('abort', abort, { once: true });
789
+ }
743
790
  const request = (async () => {
744
791
  const startedAt = Date.now();
745
792
  const response = await fetch(endpoint, {
@@ -750,6 +797,8 @@
750
797
  ...(controller ? { signal: controller.signal } : {})
751
798
  });
752
799
  const headersAt = Date.now();
800
+ observedTiming.timeToHeadersMs = headersAt - started;
801
+ observedTiming.status = response.status;
753
802
  const contentType = response.headers.get('content-type') || '';
754
803
  const buffer = response.ok ? await response.arrayBuffer() : null;
755
804
  return {
@@ -765,7 +814,13 @@
765
814
 
766
815
  return withTimeout(request, timeoutMs, () => {
767
816
  controller?.abort();
768
- }, `Overleaf ZIP download timed out after ${timeoutMs}ms`);
817
+ }, `Overleaf ZIP download timed out after ${timeoutMs}ms`).catch(error => {
818
+ error.timing = { ...observedTiming, downloadMs: Date.now() - started };
819
+ throw error;
820
+ }).finally(() => {
821
+ if (controller) writebackReads.delete(controller);
822
+ parentSignal?.removeEventListener?.('abort', abort);
823
+ });
769
824
  }
770
825
 
771
826
  function withTimeout(promise, timeoutMs, onTimeout, message) {
@@ -811,6 +866,9 @@
811
866
  const entryCount = view.getUint16(eocdOffset + 10, true);
812
867
  const centralDirectoryOffset = view.getUint32(eocdOffset + 16, true);
813
868
  const decoder = new TextDecoder('utf-8');
869
+ const nameDecoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
870
+ const seenPaths = new Set();
871
+ let pathIdentityComplete = true;
814
872
  const files = [];
815
873
  const skipped = [];
816
874
  let binaryBytes = 0;
@@ -829,7 +887,25 @@
829
887
  const commentLength = view.getUint16(offset + 32, true);
830
888
  const localHeaderOffset = view.getUint32(offset + 42, true);
831
889
  const rawName = bytes.slice(offset + 46, offset + 46 + fileNameLength);
832
- const path = normalizeSafeProjectPath(decoder.decode(rawName));
890
+ let name = '';
891
+ try { name = nameDecoder.decode(rawName); } catch (_error) { /* unknown identity is incomplete */ }
892
+ const directory = name.endsWith('/');
893
+ const path = normalizeSafeProjectPath(directory ? name.slice(0, -1) : name);
894
+ if (!path || seenPaths.has(path)) {
895
+ if (path) {
896
+ const previousIndex = files.findIndex(file => file.path === path);
897
+ if (previousIndex >= 0) files.splice(previousIndex, 1);
898
+ }
899
+ pathIdentityComplete = false;
900
+ skipped.push({ path: name, reason: path ? 'zip_path_duplicate' : 'zip_path_invalid', unknownPath: !name });
901
+ offset += 46 + fileNameLength + extraLength + commentLength;
902
+ continue;
903
+ }
904
+ seenPaths.add(path);
905
+ if (directory) {
906
+ offset += 46 + fileNameLength + extraLength + commentLength;
907
+ continue;
908
+ }
833
909
  const isText = window.CodexOverleafProjectFiles.isTextProjectPath(path);
834
910
  const shouldInclude = path && !path.endsWith('/') && (isText || options.includeBinaryFiles);
835
911
 
@@ -874,7 +950,7 @@
874
950
  offset += 46 + fileNameLength + extraLength + commentLength;
875
951
  }
876
952
 
877
- return { files, skipped };
953
+ return { files, skipped, pathIdentityComplete };
878
954
  }
879
955
 
880
956
  function uint8ArrayToBase64(bytes) {
@@ -999,16 +1075,16 @@
999
1075
  getInvalidatedAt,
1000
1076
  getRequestedSnapshotPaths,
1001
1077
  invalidateCache,
1078
+ cancelWritebackReads() { for (const controller of writebackReads) controller.abort(); },
1002
1079
  readFileTree
1003
1080
  };
1004
1081
  }
1005
1082
 
1006
1083
  function fallbackNormalizeSafeProjectPath(value) {
1007
- const text = String(value || '').replace(/\\/g, '/').replace(/\/+/g, '/').replace(/^\/+|\/+$/g, '').trim();
1008
- if (!text || text.split('/').some(part => !part || part === '.' || part === '..')) {
1009
- return '';
1010
- }
1011
- return text;
1084
+ if (typeof value !== 'string' || !value || value.length > 2048 || value.trim() !== value
1085
+ || /[\u0000-\u001f\u007f]/.test(value) || value.startsWith('/')
1086
+ || value.includes('\\') || /^[A-Za-z]:/.test(value)) return '';
1087
+ return value.split('/').some(part => !part || part === '.' || part === '..') ? '' : value;
1012
1088
  }
1013
1089
 
1014
1090
  return { create };
@@ -0,0 +1,102 @@
1
+ (function (root, factory) {
2
+ const api = factory();
3
+ if (typeof module === 'object' && module.exports) module.exports = api;
4
+ if (root) root.CodexOverleafTextCoordinates = api;
5
+ })(typeof window !== 'undefined' ? window : globalThis, function () {
6
+ 'use strict';
7
+
8
+ function resolveJumpToPositionRange(params, text, filePath) {
9
+ if (params.line === undefined && params.column === undefined) {
10
+ return {
11
+ ok: true,
12
+ from: params.from,
13
+ to: params.to
14
+ };
15
+ }
16
+
17
+ const lines = collectTextLineRanges(String(text || ''));
18
+ const lineCount = lines.length;
19
+ const lineNumber = Number(params.line);
20
+ const lineMetadata = { lineCount };
21
+ if (params.line !== undefined) {
22
+ lineMetadata.line = Number.isInteger(lineNumber) ? lineNumber : params.line;
23
+ }
24
+ if (!Number.isInteger(lineNumber) || lineNumber < 1) {
25
+ return jumpPositionOutOfRange('line_out_of_range', filePath, lineMetadata);
26
+ }
27
+
28
+ const line = lines[lineNumber - 1];
29
+ if (!line) {
30
+ return jumpPositionOutOfRange('line_out_of_range', filePath, {
31
+ line: lineNumber,
32
+ lineCount
33
+ });
34
+ }
35
+
36
+ const lineLength = line.end - line.start;
37
+ if (params.selectLine === true) {
38
+ return {
39
+ ok: true,
40
+ from: line.start,
41
+ to: line.end
42
+ };
43
+ }
44
+
45
+ if (params.column !== undefined) {
46
+ const columnNumber = Number(params.column);
47
+ const maxColumn = lineLength + 1;
48
+ if (!Number.isInteger(columnNumber) || columnNumber < 1 || columnNumber > maxColumn) {
49
+ return jumpPositionOutOfRange('column_out_of_range', filePath, {
50
+ line: lineNumber,
51
+ column: Number.isInteger(columnNumber) ? columnNumber : params.column,
52
+ lineLength
53
+ });
54
+ }
55
+ const offset = line.start + columnNumber - 1;
56
+ return {
57
+ ok: true,
58
+ from: offset,
59
+ to: offset
60
+ };
61
+ }
62
+
63
+ return {
64
+ ok: true,
65
+ from: line.start,
66
+ to: line.start
67
+ };
68
+ }
69
+
70
+ function jumpPositionOutOfRange(code, filePath, metadata = {}) {
71
+ return {
72
+ ok: false,
73
+ code,
74
+ reason: 'Requested jumpToPosition location is out of range',
75
+ path: filePath,
76
+ ...metadata
77
+ };
78
+ }
79
+
80
+ function collectTextLineRanges(text) {
81
+ const lines = [];
82
+ let start = 0;
83
+
84
+ for (let index = 0; index < text.length; index += 1) {
85
+ if (text[index] === '\r') {
86
+ lines.push({ start, end: index });
87
+ if (text[index + 1] === '\n') {
88
+ index += 1;
89
+ }
90
+ start = index + 1;
91
+ } else if (text[index] === '\n') {
92
+ lines.push({ start, end: index });
93
+ start = index + 1;
94
+ }
95
+ }
96
+
97
+ lines.push({ start, end: text.length });
98
+ return lines;
99
+ }
100
+
101
+ return { create: () => ({ resolveJumpToPositionRange }), resolveJumpToPositionRange, collectTextLineRanges };
102
+ });