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
@@ -0,0 +1,152 @@
1
+ (function initCodexOverleafWrittenChangesView(root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory();
4
+ } else {
5
+ root.CodexOverleafModuleRegistry.define('WrittenChangesView', [], factory);
6
+ }
7
+ })(typeof window !== 'undefined' ? window : globalThis, function writtenChangesViewFactory() {
8
+ 'use strict';
9
+
10
+ // Read-only receipt for changes already written to Overleaf: one folded row
11
+ // per file, and inside it only the changed lines with word-level marks.
12
+ // Reviewing-mode hunks with Accept/Reject stay in DiffReviewPanel.
13
+ const CONTEXT_LINES = 1;
14
+ const MAX_WORD_DIFF_TOKENS = 600;
15
+ // CJK characters diff one by one; Latin words, spaces and punctuation as runs.
16
+ const TOKEN = /[\u3000-\u9fff\uac00-\ud7af\uff00-\uffef]|[A-Za-z0-9_]+|\s+|\\[A-Za-z]+|[^\sA-Za-z0-9_\u3000-\u9fff\uac00-\ud7af\uff00-\uffef]/g;
17
+
18
+ function tokenize(text) {
19
+ return String(text || '').match(TOKEN) || [];
20
+ }
21
+
22
+ // Classic LCS over tokens; returns [{ type: 'same'|'del'|'ins', text }].
23
+ function diffWords(before, after) {
24
+ const a = tokenize(before), b = tokenize(after);
25
+ if (a.length > MAX_WORD_DIFF_TOKENS || b.length > MAX_WORD_DIFF_TOKENS) {
26
+ return [{ type: 'del', text: String(before) }, { type: 'ins', text: String(after) }];
27
+ }
28
+ const table = Array.from({ length: a.length + 1 }, () => new Uint16Array(b.length + 1));
29
+ for (let i = a.length - 1; i >= 0; i -= 1) {
30
+ for (let j = b.length - 1; j >= 0; j -= 1) {
31
+ table[i][j] = a[i] === b[j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
32
+ }
33
+ }
34
+ const parts = [];
35
+ const push = (type, text) => {
36
+ const last = parts[parts.length - 1];
37
+ if (last?.type === type) last.text += text; else parts.push({ type, text });
38
+ };
39
+ let i = 0, j = 0;
40
+ while (i < a.length && j < b.length) {
41
+ if (a[i] === b[j]) { push('same', a[i]); i += 1; j += 1; }
42
+ else if (table[i + 1][j] >= table[i][j + 1]) { push('del', a[i]); i += 1; }
43
+ else { push('ins', b[j]); j += 1; }
44
+ }
45
+ while (i < a.length) push('del', a[i++]);
46
+ while (j < b.length) push('ins', b[j++]);
47
+ return parts;
48
+ }
49
+
50
+ // Turns diff hunk lines into display rows: paired remove/add runs become one
51
+ // merged row; long unchanged stretches fold into a gap row.
52
+ function buildRows(lines) {
53
+ const rows = [];
54
+ for (let index = 0; index < lines.length;) {
55
+ const line = lines[index];
56
+ if (line?.type !== 'remove' && line?.type !== 'add') {
57
+ rows.push({ kind: 'context', text: String(line?.text ?? '') });
58
+ index += 1;
59
+ continue;
60
+ }
61
+ const removed = [], added = [];
62
+ while (lines[index]?.type === 'remove') removed.push(String(lines[index++].text ?? ''));
63
+ while (lines[index]?.type === 'add') added.push(String(lines[index++].text ?? ''));
64
+ const paired = Math.min(removed.length, added.length);
65
+ for (let k = 0; k < paired; k += 1) rows.push({ kind: 'change', parts: diffWords(removed[k], added[k]) });
66
+ for (const text of removed.slice(paired)) rows.push({ kind: 'change', parts: [{ type: 'del', text }] });
67
+ for (const text of added.slice(paired)) rows.push({ kind: 'change', parts: [{ type: 'ins', text }] });
68
+ }
69
+ const near = rows.map((row, index) => row.kind === 'change'
70
+ || rows.slice(Math.max(0, index - CONTEXT_LINES), index + CONTEXT_LINES + 1).some(other => other.kind === 'change'));
71
+ const out = [];
72
+ for (let index = 0; index < rows.length;) {
73
+ if (near[index]) { out.push(rows[index]); index += 1; continue; }
74
+ let end = index;
75
+ while (end < rows.length && !near[end]) end += 1;
76
+ out.push({ kind: 'gap', count: end - index });
77
+ index = end;
78
+ }
79
+ return out;
80
+ }
81
+
82
+ function countLines(diff) {
83
+ let added = 0, removed = 0;
84
+ for (const hunk of diff || []) {
85
+ for (const line of hunk?.lines || []) {
86
+ if (line?.type === 'add') added += 1;
87
+ else if (line?.type === 'remove') removed += 1;
88
+ }
89
+ }
90
+ return { added, removed };
91
+ }
92
+
93
+ function render(changes, { document: doc = globalThis.document, tx = en => en } = {}) {
94
+ const make = (tag, className, text) => {
95
+ const el = doc.createElement(tag);
96
+ if (className) el.className = className;
97
+ if (text !== undefined) el.textContent = text;
98
+ return el;
99
+ };
100
+ const list = make('div', 'run-written-changes');
101
+ list.setAttribute('role', 'list');
102
+ for (const change of changes || []) {
103
+ if (!change?.path || !Array.isArray(change.diff) || !change.diff.length) continue;
104
+ const file = make('details', 'run-written-file');
105
+ file.setAttribute('role', 'listitem');
106
+ const summary = make('summary', 'run-written-file-sum');
107
+ const name = make('span', 'run-written-file-name', change.path.split('/').pop());
108
+ name.title = change.path;
109
+ const { added, removed } = countLines(change.diff);
110
+ summary.append(name);
111
+ if (change.path.includes('/')) summary.append(make('span', 'run-written-file-dir', change.path.slice(0, change.path.lastIndexOf('/'))));
112
+ const counts = make('span', 'run-written-file-counts');
113
+ counts.append(make('span', 'run-written-add', '+' + added), make('span', 'run-written-del', '−' + removed));
114
+ counts.setAttribute('aria-label', tx(`${added} lines added, ${removed} removed`, `新增 ${added} 行,删除 ${removed} 行`));
115
+ summary.append(counts);
116
+ file.append(summary);
117
+ // Lazy body: thesis chapters produce long diffs, so build on first open.
118
+ file.addEventListener('toggle', () => {
119
+ if (!file.open || file.dataset.built) return;
120
+ file.dataset.built = 'true';
121
+ const body = make('div', 'run-written-body');
122
+ for (const hunk of change.diff) {
123
+ if (hunk?.truncated) {
124
+ body.append(make('div', 'run-written-row run-written-note', String(hunk.lines?.[0]?.text || '')));
125
+ continue;
126
+ }
127
+ const head = make('div', 'run-written-hunk', tx(`Line ${hunk?.startB ?? '?'}`, `第 ${hunk?.startB ?? '?'} 行`));
128
+ body.append(head);
129
+ for (const row of buildRows(hunk?.lines || [])) {
130
+ if (row.kind === 'gap') {
131
+ body.append(make('div', 'run-written-row run-written-gap', tx(`… ${row.count} unchanged lines`, `… ${row.count} 行未改动`)));
132
+ } else if (row.kind === 'context') {
133
+ body.append(make('div', 'run-written-row', row.text || ' '));
134
+ } else {
135
+ const line = make('div', 'run-written-row is-changed');
136
+ for (const part of row.parts) {
137
+ line.append(part.type === 'same' ? doc.createTextNode(part.text)
138
+ : make(part.type === 'ins' ? 'ins' : 'del', '', part.text));
139
+ }
140
+ body.append(line);
141
+ }
142
+ }
143
+ }
144
+ file.append(body);
145
+ });
146
+ list.append(file);
147
+ }
148
+ return list;
149
+ }
150
+
151
+ return { render, diffWords, buildRows, countLines };
152
+ });
@@ -13,6 +13,18 @@
13
13
  const treeOperations = deps.treeOperations;
14
14
  const snapshotRouter = deps.snapshotRouter;
15
15
  const transfers = new Map();
16
+ // Keep bounded receipts after payload release; status reads never re-upload.
17
+ const receipts = new Map();
18
+ function pruneReceipts() {
19
+ for (const [id, transfer] of receipts) {
20
+ if (transfer.finishedAt && Date.now() - transfer.finishedAt > 600000) receipts.delete(id);
21
+ }
22
+ while (receipts.size >= 32) {
23
+ const entry = Array.from(receipts).find(([, value]) => value.finishedAt);
24
+ if (!entry) break;
25
+ receipts.delete(entry[0]);
26
+ }
27
+ }
16
28
 
17
29
  function begin(params = {}) {
18
30
  let transferId;
@@ -30,14 +42,26 @@
30
42
  if (!/^[a-f0-9]{64}$/i.test(String(params.sha256 || ''))) {
31
43
  return failure('binary_asset_hash_invalid', 'Asset SHA-256 is missing or invalid.');
32
44
  }
45
+ if (params.overwrite === true && !/^[a-f0-9]{64}$/i.test(String(params.previousSha256 || ''))) {
46
+ return failure('binary_asset_baseline_missing', 'The original binary version is unavailable. Read the project again before replacing this file.');
47
+ }
48
+ pruneReceipts();
49
+ if (transfers.has(transferId) || receipts.has(transferId)) {
50
+ return failure('binary_upload_transfer_conflict', 'Asset transfer ID is already in use.');
51
+ }
52
+ if (transfers.size >= 32 || receipts.size >= 32) {
53
+ return failure('binary_upload_busy', 'Too many asset transfers are pending.');
54
+ }
33
55
  transfers.set(transferId, {
34
56
  id: transferId,
35
57
  path: projectPath,
36
58
  size,
37
59
  sha256: String(params.sha256).toLowerCase(),
60
+ previousSha256: params.overwrite === true ? String(params.previousSha256).toLowerCase() : '',
38
61
  mimeType: String(params.mimeType || 'application/octet-stream'),
39
62
  projectId: treeOperations.getProjectId(),
40
63
  overwrite: params.overwrite === true,
64
+ createdDirectories: [], folderChanged: false, fileUploadAttempted: false,
41
65
  chunks: [],
42
66
  received: 0
43
67
  });
@@ -62,7 +86,59 @@
62
86
  return { ok: true, transferId: transfer.id, received: transfer.received };
63
87
  }
64
88
 
65
- async function commit(params = {}) {
89
+ function commit(params = {}) {
90
+ const id = String(params.transferId || '');
91
+ const existing = receipts.get(id);
92
+ if (existing) {
93
+ if (existing.projectId !== treeOperations.getProjectId()) {
94
+ return Promise.resolve(failure('binary_upload_scope_changed', 'Asset project changed.'));
95
+ }
96
+ return existing.promise;
97
+ }
98
+ const transfer = transfers.get(id);
99
+ if (!transfer) return Promise.resolve(failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.'));
100
+ receipts.set(id, transfer);
101
+ transfer.promise = commitOnce(params).catch(error => ({
102
+ ...failure(error.code || 'binary_upload_failed', error.message),
103
+ changedDocument: transfer.mutationAttempted === true
104
+ })).then(async result => {
105
+ if (result.ok !== true && transfer.createdDirectories.length && typeof deps.cleanupCreatedDirectories === 'function') {
106
+ try {
107
+ const cleanup = await deps.cleanupCreatedDirectories(transfer.createdDirectories, {
108
+ projectId: transfer.projectId, isCurrent: () => treeOperations.getProjectId() === transfer.projectId && receipts.get(id) === transfer
109
+ });
110
+ transfer.createdDirectories = cleanup.remaining || transfer.createdDirectories;
111
+ } catch (_error) { /* Retain ownership proofs when cleanup cannot be confirmed. */ }
112
+ }
113
+ transfer.result = { ...result, ...directoryFacts(transfer, result.ok !== true) };
114
+ transfer.finishedAt = Date.now();
115
+ transfer.chunks = [];
116
+ return transfer.result;
117
+ });
118
+ return transfer.promise;
119
+ }
120
+
121
+ async function status(params = {}) {
122
+ pruneReceipts();
123
+ const transfer = receipts.get(String(params.transferId || ''));
124
+ if (!transfer || !params.runProjectId || transfer.projectId !== params.runProjectId
125
+ || treeOperations.getProjectId() !== transfer.projectId) {
126
+ return failure('binary_upload_receipt_missing', 'Asset upload receipt is unavailable for this project.');
127
+ }
128
+ if (transfer.result?.ok !== true && transfer.fileUploadAttempted === true) {
129
+ const evidence = await waitForPath(transfer, {}, 2500).catch(() => null);
130
+ if (evidence?.hashVerified === true) {
131
+ transfer.result = { ok: true, written: true, path: transfer.path,
132
+ changedDocument: true, method: 'remote-hash-reconciliation', ...directoryFacts(transfer),
133
+ entityId: evidence.observed?.id || readTreeEntityId(evidence.observed), sha256: transfer.sha256 };
134
+ }
135
+ }
136
+ return { ok: true, transferId: transfer.id, runProjectId: transfer.projectId,
137
+ state: transfer.finishedAt || transfer.result?.ok === true ? 'completed' : 'running',
138
+ result: transfer.result || null, changedDocument: transfer.mutationAttempted === true };
139
+ }
140
+
141
+ async function commitOnce(params = {}) {
66
142
  const transferId = String(params.transferId || '');
67
143
  const transfer = transfers.get(transferId);
68
144
  if (!transfer) return failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.');
@@ -92,15 +168,30 @@
92
168
  const observed = evidence?.observed;
93
169
  const hashVerified = evidence?.hashVerified === true;
94
170
  assertTransferProject(transfer);
95
- const overwriteVerified = !transfer.overwrite || hashVerified || result?.confirmedByTransport === true;
171
+ const overwriteVerified = hashVerified || result?.confirmedByTransport === true;
96
172
  if (result?.ok && observed && overwriteVerified) {
97
- return { ok: true, written: true, path: transfer.path, method: result.method, changedDocument: true };
173
+ return { ok: true, written: true, path: transfer.path, method: result.method, changedDocument: true,
174
+ entityId: observed.id || observed._id || observed.entityId || observed.fileId || '', sha256: transfer.sha256 };
98
175
  }
99
176
  if (result?.ok && observed && !overwriteVerified) errors.push('Overleaf did not expose enough evidence to verify the binary replacement.');
100
177
  if (result?.reason) errors.push(result.reason);
101
178
  } catch (error) {
179
+ if (error?.code?.startsWith('binary_asset_baseline_')) return {
180
+ ...failure(error.code, error.message), changedDocument: transfer.mutationAttempted === true
181
+ };
102
182
  errors.push(error.message);
103
183
  }
184
+ // Once submission may have happened, changing transports can duplicate
185
+ // or overwrite a successful upload. Reconcile the same attempt instead.
186
+ if (transfer.mutationAttempted === true) {
187
+ const evidence = transfer.fileUploadAttempted === true
188
+ ? await waitForPath(transfer, {}, 12000).catch(() => null) : null;
189
+ if (evidence?.hashVerified === true) {
190
+ return { ok: true, written: true, path: transfer.path,
191
+ method: 'remote-hash-reconciliation', changedDocument: true };
192
+ }
193
+ break;
194
+ }
104
195
  }
105
196
  return { ...failure('binary_upload_unavailable', errors.filter(Boolean).join('; ') || 'No supported Overleaf asset upload path succeeded.'),
106
197
  changedDocument: transfer.mutationAttempted === true };
@@ -110,16 +201,24 @@
110
201
  }
111
202
 
112
203
  function abort(params = {}) {
113
- return { ok: true, aborted: transfers.delete(String(params.transferId || '')) };
204
+ const id = String(params.transferId || '');
205
+ if (receipts.has(id)) return { ok: true, aborted: false, committed: true };
206
+ return { ok: true, aborted: transfers.delete(id) };
114
207
  }
115
208
 
116
209
  function assertTransferProject(transfer) {
117
- if (transfers.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
210
+ if (transfers.get(transfer.id) !== transfer && receipts.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
118
211
  if (!transfer.projectId || treeOperations.getProjectId() !== transfer.projectId) {
119
212
  throw new Error('Project changed during asset upload.');
120
213
  }
121
214
  }
122
215
 
216
+ function directoryFacts(transfer, requireCleanup = false) {
217
+ return { createdDirectories: transfer.createdDirectories.map(proof => ({ ...proof })),
218
+ folderChanged: transfer.folderChanged === true, fileUploadAttempted: transfer.fileUploadAttempted === true,
219
+ directoryCleanupRequired: requireCleanup && transfer.createdDirectories.length > 0 };
220
+ }
221
+
123
222
  async function uploadWithMultipart(file, transfer) {
124
223
  const projectId = treeOperations.getProjectId();
125
224
  if (!projectId || typeof windowRef.fetch !== 'function') return { ok: false, reason: 'Project upload endpoint is unavailable.' };
@@ -134,6 +233,11 @@
134
233
  const headers = csrf ? { 'X-CSRF-Token': csrf } : {};
135
234
  const query = folderId ? `?folder_id=${encodeURIComponent(folderId)}` : '';
136
235
  assertTransferProject(transfer);
236
+ await assertBinaryBaseline(transfer);
237
+ if (parentPath && await findFolderId(parentPath) !== folderId) throw baselineError('The target folder changed before replacement.');
238
+ assertTransferProject(transfer);
239
+ transfer.mutationAttempted = true;
240
+ transfer.fileUploadAttempted = true;
137
241
  const response = await windowRef.fetch(`/project/${encodeURIComponent(projectId)}/upload${query}`, {
138
242
  method: 'POST', body: form, credentials: 'same-origin', headers
139
243
  });
@@ -156,17 +260,24 @@
156
260
 
157
261
  async function uploadWithDom(file, transfer) {
158
262
  const parentPath = transfer.path.split('/').slice(0, -1).join('/');
159
- if (parentPath) {
160
- if (typeof deps.prepareUploadParent !== 'function') {
161
- return { ok: false, reason: 'Verified folder selection is unavailable for nested asset upload.' };
162
- }
163
- await deps.prepareUploadParent(parentPath, {
164
- createMissing: true,
165
- onMutation: () => { transfer.mutationAttempted = true; },
166
- isCurrent: () => treeOperations.getProjectId() === transfer.projectId && transfers.get(transfer.id) === transfer
167
- });
263
+ if (typeof deps.prepareUploadParent !== 'function' || typeof deps.isUploadParentCurrent !== 'function') {
264
+ return { ok: false, reason: 'Verified folder selection is unavailable for asset upload.' };
168
265
  }
266
+ transfer.uploadParent = await deps.prepareUploadParent(parentPath, {
267
+ createMissing: true,
268
+ onMutation: () => { transfer.mutationAttempted = true; transfer.folderChanged = true; },
269
+ onCreatedDirectory: proof => {
270
+ if (proof?.v !== 1 || proof.projectId !== transfer.projectId || typeof proof.path !== 'string'
271
+ || !transfer.path.startsWith(proof.path + '/')) return;
272
+ const next = { v: 1, projectId: proof.projectId, path: proof.path, nativeFolderId: String(proof.nativeFolderId || ''),
273
+ ownershipConfirmed: proof.ownershipConfirmed === true, observedAfterCreate: proof.observedAfterCreate === true };
274
+ const index = transfer.createdDirectories.findIndex(value => value.path === proof.path);
275
+ if (index >= 0) transfer.createdDirectories[index] = next; else transfer.createdDirectories.push(next);
276
+ },
277
+ isCurrent: () => treeOperations.getProjectId() === transfer.projectId && transfers.get(transfer.id) === transfer
278
+ });
169
279
  assertTransferProject(transfer);
280
+ transfer.uploadParentPath = parentPath;
170
281
  let input = findFileInput();
171
282
  if (!input) {
172
283
  const button = findUploadButton();
@@ -178,8 +289,13 @@
178
289
  const data = new windowRef.DataTransfer();
179
290
  data.items.add(file);
180
291
  assertTransferProject(transfer);
292
+ await assertBinaryBaseline(transfer);
293
+ assertTransferProject(transfer);
294
+ if (deps.isUploadParentCurrent(parentPath, transfer.uploadParent) !== true) throw baselineError('The selected upload folder changed before submission.');
295
+ if (input.isConnected === false || findFileInput() !== input) throw baselineError('The upload input changed before submission.');
181
296
  assignFilesToInput(input, data.files, windowRef);
182
297
  transfer.mutationAttempted = true;
298
+ transfer.fileUploadAttempted = true;
183
299
  input.dispatchEvent(new windowRef.Event('input', { bubbles: true }));
184
300
  input.dispatchEvent(new windowRef.Event('change', { bubbles: true }));
185
301
  await clickReplaceIfShown(transfer);
@@ -192,9 +308,14 @@
192
308
  for (const name of ['uploadFile', 'uploadAsset', 'createBinaryFile', 'createFile', 'addFile']) {
193
309
  if (typeof manager[name] !== 'function') continue;
194
310
  try {
311
+ await assertBinaryBaseline(transfer);
312
+ assertTransferProject(transfer);
313
+ if (treeOperations.findFileTreeManager() !== manager) throw baselineError('The upload manager changed before submission.');
314
+ transfer.mutationAttempted = true;
315
+ transfer.fileUploadAttempted = true;
195
316
  await manager[name](transfer.path, file);
196
317
  return { ok: true, method: `fileTreeManager.${name}`, confirmedByTransport: true };
197
- } catch (_error) { /* try the next adapter */ }
318
+ } catch (error) { if (error?.code?.startsWith('binary_asset_baseline_')) throw error; return { ok: false, reason: error.message }; }
198
319
  }
199
320
  return { ok: false, reason: 'Legacy file-tree manager has no compatible asset method.' };
200
321
  }
@@ -248,9 +369,84 @@
248
369
  await delay(180);
249
370
  assertTransferProject(transfer);
250
371
  if (!transfer.overwrite) return;
251
- const button = Array.from(documentRef.querySelectorAll('[role="dialog"] button')).find(node => !isCodexOwnedNode(node)
252
- && /replace|overwrite|替换|覆盖/i.test(node.textContent || ''));
253
- if (button) button.click();
372
+ const candidates = () => Array.from(documentRef.querySelectorAll('[role="dialog"] button')).filter(node => !isCodexOwnedNode(node)
373
+ && /^(replace|overwrite|替换|覆盖)(?:\b|\s|$)/i.test(String(node.textContent || '').trim()));
374
+ const buttons = candidates();
375
+ if (!buttons.length) return;
376
+ const matchesTarget = node => {
377
+ const dialog = node.closest?.('[role="dialog"]');
378
+ const text = String(dialog?.textContent || '');
379
+ return [transfer.path, transfer.path.split('/').pop()].some(path =>
380
+ new RegExp('(^|[^\\w./-])' + path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '($|[^\\w./-])').test(text));
381
+ };
382
+ const intended = buttons.filter(matchesTarget);
383
+ if (intended.length !== 1) throw baselineError('The replacement dialog could not be bound to the target file.');
384
+ const button = intended[0], dialog = button.closest('[role="dialog"]');
385
+ await assertBinaryBaseline(transfer);
386
+ assertTransferProject(transfer);
387
+ if (deps.isUploadParentCurrent(transfer.uploadParentPath, transfer.uploadParent) !== true) throw baselineError('The selected upload folder changed before replacement confirmation.');
388
+ const current = candidates().filter(matchesTarget);
389
+ if (current.length !== 1 || current[0] !== button || button.isConnected === false
390
+ || button.closest('[role="dialog"]') !== dialog) throw baselineError('The replacement dialog changed before confirmation.');
391
+ button.click();
392
+ }
393
+ async function binaryIdentity(transfer) {
394
+ assertTransferProject(transfer);
395
+ const nativeRoot = documentRef.querySelector?.('[data-testid="file-tree-list-root"]');
396
+ // The generic adapter may resolve a root basename to another folder.
397
+ // Only the native ancestry or an exact metadata path can bind old bytes.
398
+ const node = nativeRoot ? findNativeTreeNode(transfer.path, 'file') : null;
399
+ let id = readTreeEntityId(node);
400
+ if (!id) {
401
+ const list = await snapshotRouter?.buildProjectFileList({ force: true, maxAgeMs: 0, preferLightweight: true, allowZipFallback: false });
402
+ const file = list?.files?.find(file => file.path === transfer.path);
403
+ id = file?.id || file?._id || file?.entityId || file?.fileId || '';
404
+ }
405
+ assertTransferProject(transfer);
406
+ return id;
407
+ }
408
+ async function assertBinaryBaseline(transfer) {
409
+ if (!transfer.overwrite) return;
410
+ const { id, digest } = await readBaselineDigest(transfer);
411
+ assertTransferProject(transfer);
412
+ if (digest !== transfer.previousSha256) throw baselineError('The binary file changed since Codex read it. Read the project again before replacing it.');
413
+ transfer.previousEntityId = id;
414
+ }
415
+ function baselineError(message) { return Object.assign(new Error(message), { code: 'binary_asset_baseline_changed' }); }
416
+ async function readBaselineDigest(transfer) {
417
+ const controller = new AbortController();
418
+ let timer, reader;
419
+ const timeout = new Promise((_resolve, reject) => { timer = setTimeout(() => {
420
+ controller.abort(); reader?.cancel().catch(() => {});
421
+ reject(baselineError('Reading the original binary file timed out.'));
422
+ }, 8000); });
423
+ try {
424
+ return await Promise.race([timeout, (async () => {
425
+ const id = await binaryIdentity(transfer);
426
+ if (controller.signal.aborted) throw baselineError('Reading the original binary file timed out.');
427
+ if (!id || (transfer.previousEntityId && transfer.previousEntityId !== id)) throw baselineError('The binary file identity changed before replacement.');
428
+ const response = await windowRef.fetch(`/project/${encodeURIComponent(transfer.projectId)}/file/${encodeURIComponent(id)}`, {
429
+ credentials: 'same-origin', cache: 'no-store', signal: controller.signal
430
+ });
431
+ if (!response.ok || Number(response.headers?.get?.('content-length')) > MAX_ASSET_BYTES) throw baselineError('The original binary file is unavailable or exceeds the read limit.');
432
+ let bytes;
433
+ if (response.body?.getReader) {
434
+ reader = response.body.getReader(); const chunks = []; let size = 0;
435
+ for (;;) { const chunk = await reader.read(); if (chunk.done) break;
436
+ size += chunk.value.byteLength;
437
+ if (size > MAX_ASSET_BYTES) throw baselineError('The original binary file exceeds the read limit.');
438
+ chunks.push(chunk.value);
439
+ }
440
+ bytes = joinChunks(chunks, size);
441
+ } else bytes = new Uint8Array(await response.arrayBuffer());
442
+ if (bytes.byteLength > MAX_ASSET_BYTES) throw baselineError('The original binary file exceeds the read limit.');
443
+ const digest = await sha256Hex(bytes, windowRef.crypto);
444
+ if (await binaryIdentity(transfer) !== id) throw baselineError('The binary file identity changed while its original bytes were read.');
445
+ assertTransferProject(transfer);
446
+ if (controller.signal.aborted) throw baselineError('Reading the original binary file timed out.');
447
+ return { id, digest };
448
+ })()]);
449
+ } finally { clearTimeout(timer); controller.abort(); reader?.cancel().catch(() => {}); }
254
450
  }
255
451
  function readTreeEntityId(node) {
256
452
  const owner = node?.closest?.('[data-entity-id], [data-file-id], [data-folder-id], [data-id]');
@@ -293,9 +489,13 @@
293
489
  let lastObserved = null;
294
490
  while (Date.now() < deadline) {
295
491
  assertTransferProject(transfer);
296
- const node = findNativeTreeNode(projectPath, 'file')
297
- || treeOperations.findFileTreeNode(projectPath, { invalidateCache: true });
298
- let observed = node || treeOperations.projectPathExists(projectPath)
492
+ // A matching digest identifies bytes, not their project path. In the
493
+ // native tree, require the exact ancestry; the generic tree adapter
494
+ // can resolve a root name to an identically named nested file.
495
+ const nativeRoot = documentRef.querySelector?.('[data-testid="file-tree-list-root"]');
496
+ const node = nativeRoot ? findNativeTreeNode(projectPath, 'file')
497
+ : treeOperations.findFileTreeNode(projectPath, { invalidateCache: true });
498
+ let observed = node || (!nativeRoot && treeOperations.projectPathExists(projectPath))
299
499
  ? { path: projectPath, id: readTreeEntityId(node) } : null;
300
500
  if (!observed?.id) {
301
501
  try {
@@ -313,7 +513,7 @@
313
513
  assertTransferProject(transfer);
314
514
  // An existing path is not replacement evidence. Re-read identity and
315
515
  // bytes while the native uploader and file-tree events settle.
316
- if (!transfer.overwrite || hashVerified || result.confirmedByTransport === true) {
516
+ if (hashVerified || result.confirmedByTransport === true) {
317
517
  return { observed, hashVerified };
318
518
  }
319
519
  }
@@ -333,7 +533,7 @@
333
533
  return digest === transfer.sha256;
334
534
  }
335
535
  function delay(ms) { return new Promise(resolve => windowRef.setTimeout(resolve, ms)); }
336
- return { abort, append, begin, commit };
536
+ return { abort, append, begin, commit, status };
337
537
  }
338
538
 
339
539
  function normalizePath(value) {