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
@@ -0,0 +1,576 @@
1
+ (function (root, factory) {
2
+ if (typeof module === 'object' && module.exports) module.exports = factory(require('../shared/writingStyle'));
3
+ else root.CodexOverleafModuleRegistry.define('WritingStyleSettings', ['WritingStyle'], factory);
4
+ })(typeof globalThis !== 'undefined' ? globalThis : window, function (Shared) {
5
+ 'use strict';
6
+ const CHANGE_KEY = 'codex-overleaf-writing-style-changed-v1';
7
+ const LIMITS = Shared.REFERENCE_LIMITS;
8
+ const MAX_SOURCES = LIMITS.maxSources;
9
+
10
+ function create(deps) {
11
+ let scopeKey = '', scope = null, epoch = 0, meta = null, loadPromise = null;
12
+ let host = null, sources = [], sourceSnapshot = null, observer = null;
13
+ let desiredEnabled = false, setupEnabled = null, expanded = false, dirty = false;
14
+ let message = '', messageTone = '', toggleBusy = false, pollTimer = null;
15
+ let projects = [], projectLoaded = false, projectLoading = false, projectError = '';
16
+ let pickerOpen = false, query = '', filter = 'all';
17
+ let activeGeneration = null, cancelling = false;
18
+ let preview = null, previewBusy = false, previewFile = 'references/writing-style.md';
19
+ const tx = (en, zh) => deps.tx(en, zh);
20
+ const node = (tag, className, text) => {
21
+ const element = document.createElement(tag);
22
+ if (className) element.className = className;
23
+ if (text !== undefined) element.textContent = text;
24
+ return element;
25
+ };
26
+ function button(label, action, className = 'ws2-button', key) {
27
+ const element = node('button', className, label);
28
+ element.type = 'button';
29
+ if (key) element.dataset.styleFocus = key;
30
+ element.addEventListener('click', action);
31
+ return element;
32
+ }
33
+ function icon(name) {
34
+ const paths = {
35
+ book: ['M4 4h6a3 3 0 0 1 3 3v14a4 4 0 0 0-4-2H4z', 'M13 7a3 3 0 0 1 3-3h4v15h-4a4 4 0 0 0-3 2'],
36
+ project: ['M4 6h6l2 2h8v11H4z', 'M8 12h8', 'M8 15h5'],
37
+ pdf: ['M6 3h8l4 4v14H6z', 'M14 3v5h4', 'M9 12h6', 'M9 16h4'],
38
+ plus: ['M12 5v14', 'M5 12h14'],
39
+ search: ['M20 20l-5-5', 'M17 10a7 7 0 1 1-14 0 7 7 0 0 1 14 0'],
40
+ refresh: ['M20 7v5h-5', 'M4 17v-5h5', 'M5.5 8a7 7 0 0 1 11.8-3L20 8', 'M4 16l2.7 3A7 7 0 0 0 18.5 16'],
41
+ close: ['M6 6l12 12', 'M18 6L6 18'],
42
+ arrow: ['M5 12h14', 'M14 7l5 5-5 5'],
43
+ check: ['M5 12l4 4L19 6']
44
+ };
45
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
46
+ svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('fill', 'none');
47
+ svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('stroke-width', '1.6');
48
+ svg.setAttribute('stroke-linecap', 'round'); svg.setAttribute('stroke-linejoin', 'round');
49
+ svg.setAttribute('aria-hidden', 'true'); svg.setAttribute('focusable', 'false');
50
+ for (const value of paths[name] || paths.book) {
51
+ const path = document.createElementNS(svg.namespaceURI, 'path');
52
+ path.setAttribute('d', value); svg.append(path);
53
+ }
54
+ return svg;
55
+ }
56
+ function iconButton(name, label, action, key) {
57
+ const element = button('', action, 'ws2-icon-button', key);
58
+ element.title = label; element.setAttribute('aria-label', label); element.append(icon(name));
59
+ return element;
60
+ }
61
+ function currentScope() {
62
+ const value = deps.getScope();
63
+ return value?.accountScopeId && value?.projectId ? value : null;
64
+ }
65
+ function alignScope() {
66
+ const next = currentScope(), key = next ? next.accountScopeId + ':' + next.projectId : '';
67
+ if (scopeKey !== key) {
68
+ const previous = activeGeneration;
69
+ if (previous && previous.phase === 'reading') {
70
+ previous.cancelled = true;
71
+ if (previous.readId) void deps.cancelReferenceRead(previous.readId).catch(() => {});
72
+ }
73
+ scopeKey = key; scope = next; epoch++;
74
+ meta = null; loadPromise = null; sources = []; sourceSnapshot = null;
75
+ projects = []; projectLoaded = false; projectLoading = false; projectError = '';
76
+ desiredEnabled = false; setupEnabled = null; expanded = false; dirty = false;
77
+ message = ''; messageTone = ''; toggleBusy = false; pickerOpen = false; query = ''; filter = 'all';
78
+ activeGeneration = null; cancelling = false; preview = null; previewBusy = false;
79
+ clearTimeout(pollTimer); pollTimer = null;
80
+ }
81
+ return scope;
82
+ }
83
+ async function request(method, params = {}) {
84
+ const response = await deps.request({ method, params });
85
+ if (!response?.ok) {
86
+ const error = new Error(response?.error?.message || tx('The request could not be completed.', '请求未能完成。'));
87
+ error.code = response?.error?.code || 'writing_style_failed'; throw error;
88
+ }
89
+ return response.result;
90
+ }
91
+ function restoreSources(value) {
92
+ sources = (value.sources || []).map(source => source.kind === 'project'
93
+ ? { kind: 'project', projectId: source.projectId, name: source.name, capturedAt: source.capturedAt }
94
+ : { kind: 'saved', sourceId: source.id, name: source.name });
95
+ sourceSnapshot = value.snapshot || null;
96
+ }
97
+ async function ensureLoaded(fresh = false) {
98
+ const target = alignScope();
99
+ if (!target) return null;
100
+ if (loadPromise) return loadPromise;
101
+ if (meta && !fresh) return meta;
102
+ const generation = epoch;
103
+ loadPromise = request('writing-style.get', target).then(value => {
104
+ if (generation !== epoch) return null;
105
+ meta = value;
106
+ if (setupEnabled === null) desiredEnabled = value.enabled === true;
107
+ if (!dirty && !activeGeneration) restoreSources(value);
108
+ if (value.error && !message) { message = value.error; messageTone = 'error'; }
109
+ clearTimeout(pollTimer); pollTimer = null;
110
+ if (value.building) pollTimer = setTimeout(() => { void ensureLoaded(true).catch(() => {}); }, 1800);
111
+ else if (!activeGeneration) cancelling = false;
112
+ render(); return value;
113
+ }).catch(error => {
114
+ if (generation === epoch) { message = error.message; messageTone = 'error'; render(); }
115
+ throw error;
116
+ }).finally(() => { if (generation === epoch) loadPromise = null; });
117
+ return loadPromise;
118
+ }
119
+ function capture() {
120
+ if (!alignScope()) return null;
121
+ if (!meta) throw new Error(tx('Writing-style settings are loading. Try again shortly.', '写作风格设置正在加载,请稍后再试。'));
122
+ if (!desiredEnabled) return null;
123
+ if (!meta.ready) throw new Error(tx('Finish setting up the writing style in General, or turn it off.', '请在常规设置中完成写作风格设置,或关闭此功能。'));
124
+ return Shared.normalizeSnapshot(meta.snapshot);
125
+ }
126
+ function changed() { dirty = true; preview = null; message = ''; messageTone = ''; render(); }
127
+ async function notify(target) {
128
+ await chrome.storage.local.set({ [CHANGE_KEY]: { ...target, changedAt: Date.now(), nonce: Math.random().toString(36) } });
129
+ }
130
+ function working() { return Boolean(activeGeneration || meta?.building); }
131
+ async function toggle(value) {
132
+ const target = alignScope(), generation = epoch;
133
+ if (!target || toggleBusy) return;
134
+ desiredEnabled = value; expanded = value || expanded; message = ''; messageTone = '';
135
+ if (value && !meta?.ready) {
136
+ setupEnabled = true; pickerOpen = sources.length === 0; render();
137
+ if (pickerOpen) void loadProjects();
138
+ return;
139
+ }
140
+ setupEnabled = null; toggleBusy = true; render();
141
+ try {
142
+ const next = await request('writing-style.set', { ...target, enabled: value });
143
+ if (epoch !== generation) return;
144
+ meta = next; await notify(target);
145
+ } catch (error) {
146
+ if (epoch === generation) { desiredEnabled = meta?.enabled === true; message = error.message; messageTone = 'error'; }
147
+ } finally { if (epoch === generation) { toggleBusy = false; render(); } }
148
+ }
149
+ async function loadProjects(force = false) {
150
+ const target = alignScope(), generation = epoch;
151
+ if (!target || projectLoading || (projectLoaded && !force)) return;
152
+ projectLoading = true; projectError = ''; render();
153
+ try {
154
+ const values = await deps.listProjects(target);
155
+ if (!Array.isArray(values)) throw new Error(tx('The project directory could not be read.', '项目目录未能读取。'));
156
+ if (generation === epoch) { projects = values; projectLoaded = true; }
157
+ } catch (error) {
158
+ if (generation === epoch) { projects = []; projectLoaded = false; projectError = error.message; }
159
+ } finally { if (generation === epoch) { projectLoading = false; render(); } }
160
+ }
161
+ function selectProject(project, selected) {
162
+ if (working()) return;
163
+ const index = sources.findIndex(source => source.kind === 'project' && source.projectId === project.projectId);
164
+ if (!selected && index >= 0) sources.splice(index, 1);
165
+ if (selected && index < 0 && sources.length < MAX_SOURCES) {
166
+ sources.push({ kind: 'project', projectId: project.projectId, name: project.name });
167
+ }
168
+ changed();
169
+ }
170
+ function addFiles(files) {
171
+ if (working()) return;
172
+ message = ''; messageTone = '';
173
+ for (const file of Array.from(files || [])) {
174
+ if (sources.length >= MAX_SOURCES) { message = tx('Three references selected. Remove one to add another.', '已选三份资料,移除一份后可继续添加。'); break; }
175
+ if (!/\.pdf$/i.test(file.name) || file.size > LIMITS.maxPdfBytes || file.size === 0) {
176
+ message = tx('Choose a text-based PDF up to 20 MiB.', '请选择不超过 20 MiB、含文字层的 PDF。'); messageTone = 'error'; break;
177
+ }
178
+ if (sources.some(source => source.file?.name === file.name && source.file?.size === file.size && source.file?.lastModified === file.lastModified)) continue;
179
+ if (sources.reduce((sum, source) => sum + (source.file?.size || 0), file.size) > LIMITS.maxPdfTotalBytes) {
180
+ message = tx('PDFs can total up to 40 MiB.', 'PDF 合计上限为 40 MiB。'); messageTone = 'error'; break;
181
+ }
182
+ sources.push({ kind: 'pdf', id: crypto.randomUUID(), name: file.name, file });
183
+ dirty = true; preview = null;
184
+ }
185
+ render();
186
+ }
187
+ async function encode(file) {
188
+ const bytes = new Uint8Array(await file.arrayBuffer());
189
+ let binary = '';
190
+ for (let offset = 0; offset < bytes.length; offset += 8192) binary += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
191
+ return btoa(binary);
192
+ }
193
+ async function generate() {
194
+ const target = alignScope(), generation = epoch;
195
+ if (!target || working() || deps.isRunning() || !sources.length) return;
196
+ const chosen = sources.map(value => ({ ...value })), reuseSnapshot = sourceSnapshot;
197
+ const operation = { phase: 'reading', cancelled: false, readId: '', requestId: '', generation };
198
+ activeGeneration = operation; cancelling = false; pickerOpen = false; preview = null;
199
+ message = ''; messageTone = ''; expanded = true; render();
200
+ const stillActive = () => {
201
+ if (operation.cancelled || generation !== epoch) {
202
+ const error = new Error(tx('Style generation cancelled.', '已取消风格生成。'));
203
+ error.code = 'codex_cancelled'; throw error;
204
+ }
205
+ };
206
+ try {
207
+ const settings = await deps.getModelSettings();
208
+ stillActive();
209
+ const attachments = [], selected = [];
210
+ for (const source of chosen) {
211
+ stillActive();
212
+ if (source.kind === 'pdf') {
213
+ attachments.push({ id: source.id, name: source.name, size: source.file.size,
214
+ mimeType: 'application/pdf', kind: 'file', contentBase64: await encode(source.file) });
215
+ selected.push({ kind: 'pdf', attachmentId: source.id });
216
+ } else if (source.kind === 'project') {
217
+ operation.readId = crypto.randomUUID(); operation.sourceName = source.name; render();
218
+ const snapshot = await deps.getReferenceSnapshot(source.projectId, operation.readId);
219
+ operation.readId = ''; stillActive();
220
+ if (snapshot?.projectId !== source.projectId || snapshot?.capabilities?.referenceTextComplete !== true) {
221
+ throw new Error(tx('The reference project could not be read completely: ', '参考项目未能完整读取:') + source.name);
222
+ }
223
+ const body = new Blob([JSON.stringify(snapshot)], { type: 'application/json' });
224
+ if (body.size > LIMITS.maxProjectBytes) throw new Error(tx('Reference project is too large: ', '参考项目过大:') + source.name);
225
+ const id = crypto.randomUUID();
226
+ attachments.push({ id, name: source.projectId + '.writing-reference.json', size: body.size,
227
+ mimeType: 'application/json', kind: 'file', contentBase64: await encode(body) });
228
+ selected.push({ kind: 'project', projectId: source.projectId, name: source.name, attachmentId: id });
229
+ } else selected.push({ kind: 'saved', sourceId: source.sourceId });
230
+ }
231
+ stillActive();
232
+ operation.phase = 'learning'; operation.sourceName = ''; render();
233
+ const tracked = await deps.startBuild({ ...settings, ...target, mode: 'ask',
234
+ task: 'Generate the selected project writing skill and its section references.',
235
+ attachments, writingStyle: null,
236
+ writingStyleBuild: { enableAfterBuild: desiredEnabled, reuseSnapshot, sources: selected }
237
+ });
238
+ operation.requestId = tracked.id;
239
+ if (operation.cancelled) await request('codex.cancel', { requestId: tracked.id });
240
+ if (generation === epoch) { render(); void ensureLoaded(true).catch(() => {}); }
241
+ const response = await tracked.promise;
242
+ if (generation !== epoch) return;
243
+ if (!response?.ok) {
244
+ const error = new Error(response?.error?.message || tx('Style generation failed.', '风格生成失败。'));
245
+ error.code = response?.error?.code; throw error;
246
+ }
247
+ const result = response.result?.writingStyle;
248
+ if (!result?.ready) throw new Error(tx('No completed writing style was returned.', '未返回完整的写作风格。'));
249
+ meta = result; dirty = false; setupEnabled = null; desiredEnabled = result.enabled === true;
250
+ restoreSources(result);
251
+ message = tx('Writing style is ready.', '写作风格已就绪。'); messageTone = 'success';
252
+ await notify(target);
253
+ } catch (error) {
254
+ if (generation === epoch) {
255
+ message = operation.cancelled || error.code === 'codex_cancelled'
256
+ ? tx('Style generation cancelled.', '已取消风格生成。') : error.message;
257
+ messageTone = operation.cancelled || error.code === 'codex_cancelled' ? '' : 'error';
258
+ }
259
+ } finally {
260
+ if (activeGeneration === operation) {
261
+ activeGeneration = null; cancelling = false;
262
+ await ensureLoaded(true).catch(() => {}); render();
263
+ }
264
+ }
265
+ }
266
+ async function cancel() {
267
+ if (cancelling) return;
268
+ const operation = activeGeneration, generation = epoch;
269
+ const id = operation?.requestId || meta?.building?.requestId;
270
+ if (!operation && !id) return;
271
+ cancelling = true; message = ''; messageTone = '';
272
+ if (operation) operation.cancelled = true;
273
+ render();
274
+ try {
275
+ if (operation?.readId) await deps.cancelReferenceRead(operation.readId);
276
+ if (id) await request('codex.cancel', { requestId: id });
277
+ } catch (error) {
278
+ if (generation === epoch) { cancelling = false; message = error.message; messageTone = 'error'; render(); }
279
+ }
280
+ if (!operation && generation === epoch) await ensureLoaded(true).catch(() => {});
281
+ }
282
+ async function loadPreview() {
283
+ const target = alignScope(), generation = epoch;
284
+ if (!target || previewBusy) return;
285
+ if (preview) { preview = null; render(); return; }
286
+ previewBusy = true; render();
287
+ try {
288
+ const value = await request('writing-style.get', { ...target, includePreview: true });
289
+ if (generation === epoch) { preview = value.preview || []; previewFile = 'references/writing-style.md'; }
290
+ } catch (error) {
291
+ if (generation === epoch) { message = error.message; messageTone = 'error'; }
292
+ } finally { if (generation === epoch) { previewBusy = false; render(); } }
293
+ }
294
+ function displayDate(value) {
295
+ const date = new Date(value);
296
+ return Number.isNaN(date.getTime()) ? '' : date.toLocaleDateString(tx('en', 'zh-CN'), { month: 'short', day: 'numeric' });
297
+ }
298
+ function preserveFocus() {
299
+ const active = document.activeElement;
300
+ if (!host?.contains(active) || !active.dataset?.styleFocus) return null;
301
+ return { key: active.dataset.styleFocus, start: active.selectionStart, end: active.selectionEnd };
302
+ }
303
+ function restoreFocus(focus) {
304
+ if (!focus) return;
305
+ const target = host.querySelector('[data-style-focus="' + focus.key + '"]');
306
+ if (!target || target.disabled) return;
307
+ target.focus({ preventScroll: true });
308
+ if (typeof focus.start === 'number' && typeof target.setSelectionRange === 'function') {
309
+ try { target.setSelectionRange(focus.start, focus.end); } catch (_) {}
310
+ }
311
+ }
312
+ function renderPicker() {
313
+ const panel = node('section', 'ws2-picker');
314
+ panel.setAttribute('aria-label', tx('Choose reference projects', '选择参考项目'));
315
+ const heading = node('div', 'ws2-picker-heading');
316
+ heading.append(node('strong', '', tx('Choose projects', '选择项目')));
317
+ const reload = iconButton('refresh', tx('Reload project directory', '刷新项目目录'), () => { void loadProjects(true); }, 'reload-projects');
318
+ reload.disabled = projectLoading;
319
+ heading.append(reload, iconButton('close', tx('Close project picker', '关闭项目选择'), () => { pickerOpen = false; render(); }, 'close-picker'));
320
+ panel.append(heading);
321
+ const search = node('label', 'ws2-search');
322
+ search.append(icon('search'));
323
+ const input = node('input'); input.type = 'search'; input.value = query; input.dataset.styleFocus = 'project-search';
324
+ input.placeholder = tx('Search all Overleaf projects', '搜索全部 Overleaf 项目');
325
+ input.setAttribute('aria-label', input.placeholder);
326
+ input.addEventListener('input', () => { query = input.value; render(); });
327
+ search.append(input); panel.append(search);
328
+ const filters = node('div', 'ws2-filters');
329
+ for (const [value, label] of [['all', tx('All', '全部')], ['active', tx('Active', '常规')], ['archived', tx('Archived', '已归档')]]) {
330
+ const count = value === 'all' ? projects.length : projects.filter(p => p.archived === (value === 'archived')).length;
331
+ const item = button(label + (projectLoaded ? ' ' + count : ''), () => { filter = value; render(); }, 'ws2-filter', 'filter-' + value);
332
+ item.setAttribute('aria-pressed', String(filter === value)); filters.append(item);
333
+ }
334
+ panel.append(filters);
335
+ const list = node('div', 'ws2-projects');
336
+ list.setAttribute('role', 'group'); list.setAttribute('aria-label', tx('Overleaf projects', 'Overleaf 项目'));
337
+ if (projectLoading) {
338
+ const loading = node('div', 'ws2-picker-state');
339
+ loading.append(node('span', 'ws2-pulse'), node('span', '', tx('Loading project directory…', '正在加载项目目录…')));
340
+ list.append(loading);
341
+ } else if (projectError) {
342
+ const error = node('div', 'ws2-picker-state ws2-picker-error');
343
+ error.append(node('strong', '', tx('Projects could not be loaded', '项目列表未能加载')), node('p', '', projectError),
344
+ button(tx('Retry', '重试'), () => { void loadProjects(true); }, 'ws2-button', 'retry-projects'));
345
+ list.append(error);
346
+ } else {
347
+ const searchText = query.trim().toLocaleLowerCase();
348
+ const matches = projects.filter(project => (!searchText || project.name.toLocaleLowerCase().includes(searchText))
349
+ && (filter === 'all' || project.archived === (filter === 'archived')));
350
+ for (const project of matches) {
351
+ const selected = sources.some(source => source.kind === 'project' && source.projectId === project.projectId);
352
+ const row = node('label', 'ws2-project');
353
+ row.dataset.selected = String(selected);
354
+ const checkbox = node('input', 'ws2-check'); checkbox.type = 'checkbox'; checkbox.checked = selected;
355
+ checkbox.dataset.styleFocus = 'project-' + project.projectId;
356
+ checkbox.disabled = working() || (!selected && sources.length >= MAX_SOURCES);
357
+ checkbox.setAttribute('aria-label', project.name);
358
+ checkbox.addEventListener('change', () => selectProject(project, checkbox.checked));
359
+ const copy = node('span', 'ws2-project-copy');
360
+ const title = node('span', 'ws2-project-name', project.name); title.title = project.name;
361
+ copy.append(title);
362
+ const tags = [];
363
+ if (project.projectId === scope?.projectId) tags.push(tx('Current project', '当前项目'));
364
+ if (project.shared) tags.push(tx('Shared', '共享'));
365
+ if (project.archived) tags.push(tx('Archived', '已归档'));
366
+ if (project.readOnly) tags.push(tx('Read only', '只读'));
367
+ if (!tags.length) tags.push(tx('Overleaf project', 'Overleaf 项目'));
368
+ copy.append(node('small', '', tags.join(' · ')));
369
+ row.append(checkbox, copy); list.append(row);
370
+ }
371
+ if (!matches.length) list.append(node('div', 'ws2-picker-state',
372
+ query ? tx('No projects match this search.', '没有匹配的项目。') : tx('No projects in this group.', '此分组暂无项目。')));
373
+ }
374
+ panel.append(list);
375
+ const footer = node('div', 'ws2-picker-footer');
376
+ footer.append(node('span', '', tx(sources.length + ' of 3 references selected', '已选 ' + sources.length + ' / 3 份资料')),
377
+ button(tx('Done', '完成'), () => { pickerOpen = false; render(); }, 'ws2-button ws2-button-primary', 'picker-done'));
378
+ panel.append(footer); return panel;
379
+ }
380
+ function renderSources() {
381
+ const section = node('section', 'ws2-library');
382
+ const heading = node('div', 'ws2-library-heading');
383
+ const title = node('div', 'ws2-label', tx('Reference papers', '参考文稿'));
384
+ title.append(node('span', 'ws2-count', sources.length + ' / 3'));
385
+ heading.append(title);
386
+ const controls = node('div', 'ws2-library-actions');
387
+ const addProjects = button(tx('Projects', '项目'), () => {
388
+ pickerOpen = !pickerOpen; query = ''; filter = 'all'; render();
389
+ if (pickerOpen) { void loadProjects(); host.querySelector('[data-style-focus="project-search"]')?.focus(); }
390
+ }, 'ws2-button ws2-button-quiet', 'add-projects');
391
+ addProjects.prepend(icon('plus')); addProjects.disabled = working();
392
+ addProjects.setAttribute('aria-expanded', String(pickerOpen));
393
+ const input = node('input'); input.type = 'file'; input.accept = '.pdf,application/pdf'; input.multiple = true; input.hidden = true;
394
+ input.addEventListener('change', () => addFiles(input.files));
395
+ const addPdf = button(tx('Add PDF', '添加 PDF'), () => input.click(), 'ws2-button ws2-button-quiet', 'add-pdf');
396
+ addPdf.prepend(icon('plus')); addPdf.disabled = working() || sources.length >= MAX_SOURCES;
397
+ controls.append(addProjects, addPdf, input); heading.append(controls); section.append(heading);
398
+ if (pickerOpen) section.append(renderPicker());
399
+ const list = node('div', 'ws2-source-list');
400
+ if (!sources.length) {
401
+ const empty = node('div', 'ws2-empty');
402
+ const mark = node('div', 'ws2-empty-icon'); mark.append(icon('book'));
403
+ const copy = node('div');
404
+ copy.append(node('strong', '', tx('Start with writing worth learning from', '从值得参考的文稿开始')),
405
+ node('p', '', tx('Choose a project or add a PDF with the style to follow.', '选择项目或添加 PDF,作为写作风格的参考。')));
406
+ empty.append(mark, copy); list.append(empty);
407
+ }
408
+ sources.forEach((source, index) => {
409
+ const row = node('div', 'ws2-source');
410
+ const mark = node('div', 'ws2-source-icon'); mark.append(icon(source.kind === 'project' ? 'project' : 'pdf'));
411
+ const copy = node('div', 'ws2-source-copy'), title = node('strong', '', source.name); title.title = source.name;
412
+ const detail = source.kind === 'project'
413
+ ? (source.projectId === scope?.projectId ? tx('Current project', '当前项目') : tx('Overleaf project', 'Overleaf 项目'))
414
+ + ' · ' + tx('Latest saved text', '最新已保存正文')
415
+ : source.kind === 'saved' ? tx('PDF · Reference saved', 'PDF · 已保存参考')
416
+ : 'PDF · ' + (source.file.size < 1024 * 1024 ? Math.max(1, Math.round(source.file.size / 1024)) + ' KB' : (source.file.size / 1024 / 1024).toFixed(1) + ' MiB');
417
+ copy.append(title, node('small', '', detail));
418
+ const remove = iconButton('close', tx('Remove ', '移除 ') + source.name, () => { sources.splice(index, 1); changed(); }, 'remove-' + index);
419
+ remove.disabled = working(); row.append(mark, copy, remove); list.append(row);
420
+ });
421
+ section.append(list);
422
+ section.append(node('p', 'ws2-caption', tx(
423
+ 'Choose up to 3 references. Text-based PDFs: 20 MiB each, 40 MiB total.',
424
+ '最多 3 份参考。PDF 需含文字层,单份 20 MiB、合计 40 MiB。')));
425
+ return section;
426
+ }
427
+ function renderGuide(text) {
428
+ const body = node('div', 'ws2-guide-body');
429
+ for (const block of String(text || '').split(/\n\s*\n/)) {
430
+ if (!block.trim()) continue;
431
+ if (/^#{1,3}\s/.test(block)) body.append(node('h4', '', block.replace(/^#{1,3}\s*/, '')));
432
+ else if (/^(?:[-*]\s|\d+\.\s)/.test(block)) {
433
+ const list = node('ul');
434
+ for (const line of block.split('\n')) list.append(node('li', '', line.replace(/^(?:[-*]\s|\d+\.\s)/, '')));
435
+ body.append(list);
436
+ } else body.append(node('p', '', block));
437
+ }
438
+ return body;
439
+ }
440
+ function renderPreview() {
441
+ const section = node('section', 'ws2-preview');
442
+ const nav = node('div', 'ws2-preview-tabs'); nav.setAttribute('role', 'group');
443
+ nav.setAttribute('aria-label', tx('Generated writing guidance', '生成的写作指导'));
444
+ const files = [{ file: 'references/writing-style.md', title: tx('Overview', '概览') }, ...(meta?.sections || [])];
445
+ for (const item of files) {
446
+ if (!preview.some(value => value.file === item.file)) continue;
447
+ const tab = button(item.title, () => { previewFile = item.file; render(); }, 'ws2-filter');
448
+ tab.setAttribute('aria-pressed', String(previewFile === item.file)); nav.append(tab);
449
+ }
450
+ section.append(nav, renderGuide(preview.find(item => item.file === previewFile)?.content || ''));
451
+ const skill = preview.find(item => item.file === 'SKILL.md');
452
+ if (skill) {
453
+ const details = node('details', 'ws2-skill-source');
454
+ details.append(node('summary', '', tx('Skill instructions', '技能指令')), node('pre', '', skill.content));
455
+ section.append(details);
456
+ }
457
+ return section;
458
+ }
459
+ function render() {
460
+ if (!host?.isConnected) return;
461
+ alignScope();
462
+ const focus = preserveFocus();
463
+ const oldScroll = host.querySelector('.ws2-projects')?.scrollTop || 0;
464
+ const heading = host.closest('[data-set-group]')?.querySelector('[data-writing-style-heading]');
465
+ if (heading) heading.textContent = tx('Writing style', '写作风格');
466
+ host.classList.add('ws2'); host.replaceChildren();
467
+ const top = node('div', 'ws2-heading');
468
+ const copy = node('div', 'ws2-heading-copy');
469
+ const title = node('label', 'ws2-title', tx('Write in My Style', '沿用写作风格'));
470
+ title.htmlFor = 'codex-writing-style-enabled';
471
+ title.append(node('span', 'ws2-beta', tx('Experimental', '实验性')));
472
+ copy.append(title, node('p', 'ws2-description', tx(
473
+ 'Learn from selected papers. Bring their voice and structure into the next draft.',
474
+ '学习参考文稿的表达与组织方式,融入后续写作。')));
475
+ const toggleInput = node('input', 'codex-switch');
476
+ toggleInput.type = 'checkbox'; toggleInput.id = 'codex-writing-style-enabled';
477
+ toggleInput.dataset.styleFocus = 'enable-style'; toggleInput.checked = desiredEnabled;
478
+ toggleInput.disabled = !scope || !meta || toggleBusy || (working() && !meta?.ready);
479
+ toggleInput.addEventListener('change', () => { void toggle(toggleInput.checked); });
480
+ top.append(copy, toggleInput); host.append(top);
481
+ if (meta?.ready) {
482
+ const summary = node('div', 'ws2-profile');
483
+ const mark = node('span', 'ws2-profile-icon'); mark.append(icon('check'));
484
+ const info = node('div', 'ws2-profile-copy');
485
+ const label = node('strong', '', meta.label || tx('Project writing style', '项目写作风格'));
486
+ label.title = meta.label || '';
487
+ info.append(label, node('small', '', [
488
+ tx(meta.sources.length + ' references', meta.sources.length + ' 份参考'),
489
+ meta.traits?.length
490
+ ? tx(meta.traits.length + ' style traits', meta.traits.length + ' 项风格特征')
491
+ : tx(meta.sections.length + ' example groups', meta.sections.length + ' 组章节样例'),
492
+ displayDate(meta.createdAt)
493
+ ].filter(Boolean).join(' · ')));
494
+ const view = button(preview ? tx('Hide preview', '收起预览') : tx('Preview style', '预览风格'),
495
+ () => { void loadPreview(); }, 'ws2-button ws2-button-quiet', 'preview-style');
496
+ view.disabled = previewBusy;
497
+ summary.append(mark, info, view); host.append(summary);
498
+ }
499
+ const editing = expanded || working();
500
+ const manage = button(editing ? tx('Hide references', '收起参考资料')
501
+ : meta?.ready ? tx('Manage references', '管理参考资料') : tx('Choose references', '选择参考资料'),
502
+ () => { expanded = !expanded; pickerOpen = false; render(); }, 'ws2-manage', 'manage-references');
503
+ manage.setAttribute('aria-expanded', String(editing));
504
+ manage.disabled = working(); manage.append(icon('arrow')); host.append(manage);
505
+ if (editing) {
506
+ host.append(renderSources());
507
+ if (working()) {
508
+ const progress = node('div', 'ws2-progress'); progress.setAttribute('role', 'status');
509
+ const phase = activeGeneration?.phase || 'learning';
510
+ const headline = cancelling ? tx('Cancelling generation…', '正在取消生成…')
511
+ : phase === 'reading' ? tx('Reading selected references', '正在读取参考资料')
512
+ : tx('Learning the writing style', '正在整理写作风格');
513
+ const line = node('div', 'ws2-progress-title'); line.append(node('span', 'ws2-pulse'), node('strong', '', headline));
514
+ const detail = activeGeneration?.sourceName || (phase === 'learning'
515
+ ? tx('Organizing guidance and section examples.', '整理风格指导与章节示例。')
516
+ : tx('Only selected sources are read.', '仅读取已选资料。'));
517
+ progress.append(line, node('p', '', detail));
518
+ const steps = node('div', 'ws2-steps');
519
+ for (const [index, label] of [tx('Read references', '读取资料'), tx('Learn style', '提炼风格'), tx('Ready', '可使用')].entries()) {
520
+ const step = node('span', '', label); step.dataset.active = String(index <= (phase === 'reading' ? 0 : 1)); steps.append(step);
521
+ }
522
+ progress.append(steps);
523
+ const stop = button(tx('Cancel', '取消'), () => { void cancel(); }, 'ws2-button', 'cancel-generation');
524
+ stop.disabled = cancelling; progress.append(stop); host.append(progress);
525
+ } else {
526
+ const footer = node('div', 'ws2-actions');
527
+ const help = node('div', 'ws2-action-copy');
528
+ help.append(node('span', '', dirty && meta?.ready ? tx('Reference changes are not applied yet', '参考资料变更待生成')
529
+ : tx('Generate once, reuse while writing', '生成一次,后续写作按需参考')));
530
+ help.append(node('small', '', tx('Selected text is sent to the current model.', '选定正文将发送给当前模型。')));
531
+ footer.append(help);
532
+ if (dirty && meta?.ready) footer.append(button(tx('Discard', '撤销选择'), () => {
533
+ restoreSources(meta); dirty = false; message = ''; messageTone = ''; pickerOpen = false; render();
534
+ }, 'ws2-button ws2-button-quiet', 'discard-references'));
535
+ const generateButton = button(meta?.ready ? tx('Update style', '更新风格') : tx('Generate style', '生成风格'),
536
+ () => { void generate(); }, 'ws2-button ws2-button-primary', 'generate-style');
537
+ generateButton.disabled = !scope || !meta || !sources.length || deps.isRunning();
538
+ footer.append(generateButton); host.append(footer);
539
+ if (deps.isRunning()) host.append(node('p', 'ws2-caption', tx('Available after the current task finishes.', '当前任务结束后可生成。')));
540
+ }
541
+ }
542
+ if (message || !meta) {
543
+ const status = node('div', 'ws2-status', message || tx('Loading writing-style settings…', '正在加载写作风格设置…'));
544
+ status.dataset.tone = messageTone;
545
+ status.setAttribute('role', messageTone === 'error' ? 'alert' : 'status');
546
+ if (!meta && message) status.append(button(tx('Retry', '重试'), () => {
547
+ message = ''; messageTone = ''; render(); void ensureLoaded(true).catch(() => {});
548
+ }, 'ws2-button', 'retry-settings'));
549
+ host.append(status);
550
+ }
551
+ if (preview) host.append(renderPreview());
552
+ const scroll = host.querySelector('.ws2-projects');
553
+ if (scroll) scroll.scrollTop = oldScroll;
554
+ restoreFocus(focus);
555
+ }
556
+ function mount(panel) {
557
+ const next = panel?.querySelector('[data-writing-style-settings]');
558
+ if (!next) return;
559
+ host = next;
560
+ if (!observer) {
561
+ observer = new MutationObserver(() => { render(); void ensureLoaded().catch(() => {}); });
562
+ observer.observe(panel, { attributes: true, attributeFilter: ['data-view', 'data-running', 'data-settings-scope'] });
563
+ chrome.storage.onChanged.addListener((changes, area) => {
564
+ const changed = changes[CHANGE_KEY]?.newValue;
565
+ if (area === 'local' && changed && changed.accountScopeId === scope?.accountScopeId && changed.projectId === scope?.projectId) {
566
+ void ensureLoaded(true).catch(() => {});
567
+ }
568
+ });
569
+ }
570
+ render(); void ensureLoaded().catch(() => {});
571
+ }
572
+ function sync() { alignScope(); render(); void ensureLoaded().catch(() => {}); }
573
+ return { mount, sync, ensureLoaded, capture };
574
+ }
575
+ return { create };
576
+ });