codex-overleaf-link 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/README.md +272 -311
  2. package/README.zh-CN.md +287 -326
  3. package/extension/bootstrap/background.js +47 -10
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +10 -16
  6. package/extension/bootstrap/update.js +25 -2
  7. package/extension/runtime-manifest.json +3 -1
  8. package/extension/src/background.js +28 -3
  9. package/extension/src/backgroundSharedSessions.js +380 -0
  10. package/extension/src/backgroundUpdateCoordinator.js +442 -379
  11. package/extension/src/content/activeTurnControl.js +8 -0
  12. package/extension/src/content/assetTransferBroker.js +186 -12
  13. package/extension/src/content/changeHistoryView.js +175 -0
  14. package/extension/src/content/composerAttachments.js +24 -22
  15. package/extension/src/content/composerPanel.js +23 -8
  16. package/extension/src/content/contentRuntime.js +572 -571
  17. package/extension/src/content/diagnosticsController.js +1 -2
  18. package/extension/src/content/diagnosticsExport.js +222 -0
  19. package/extension/src/content/diagnosticsPanel.js +48 -19
  20. package/extension/src/content/diffReviewPanel.js +8 -5
  21. package/extension/src/content/generated/content.bundle.js +8974 -1781
  22. package/extension/src/content/generated/content.bundle.meta.json +22 -3
  23. package/extension/src/content/legacyGlobalRegistry.js +19 -1
  24. package/extension/src/content/localSkillsPanel.js +42 -7
  25. package/extension/src/content/markdownDomRenderer.js +9 -4
  26. package/extension/src/content/modelPicker.js +1 -1
  27. package/extension/src/content/pageBridgeClient.js +18 -3
  28. package/extension/src/content/panelMaintenance.js +51 -120
  29. package/extension/src/content/panelRenderer.js +4 -0
  30. package/extension/src/content/pendingInputView.js +109 -76
  31. package/extension/src/content/providerSettingsCoordinator.js +17 -6
  32. package/extension/src/content/providerSettingsDialog.js +661 -108
  33. package/extension/src/content/recentProjects.js +364 -356
  34. package/extension/src/content/runActivitySummary.js +631 -0
  35. package/extension/src/content/runController.js +3 -1
  36. package/extension/src/content/runFailureNotice.js +14 -1
  37. package/extension/src/content/runGuidanceController.js +4 -3
  38. package/extension/src/content/runPresence.js +306 -0
  39. package/extension/src/content/runQueueScheduler.js +2 -1
  40. package/extension/src/content/runResultActions.js +123 -3
  41. package/extension/src/content/runTimelineView.js +194 -164
  42. package/extension/src/content/scopedPersistenceCoordinator.js +2 -1
  43. package/extension/src/content/scopedPersistencePanelState.js +45 -2
  44. package/extension/src/content/selectionContextView.js +300 -0
  45. package/extension/src/content/sessionPersistence.js +10 -2
  46. package/extension/src/content/settingsPanel.js +93 -54
  47. package/extension/src/content/settingsWorkbench.js +374 -0
  48. package/extension/src/content/sharedSessionViewSync.js +72 -0
  49. package/extension/src/content/subagentActivityView.js +195 -0
  50. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  51. package/extension/src/content/updateNotice.js +121 -50
  52. package/extension/src/content/writebackController.js +257 -0
  53. package/extension/src/content/writebackOrchestrator.js +146 -141
  54. package/extension/src/content/writebackPlan.js +231 -0
  55. package/extension/src/content/writebackRecovery.js +122 -0
  56. package/extension/src/content/writingStyleSettings.js +576 -0
  57. package/extension/src/content/writtenChangesView.js +152 -0
  58. package/extension/src/page/binaryAssetUploader.js +97 -19
  59. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  60. package/extension/src/page/referenceProjects.js +148 -0
  61. package/extension/src/page/saveState.js +58 -3
  62. package/extension/src/page/snapshotRouter.js +44 -13
  63. package/extension/src/page/textCoordinates.js +102 -0
  64. package/extension/src/page/textFileCreator.js +350 -59
  65. package/extension/src/page/trackedChangeCapture.js +42 -56
  66. package/extension/src/page/trackedChangeOwnership.js +198 -0
  67. package/extension/src/page/trackedChangeReplay.js +164 -0
  68. package/extension/src/page/trackedChangesLifecycle.js +139 -150
  69. package/extension/src/page/treeOperations.js +36 -29
  70. package/extension/src/page/writebackReceiptJournal.js +112 -0
  71. package/extension/src/page/writebackRouter.js +176 -136
  72. package/extension/src/pageBridge.js +59 -101
  73. package/extension/src/popup.js +203 -59
  74. package/extension/src/shared/agentTranscript.js +18 -4
  75. package/extension/src/shared/auditRecords.js +71 -0
  76. package/extension/src/shared/changeHistoryModel.js +94 -0
  77. package/extension/src/shared/compatibility.js +1 -1
  78. package/extension/src/shared/compileAdapter.js +42 -0
  79. package/extension/src/shared/failureReasons.js +20 -0
  80. package/extension/src/shared/globalPreferences.js +1 -1
  81. package/extension/src/shared/i18n.js +187 -65
  82. package/extension/src/shared/managedUpdateProjection.js +6 -3
  83. package/extension/src/shared/pageRpcContract.js +7 -1
  84. package/extension/src/shared/providerProfiles.js +109 -0
  85. package/extension/src/shared/runActivityModel.js +361 -0
  86. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  87. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  88. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  89. package/extension/src/shared/selectionContext.js +28 -0
  90. package/extension/src/shared/sessionState.js +97 -6
  91. package/extension/src/shared/sharedSessionBridge.js +288 -0
  92. package/extension/src/shared/storageDb.js +23 -55
  93. package/extension/src/shared/storageMigration.js +3 -0
  94. package/extension/src/shared/storageRunActions.js +54 -4
  95. package/extension/src/shared/storageValuePruning.js +40 -0
  96. package/extension/src/shared/subagentActivityModel.js +174 -0
  97. package/extension/src/shared/undoOperations.js +87 -0
  98. package/extension/src/shared/updateConsent.js +37 -8
  99. package/extension/src/shared/writebackIntent.js +62 -0
  100. package/extension/src/shared/writebackSettlement.js +29 -10
  101. package/extension/src/shared/writingStyle.js +33 -0
  102. package/extension/styles/panel.css +2136 -744
  103. package/extension/styles/popup.css +110 -0
  104. package/native-host/src/anthropicBridgeRequest.js +2 -1
  105. package/native-host/src/codexModelRecovery.js +107 -0
  106. package/native-host/src/codexPromptAssembly.js +2 -0
  107. package/native-host/src/codexProviderLaunch.js +3 -3
  108. package/native-host/src/codexRuntimeIdentity.js +1 -0
  109. package/native-host/src/codexSessionRunner.js +146 -177
  110. package/native-host/src/codexSessionTiming.js +71 -0
  111. package/native-host/src/managedInstall.js +5 -4
  112. package/native-host/src/nativeEnvironment.js +70 -0
  113. package/native-host/src/nativeQuotas.js +15 -8
  114. package/native-host/src/overleafWorkspaceRules.js +16 -0
  115. package/native-host/src/processStopBarrier.js +80 -0
  116. package/native-host/src/providerModelCatalog.js +4 -3
  117. package/native-host/src/providerProfile.js +13 -1
  118. package/native-host/src/providerReasoning.js +9 -5
  119. package/native-host/src/providerStore.js +4 -0
  120. package/native-host/src/readProgressGuard.js +84 -17
  121. package/native-host/src/releaseTransport.js +14 -5
  122. package/native-host/src/selectionScope.js +58 -0
  123. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  124. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  125. package/native-host/src/subagentBroker.js +102 -98
  126. package/native-host/src/subagentTelemetry.js +316 -0
  127. package/native-host/src/subagentWorkspace.js +103 -0
  128. package/native-host/src/subagentWorkspacePath.js +31 -0
  129. package/native-host/src/taskRunnerRuntime.js +55 -5
  130. package/native-host/src/turnAttachments.js +113 -0
  131. package/native-host/src/updateManager.js +122 -12
  132. package/native-host/src/updateNetworkControl.js +75 -0
  133. package/native-host/src/updateTrust.js +21 -2
  134. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  135. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  136. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  137. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  138. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  139. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  140. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  141. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  142. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  143. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  144. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  145. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  146. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  147. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  148. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  149. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  150. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  151. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  152. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  153. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  154. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  155. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  156. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  157. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  158. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  159. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  160. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  161. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  162. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  163. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  164. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  165. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  166. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  167. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  168. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  169. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  170. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  171. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  172. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  173. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  174. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  175. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  176. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  177. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  178. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  179. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  180. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  181. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  182. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  183. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  184. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  185. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  186. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  187. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  188. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  189. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  190. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  191. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  192. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  193. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  194. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  195. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  196. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  197. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  198. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  199. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  200. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  201. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  202. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  203. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  303. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  304. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  305. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  306. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  307. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  308. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  309. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  310. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  311. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  312. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  313. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  314. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  315. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  316. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  317. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  318. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  319. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  320. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  321. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  322. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  323. package/native-host/src/writingStyleEvidence.js +102 -0
  324. package/native-host/src/writingStylePdfWorker.js +35 -0
  325. package/native-host/src/writingStyleProfile.js +165 -0
  326. package/native-host/src/writingStyleRuntime.js +447 -0
  327. package/package.json +1 -1
@@ -98,6 +98,11 @@
98
98
  if (!journal?.terminal && !journal?.ownerLost) {
99
99
  continue;
100
100
  }
101
+ // Native completion can precede the owner's writeback and durable
102
+ // session save. A passive shared-history reader must not settle it.
103
+ if (options.requireOwnerLost === true && journal.ownerLost !== true) {
104
+ continue;
105
+ }
101
106
  const session = options.findSession?.(journal.sessionId);
102
107
  if (!session) {
103
108
  continue;
@@ -112,6 +117,9 @@
112
117
  if (!record) {
113
118
  continue;
114
119
  }
120
+ if (record.status === 'running') {
121
+ options.normalizeInterruptedRun?.(record);
122
+ }
115
123
  const reloadReports = (record.events || []).filter(event => event?.failure?.source === 'panel_reload');
116
124
  record.events = (record.events || []).filter(event => event?.failure?.source !== 'panel_reload');
117
125
  const lastSequence = Number(record.nativeEventSeq || 0);
@@ -9,38 +9,128 @@
9
9
 
10
10
  function create(deps = {}) {
11
11
  const { callPageBridge, sendBackgroundNative } = deps;
12
+ const now = deps.now || Date.now;
13
+ const delay = deps.delay || (ms => new Promise(resolve => setTimeout(resolve, ms)));
12
14
 
13
15
  async function applyOperations(input = {}) {
14
16
  const results = [];
15
- let textBatch = [];
17
+ const deadlineAt = Number.isFinite(input.deadlineAt) && input.deadlineAt > 0 ? input.deadlineAt : null;
18
+ const invalidDeadline = input.deadlineAt !== undefined && deadlineAt === null;
19
+ const expired = () => invalidDeadline || (deadlineAt !== null && now() >= deadlineAt);
20
+ let textBatch = [], blocked = false;
21
+ function skipUndispatched(operations) {
22
+ const result = normalizeTextResult({ ok: false,
23
+ code: blocked ? 'writeback_tail_not_started' : 'writeback_deadline_exceeded',
24
+ error: blocked ? 'An earlier writeback is unconfirmed. This operation was not dispatched.'
25
+ : invalidDeadline ? 'The writeback deadline is invalid. This operation was not dispatched.'
26
+ : 'The writeback deadline has elapsed. This operation was not dispatched.',
27
+ changedDocument: false }, operations);
28
+ result.receiptUnconfirmed = false;
29
+ results.push(result);
30
+ }
16
31
  async function flushTextBatch() {
17
32
  if (!textBatch.length) return;
18
- results.push(await callPageBridge('applyOperations', {
19
- operations: textBatch,
33
+ const operations = textBatch;
34
+ textBatch = [];
35
+ if (blocked || expired()) { skipUndispatched(operations); return; }
36
+ const response = await callPageBridge('applyOperations', {
37
+ operations,
20
38
  baseFiles: input.baseFiles || [],
39
+ reviewingPolicy: input.reviewingPolicy || '',
40
+ ...(deadlineAt !== null ? { deadlineAt } : {}),
21
41
  requireReviewing: input.requireReviewing === true,
22
42
  requireEditing: input.requireEditing === true,
43
+ retryCreates: input.retryCreates === true,
23
44
  runProjectId: input.runProjectId || ''
24
- }));
25
- textBatch = [];
45
+ });
46
+ const recovered = response?.code === 'page_bridge_timeout' && response.requestId
47
+ ? await recoverTextReceipt(response, operations, input.runProjectId || '', deadlineAt)
48
+ : response;
49
+ const result = normalizeTextResult(recovered, operations);
50
+ results.push(result);
51
+ blocked = result.receiptUnconfirmed === true;
26
52
  }
27
53
  for (const operation of Array.isArray(input.operations) ? input.operations : []) {
54
+ if (blocked || expired()) {
55
+ skipUndispatched([operation]);
56
+ continue;
57
+ }
28
58
  if (!BINARY_TYPES.has(operation?.type)) {
29
59
  textBatch.push(operation);
30
60
  continue;
31
61
  }
32
62
  await flushTextBatch();
33
- results.push(await applyBinaryOperation(operation, input));
63
+ if (blocked || expired()) {
64
+ skipUndispatched([operation]);
65
+ continue;
66
+ }
67
+ const binaryResult = await applyBinaryOperation(operation, input);
68
+ results.push(binaryResult);
69
+ blocked = binaryResult.receiptUnconfirmed === true;
34
70
  }
35
71
  await flushTextBatch();
36
72
  return mergeApplyResults(results);
37
73
  }
38
74
 
75
+ async function recoverTextReceipt(timeout, operations, runProjectId, deadlineAt = null) {
76
+ const budget = Number.isFinite(deps.receiptTimeoutMs) ? Math.max(0, deps.receiptTimeoutMs)
77
+ : Math.min(600000, Math.max(90000, operations.length * 60000));
78
+ const deadline = deadlineAt ?? (now() + budget);
79
+ let partial = {}, state = 'unavailable', recoveryError = '', first = true;
80
+ while (deadlineAt === null ? (first || now() <= deadline) : now() < deadline) {
81
+ first = false;
82
+ let receipt, transportFailed = false;
83
+ try {
84
+ receipt = await callPageBridge('getWritebackReceipt', { requestId: timeout.requestId, runProjectId,
85
+ ...(deadlineAt !== null ? { deadlineAt } : {}) });
86
+ } catch (error) {
87
+ if (error?.code === 'codex_cancelled' || error?.cancelled === true) throw error;
88
+ recoveryError = error?.message || String(error);
89
+ if (!isTransientReceiptFailure(error)) break;
90
+ transportFailed = true;
91
+ }
92
+ if (!transportFailed) {
93
+ if (receipt?.code === 'codex_cancelled' || receipt?.cancelled === true) {
94
+ throw responseError(receipt, 'codex_cancelled');
95
+ }
96
+ if (receipt?.ok !== true) {
97
+ recoveryError = receipt?.error?.message || receipt?.error || receipt?.reason
98
+ || 'Writeback receipt could not be read.';
99
+ if (!isTransientReceiptFailure(receipt)) break;
100
+ } else {
101
+ if (receipt.requestId !== timeout.requestId || receipt.runProjectId !== runProjectId) {
102
+ recoveryError = 'Writeback receipt identity could not be confirmed.';
103
+ break;
104
+ }
105
+ state = receipt.state;
106
+ if (receipt.result && typeof receipt.result === 'object') partial = receipt.result;
107
+ if (state === 'completed') {
108
+ if (!receipt.result || typeof receipt.result !== 'object') {
109
+ recoveryError = 'The completed writeback receipt has no result.';
110
+ break;
111
+ }
112
+ if (deadlineAt !== null && now() > deadline) break;
113
+ return { ...partial,
114
+ receiptRecovery: { requestId: timeout.requestId, recovered: true, initialError: timeout.error } };
115
+ }
116
+ if (state !== 'running') {
117
+ recoveryError = 'The original writeback receipt is no longer available.';
118
+ break;
119
+ }
120
+ }
121
+ }
122
+ if (now() >= deadline) break;
123
+ await delay(Math.min(deps.receiptPollMs || 500, Math.max(1, deadline - now())));
124
+ }
125
+ return { ...timeout, ...partial, ok: false, code: timeout.code, error: timeout.error,
126
+ requestId: timeout.requestId, receiptUnconfirmed: true, receiptState: state, recoveryError };
127
+ }
128
+
39
129
  async function applyBinaryOperation(operation, input) {
40
130
  const transferId = `asset_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
41
131
  const assetRef = operation.assetRef || null;
42
132
  const safeOperation = sanitizeOperation(operation);
43
- let pageBegun = false;
133
+ let pageBegun = false, commitAttempted = false;
44
134
  try {
45
135
  const begin = await callPageBridge('binaryUploadBegin', {
46
136
  transferId,
@@ -66,6 +156,7 @@
66
156
  } else {
67
157
  throw codedError('binary_asset_payload_missing', 'Binary asset payload is unavailable.');
68
158
  }
159
+ commitAttempted = true;
69
160
  const committed = await callPageBridge('binaryUploadCommit', {
70
161
  transferId,
71
162
  runProjectId: input.runProjectId || ''
@@ -73,12 +164,37 @@
73
164
  if (!committed?.ok) throw responseError(committed, committed?.code || 'binary_upload_failed');
74
165
  return { ok: true, applied: [{ operation: safeOperation, result: committed }], skipped: [] };
75
166
  } catch (error) {
76
- if (pageBegun) {
167
+ let changedDocument = error?.changedDocument === true
168
+ || (commitAttempted && error?.changedDocument !== false);
169
+ if (commitAttempted && changedDocument) {
170
+ const deadline = now() + 30000;
171
+ do {
172
+ const receipt = await callPageBridge('binaryUploadStatus', {
173
+ transferId, runProjectId: input.runProjectId || ''
174
+ }).catch(() => null);
175
+ if (!receipt?.ok || receipt.transferId !== transferId
176
+ || receipt.runProjectId !== input.runProjectId) break;
177
+ if (receipt.result?.ok === true) {
178
+ return { ok: true, applied: [{ operation: safeOperation, result: receipt.result }], skipped: [],
179
+ receiptRecovery: { transferId, recovered: true, initialError: error.message } };
180
+ }
181
+ if (receipt.state === 'completed' && receipt.changedDocument === false) {
182
+ changedDocument = false;
183
+ break;
184
+ }
185
+ if (now() >= deadline) break;
186
+ await delay(Math.min(500, Math.max(1, deadline - now())));
187
+ } while (now() <= deadline);
188
+ }
189
+ if (pageBegun && !commitAttempted) {
77
190
  await callPageBridge('binaryUploadAbort', { transferId, runProjectId: input.runProjectId || '' }).catch(() => null);
78
191
  }
79
192
  const code = error.code || 'binary_upload_failed';
80
193
  return {
81
194
  ok: false,
195
+ receiptUnconfirmed: changedDocument,
196
+ error: error.message || 'Binary asset upload failed.',
197
+ code,
82
198
  applied: [],
83
199
  skipped: [{
84
200
  operation: safeOperation,
@@ -87,8 +203,8 @@
87
203
  ok: false,
88
204
  code,
89
205
  reason: error.message || 'Binary asset upload failed.',
90
- changedDocument: false,
91
- failure: { code, stage: 'write', file: operation.path, changedDocument: false }
206
+ changedDocument,
207
+ failure: { code, stage: 'write', file: operation.path, changedDocument }
92
208
  }
93
209
  }]
94
210
  };
@@ -129,6 +245,42 @@
129
245
  return { applyOperations };
130
246
  }
131
247
 
248
+ function normalizeTextResult(response, operations) {
249
+ const result = response && typeof response === 'object' ? { ...response } : { ok: false };
250
+ result.applied = Array.isArray(result.applied) ? result.applied : [];
251
+ result.skipped = Array.isArray(result.skipped) ? result.skipped.slice() : [];
252
+ const key = operation => [operation?.type, operation?.path, operation?.to || ''].join('\0');
253
+ const known = new Map();
254
+ for (const entry of [...result.applied, ...result.skipped]) {
255
+ const id = key(entry.operation); known.set(id, (known.get(id) || 0) + 1);
256
+ }
257
+ const missing = [];
258
+ for (const operation of operations) {
259
+ const id = key(operation), count = known.get(id) || 0;
260
+ if (count) known.set(id, count - 1); else missing.push(operation);
261
+ }
262
+ if (!missing.length && (result.ok !== false || result.skipped.length)) return result;
263
+ const code = result.code || 'writeback_result_unconfirmed';
264
+ const error = typeof result.error === 'string' ? result.error
265
+ : result.error?.message || 'The page did not return a complete writeback result.';
266
+ const changedDocument = result.changedDocument !== false;
267
+ for (const operation of missing.length ? missing : [null]) {
268
+ const reason = changedDocument
269
+ ? 'Write status is unconfirmed; Overleaf may already contain changes. Check the project before retrying. ' + error
270
+ : error;
271
+ result.skipped.push({ operation, path: operation?.path || '', result: {
272
+ ok: false, code, reason, changedDocument, failure: {
273
+ code, stage: 'write', severity: 'warning', terminalState: 'needs_review', retryable: false,
274
+ file: operation?.path || '', userMessage: reason,
275
+ nextAction: 'Check the current Overleaf files and original writeback result before starting another write.',
276
+ changedDocument, evidence: { requestId: result.requestId || '', receiptState: result.receiptState || '',
277
+ transportError: error, recoveryError: result.recoveryError || '' }
278
+ }
279
+ } });
280
+ }
281
+ return { ...result, ok: false, code, error, receiptUnconfirmed: true };
282
+ }
283
+
132
284
  function mergeApplyResults(results) {
133
285
  const merged = { ok: true, applied: [], skipped: [], trackedChanges: [] };
134
286
  for (const result of results) {
@@ -139,8 +291,18 @@
139
291
  // recordUndoFromApply classify a successful Track write as legacy Undo,
140
292
  // which hides Accept and prevents the lifecycle from surviving reload.
141
293
  merged.trackedChanges.push(...(Array.isArray(result?.trackedChanges) ? result.trackedChanges : []));
294
+ if (result?.reviewingPolicy !== undefined) merged.reviewingPolicy = result.reviewingPolicy;
142
295
  if (result?.trackedChangeCaptures?.length) (merged.trackedChangeCaptures ||= []).push(...result.trackedChangeCaptures);
143
- if (result?.ok !== true) merged.ok = false;
296
+ if (result?.ok !== true) {
297
+ merged.ok = false;
298
+ for (const key of ['code', 'error', 'failure', 'requestId', 'receiptState', 'recoveryError']) {
299
+ if (result?.[key] !== undefined && merged[key] === undefined) merged[key] = result[key];
300
+ }
301
+ if (result?.receiptUnconfirmed !== undefined) {
302
+ merged.receiptUnconfirmed = merged.receiptUnconfirmed === true || result.receiptUnconfirmed === true;
303
+ }
304
+ }
305
+ if (result?.receiptRecovery) (merged.receiptRecoveries ||= []).push(result.receiptRecovery);
144
306
  }
145
307
  if (merged.trackedChangeCaptures?.some(capture => capture.state !== 'observed')) merged.trackedChanges = [];
146
308
  if (merged.skipped.length) merged.ok = false;
@@ -160,11 +322,23 @@
160
322
  return safe;
161
323
  }
162
324
 
325
+ function isTransientReceiptFailure(value) {
326
+ const code = value?.code || value?.error?.code;
327
+ if (code) return ['page_bridge_timeout', 'page_bridge_unavailable', 'page_bridge_transport_error',
328
+ 'page_bridge_disconnected', 'network_error', 'ETIMEDOUT', 'ECONNRESET', 'ECONNABORTED',
329
+ 'EPIPE', 'ERR_NETWORK'].includes(code);
330
+ const message = value?.message || value?.error?.message || value?.error || '';
331
+ return /timed?\s*out|timeout|network|transport|bridge unavailable|port.*closed|receiving end|socket hang up/i.test(String(message));
332
+ }
333
+
163
334
  function responseError(response, fallbackCode) {
164
- return codedError(
335
+ const error = codedError(
165
336
  response?.code || response?.error?.code || fallbackCode,
166
337
  response?.reason || response?.error?.message || response?.error || 'Asset transfer failed.'
167
338
  );
339
+ if (typeof response?.changedDocument === 'boolean') error.changedDocument = response.changedDocument;
340
+ error.stage = response?.failure?.stage || 'write';
341
+ return error;
168
342
  }
169
343
  function codedError(code, message) { const error = new Error(String(message || code)); error.code = code; return error; }
170
344
  return { create, mergeApplyResults };
@@ -0,0 +1,175 @@
1
+ (function initChangeHistoryView(root, factory) {
2
+ root.CodexOverleafModuleRegistry.define('ChangeHistoryView', ['ChangeHistoryModel'], factory);
3
+ })(typeof globalThis !== 'undefined' ? globalThis : window, function changeHistoryViewFactory(Model) {
4
+ 'use strict';
5
+ function create(deps) {
6
+ const { tx, StorageDb, getState, getCurrentProjectId, getAccountScopeId,
7
+ getSettingsPanelInstance, onHistoryRowJump, sanitizeText } = deps;
8
+ let generation = 0, owner = null, records = [], storedSessions = [], limit = 8, loading = false, error = '';
9
+ const container = () => getSettingsPanelInstance()?.container;
10
+ const make = (tag, className, text) => {
11
+ const element = document.createElement(tag); element.className = className;
12
+ if (text !== undefined) element.textContent = text;
13
+ return element;
14
+ };
15
+ function icon(kind) {
16
+ const node = make('span', 'codex-history-icon');
17
+ node.setAttribute('aria-hidden', 'true');
18
+ const paths = {
19
+ file: '<path d="M9 2H4v12h8V5L9 2Z M9 2v4h3 M6 9h4 M6 11h3"/>',
20
+ message: '<path d="M3 3h10v8H7l-4 3V3Z M5 6h6 M5 8h4"/>',
21
+ arrow: '<path d="M4 12 12 4 M5 4h7v7"/>'
22
+ };
23
+ node.innerHTML = '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">' + paths[kind] + '</svg>';
24
+ return node;
25
+ }
26
+ function label(state) {
27
+ return ({
28
+ pending: tx('Pending review', '待接受'), accepted: tx('Accepted', '已接受'),
29
+ undone: tx('Undone', '已撤销'), attention: tx('Needs attention', '需检查'),
30
+ written: tx('Written', '已写入'), running: tx('In progress', '进行中'),
31
+ unchanged: tx('No file changes', '未修改文件'), unknown: tx('Status unavailable', '状态不可用')
32
+ })[state] || '';
33
+ }
34
+ function title(row) {
35
+ if (row.summary) return row.summary;
36
+ if (!row.hasWrites) return tx('Task without file changes', '未修改文件的任务');
37
+ if (row.status === 'attention') return row.paths.length === 1
38
+ ? tx('Check changes to ', '检查文件修改:') + row.paths[0] : tx('File changes need attention', '文件修改需要检查');
39
+ if (row.paths.length === 1) {
40
+ const verbs = { create: ['Created ', '新建 '], 'binary-create': ['Created ', '新建 '],
41
+ delete: ['Deleted ', '删除 '], move: ['Moved ', '移动 '], rename: ['Renamed ', '重命名 '] };
42
+ const verb = verbs[row.operation] || ['Updated ', '修改 '];
43
+ return tx(verb[0], verb[1]) + row.paths[0];
44
+ }
45
+ return row.paths.length ? tx('Changed ' + row.paths.length + ' files', '修改 ' + row.paths.length + ' 个文件')
46
+ : tx('Project file changes', '项目文件修改');
47
+ }
48
+ function when(value) {
49
+ const date = new Date(value);
50
+ if (!Number.isFinite(date.getTime())) return tx('Time unavailable', '时间不可用');
51
+ const locale = getState()?.locale === 'zh' ? 'zh-CN' : 'en';
52
+ const now = new Date(), yesterday = new Date(now); yesterday.setDate(now.getDate() - 1);
53
+ const day = date.toDateString() === now.toDateString() ? tx('Today', '今天')
54
+ : date.toDateString() === yesterday.toDateString() ? tx('Yesterday', '昨天')
55
+ : date.toLocaleDateString(locale, { month: 'short', day: 'numeric', ...(date.getFullYear() !== now.getFullYear() ? { year: 'numeric' } : {}) });
56
+ return day + ' ' + date.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit', hour12: false });
57
+ }
58
+ function current(snapshot) {
59
+ return snapshot && snapshot.projectId === getCurrentProjectId()
60
+ && snapshot.accountScopeId === getAccountScopeId?.() && snapshot.container === container();
61
+ }
62
+ function message(list, content, retry = false) {
63
+ const empty = make('div', 'codex-history-empty', content);
64
+ if (retry) {
65
+ const button = make('button', 'codex-history-jump', tx('Try again', '重试'));
66
+ button.type = 'button'; button.addEventListener('click', load); empty.append(button);
67
+ }
68
+ list.replaceChildren(empty);
69
+ }
70
+ function render() {
71
+ const host = container(), list = host?.querySelector('[data-history-list]');
72
+ if (!list) return;
73
+ const search = host.querySelector('[data-history-filter]');
74
+ search.placeholder = tx('Search files or summaries', '搜索文件或修改摘要');
75
+ search.setAttribute('aria-label', search.placeholder);
76
+ const count = host.querySelector('[data-history-count]');
77
+ const toggle = host.querySelector('[data-history-include-empty]');
78
+ const includeLabel = host.querySelector('[data-history-include-label]');
79
+ const more = host.querySelector('[data-history-more]');
80
+ if (count) count.textContent = '';
81
+ if (more) more.hidden = true;
82
+ if (loading || error || !current(owner)) {
83
+ if (toggle?.parentElement) toggle.parentElement.hidden = true;
84
+ message(list, loading ? tx('Loading file changes…', '正在读取文件修改记录…')
85
+ : error || tx('Open this project again to load its history.', '重新打开本项目以读取历史记录。'), Boolean(error));
86
+ return;
87
+ }
88
+ const state = getState();
89
+ const projected = Model.project(records, {
90
+ projectId: owner.projectId, accountScopeId: owner.accountScopeId,
91
+ storedSessions, sessions: state.sessions, activeSessionId: state.activeSessionId, activeRuns: state.runs,
92
+ sanitizeText
93
+ });
94
+ const query = search.value.trim().toLowerCase();
95
+ const selected = host.querySelector('[data-history-status]')?.value || 'all';
96
+ const rows = projected.filter(row => (toggle?.checked || row.hasWrites)
97
+ && (selected === 'all' || row.status === selected)
98
+ && (!query || [title(row), ...row.paths].join(' ').toLowerCase().includes(query)));
99
+ if (count) count.textContent = tx(rows.length + ' records', rows.length + ' 条');
100
+ const noWrites = projected.filter(row => !row.hasWrites).length;
101
+ if (toggle?.parentElement) toggle.parentElement.hidden = noWrites === 0;
102
+ if (includeLabel) includeLabel.textContent = tx('Include ' + noWrites + ' tasks without file changes', '同时显示 ' + noWrites + ' 条未修改文件的任务');
103
+ list.replaceChildren();
104
+ if (!rows.length) {
105
+ message(list, query || selected !== 'all'
106
+ ? tx('No matching file changes. Try another file name or status.', '没有匹配的修改记录,可尝试其他文件名或状态。')
107
+ : tx('No file changes recorded for this project yet.', '本项目还没有已记录的文件修改。'));
108
+ }
109
+ for (const row of rows.slice(0, limit)) {
110
+ const element = make('article', 'codex-history-row');
111
+ element.dataset.resultStatus = row.status;
112
+ const mark = make('span', 'codex-history-mark'); mark.append(icon(row.hasWrites ? 'file' : 'message'));
113
+ const body = make('div', 'codex-history-copy'), top = make('div', 'codex-history-top');
114
+ const name = make('div', 'codex-history-task', title(row));
115
+ const status = make('span', 'codex-history-status', label(row.status)); status.dataset.state = row.status;
116
+ top.append(name, status);
117
+ const files = make('div', 'codex-history-files', row.paths.length ? row.paths.join(' · ')
118
+ : row.hasWrites ? tx('Detailed file names are no longer available.', '详细文件名已不可用。')
119
+ : tx('No files were written to Overleaf.', '没有向 Overleaf 写入文件。'));
120
+ const footer = make('div', 'codex-history-bottom');
121
+ const fileCount = row.paths.length ? tx(' · ' + row.paths.length + ' file(s)', ' · ' + row.paths.length + ' 个文件') : '';
122
+ footer.append(make('time', 'codex-history-when', when(row.createdAt) + fileCount));
123
+ const jump = make('button', 'codex-history-jump');
124
+ jump.type = 'button'; jump.disabled = !row.canJump;
125
+ jump.append(make('span', '', row.canJump ? tx('View conversation', '查看对应对话') : tx('Conversation unavailable', '对话不可用')));
126
+ if (row.canJump) jump.append(icon('arrow'));
127
+ jump.title = row.canJump ? tx('Open the original run', '定位到对应轮次')
128
+ : tx('The original run is no longer in the loaded conversation history.', '原轮次已不在当前可用的对话历史中。');
129
+ jump.setAttribute('aria-label', jump.textContent + ': ' + title(row));
130
+ const snapshot = owner;
131
+ jump.addEventListener('click', () => {
132
+ if (current(snapshot)) void onHistoryRowJump?.(row);
133
+ });
134
+ footer.append(jump); body.append(top, files, footer); element.append(mark, body); list.append(element);
135
+ }
136
+ if (more) {
137
+ more.hidden = rows.length <= limit;
138
+ more.textContent = tx('Show more (' + Math.min(8, rows.length - limit) + ')', '再显示 ' + Math.min(8, rows.length - limit) + ' 条');
139
+ more.onclick = () => { limit += 8; render(); };
140
+ }
141
+ }
142
+ async function load() {
143
+ const host = container();
144
+ if (!host?.querySelector('[data-history-list]')) return;
145
+ const requestId = ++generation;
146
+ owner = { projectId: getCurrentProjectId(), accountScopeId: getAccountScopeId?.() || '', container: host };
147
+ const snapshot = owner;
148
+ records = []; storedSessions = []; limit = 8; error = '';
149
+ if (!snapshot.accountScopeId || !snapshot.projectId) {
150
+ loading = false; error = tx('Project identity is unavailable. Reopen the project to load its history.', '暂时无法确认项目身份,请重新打开项目后查看历史。'); render(); return;
151
+ }
152
+ loading = true; render();
153
+ let timer;
154
+ try {
155
+ const data = await Promise.race([
156
+ Promise.all([
157
+ StorageDb.getAllByIndex('auditLogs', 'projectId', snapshot.projectId),
158
+ StorageDb.getAllByIndex('sessions', 'projectId', snapshot.projectId)
159
+ ]),
160
+ new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('history_load_timeout')), 3000); })
161
+ ]);
162
+ if (requestId !== generation || !current(snapshot)) return;
163
+ [records, storedSessions] = data;
164
+ loading = false; render();
165
+ } catch (_) {
166
+ if (requestId !== generation || !current(snapshot)) return;
167
+ loading = false; error = tx('File changes could not be loaded.', '暂时无法读取文件修改记录。'); render();
168
+ } finally { clearTimeout(timer); }
169
+ }
170
+ return { load, filter() { limit = 8; render(); }, invalidate() {
171
+ generation += 1; records = []; storedSessions = []; owner = null; loading = false; error = '';
172
+ } };
173
+ }
174
+ return { create };
175
+ });
@@ -361,54 +361,56 @@
361
361
  }
362
362
 
363
363
  function renderAttachmentPreviewList(attachments = [], container, options = {}) {
364
- if (!container) {
365
- return;
366
- }
364
+ if (!container) return;
367
365
  container.replaceChildren();
368
366
  const items = Array.isArray(attachments) ? attachments : [];
369
367
  container.hidden = items.length === 0;
370
368
  for (const attachment of items) {
371
369
  const isImage = attachment.kind === 'image' && attachment.previewDataUrl;
372
- const card = document.createElement(isImage ? 'button' : 'div');
370
+ const label = attachment.name || (options.tx ? options.tx('Attachment', '附件') : 'Attachment');
371
+ const card = document.createElement('div');
373
372
  card.className = 'codex-attachment-preview-card';
374
373
  card.dataset.kind = isImage ? 'image' : 'file';
374
+ card.title = label + ' · ' + formatFileSize(attachment.size);
375
375
  if (isImage) {
376
- card.type = 'button';
377
- card.title = options.tx
378
- ? options.tx(`Open ${attachment.name}`, `打开 ${attachment.name}`)
379
- : `Open ${attachment.name}`;
380
- card.addEventListener('click', () => showAttachmentPreviewDialog(attachment, options));
376
+ const preview = document.createElement('button');
377
+ preview.type = 'button';
378
+ preview.className = 'codex-attachment-preview-open';
379
+ preview.setAttribute('aria-label', options.tx ? options.tx('Preview ' + label, '预览 ' + label) : 'Preview ' + label);
380
+ preview.addEventListener('click', () => showAttachmentPreviewDialog(attachment, options));
381
381
  const image = document.createElement('img');
382
382
  image.src = attachment.previewDataUrl;
383
- image.alt = attachment.name || (options.tx ? options.tx('Attached image', '已附加图片') : 'Attached image');
384
- card.append(image);
383
+ image.alt = label;
384
+ preview.append(image);
385
+ card.append(preview);
385
386
  } else {
386
387
  const icon = document.createElement('span');
387
388
  icon.className = 'codex-attachment-file-icon';
388
389
  icon.textContent = getAttachmentIconLabel(attachment);
390
+ icon.setAttribute('aria-hidden', 'true');
389
391
  card.append(icon);
390
392
  }
391
-
392
393
  const meta = document.createElement('span');
393
394
  meta.className = 'codex-attachment-preview-meta';
394
395
  const name = document.createElement('span');
395
396
  name.className = 'codex-attachment-preview-name';
396
- name.textContent = attachment.name || (options.tx ? options.tx('Attachment', '附件') : 'Attachment');
397
- const size = document.createElement('span');
398
- size.className = 'codex-attachment-preview-size';
399
- size.textContent = formatFileSize(attachment.size);
400
- meta.append(name, size);
397
+ name.textContent = label;
398
+ meta.append(name);
399
+ if (!isImage) {
400
+ const size = document.createElement('span');
401
+ size.className = 'codex-attachment-preview-size';
402
+ size.textContent = formatFileSize(attachment.size);
403
+ meta.append(size);
404
+ }
401
405
  card.append(meta);
402
-
403
406
  if (options.removable) {
404
407
  const remove = document.createElement('button');
405
408
  remove.type = 'button';
406
409
  remove.className = 'codex-attachment-preview-remove';
407
410
  remove.textContent = '×';
408
- remove.title = options.tx ? options.tx('Remove attachment', '移除附件') : 'Remove attachment';
409
- remove.setAttribute('aria-label', options.tx
410
- ? options.tx(`Remove ${attachment.name}`, `移除 ${attachment.name}`)
411
- : `Remove ${attachment.name}`);
411
+ const removeLabel = options.tx ? options.tx('Remove ' + label, '移除 ' + label) : 'Remove ' + label;
412
+ remove.title = removeLabel;
413
+ remove.setAttribute('aria-label', removeLabel);
412
414
  remove.addEventListener('click', event => {
413
415
  event.preventDefault();
414
416
  event.stopPropagation();
@@ -18,6 +18,7 @@
18
18
  <div class="codex-pending-inputs" data-pending-inputs hidden></div>
19
19
  <form class="codex-composer" data-composer-form>
20
20
  <div class="codex-attachment-strip codex-attachment-preview-list" data-attachment-strip hidden></div>
21
+ <div class="codex-composer-selection" data-composer-selection hidden></div>
21
22
  <div class="codex-composer-skill-context" data-composer-skill-context hidden>
22
23
  <span class="codex-composer-skill-icon" aria-hidden="true">◇</span>
23
24
  <span data-composer-skill-label></span>
@@ -35,17 +36,31 @@
35
36
  <option value="ask">Ask</option>
36
37
  <option value="auto">Auto</option>
37
38
  </select>
39
+ <div class="codex-write-preferences">
40
+ <label class="codex-review-toggle" title="When enabled, Codex checks or switches Overleaf Reviewing/Track Changes before writing. Deletes still require confirmation.">
41
+ <input type="checkbox" data-require-reviewing>
42
+ <svg class="codex-composer-option-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
43
+ <path d="m9 11 8-8 4 4-8 8M9 11l4 4-3 3H6v-4l3-3ZM3 21h7"></path>
44
+ </svg>
45
+ <span class="codex-review-label" data-i18n="requireReviewing">Track</span>
46
+ <svg class="codex-composer-option-check" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
47
+ <path d="m3.5 8 3 3 6-6"></path>
48
+ </svg>
49
+ </label>
50
+ <label class="codex-recompile-toggle" title="Automatically recompile in Overleaf after Codex writes and record the result. Toggling this setting does not compile immediately. Ask mode does not trigger compilation.">
51
+ <input type="checkbox" data-auto-recompile>
52
+ <svg class="codex-composer-option-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
53
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8ZM14 2v6h6m-11 7 2 2 4-4"></path>
54
+ </svg>
55
+ <span class="codex-recompile-label" data-i18n="autoCompile">Auto compile</span>
56
+ <svg class="codex-composer-option-check" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
57
+ <path d="m3.5 8 3 3 6-6"></path>
58
+ </svg>
59
+ </label>
60
+ </div>
38
61
  </div>
39
62
  <div class="codex-composer-toolbar">
40
63
  <button type="button" data-add-context title="Add @ context" aria-label="Add @ context" aria-expanded="false">+</button>
41
- <label class="codex-review-toggle" title="When enabled, Codex checks or switches Overleaf Reviewing/Track Changes before writing. Deletes still require confirmation.">
42
- <input type="checkbox" data-require-reviewing>
43
- <span class="codex-review-label" data-i18n="requireReviewing">Track</span>
44
- </label>
45
- <label class="codex-recompile-toggle" title="After Codex writes, click Overleaf Recompile and record the compile result for this task. Ask mode will not trigger it.">
46
- <input type="checkbox" data-auto-recompile>
47
- <span class="codex-recompile-label" data-i18n="autoCompile">Compile</span>
48
- </label>
49
64
  <div class="codex-model-config" data-model-config>
50
65
  <button type="button" class="codex-model-config-button" data-model-config-toggle aria-haspopup="menu" aria-expanded="false">
51
66
  <span class="codex-model-speed-indicator" data-speed-indicator hidden aria-hidden="true">