codex-overleaf-link 2.4.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/README.md +272 -314
  2. package/README.zh-CN.md +287 -329
  3. package/extension/bootstrap/background.js +47 -10
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +10 -16
  6. package/extension/bootstrap/update.js +25 -2
  7. package/extension/runtime-manifest.json +3 -1
  8. package/extension/src/background.js +28 -3
  9. package/extension/src/backgroundSharedSessions.js +380 -0
  10. package/extension/src/backgroundUpdateCoordinator.js +442 -379
  11. package/extension/src/content/activeTurnControl.js +8 -0
  12. package/extension/src/content/assetTransferBroker.js +186 -12
  13. package/extension/src/content/changeHistoryView.js +175 -0
  14. package/extension/src/content/composerAttachments.js +24 -22
  15. package/extension/src/content/composerPanel.js +23 -8
  16. package/extension/src/content/contentRuntime.js +507 -507
  17. package/extension/src/content/diagnosticsController.js +1 -2
  18. package/extension/src/content/diagnosticsExport.js +222 -0
  19. package/extension/src/content/diagnosticsPanel.js +48 -19
  20. package/extension/src/content/diffReviewPanel.js +8 -5
  21. package/extension/src/content/generated/content.bundle.js +8330 -1349
  22. package/extension/src/content/generated/content.bundle.meta.json +22 -3
  23. package/extension/src/content/legacyGlobalRegistry.js +19 -1
  24. package/extension/src/content/localSkillsPanel.js +42 -7
  25. package/extension/src/content/markdownDomRenderer.js +9 -4
  26. package/extension/src/content/modelPicker.js +1 -1
  27. package/extension/src/content/pageBridgeClient.js +18 -3
  28. package/extension/src/content/panelMaintenance.js +48 -121
  29. package/extension/src/content/panelRenderer.js +4 -0
  30. package/extension/src/content/pendingInputView.js +109 -76
  31. package/extension/src/content/providerSettingsCoordinator.js +17 -6
  32. package/extension/src/content/providerSettingsDialog.js +661 -108
  33. package/extension/src/content/recentProjects.js +10 -1
  34. package/extension/src/content/runActivitySummary.js +631 -0
  35. package/extension/src/content/runController.js +3 -1
  36. package/extension/src/content/runFailureNotice.js +14 -1
  37. package/extension/src/content/runGuidanceController.js +4 -3
  38. package/extension/src/content/runPresence.js +306 -0
  39. package/extension/src/content/runQueueScheduler.js +2 -1
  40. package/extension/src/content/runResultActions.js +123 -3
  41. package/extension/src/content/runTimelineView.js +186 -154
  42. package/extension/src/content/selectionContextView.js +300 -0
  43. package/extension/src/content/sessionPersistence.js +6 -1
  44. package/extension/src/content/settingsPanel.js +77 -52
  45. package/extension/src/content/settingsWorkbench.js +374 -0
  46. package/extension/src/content/sharedSessionViewSync.js +72 -0
  47. package/extension/src/content/subagentActivityView.js +195 -0
  48. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  49. package/extension/src/content/updateNotice.js +121 -50
  50. package/extension/src/content/writebackController.js +257 -0
  51. package/extension/src/content/writebackOrchestrator.js +146 -141
  52. package/extension/src/content/writebackPlan.js +231 -0
  53. package/extension/src/content/writebackRecovery.js +122 -0
  54. package/extension/src/content/writingStyleSettings.js +576 -0
  55. package/extension/src/content/writtenChangesView.js +152 -0
  56. package/extension/src/page/binaryAssetUploader.js +97 -19
  57. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  58. package/extension/src/page/referenceProjects.js +148 -0
  59. package/extension/src/page/saveState.js +14 -2
  60. package/extension/src/page/snapshotRouter.js +44 -13
  61. package/extension/src/page/textCoordinates.js +102 -0
  62. package/extension/src/page/textFileCreator.js +350 -59
  63. package/extension/src/page/trackedChangeCapture.js +42 -56
  64. package/extension/src/page/trackedChangeOwnership.js +198 -0
  65. package/extension/src/page/trackedChangeReplay.js +164 -0
  66. package/extension/src/page/trackedChangesLifecycle.js +135 -149
  67. package/extension/src/page/treeOperations.js +26 -2
  68. package/extension/src/page/writebackReceiptJournal.js +112 -0
  69. package/extension/src/page/writebackRouter.js +175 -135
  70. package/extension/src/pageBridge.js +56 -101
  71. package/extension/src/popup.js +203 -59
  72. package/extension/src/shared/agentTranscript.js +18 -4
  73. package/extension/src/shared/auditRecords.js +71 -0
  74. package/extension/src/shared/changeHistoryModel.js +94 -0
  75. package/extension/src/shared/compatibility.js +1 -1
  76. package/extension/src/shared/compileAdapter.js +42 -0
  77. package/extension/src/shared/failureReasons.js +20 -0
  78. package/extension/src/shared/globalPreferences.js +1 -1
  79. package/extension/src/shared/i18n.js +93 -25
  80. package/extension/src/shared/managedUpdateProjection.js +6 -3
  81. package/extension/src/shared/pageRpcContract.js +7 -1
  82. package/extension/src/shared/providerProfiles.js +109 -0
  83. package/extension/src/shared/runActivityModel.js +361 -0
  84. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  85. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  86. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  87. package/extension/src/shared/selectionContext.js +28 -0
  88. package/extension/src/shared/sessionState.js +97 -6
  89. package/extension/src/shared/sharedSessionBridge.js +288 -0
  90. package/extension/src/shared/storageDb.js +22 -55
  91. package/extension/src/shared/storageMigration.js +3 -0
  92. package/extension/src/shared/storageRunActions.js +10 -4
  93. package/extension/src/shared/storageValuePruning.js +40 -0
  94. package/extension/src/shared/subagentActivityModel.js +174 -0
  95. package/extension/src/shared/undoOperations.js +87 -0
  96. package/extension/src/shared/updateConsent.js +37 -8
  97. package/extension/src/shared/writebackIntent.js +62 -0
  98. package/extension/src/shared/writebackSettlement.js +29 -10
  99. package/extension/src/shared/writingStyle.js +33 -0
  100. package/extension/styles/panel.css +1972 -384
  101. package/extension/styles/popup.css +110 -0
  102. package/native-host/src/anthropicBridgeRequest.js +2 -1
  103. package/native-host/src/codexModelRecovery.js +107 -0
  104. package/native-host/src/codexPromptAssembly.js +2 -0
  105. package/native-host/src/codexProviderLaunch.js +3 -3
  106. package/native-host/src/codexRuntimeIdentity.js +1 -0
  107. package/native-host/src/codexSessionRunner.js +106 -140
  108. package/native-host/src/codexSessionTiming.js +3 -0
  109. package/native-host/src/managedInstall.js +5 -4
  110. package/native-host/src/nativeEnvironment.js +70 -0
  111. package/native-host/src/nativeQuotas.js +15 -8
  112. package/native-host/src/overleafWorkspaceRules.js +16 -0
  113. package/native-host/src/processStopBarrier.js +80 -0
  114. package/native-host/src/providerModelCatalog.js +4 -3
  115. package/native-host/src/providerProfile.js +13 -1
  116. package/native-host/src/providerReasoning.js +9 -5
  117. package/native-host/src/providerStore.js +4 -0
  118. package/native-host/src/releaseTransport.js +14 -5
  119. package/native-host/src/selectionScope.js +58 -0
  120. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  121. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  122. package/native-host/src/subagentBroker.js +102 -98
  123. package/native-host/src/subagentTelemetry.js +316 -0
  124. package/native-host/src/subagentWorkspace.js +103 -0
  125. package/native-host/src/subagentWorkspacePath.js +31 -0
  126. package/native-host/src/taskRunnerRuntime.js +55 -5
  127. package/native-host/src/turnAttachments.js +113 -0
  128. package/native-host/src/updateManager.js +122 -12
  129. package/native-host/src/updateNetworkControl.js +75 -0
  130. package/native-host/src/updateTrust.js +21 -2
  131. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  132. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  133. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  134. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  135. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  136. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  137. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  138. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  139. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  140. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  141. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  142. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  143. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  144. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  145. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  146. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  147. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  148. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  149. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  150. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  151. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  152. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  153. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  154. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  155. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  156. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  157. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  158. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  159. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  160. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  161. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  162. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  163. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  164. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  165. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  166. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  167. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  168. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  169. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  170. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  171. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  172. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  173. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  174. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  175. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  176. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  177. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  178. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  179. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  180. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  181. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  182. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  183. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  184. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  185. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  186. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  187. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  188. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  189. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  190. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  191. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  192. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  193. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  194. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  195. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  196. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  197. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  198. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  199. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  200. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  201. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  202. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  203. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  303. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  304. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  305. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  306. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  307. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  308. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  309. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  310. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  311. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  312. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  313. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  314. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  315. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  316. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  317. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  318. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  319. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  320. package/native-host/src/writingStyleEvidence.js +102 -0
  321. package/native-host/src/writingStylePdfWorker.js +35 -0
  322. package/native-host/src/writingStyleProfile.js +165 -0
  323. package/native-host/src/writingStyleRuntime.js +447 -0
  324. package/package.json +1 -1
@@ -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,6 +42,13 @@
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
+ pruneReceipts();
46
+ if (transfers.has(transferId) || receipts.has(transferId)) {
47
+ return failure('binary_upload_transfer_conflict', 'Asset transfer ID is already in use.');
48
+ }
49
+ if (transfers.size >= 32 || receipts.size >= 32) {
50
+ return failure('binary_upload_busy', 'Too many asset transfers are pending.');
51
+ }
33
52
  transfers.set(transferId, {
34
53
  id: transferId,
35
54
  path: projectPath,
@@ -62,7 +81,50 @@
62
81
  return { ok: true, transferId: transfer.id, received: transfer.received };
63
82
  }
64
83
 
65
- async function commit(params = {}) {
84
+ function commit(params = {}) {
85
+ const id = String(params.transferId || '');
86
+ const existing = receipts.get(id);
87
+ if (existing) {
88
+ if (existing.projectId !== treeOperations.getProjectId()) {
89
+ return Promise.resolve(failure('binary_upload_scope_changed', 'Asset project changed.'));
90
+ }
91
+ return existing.promise;
92
+ }
93
+ const transfer = transfers.get(id);
94
+ if (!transfer) return Promise.resolve(failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.'));
95
+ receipts.set(id, transfer);
96
+ transfer.promise = commitOnce(params).catch(error => ({
97
+ ...failure(error.code || 'binary_upload_failed', error.message),
98
+ changedDocument: transfer.mutationAttempted === true
99
+ })).then(result => {
100
+ transfer.result = result;
101
+ transfer.finishedAt = Date.now();
102
+ transfer.chunks = [];
103
+ return result;
104
+ });
105
+ return transfer.promise;
106
+ }
107
+
108
+ async function status(params = {}) {
109
+ pruneReceipts();
110
+ const transfer = receipts.get(String(params.transferId || ''));
111
+ if (!transfer || !params.runProjectId || transfer.projectId !== params.runProjectId
112
+ || treeOperations.getProjectId() !== transfer.projectId) {
113
+ return failure('binary_upload_receipt_missing', 'Asset upload receipt is unavailable for this project.');
114
+ }
115
+ if (transfer.result?.ok !== true && transfer.mutationAttempted === true) {
116
+ const evidence = await waitForPath(transfer, {}, 2500).catch(() => null);
117
+ if (evidence?.hashVerified === true) {
118
+ transfer.result = { ok: true, written: true, path: transfer.path,
119
+ changedDocument: true, method: 'remote-hash-reconciliation' };
120
+ }
121
+ }
122
+ return { ok: true, transferId: transfer.id, runProjectId: transfer.projectId,
123
+ state: transfer.finishedAt || transfer.result?.ok === true ? 'completed' : 'running',
124
+ result: transfer.result || null, changedDocument: transfer.mutationAttempted === true };
125
+ }
126
+
127
+ async function commitOnce(params = {}) {
66
128
  const transferId = String(params.transferId || '');
67
129
  const transfer = transfers.get(transferId);
68
130
  if (!transfer) return failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.');
@@ -92,7 +154,7 @@
92
154
  const observed = evidence?.observed;
93
155
  const hashVerified = evidence?.hashVerified === true;
94
156
  assertTransferProject(transfer);
95
- const overwriteVerified = !transfer.overwrite || hashVerified || result?.confirmedByTransport === true;
157
+ const overwriteVerified = hashVerified || result?.confirmedByTransport === true;
96
158
  if (result?.ok && observed && overwriteVerified) {
97
159
  return { ok: true, written: true, path: transfer.path, method: result.method, changedDocument: true };
98
160
  }
@@ -101,6 +163,16 @@
101
163
  } catch (error) {
102
164
  errors.push(error.message);
103
165
  }
166
+ // Once submission may have happened, changing transports can duplicate
167
+ // or overwrite a successful upload. Reconcile the same attempt instead.
168
+ if (transfer.mutationAttempted === true) {
169
+ const evidence = await waitForPath(transfer, {}, 12000).catch(() => null);
170
+ if (evidence?.hashVerified === true) {
171
+ return { ok: true, written: true, path: transfer.path,
172
+ method: 'remote-hash-reconciliation', changedDocument: true };
173
+ }
174
+ break;
175
+ }
104
176
  }
105
177
  return { ...failure('binary_upload_unavailable', errors.filter(Boolean).join('; ') || 'No supported Overleaf asset upload path succeeded.'),
106
178
  changedDocument: transfer.mutationAttempted === true };
@@ -110,11 +182,13 @@
110
182
  }
111
183
 
112
184
  function abort(params = {}) {
113
- return { ok: true, aborted: transfers.delete(String(params.transferId || '')) };
185
+ const id = String(params.transferId || '');
186
+ if (receipts.has(id)) return { ok: true, aborted: false, committed: true };
187
+ return { ok: true, aborted: transfers.delete(id) };
114
188
  }
115
189
 
116
190
  function assertTransferProject(transfer) {
117
- if (transfers.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
191
+ if (transfers.get(transfer.id) !== transfer && receipts.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
118
192
  if (!transfer.projectId || treeOperations.getProjectId() !== transfer.projectId) {
119
193
  throw new Error('Project changed during asset upload.');
120
194
  }
@@ -134,6 +208,7 @@
134
208
  const headers = csrf ? { 'X-CSRF-Token': csrf } : {};
135
209
  const query = folderId ? `?folder_id=${encodeURIComponent(folderId)}` : '';
136
210
  assertTransferProject(transfer);
211
+ transfer.mutationAttempted = true;
137
212
  const response = await windowRef.fetch(`/project/${encodeURIComponent(projectId)}/upload${query}`, {
138
213
  method: 'POST', body: form, credentials: 'same-origin', headers
139
214
  });
@@ -156,16 +231,14 @@
156
231
 
157
232
  async function uploadWithDom(file, transfer) {
158
233
  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
- });
234
+ if (typeof deps.prepareUploadParent !== 'function') {
235
+ return { ok: false, reason: 'Verified folder selection is unavailable for asset upload.' };
168
236
  }
237
+ await deps.prepareUploadParent(parentPath, {
238
+ createMissing: true,
239
+ onMutation: () => { transfer.mutationAttempted = true; },
240
+ isCurrent: () => treeOperations.getProjectId() === transfer.projectId && transfers.get(transfer.id) === transfer
241
+ });
169
242
  assertTransferProject(transfer);
170
243
  let input = findFileInput();
171
244
  if (!input) {
@@ -192,9 +265,10 @@
192
265
  for (const name of ['uploadFile', 'uploadAsset', 'createBinaryFile', 'createFile', 'addFile']) {
193
266
  if (typeof manager[name] !== 'function') continue;
194
267
  try {
268
+ transfer.mutationAttempted = true;
195
269
  await manager[name](transfer.path, file);
196
270
  return { ok: true, method: `fileTreeManager.${name}`, confirmedByTransport: true };
197
- } catch (_error) { /* try the next adapter */ }
271
+ } catch (error) { return { ok: false, reason: error.message }; }
198
272
  }
199
273
  return { ok: false, reason: 'Legacy file-tree manager has no compatible asset method.' };
200
274
  }
@@ -293,9 +367,13 @@
293
367
  let lastObserved = null;
294
368
  while (Date.now() < deadline) {
295
369
  assertTransferProject(transfer);
296
- const node = findNativeTreeNode(projectPath, 'file')
297
- || treeOperations.findFileTreeNode(projectPath, { invalidateCache: true });
298
- let observed = node || treeOperations.projectPathExists(projectPath)
370
+ // A matching digest identifies bytes, not their project path. In the
371
+ // native tree, require the exact ancestry; the generic tree adapter
372
+ // can resolve a root name to an identically named nested file.
373
+ const nativeRoot = documentRef.querySelector?.('[data-testid="file-tree-list-root"]');
374
+ const node = nativeRoot ? findNativeTreeNode(projectPath, 'file')
375
+ : treeOperations.findFileTreeNode(projectPath, { invalidateCache: true });
376
+ let observed = node || (!nativeRoot && treeOperations.projectPathExists(projectPath))
299
377
  ? { path: projectPath, id: readTreeEntityId(node) } : null;
300
378
  if (!observed?.id) {
301
379
  try {
@@ -313,7 +391,7 @@
313
391
  assertTransferProject(transfer);
314
392
  // An existing path is not replacement evidence. Re-read identity and
315
393
  // bytes while the native uploader and file-tree events settle.
316
- if (!transfer.overwrite || hashVerified || result.confirmedByTransport === true) {
394
+ if (hashVerified || result.confirmedByTransport === true) {
317
395
  return { observed, hashVerified };
318
396
  }
319
397
  }
@@ -333,7 +411,7 @@
333
411
  return digest === transfer.sha256;
334
412
  }
335
413
  function delay(ms) { return new Promise(resolve => windowRef.setTimeout(resolve, ms)); }
336
- return { abort, append, begin, commit };
414
+ return { abort, append, begin, commit, status };
337
415
  }
338
416
 
339
417
  function normalizePath(value) {
@@ -120,6 +120,7 @@
120
120
  deps.getProjectId?.() || pageWindow.location.pathname || pageWindow.location.href,
121
121
  params.preferLightweight ? 'lightweight' : 'full',
122
122
  params.zipOnly ? 'zip-only' : 'zip-or-page',
123
+ params.serverOnly === true ? 'server-only' : 'editor-overlay',
123
124
  params.allowZipFallback === false ? 'no-zip-fallback' : 'zip-fallback',
124
125
  params.allowEditorNavigation === false ? 'no-editor-navigation' : params.allowEditorNavigation === true ? 'editor-navigation' : 'default-editor-navigation',
125
126
  params.requireFullProject ? 'require-full-project' : 'allow-partial-project',
@@ -0,0 +1,148 @@
1
+ (function (root, factory) {
2
+ const api = factory();
3
+ if (typeof module === 'object' && module.exports) module.exports = api;
4
+ if (root) root.CodexOverleafReferenceProjects = api;
5
+ })(typeof window !== 'undefined' ? window : globalThis, function () {
6
+ 'use strict';
7
+ const MAX_CATALOG_BYTES = 8 * 1024 * 1024;
8
+ const MAX_PROJECT_BYTES = 4 * 1024 * 1024;
9
+ const validId = value => /^[a-f0-9]{24}$/i.test(value);
10
+
11
+ function failure(code, message) { return Object.assign(new Error(message), { code }); }
12
+
13
+ function parseCatalog(doc) {
14
+ const element = doc.querySelector('meta[name="ol-prefetchedProjectsBlob"]')
15
+ || doc.querySelector('meta[name="ol-projects"]');
16
+ if (!element) throw failure('reference_catalog_unavailable',
17
+ 'Overleaf did not return its project directory. Check that this account is signed in, then retry.');
18
+ let data;
19
+ try { data = JSON.parse(element.getAttribute('content') || ''); }
20
+ catch (_) { throw failure('reference_catalog_invalid', 'Overleaf returned an unreadable project directory. Retry loading projects.'); }
21
+ const rows = Array.isArray(data) ? data : data?.projects;
22
+ if (!Array.isArray(rows)) throw failure('reference_catalog_invalid', 'Overleaf returned an invalid project directory.');
23
+ const total = Array.isArray(data) ? rows.length : Number(data.totalSize);
24
+ if (Number.isFinite(total) && total > rows.length) {
25
+ throw failure('reference_catalog_incomplete', 'Overleaf returned only part of the project directory. Refresh Overleaf and retry loading projects.');
26
+ }
27
+ const result = new Map();
28
+ for (const row of rows) {
29
+ if (row?.trashed === true) continue;
30
+ const projectId = String(row?.id || row?._id || '');
31
+ if (!validId(projectId) || typeof row?.name !== 'string') {
32
+ throw failure('reference_catalog_invalid', 'A project entry could not be read. Retry loading the directory.');
33
+ }
34
+ result.set(projectId, {
35
+ projectId, name: row.name.slice(0, 300),
36
+ archived: row.archived === true,
37
+ shared: row.accessLevel !== 'owner',
38
+ readOnly: ['readOnly', 'read-only', 'read'].includes(row.accessLevel),
39
+ lastUpdated: typeof row.lastUpdated === 'string' ? row.lastUpdated : ''
40
+ });
41
+ }
42
+ return [...result.values()].sort((a, b) =>
43
+ (Date.parse(b.lastUpdated) || 0) - (Date.parse(a.lastUpdated) || 0) || a.name.localeCompare(b.name));
44
+ }
45
+
46
+ function create(deps = {}) {
47
+ const pageWindow = deps.window || window;
48
+ const fetchImpl = deps.fetch || pageWindow.fetch.bind(pageWindow);
49
+ const reads = new Map();
50
+
51
+ async function catalogText(response) {
52
+ if (Number(response.headers.get('content-length')) > MAX_CATALOG_BYTES) {
53
+ throw failure('reference_catalog_too_large', 'The Overleaf project directory is too large to load.');
54
+ }
55
+ if (!response.body?.getReader) {
56
+ const text = await response.text();
57
+ if (new TextEncoder().encode(text).byteLength > MAX_CATALOG_BYTES) {
58
+ throw failure('reference_catalog_too_large', 'The Overleaf project directory is too large to load.');
59
+ }
60
+ return text;
61
+ }
62
+ const reader = response.body.getReader(), decoder = new TextDecoder();
63
+ let bytes = 0, text = '';
64
+ try {
65
+ while (true) {
66
+ const chunk = await reader.read();
67
+ if (chunk.done) break;
68
+ bytes += chunk.value.byteLength;
69
+ if (bytes > MAX_CATALOG_BYTES) {
70
+ await reader.cancel();
71
+ throw failure('reference_catalog_too_large', 'The Overleaf project directory is too large to load.');
72
+ }
73
+ text += decoder.decode(chunk.value, { stream: true });
74
+ }
75
+ return text + decoder.decode();
76
+ } finally { reader.releaseLock(); }
77
+ }
78
+
79
+ async function listProjects() {
80
+ const controller = new AbortController();
81
+ const timer = setTimeout(() => controller.abort(), 20000);
82
+ try {
83
+ const response = await fetchImpl(new URL('/project', pageWindow.location.origin).href, {
84
+ credentials: 'include', cache: 'no-store', headers: { accept: 'text/html' },
85
+ signal: controller.signal
86
+ });
87
+ if (!response.ok) throw failure('reference_catalog_http', 'Overleaf could not load projects (HTTP ' + response.status + '). Retry loading projects.');
88
+ if (response.url && new URL(response.url).origin !== pageWindow.location.origin) {
89
+ throw failure('reference_catalog_login', 'Sign in to Overleaf on this domain, then retry loading projects.');
90
+ }
91
+ const text = await catalogText(response);
92
+ const parser = new pageWindow.DOMParser();
93
+ const projects = parseCatalog(parser.parseFromString(text, 'text/html'));
94
+ return { ok: true, projects, total: projects.length, capturedAt: new Date().toISOString() };
95
+ } catch (error) {
96
+ return { ok: false, code: error.code || 'reference_catalog_failed',
97
+ error: controller.signal.aborted ? 'Loading Overleaf projects timed out. Retry loading projects.' : error.message };
98
+ } finally { clearTimeout(timer); }
99
+ }
100
+
101
+ async function readProject(params = {}) {
102
+ const projectId = String(params.projectId || '');
103
+ const requestId = String(params.requestId || '');
104
+ if (!validId(projectId) || !requestId || requestId.length > 160) {
105
+ return { ok: false, code: 'reference_project_invalid', error: 'Reference project identity is invalid.' };
106
+ }
107
+ if (reads.has(requestId)) return { ok: false, code: 'reference_read_busy', error: 'This reference read is already running.' };
108
+ const controller = new AbortController();
109
+ reads.set(requestId, controller);
110
+ try {
111
+ const snapshot = await deps.snapshotRouter.fetchProjectZipSnapshot({
112
+ includeBinaryFiles: false, includeContent: true, zipTimeoutMs: 30000
113
+ }, { projectId, signal: controller.signal });
114
+ if (controller.signal.aborted) throw failure('reference_read_cancelled', 'Reference reading was cancelled.');
115
+ if (!snapshot?.ok) throw failure('reference_project_unavailable', snapshot?.reason || 'The reference project could not be read.');
116
+ const files = (snapshot.files || []).filter(file => /\.tex$/i.test(file.path || ''));
117
+ if (!files.length) throw failure('reference_project_empty', 'This project contains no readable TeX files.');
118
+ if (files.length > 1000 || files.some(file => typeof file.content !== 'string')) {
119
+ throw failure('reference_project_incomplete', 'The reference project could not be read completely.');
120
+ }
121
+ const skipped = snapshot.skipped || [];
122
+ if (skipped.some(file => /\.tex$/i.test(typeof file === 'string' ? file : file?.path || ''))) {
123
+ throw failure('reference_project_incomplete', 'Some TeX files in the reference project could not be read.');
124
+ }
125
+ const encoder = new TextEncoder();
126
+ const size = files.reduce((sum, file) => sum + encoder.encode(file.content).byteLength, 0);
127
+ if (size > MAX_PROJECT_BYTES) throw failure('reference_project_too_large', 'Reference project text exceeds 4 MiB. Add a representative PDF instead.');
128
+ return {
129
+ ok: true, projectId, capturedAt: new Date().toISOString(),
130
+ files: files.map(file => ({ path: file.path, content: file.content, kind: 'text' })),
131
+ capabilities: { referenceTextComplete: true, method: 'overleaf-reference-zip' }
132
+ };
133
+ } catch (error) {
134
+ return { ok: false, code: error.code || 'reference_project_failed', error: error.message };
135
+ } finally { reads.delete(requestId); }
136
+ }
137
+
138
+ function cancelRead(params = {}) {
139
+ const requestId = String(params.requestId || '');
140
+ const controller = reads.get(requestId);
141
+ if (controller) controller.abort();
142
+ return { ok: true, cancelled: Boolean(controller) };
143
+ }
144
+
145
+ return { listProjects, readProject, cancelRead };
146
+ }
147
+ return { create, parseCatalog };
148
+ });
@@ -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
  }