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
@@ -10,6 +10,19 @@
10
10
  const REVIEW_CODES = new Set(['write_timeout', 'partial_write_needs_review']);
11
11
  const CANCEL_CODES = new Set(['cancelled', 'user_cancelled', 'codex_cancelled']);
12
12
 
13
+ // Old attempts remain in the stored audit trail; a resolved undo must not
14
+ // advertise them as a current failure in the transcript or its summary.
15
+ function projectResolvedUndo(run) {
16
+ if (!run || (run.undoStatus !== 'applied' && run.trackedChangeStatus !== 'rejected')
17
+ || !Array.isArray(run.events)) return run;
18
+ const events = run.events.filter(event => {
19
+ if (event?.status !== 'failed' || event.streamRole
20
+ || ['report', 'guidance', 'stream'].includes(event.kind)) return true;
21
+ return !/^(?:Undo result:\s*(?:undone|rejected)\s+\d+\s+(?:item\(s\)|file\(s\)|tracked change\(s\)),\s*skipped\s+\d+(?:\s+item\(s\))?|撤销结果[::]\s*已(?:撤销|拒绝)\s*\d+\s*(?:项|条留痕|个文件),\s*跳过\s*\d+\s*(?:项|条))$/.test(String(event.title || '').trim());
22
+ });
23
+ return events.length === run.events.length ? run : { ...run, events };
24
+ }
25
+
13
26
  function create({ tr, projectRunSettlement, sanitizeText = value => value,
14
27
  document: documentRef = globalThis.document } = {}) {
15
28
  const notices = new WeakMap();
@@ -100,5 +113,5 @@
100
113
  && !path.split('/').includes('..') ? path.slice(0, 300) : '';
101
114
  }
102
115
 
103
- return Object.freeze({ create });
116
+ return Object.freeze({ create, projectResolvedUndo });
104
117
  });
@@ -49,13 +49,14 @@
49
49
  function appendToView(event, view) {
50
50
  const text = deps.RunGuidanceView?.getGuidanceText(event);
51
51
  if (!text) return false;
52
- const guidance = view.guidance || view.root?.querySelector('[data-run-guidance]');
53
- const target = view.terminalStatus ? guidance : view.events;
52
+ const root = view.root || view.events?.closest('[data-run-id]');
53
+ const guidance = view.guidance || root?.querySelector('[data-run-guidance]');
54
+ const target = guidance || view.events;
54
55
  if (!target) return false;
55
56
  view.guidance = guidance;
56
57
  const rendered = deps.renderRunEvent(event);
57
58
  const existing = event.guidanceId
58
- ? view.root?.querySelector(`[data-guidance-id="${deps.cssEscape(event.guidanceId)}"]`)
59
+ ? root?.querySelector(`[data-guidance-id="${deps.cssEscape(event.guidanceId)}"]`)
59
60
  : null;
60
61
  if (existing) existing.replaceWith(rendered);
61
62
  else target.append(rendered);
@@ -0,0 +1,306 @@
1
+ (function initRunPresence(root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory();
4
+ } else {
5
+ root.CodexOverleafModuleRegistry.define('RunPresence', [], factory);
6
+ }
7
+ })(typeof window !== 'undefined' ? window : globalThis, function runPresenceFactory() {
8
+ 'use strict';
9
+
10
+ // Presentation-only run header: the animated brand mark, the live label and
11
+ // the settled summary. It never reads or writes run records.
12
+ const VERBS = {
13
+ en: ['Vibe writing', 'Typesetting', 'Kerning', 'Footnoting', 'Citing sources', 'Peer-reviewing',
14
+ 'Polishing prose', 'Balancing braces', 'Chasing references', 'Proofreading', 'Untangling macros',
15
+ 'Hunting overfull hboxes'],
16
+ zh: ['推敲中', '斟酌措辞', '排版中', '查文献', '对齐公式', '润色中', '翻稿纸', '补脚注', '配平括号',
17
+ '追引用', '校对中', '抓 overfull hbox']
18
+ };
19
+ const TIPS = {
20
+ en: ['Type @ to add a file as context, e.g. @sections/intro.tex.',
21
+ 'Ask for an edit, not a rewrite: “tighten §2, keep every citation”.',
22
+ 'Compile errors can go straight back to Codex from the log.',
23
+ '\\label everything. Cross-references are cheaper than prose.',
24
+ 'Reviewer 2 is not here. Yet.',
25
+ 'Overfull \\hbox? Ask Codex to rephrase the line, not shrink the font.'],
26
+ zh: ['输入 @ 把文件加入上下文,比如 @sections/intro.tex。',
27
+ '让它“改”而不是“重写”:“精简 §2,保留所有引用”。',
28
+ '编译报错可以直接从日志发回给 Codex。',
29
+ '多打 \\label。交叉引用比文字描述省事。',
30
+ 'Reviewer 2 还没来,趁现在。',
31
+ 'Overfull \\hbox?让 Codex 改写这一行,而不是缩字号。']
32
+ };
33
+ // The header word changes rarely: at 1, 3, 7 and 15 minutes, so even a long run shows only a few words.
34
+ const WORD_AT_MS = [0, 60000, 180000, 420000, 900000];
35
+ const LONG_RUN_MS = 120000;
36
+ // Layers (inlined in panel.css) are pixel-exact slices of assets/icons/codex-overleaf-icon.png;
37
+ // the origins (percent of the square crop) are the leaf stem and cloud centre.
38
+ const MARK_LAYERS = ['cloud', 'cursor', 'leaf'];
39
+ const MARK_STYLE = '--mark-leaf-origin:70.6% 41.6%;--mark-cloud-origin:44.3% 55%';
40
+ const ICONS = {
41
+ alert: '<circle cx="8" cy="8" r="6"/><path d="M8 4.5v4 M8 11h.01"/>',
42
+ check: '<path d="m3.5 8 3 3 6-6"/>',
43
+ file: '<path d="M9 2H4v12h8V5L9 2Z M9 2v4h3 M6 9h4 M6 11h3"/>',
44
+ refresh: '<path d="M4.2 5.3A5 5 0 1 1 3.6 10"/><path d="M4.2 2.2v3.1H1.1"/>'
45
+ };
46
+
47
+ const escapeHtml = value => String(value ?? '').replace(/[&<>"']/g, char => ({
48
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
49
+ })[char]);
50
+
51
+ function icon(name, className = 'codex-glyph') {
52
+ return '<svg class="' + className + '" viewBox="0 0 16 16" aria-hidden="true" focusable="false">'
53
+ + (ICONS[name] || ICONS.check) + '</svg>';
54
+ }
55
+
56
+ function qed(className = '') {
57
+ return '<span class="run-qed' + (className ? ' ' + className : '') + '" aria-hidden="true">'
58
+ + '<svg viewBox="0 0 11 11" focusable="false"><rect x="1" y="1" width="9" height="9" rx=".6"/></svg></span>';
59
+ }
60
+
61
+ function pickDifferent(list, previous, random) {
62
+ if (list.length < 2) return list[0] || '';
63
+ const index = Math.min(list.length - 1, Math.floor(random() * list.length));
64
+ // Step past a repeat instead of re-rolling, so a fixed random source can never spin.
65
+ return list[index] === previous ? list[(index + 1) % list.length] : list[index];
66
+ }
67
+
68
+ // Tool rows win over playful verbs; the label names the real action.
69
+ function activityVerb(item, tx) {
70
+ const meta = item?.meta || {};
71
+ if (item?.kind === 'explore') {
72
+ return ({ read: tx('Reading', '读取'), search: tx('Searching', '搜索'), list: tx('Listing', '查看') })[meta.action]
73
+ || tx('Exploring', '查阅');
74
+ }
75
+ if (item?.kind === 'edit') {
76
+ return ({ add: tx('Creating', '新建'), delete: tx('Deleting', '删除'), move: tx('Moving', '移动') })[item.file?.operation]
77
+ || tx('Editing', '修改');
78
+ }
79
+ return ({ command: tx('Running', '执行'), compile: tx('Compiling', '编译'), sync: tx('Syncing to Overleaf', '同步到 Overleaf'),
80
+ tool: tx('Using', '调用'), agent: tx('Delegating', '委派子代理'), plan: tx('Planning', '规划') })[item?.kind]
81
+ || tx('Working', '处理中');
82
+ }
83
+
84
+ // Header-only command label: drop the shell wrapper and the leading `cd …&&`, keep the first command.
85
+ function commandSummary(command) {
86
+ let text = String(command || '').trim().replace(/^(?:\/usr)?\/bin\/(?:ba|z)?sh\s+-l?c\s+/, '');
87
+ if (/^(["']).*\1$/s.test(text)) text = text.slice(1, -1);
88
+ text = text.replace(/^cd\s+.*?&&\s*/, '').split(/\s*(?:&&|\|\||[|;])\s*/)[0].replace(/\s+/g, ' ').trim();
89
+ return text.length > 48 ? text.slice(0, 47) + '…' : text;
90
+ }
91
+
92
+ // Settled header: ∎ (end of proof) followed by what the run actually did.
93
+ function settledHtml(facts = {}, tx, formatElapsed) {
94
+ const hasElapsed = Number.isFinite(facts.elapsedMs);
95
+ const elapsed = hasElapsed ? '<span>' + escapeHtml(formatElapsed(facts.elapsedMs)) + '</span>' : '';
96
+ const sep = '<span class="run-summary-sep">·</span>';
97
+ if (facts.kind === 'stopped') {
98
+ return qed('is-open' + (facts.animate ? ' is-settling' : '')) + '<span class="run-summary-stopped">\\end{draft}</span>'
99
+ + (hasElapsed ? sep + '<span>' + escapeHtml(tx('stopped at ', '停在 ') + formatElapsed(facts.elapsedMs)) + '</span>' : '');
100
+ }
101
+ const parts = elapsed ? [elapsed] : [];
102
+ if (facts.files > 0) {
103
+ const files = tx(facts.files + (facts.files === 1 ? ' file' : ' files'), facts.files + ' 个文件');
104
+ // A clipped history only proves the files it recorded.
105
+ parts.push('<span>' + escapeHtml(facts.filesClipped ? tx(files + ' recorded', '已记录 ' + files) : files) + '</span>');
106
+ if (facts.added || facts.removed) {
107
+ parts.push('<span><span class="run-summary-add">+' + Number(facts.added || 0) + '</span> <span class="run-summary-del">−'
108
+ + Number(facts.removed || 0) + '</span></span>');
109
+ }
110
+ } else if (facts.steps > 0) {
111
+ parts.push('<span>' + escapeHtml(tx(facts.steps + ' steps', facts.steps + ' 步')) + '</span>');
112
+ }
113
+ if (facts.compiled) parts.push('<span>' + escapeHtml(tx('compiled', '已编译')) + '</span>');
114
+ if (facts.firstCompile) {
115
+ parts.push('<span class="run-summary-qed">' + escapeHtml(tx('Q.E.D. — first clean compile', 'Q.E.D. · 首次编译通过')) + '</span>');
116
+ }
117
+ const mark = qed(facts.animate ? 'is-settling' + (facts.firstCompile ? ' is-first' : '') : '');
118
+ return '<span class="codex-sr-only">' + escapeHtml(tx('Done', '已完成')) + '</span>' + mark + parts.join(sep);
119
+ }
120
+
121
+ function create(deps = {}) {
122
+ const tx = deps.tx || ((en) => en);
123
+ const locale = () => (deps.getLocale?.() === 'zh' ? 'zh' : 'en');
124
+ const formatElapsed = deps.formatElapsed || (ms => Math.round(ms / 1000) + 's');
125
+ const now = deps.now || Date.now;
126
+ const random = deps.random || Math.random;
127
+ const storage = deps.storage === undefined ? safeStorage() : deps.storage;
128
+ // Wrapped, never stored bare: browser timers throw "Illegal invocation" when called as methods of another object.
129
+ const timers = { later: (fn, ms) => (deps.setTimeout || globalThis.setTimeout)(fn, ms) };
130
+ const doc = () => deps.document || globalThis.document;
131
+ const live = new WeakMap();
132
+ const longRunsShown = new Set();
133
+ let tipIndex = Math.floor(random() * TIPS.en.length);
134
+
135
+ function safeStorage() {
136
+ try { return globalThis.localStorage || null; } catch { return null; }
137
+ }
138
+ function once(key) {
139
+ try {
140
+ if (!storage || storage.getItem(key)) return false;
141
+ storage.setItem(key, '1');
142
+ return true;
143
+ } catch { return false; }
144
+ }
145
+ function isLateNight() {
146
+ const hour = new Date(now()).getHours();
147
+ return hour >= 23 || hour < 5;
148
+ }
149
+ function lateNightEgg() {
150
+ if (!isLateNight()) return '';
151
+ // One "night" runs until 05:00, so a 01:00 run belongs to the previous date.
152
+ const night = new Date(now() - 5 * 3600000).toDateString();
153
+ return once('codex-overleaf:egg:late-night:' + night) ? tx('Burning the midnight oil', '挑灯夜战') : '';
154
+ }
155
+
156
+ function markHtml(className = '') {
157
+ return '<span class="run-mark' + (className ? ' ' + className : '') + '" aria-hidden="true" style="' + MARK_STYLE + '">'
158
+ + MARK_LAYERS.map(layer => '<span class="run-mark-' + layer + '"></span>').join('') + '</span>';
159
+ }
160
+
161
+ function stop(el) {
162
+ live.delete(el);
163
+ }
164
+
165
+ function swapLabel(state, html, egg = false) {
166
+ const box = state.label;
167
+ const next = doc().createElement('span');
168
+ next.className = 'is-in';
169
+ next.innerHTML = '<span class="run-shimmer' + (egg ? ' is-egg' : '') + '">' + html + '…</span>';
170
+ // Drop anything still fading out, so a slow timer can never leave the label blank.
171
+ for (const stale of [...(box.children || [])]) if (stale.className === 'is-out') stale.remove();
172
+ const old = box.lastElementChild;
173
+ box.append(next);
174
+ if (old) {
175
+ old.className = 'is-out';
176
+ timers.later(() => old.remove(), 280);
177
+ }
178
+ }
179
+
180
+ function nextIdle(state) {
181
+ const egg = state.eggs.shift();
182
+ if (egg) { swapLabel(state, escapeHtml(egg), true); return; }
183
+ state.verb = pickDifferent(VERBS[locale()], state.verb, random);
184
+ swapLabel(state, escapeHtml(state.verb));
185
+ }
186
+
187
+ function wordSlot(elapsedMs) {
188
+ let slot = 0;
189
+ for (let index = 0; index < WORD_AT_MS.length; index += 1) if (elapsedMs >= WORD_AT_MS[index]) slot = index;
190
+ return slot;
191
+ }
192
+
193
+ function ensureRunning(el, runId) {
194
+ let state = live.get(el);
195
+ if (state && state.runId === runId) return state;
196
+ stop(el);
197
+ el.dataset.presence = 'running';
198
+ el.innerHTML = markHtml() + '<span class="codex-sr-only">' + escapeHtml(tx('Running', '运行中')) + '</span>'
199
+ + '<span class="run-presence-label"></span><span class="run-presence-timer"></span>';
200
+ state = { runId, key: '', verb: '', slot: -1, elapsedMs: 0, eggs: [], label: el.querySelector('.run-presence-label'),
201
+ timer: el.querySelector('.run-presence-timer') };
202
+ const late = lateNightEgg();
203
+ if (late) state.eggs.push(late);
204
+ live.set(el, state);
205
+ return state;
206
+ }
207
+
208
+ // label: { kind: 'stage', text } | { kind: 'idle' }. Tool steps live in the round list, not the header.
209
+ function paintRunning(el, { runId = '', label = { kind: 'idle' }, elapsedMs } = {}) {
210
+ if (!el) return;
211
+ const state = ensureRunning(el, runId);
212
+ state.idle = label.kind !== 'stage';
213
+ if (elapsedMs !== undefined) tick(el, elapsedMs);
214
+ if (!state.idle) {
215
+ const key = 's:' + label.text;
216
+ if (key !== state.key) { state.key = key; swapLabel(state, escapeHtml(label.text)); }
217
+ return;
218
+ }
219
+ refreshWord(state);
220
+ }
221
+
222
+ function refreshWord(state) {
223
+ const slot = wordSlot(state.elapsedMs);
224
+ if (state.key === 'idle' && slot === state.slot) return;
225
+ state.key = 'idle';
226
+ state.slot = slot;
227
+ nextIdle(state);
228
+ }
229
+
230
+ function tick(el, elapsedMs) {
231
+ const state = live.get(el);
232
+ if (!state) return;
233
+ const value = formatElapsed(elapsedMs);
234
+ if (state.timer.textContent !== value) state.timer.textContent = value;
235
+ state.elapsedMs = elapsedMs;
236
+ if (elapsedMs > LONG_RUN_MS && state.runId && !longRunsShown.has(state.runId)) {
237
+ longRunsShown.add(state.runId);
238
+ state.eggs.push(tx('This is a long proof', '这是个长证明'));
239
+ }
240
+ if (state.idle) refreshWord(state);
241
+ }
242
+
243
+ // facts: { kind: 'done' | 'stopped' | 'failed' | 'plain', text, elapsedMs, files, added, removed,
244
+ // steps, compiled, projectId, animate }
245
+ function paintSettled(el, facts = {}) {
246
+ if (!el) return;
247
+ stop(el);
248
+ const firstCompile = facts.kind === 'done' && facts.animate && facts.compiled && facts.projectId
249
+ ? once('codex-overleaf:egg:first-compile:' + facts.projectId) : false;
250
+ const stamp = JSON.stringify({ ...facts, animate: false });
251
+ if (el.dataset.presenceStamp === stamp && !facts.animate) return;
252
+ el.dataset.presenceStamp = stamp;
253
+ el.dataset.presence = facts.kind || 'plain';
254
+ if (facts.kind === 'done' || facts.kind === 'stopped') {
255
+ el.innerHTML = settledHtml({ ...facts, firstCompile }, tx, formatElapsed);
256
+ } else if (facts.kind === 'failed') {
257
+ el.innerHTML = icon('alert') + '<span>' + escapeHtml(facts.text) + '</span>';
258
+ } else {
259
+ el.textContent = facts.text || '';
260
+ }
261
+ }
262
+
263
+ function emptyTitle() {
264
+ return isLateNight() ? tx("Still up? Let's finish this section.", '还没睡?把这一节写完吧。')
265
+ : tx('What are we writing today?', '今天写点什么?');
266
+ }
267
+
268
+ // Decorates the existing empty-state element with the mark, project chip and a tip.
269
+ function decorateEmpty(empty, { projectName = '' } = {}) {
270
+ const mark = doc().createElement('div');
271
+ mark.className = 'run-empty-mark';
272
+ mark.innerHTML = markHtml('is-large');
273
+ empty.prepend(mark);
274
+ const title = empty.querySelector('.empty-runs-title');
275
+ if (title) title.textContent = emptyTitle();
276
+ if (projectName) {
277
+ const chip = doc().createElement('span');
278
+ chip.className = 'run-empty-project';
279
+ chip.innerHTML = icon('file') + '<span></span>';
280
+ chip.lastElementChild.textContent = projectName;
281
+ title?.after(chip);
282
+ }
283
+ const tip = doc().createElement('div');
284
+ tip.className = 'run-empty-tip';
285
+ tip.innerHTML = '<span class="run-empty-pilcrow" aria-hidden="true">¶</span><span class="run-empty-tip-text"></span>'
286
+ + '<button type="button" class="run-empty-tip-next">' + icon('refresh') + '</button>';
287
+ const next = tip.querySelector('button');
288
+ next.title = tx('Next tip', '换一条');
289
+ next.setAttribute('aria-label', next.title);
290
+ const show = () => {
291
+ const old = tip.querySelector('.run-empty-tip-text');
292
+ const fresh = old.cloneNode(false);
293
+ fresh.textContent = TIPS[locale()][tipIndex % TIPS.en.length];
294
+ old.replaceWith(fresh);
295
+ };
296
+ next.addEventListener('click', () => { tipIndex += 1; show(); });
297
+ show();
298
+ empty.append(tip);
299
+ return empty;
300
+ }
301
+
302
+ return { markHtml, paintRunning, tick, paintSettled, decorateEmpty, stop };
303
+ }
304
+
305
+ return { create, VERBS, TIPS, activityVerb, commandSummary, settledHtml, icon };
306
+ });
@@ -322,7 +322,8 @@
322
322
  tr: options.tr,
323
323
  onGuide: itemId => guide(itemId).catch(error => options.toast?.(error.message, 'warning')),
324
324
  onRemove: remove,
325
- onResume: resume
325
+ onResume: resume,
326
+ onToast: message => options.toast?.(message, 'warning')
326
327
  });
327
328
  if (view) {
328
329
  view.container = container;
@@ -107,17 +107,137 @@
107
107
  if (!copied) throw new Error('clipboard_copy_failed');
108
108
  }
109
109
 
110
- return Object.freeze({ configureResultActions, projectUndoAvailability });
110
+ return Object.freeze({ configureResultActions, projectUndoAvailability, splitFlatCompletionReport, projectCompletionMeta, summarizeCompletionMeta });
111
+ }
112
+
113
+
114
+ const FLAT_REPORT_STATUS_SECTIONS = [
115
+ { key: 'saveState', prefixes: ['Save:', '保存:'], en: 'Save', zh: '保存' },
116
+ { key: 'unchangedReason', prefixes: ['Why nothing changed:', '未修改原因:'], en: 'Why nothing changed', zh: '未修改原因' },
117
+ { key: 'writeResult', prefixes: ['Write result:', '写入结果:'], en: 'Write result', zh: '写入结果' },
118
+ { key: 'undo', prefixes: ['Undo:', '可撤销:'], en: 'Undo', zh: '可撤销' },
119
+ { key: 'nextStep', prefixes: ['Next:', '下一步:'], en: 'Next', zh: '下一步' }
120
+ ];
121
+
122
+ function splitFlatCompletionReport(text, tx = value => value) {
123
+ const raw = typeof text === 'string' ? text : '';
124
+ if (!raw.trim()) return { body: raw, meta: [] };
125
+ const bodySections = [], meta = [];
126
+ for (const section of raw.split(/\n{2,}/)) {
127
+ const trimmed = section.trim();
128
+ if (!trimmed) continue;
129
+ const entry = trimmed.includes('\n') ? null
130
+ : FLAT_REPORT_STATUS_SECTIONS.find(item => item.prefixes.some(prefix => trimmed.startsWith(prefix)));
131
+ if (entry) {
132
+ const prefix = entry.prefixes.find(value => trimmed.startsWith(value));
133
+ const value = trimmed.slice(prefix.length).trim();
134
+ if (value) {
135
+ meta.push({ key: entry.key, label: tx(entry.en, entry.zh), value });
136
+ continue;
137
+ }
138
+ }
139
+ bodySections.push(trimmed);
140
+ }
141
+ return { body: bodySections.join('\n\n'), meta };
142
+ }
143
+
144
+ function projectCompletionMeta(meta, run, options) {
145
+ const { tx, trackedChangeInFlight, isTrackedChangeLifecycleRun, projectRunSettlement } = options;
146
+ if (!Array.isArray(meta) || !run) return meta;
147
+ const undone = run.undoStatus === 'applied' || run.trackedChangeStatus === 'rejected';
148
+ const rows = undone ? meta.filter(row => row?.key !== 'saveState') : meta;
149
+ const inFlight = trackedChangeInFlight?.get(run.id);
150
+ const tracked = isTrackedChangeLifecycleRun(run);
151
+ const pendingCreatedUndo = tracked && run.trackedChangeStatus === 'needs_review' && run.undoOperations?.some(operation => operation?.undoCreatedFile?.v === 1);
152
+ let undo;
153
+ if (run.undoStatus === 'running' || inFlight === 'reject') {
154
+ undo = tx('Undoing changes...', '正在撤销修改…');
155
+ } else if (run.undoStatus === 'applied' || (tracked && run.trackedChangeStatus === 'rejected')) {
156
+ undo = tx('This run\'s changes have been undone.', '本轮修改已撤销。');
157
+ } else if (tracked && run.trackedChangeStatus === 'accepted') {
158
+ undo = tx('Changes accepted; undo is no longer available.', '修改已接受,无法再撤销。');
159
+ } else if (run.undoStatus === 'partial' || pendingCreatedUndo) {
160
+ undo = hasUndoProgress(run)
161
+ ? projectRunSettlement(run).canUndo
162
+ ? tx(pendingCreatedUndo ? 'Some changes were undone; retry the remaining files.' : 'Some changes were undone; remaining changes can still be undone.', pendingCreatedUndo ? '已撤销部分修改,可重试剩余文件。' : '已撤销部分修改,剩余修改仍可撤销。')
163
+ : tx('Some changes were undone.', '已撤销部分修改。')
164
+ : projectRunSettlement(run).canUndo
165
+ ? tx(pendingCreatedUndo ? 'Undo is incomplete; retry the remaining files.' : 'Undo is incomplete; remaining changes can still be undone.', pendingCreatedUndo ? '撤销未完成,可重试剩余文件。' : '撤销未完成,剩余修改仍可撤销。')
166
+ : tx('Undo is incomplete.', '撤销未完成。');
167
+ }
168
+ if (!undo && !run.saveCheck && !run.saveConfirmedAt) return rows;
169
+ return rows.map(row => row?.key === 'undo' && undo
170
+ ? { ...row, label: tx('Undo', '撤销'), value: undo }
171
+ : row?.key === 'saveState' && (run.saveCheck || run.saveConfirmedAt)
172
+ ? { ...row, label: tx('Save', '保存'), value: run.saveCheck
173
+ ? tx('Pending confirmation', '待确认') : tx('Saved', '已确认保存') }
174
+ : row);
175
+ }
176
+
177
+ function hasUndoProgress(run) {
178
+ const failures = run?.settlement?.failures || run?.settlementFacts?.failures || [];
179
+ return (Array.isArray(run?.events) ? run.events : []).some(event => {
180
+ if (!['completed', 'failed'].includes(event?.status) || event.streamRole || ['report', 'guidance', 'stream'].includes(event.kind)) return false;
181
+ const count = String(event.title || '').match(/^(?:Undo result: (?:undone|rejected) (\d+) (?:item\(s\)|file\(s\)|tracked change\(s\)), skipped \d+(?: item\(s\))?|撤销结果:已(?:撤销|拒绝) (\d+) (?:项|条留痕|个文件),跳过 \d+ (?:项|条))$/);
182
+ if (!(Number(count?.[1] || count?.[2]) > 0)) return false;
183
+ const detail = event.detail || {}, applied = detail.Undone || detail.Rejected || detail['已撤销'] || detail['已拒绝'];
184
+ const skipped = detail.Skipped || detail['跳过'], path = item => item?.File || item?.['文件'];
185
+ return Array.isArray(applied) && Array.isArray(skipped) && applied.some(item =>
186
+ typeof path(item) === 'string' && path(item).trim() && !skipped.some(other => path(other) === path(item))
187
+ && !(Array.isArray(failures) && failures.some(failure => failure?.code === 'undo_not_verified'
188
+ && (!failure.file || failure.file === path(item)))));
189
+ });
190
+ }
191
+
192
+ // Generic reasons say nothing beyond "no files were written", so they stay in
193
+ // the details. Both locales match because a report keeps its original locale.
194
+ const GENERIC_UNCHANGED_REASONS = new Set([
195
+ 'No file changes need to sync back to Overleaf.', '没有产生需要同步回 Overleaf 的文件改动。',
196
+ 'No approved file changes remain to sync back to Overleaf.', '没有剩余已确认的文件改动需要同步回 Overleaf。',
197
+ 'This run was Ask mode.', '这轮是只问不改。'
198
+ ]);
199
+
200
+ // One summary line for the meta rows: what was written, whether it can be
201
+ // undone, and a pending save. The rows themselves stay behind the disclosure,
202
+ // which opens by itself only when something needs the user's attention.
203
+ function summarizeCompletionMeta(rows, run, { tx, failed = false, unconfirmed = false } = {}) {
204
+ const find = key => String(rows.find(row => row?.key === key)?.value || '').trim();
205
+ const counts = find('writeResult').match(/wrote (\d+) items?, skipped (\d+)|已写入 (\d+) 项,跳过 (\d+) 项/);
206
+ const wrote = Number(counts?.[1] ?? counts?.[3] ?? 0), skipped = Number(counts?.[2] ?? counts?.[4] ?? 0);
207
+ const reason = find('unchangedReason');
208
+ const undone = run?.undoStatus === 'applied' || run?.trackedChangeStatus === 'rejected';
209
+ const savePending = !undone && (Boolean(run?.saveCheck) || (!run?.saveConfirmedAt && /^(?:Pending|待确认)/.test(find('saveState'))));
210
+ const pendingCreatedUndo = run?.trackedChangeStatus === 'needs_review'
211
+ && run.undoOperations?.some(operation => operation?.undoCreatedFile?.v === 1);
212
+ const facts = [];
213
+ if (failed) facts.push({ key: 'write', tone: 'fail', text: unconfirmed ? tx('Write not confirmed', '写入结果未确认') : tx('Write incomplete', '写入未完成') });
214
+ else if (wrote) facts.push({ key: 'write', text: tx(`Wrote ${wrote} item${wrote === 1 ? '' : 's'}`, `已写入 ${wrote} 项`) });
215
+ else if (reason && !GENERIC_UNCHANGED_REASONS.has(reason)) facts.push({ key: 'write', text: tx('Not written: ', '未写入:') + reason.replace(/[.。]$/, '') });
216
+ else facts.push({ key: 'write', text: tx('No files written', '未写入文件') });
217
+ if (skipped) facts.push({ key: 'skip', tone: 'warn', text: tx(`${skipped} skipped`, `跳过 ${skipped} 项`) });
218
+ const undoable = Number(find('undo').match(/(\d+)/)?.[1] || 0);
219
+ const undo = run?.undoStatus === 'running' || /^(?:Undoing changes\.\.\.|正在撤销修改…)$/.test(find('undo')) ? tx('Undoing…', '正在撤销…')
220
+ : run?.undoStatus === 'applied' || run?.trackedChangeStatus === 'rejected' ? tx('Changes undone', '已撤销本轮修改')
221
+ : run?.trackedChangeStatus === 'accepted' ? tx('Changes accepted', '修改已接受')
222
+ : pendingCreatedUndo || run?.undoStatus === 'partial'
223
+ ? (hasUndoProgress(run) ? tx('Partly undone', '已撤销部分修改') : tx('Undo incomplete', '撤销未完成'))
224
+ : undoable ? tx(`${undoable} undoable`, `可撤销 ${undoable} 项`)
225
+ : failed ? '' : tx('Nothing to undo', '无可撤销写入');
226
+ if (undo) facts.push({ key: 'undo', text: undo });
227
+ if (savePending) facts.push({ key: 'save', tone: 'warn', text: tx('Save pending', '保存待确认') });
228
+ const attention = failed || skipped > 0;
229
+ const warnKeys = new Set([...(attention ? ['writeResult', 'nextStep'] : []), ...(savePending ? ['saveState'] : [])]);
230
+ return { facts, open: attention, warnKeys };
111
231
  }
112
232
 
113
233
  function projectUndoAvailability(run, projectRunSettlement) {
114
234
  const payload = run.recoveryPayload && typeof run.recoveryPayload === 'object'
115
235
  ? run.recoveryPayload : run;
116
236
  if (!Array.isArray(payload.appliedOperations)) return projectRunSettlement(run);
117
- // Forward binary writes do not provide a rollback. Filter them only for
237
+ // Forward creation/asset writes alone do not provide a rollback. Filter them only for
118
238
  // display; preserve the canonical settlement and every real recovery field.
119
239
  const appliedOperations = payload.appliedOperations.filter(operation =>
120
- operation?.type !== 'binary-create' && operation?.type !== 'overwrite-binary');
240
+ operation?.type !== 'create' && operation?.type !== 'binary-create' && operation?.type !== 'overwrite-binary');
121
241
  if (appliedOperations.length === payload.appliedOperations.length) return projectRunSettlement(run);
122
242
  return projectRunSettlement({
123
243
  ...run,