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.
- package/README.md +272 -311
- package/README.zh-CN.md +287 -326
- package/extension/bootstrap/background.js +47 -10
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +10 -16
- package/extension/bootstrap/update.js +25 -2
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +28 -3
- package/extension/src/backgroundSharedSessions.js +380 -0
- package/extension/src/backgroundUpdateCoordinator.js +442 -379
- package/extension/src/content/activeTurnControl.js +8 -0
- package/extension/src/content/assetTransferBroker.js +186 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +24 -22
- package/extension/src/content/composerPanel.js +23 -8
- package/extension/src/content/contentRuntime.js +572 -571
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diagnosticsExport.js +222 -0
- package/extension/src/content/diagnosticsPanel.js +48 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/generated/content.bundle.js +8974 -1781
- package/extension/src/content/generated/content.bundle.meta.json +22 -3
- package/extension/src/content/legacyGlobalRegistry.js +19 -1
- package/extension/src/content/localSkillsPanel.js +42 -7
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/pageBridgeClient.js +18 -3
- package/extension/src/content/panelMaintenance.js +51 -120
- package/extension/src/content/panelRenderer.js +4 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/providerSettingsCoordinator.js +17 -6
- package/extension/src/content/providerSettingsDialog.js +661 -108
- package/extension/src/content/recentProjects.js +364 -356
- package/extension/src/content/runActivitySummary.js +631 -0
- package/extension/src/content/runController.js +3 -1
- package/extension/src/content/runFailureNotice.js +14 -1
- package/extension/src/content/runGuidanceController.js +4 -3
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +2 -1
- package/extension/src/content/runResultActions.js +123 -3
- package/extension/src/content/runTimelineView.js +194 -164
- package/extension/src/content/scopedPersistenceCoordinator.js +2 -1
- package/extension/src/content/scopedPersistencePanelState.js +45 -2
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sessionPersistence.js +10 -2
- package/extension/src/content/settingsPanel.js +93 -54
- package/extension/src/content/settingsWorkbench.js +374 -0
- package/extension/src/content/sharedSessionViewSync.js +72 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateNotice.js +121 -50
- package/extension/src/content/writebackController.js +257 -0
- package/extension/src/content/writebackOrchestrator.js +146 -141
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +576 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +97 -19
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/referenceProjects.js +148 -0
- package/extension/src/page/saveState.js +58 -3
- package/extension/src/page/snapshotRouter.js +44 -13
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +350 -59
- package/extension/src/page/trackedChangeCapture.js +42 -56
- package/extension/src/page/trackedChangeOwnership.js +198 -0
- package/extension/src/page/trackedChangeReplay.js +164 -0
- package/extension/src/page/trackedChangesLifecycle.js +139 -150
- package/extension/src/page/treeOperations.js +36 -29
- package/extension/src/page/writebackReceiptJournal.js +112 -0
- package/extension/src/page/writebackRouter.js +176 -136
- package/extension/src/pageBridge.js +59 -101
- package/extension/src/popup.js +203 -59
- package/extension/src/shared/agentTranscript.js +18 -4
- package/extension/src/shared/auditRecords.js +71 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +42 -0
- package/extension/src/shared/failureReasons.js +20 -0
- package/extension/src/shared/globalPreferences.js +1 -1
- package/extension/src/shared/i18n.js +187 -65
- package/extension/src/shared/managedUpdateProjection.js +6 -3
- package/extension/src/shared/pageRpcContract.js +7 -1
- package/extension/src/shared/providerProfiles.js +109 -0
- package/extension/src/shared/runActivityModel.js +361 -0
- package/extension/src/shared/runExecutionSnapshot.js +8 -2
- package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +28 -0
- package/extension/src/shared/sessionState.js +97 -6
- package/extension/src/shared/sharedSessionBridge.js +288 -0
- package/extension/src/shared/storageDb.js +23 -55
- package/extension/src/shared/storageMigration.js +3 -0
- package/extension/src/shared/storageRunActions.js +54 -4
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +87 -0
- package/extension/src/shared/updateConsent.js +37 -8
- package/extension/src/shared/writebackIntent.js +62 -0
- package/extension/src/shared/writebackSettlement.js +29 -10
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +2136 -744
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +2 -1
- package/native-host/src/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +2 -0
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +146 -177
- package/native-host/src/codexSessionTiming.js +71 -0
- package/native-host/src/managedInstall.js +5 -4
- package/native-host/src/nativeEnvironment.js +70 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +13 -1
- package/native-host/src/providerReasoning.js +9 -5
- package/native-host/src/providerStore.js +4 -0
- package/native-host/src/readProgressGuard.js +84 -17
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/selectionScope.js +58 -0
- package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
- package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
- package/native-host/src/subagentBroker.js +102 -98
- package/native-host/src/subagentTelemetry.js +316 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +31 -0
- package/native-host/src/taskRunnerRuntime.js +55 -5
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +122 -12
- package/native-host/src/updateNetworkControl.js +75 -0
- package/native-host/src/updateTrust.js +21 -2
- package/native-host/src/vendor/pdfjs/LICENSE +177 -0
- package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
- package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/native-host/src/writingStyleEvidence.js +102 -0
- package/native-host/src/writingStylePdfWorker.js +35 -0
- package/native-host/src/writingStyleProfile.js +165 -0
- package/native-host/src/writingStyleRuntime.js +447 -0
- package/package.json +1 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
(function initCodexOverleafWrittenChangesView(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('WrittenChangesView', [], factory);
|
|
6
|
+
}
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function writtenChangesViewFactory() {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
// Read-only receipt for changes already written to Overleaf: one folded row
|
|
11
|
+
// per file, and inside it only the changed lines with word-level marks.
|
|
12
|
+
// Reviewing-mode hunks with Accept/Reject stay in DiffReviewPanel.
|
|
13
|
+
const CONTEXT_LINES = 1;
|
|
14
|
+
const MAX_WORD_DIFF_TOKENS = 600;
|
|
15
|
+
// CJK characters diff one by one; Latin words, spaces and punctuation as runs.
|
|
16
|
+
const TOKEN = /[\u3000-\u9fff\uac00-\ud7af\uff00-\uffef]|[A-Za-z0-9_]+|\s+|\\[A-Za-z]+|[^\sA-Za-z0-9_\u3000-\u9fff\uac00-\ud7af\uff00-\uffef]/g;
|
|
17
|
+
|
|
18
|
+
function tokenize(text) {
|
|
19
|
+
return String(text || '').match(TOKEN) || [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Classic LCS over tokens; returns [{ type: 'same'|'del'|'ins', text }].
|
|
23
|
+
function diffWords(before, after) {
|
|
24
|
+
const a = tokenize(before), b = tokenize(after);
|
|
25
|
+
if (a.length > MAX_WORD_DIFF_TOKENS || b.length > MAX_WORD_DIFF_TOKENS) {
|
|
26
|
+
return [{ type: 'del', text: String(before) }, { type: 'ins', text: String(after) }];
|
|
27
|
+
}
|
|
28
|
+
const table = Array.from({ length: a.length + 1 }, () => new Uint16Array(b.length + 1));
|
|
29
|
+
for (let i = a.length - 1; i >= 0; i -= 1) {
|
|
30
|
+
for (let j = b.length - 1; j >= 0; j -= 1) {
|
|
31
|
+
table[i][j] = a[i] === b[j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const parts = [];
|
|
35
|
+
const push = (type, text) => {
|
|
36
|
+
const last = parts[parts.length - 1];
|
|
37
|
+
if (last?.type === type) last.text += text; else parts.push({ type, text });
|
|
38
|
+
};
|
|
39
|
+
let i = 0, j = 0;
|
|
40
|
+
while (i < a.length && j < b.length) {
|
|
41
|
+
if (a[i] === b[j]) { push('same', a[i]); i += 1; j += 1; }
|
|
42
|
+
else if (table[i + 1][j] >= table[i][j + 1]) { push('del', a[i]); i += 1; }
|
|
43
|
+
else { push('ins', b[j]); j += 1; }
|
|
44
|
+
}
|
|
45
|
+
while (i < a.length) push('del', a[i++]);
|
|
46
|
+
while (j < b.length) push('ins', b[j++]);
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Turns diff hunk lines into display rows: paired remove/add runs become one
|
|
51
|
+
// merged row; long unchanged stretches fold into a gap row.
|
|
52
|
+
function buildRows(lines) {
|
|
53
|
+
const rows = [];
|
|
54
|
+
for (let index = 0; index < lines.length;) {
|
|
55
|
+
const line = lines[index];
|
|
56
|
+
if (line?.type !== 'remove' && line?.type !== 'add') {
|
|
57
|
+
rows.push({ kind: 'context', text: String(line?.text ?? '') });
|
|
58
|
+
index += 1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const removed = [], added = [];
|
|
62
|
+
while (lines[index]?.type === 'remove') removed.push(String(lines[index++].text ?? ''));
|
|
63
|
+
while (lines[index]?.type === 'add') added.push(String(lines[index++].text ?? ''));
|
|
64
|
+
const paired = Math.min(removed.length, added.length);
|
|
65
|
+
for (let k = 0; k < paired; k += 1) rows.push({ kind: 'change', parts: diffWords(removed[k], added[k]) });
|
|
66
|
+
for (const text of removed.slice(paired)) rows.push({ kind: 'change', parts: [{ type: 'del', text }] });
|
|
67
|
+
for (const text of added.slice(paired)) rows.push({ kind: 'change', parts: [{ type: 'ins', text }] });
|
|
68
|
+
}
|
|
69
|
+
const near = rows.map((row, index) => row.kind === 'change'
|
|
70
|
+
|| rows.slice(Math.max(0, index - CONTEXT_LINES), index + CONTEXT_LINES + 1).some(other => other.kind === 'change'));
|
|
71
|
+
const out = [];
|
|
72
|
+
for (let index = 0; index < rows.length;) {
|
|
73
|
+
if (near[index]) { out.push(rows[index]); index += 1; continue; }
|
|
74
|
+
let end = index;
|
|
75
|
+
while (end < rows.length && !near[end]) end += 1;
|
|
76
|
+
out.push({ kind: 'gap', count: end - index });
|
|
77
|
+
index = end;
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function countLines(diff) {
|
|
83
|
+
let added = 0, removed = 0;
|
|
84
|
+
for (const hunk of diff || []) {
|
|
85
|
+
for (const line of hunk?.lines || []) {
|
|
86
|
+
if (line?.type === 'add') added += 1;
|
|
87
|
+
else if (line?.type === 'remove') removed += 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { added, removed };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function render(changes, { document: doc = globalThis.document, tx = en => en } = {}) {
|
|
94
|
+
const make = (tag, className, text) => {
|
|
95
|
+
const el = doc.createElement(tag);
|
|
96
|
+
if (className) el.className = className;
|
|
97
|
+
if (text !== undefined) el.textContent = text;
|
|
98
|
+
return el;
|
|
99
|
+
};
|
|
100
|
+
const list = make('div', 'run-written-changes');
|
|
101
|
+
list.setAttribute('role', 'list');
|
|
102
|
+
for (const change of changes || []) {
|
|
103
|
+
if (!change?.path || !Array.isArray(change.diff) || !change.diff.length) continue;
|
|
104
|
+
const file = make('details', 'run-written-file');
|
|
105
|
+
file.setAttribute('role', 'listitem');
|
|
106
|
+
const summary = make('summary', 'run-written-file-sum');
|
|
107
|
+
const name = make('span', 'run-written-file-name', change.path.split('/').pop());
|
|
108
|
+
name.title = change.path;
|
|
109
|
+
const { added, removed } = countLines(change.diff);
|
|
110
|
+
summary.append(name);
|
|
111
|
+
if (change.path.includes('/')) summary.append(make('span', 'run-written-file-dir', change.path.slice(0, change.path.lastIndexOf('/'))));
|
|
112
|
+
const counts = make('span', 'run-written-file-counts');
|
|
113
|
+
counts.append(make('span', 'run-written-add', '+' + added), make('span', 'run-written-del', '−' + removed));
|
|
114
|
+
counts.setAttribute('aria-label', tx(`${added} lines added, ${removed} removed`, `新增 ${added} 行,删除 ${removed} 行`));
|
|
115
|
+
summary.append(counts);
|
|
116
|
+
file.append(summary);
|
|
117
|
+
// Lazy body: thesis chapters produce long diffs, so build on first open.
|
|
118
|
+
file.addEventListener('toggle', () => {
|
|
119
|
+
if (!file.open || file.dataset.built) return;
|
|
120
|
+
file.dataset.built = 'true';
|
|
121
|
+
const body = make('div', 'run-written-body');
|
|
122
|
+
for (const hunk of change.diff) {
|
|
123
|
+
if (hunk?.truncated) {
|
|
124
|
+
body.append(make('div', 'run-written-row run-written-note', String(hunk.lines?.[0]?.text || '')));
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const head = make('div', 'run-written-hunk', tx(`Line ${hunk?.startB ?? '?'}`, `第 ${hunk?.startB ?? '?'} 行`));
|
|
128
|
+
body.append(head);
|
|
129
|
+
for (const row of buildRows(hunk?.lines || [])) {
|
|
130
|
+
if (row.kind === 'gap') {
|
|
131
|
+
body.append(make('div', 'run-written-row run-written-gap', tx(`… ${row.count} unchanged lines`, `… ${row.count} 行未改动`)));
|
|
132
|
+
} else if (row.kind === 'context') {
|
|
133
|
+
body.append(make('div', 'run-written-row', row.text || ' '));
|
|
134
|
+
} else {
|
|
135
|
+
const line = make('div', 'run-written-row is-changed');
|
|
136
|
+
for (const part of row.parts) {
|
|
137
|
+
line.append(part.type === 'same' ? doc.createTextNode(part.text)
|
|
138
|
+
: make(part.type === 'ins' ? 'ins' : 'del', '', part.text));
|
|
139
|
+
}
|
|
140
|
+
body.append(line);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
file.append(body);
|
|
145
|
+
});
|
|
146
|
+
list.append(file);
|
|
147
|
+
}
|
|
148
|
+
return list;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { render, diffWords, buildRows, countLines };
|
|
152
|
+
});
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
const treeOperations = deps.treeOperations;
|
|
14
14
|
const snapshotRouter = deps.snapshotRouter;
|
|
15
15
|
const transfers = new Map();
|
|
16
|
+
// Keep bounded receipts after payload release; status reads never re-upload.
|
|
17
|
+
const receipts = new Map();
|
|
18
|
+
function pruneReceipts() {
|
|
19
|
+
for (const [id, transfer] of receipts) {
|
|
20
|
+
if (transfer.finishedAt && Date.now() - transfer.finishedAt > 600000) receipts.delete(id);
|
|
21
|
+
}
|
|
22
|
+
while (receipts.size >= 32) {
|
|
23
|
+
const entry = Array.from(receipts).find(([, value]) => value.finishedAt);
|
|
24
|
+
if (!entry) break;
|
|
25
|
+
receipts.delete(entry[0]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
16
28
|
|
|
17
29
|
function begin(params = {}) {
|
|
18
30
|
let transferId;
|
|
@@ -30,6 +42,13 @@
|
|
|
30
42
|
if (!/^[a-f0-9]{64}$/i.test(String(params.sha256 || ''))) {
|
|
31
43
|
return failure('binary_asset_hash_invalid', 'Asset SHA-256 is missing or invalid.');
|
|
32
44
|
}
|
|
45
|
+
pruneReceipts();
|
|
46
|
+
if (transfers.has(transferId) || receipts.has(transferId)) {
|
|
47
|
+
return failure('binary_upload_transfer_conflict', 'Asset transfer ID is already in use.');
|
|
48
|
+
}
|
|
49
|
+
if (transfers.size >= 32 || receipts.size >= 32) {
|
|
50
|
+
return failure('binary_upload_busy', 'Too many asset transfers are pending.');
|
|
51
|
+
}
|
|
33
52
|
transfers.set(transferId, {
|
|
34
53
|
id: transferId,
|
|
35
54
|
path: projectPath,
|
|
@@ -62,7 +81,50 @@
|
|
|
62
81
|
return { ok: true, transferId: transfer.id, received: transfer.received };
|
|
63
82
|
}
|
|
64
83
|
|
|
65
|
-
|
|
84
|
+
function commit(params = {}) {
|
|
85
|
+
const id = String(params.transferId || '');
|
|
86
|
+
const existing = receipts.get(id);
|
|
87
|
+
if (existing) {
|
|
88
|
+
if (existing.projectId !== treeOperations.getProjectId()) {
|
|
89
|
+
return Promise.resolve(failure('binary_upload_scope_changed', 'Asset project changed.'));
|
|
90
|
+
}
|
|
91
|
+
return existing.promise;
|
|
92
|
+
}
|
|
93
|
+
const transfer = transfers.get(id);
|
|
94
|
+
if (!transfer) return Promise.resolve(failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.'));
|
|
95
|
+
receipts.set(id, transfer);
|
|
96
|
+
transfer.promise = commitOnce(params).catch(error => ({
|
|
97
|
+
...failure(error.code || 'binary_upload_failed', error.message),
|
|
98
|
+
changedDocument: transfer.mutationAttempted === true
|
|
99
|
+
})).then(result => {
|
|
100
|
+
transfer.result = result;
|
|
101
|
+
transfer.finishedAt = Date.now();
|
|
102
|
+
transfer.chunks = [];
|
|
103
|
+
return result;
|
|
104
|
+
});
|
|
105
|
+
return transfer.promise;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function status(params = {}) {
|
|
109
|
+
pruneReceipts();
|
|
110
|
+
const transfer = receipts.get(String(params.transferId || ''));
|
|
111
|
+
if (!transfer || !params.runProjectId || transfer.projectId !== params.runProjectId
|
|
112
|
+
|| treeOperations.getProjectId() !== transfer.projectId) {
|
|
113
|
+
return failure('binary_upload_receipt_missing', 'Asset upload receipt is unavailable for this project.');
|
|
114
|
+
}
|
|
115
|
+
if (transfer.result?.ok !== true && transfer.mutationAttempted === true) {
|
|
116
|
+
const evidence = await waitForPath(transfer, {}, 2500).catch(() => null);
|
|
117
|
+
if (evidence?.hashVerified === true) {
|
|
118
|
+
transfer.result = { ok: true, written: true, path: transfer.path,
|
|
119
|
+
changedDocument: true, method: 'remote-hash-reconciliation' };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { ok: true, transferId: transfer.id, runProjectId: transfer.projectId,
|
|
123
|
+
state: transfer.finishedAt || transfer.result?.ok === true ? 'completed' : 'running',
|
|
124
|
+
result: transfer.result || null, changedDocument: transfer.mutationAttempted === true };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function commitOnce(params = {}) {
|
|
66
128
|
const transferId = String(params.transferId || '');
|
|
67
129
|
const transfer = transfers.get(transferId);
|
|
68
130
|
if (!transfer) return failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.');
|
|
@@ -92,7 +154,7 @@
|
|
|
92
154
|
const observed = evidence?.observed;
|
|
93
155
|
const hashVerified = evidence?.hashVerified === true;
|
|
94
156
|
assertTransferProject(transfer);
|
|
95
|
-
const overwriteVerified =
|
|
157
|
+
const overwriteVerified = hashVerified || result?.confirmedByTransport === true;
|
|
96
158
|
if (result?.ok && observed && overwriteVerified) {
|
|
97
159
|
return { ok: true, written: true, path: transfer.path, method: result.method, changedDocument: true };
|
|
98
160
|
}
|
|
@@ -101,6 +163,16 @@
|
|
|
101
163
|
} catch (error) {
|
|
102
164
|
errors.push(error.message);
|
|
103
165
|
}
|
|
166
|
+
// Once submission may have happened, changing transports can duplicate
|
|
167
|
+
// or overwrite a successful upload. Reconcile the same attempt instead.
|
|
168
|
+
if (transfer.mutationAttempted === true) {
|
|
169
|
+
const evidence = await waitForPath(transfer, {}, 12000).catch(() => null);
|
|
170
|
+
if (evidence?.hashVerified === true) {
|
|
171
|
+
return { ok: true, written: true, path: transfer.path,
|
|
172
|
+
method: 'remote-hash-reconciliation', changedDocument: true };
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
104
176
|
}
|
|
105
177
|
return { ...failure('binary_upload_unavailable', errors.filter(Boolean).join('; ') || 'No supported Overleaf asset upload path succeeded.'),
|
|
106
178
|
changedDocument: transfer.mutationAttempted === true };
|
|
@@ -110,11 +182,13 @@
|
|
|
110
182
|
}
|
|
111
183
|
|
|
112
184
|
function abort(params = {}) {
|
|
113
|
-
|
|
185
|
+
const id = String(params.transferId || '');
|
|
186
|
+
if (receipts.has(id)) return { ok: true, aborted: false, committed: true };
|
|
187
|
+
return { ok: true, aborted: transfers.delete(id) };
|
|
114
188
|
}
|
|
115
189
|
|
|
116
190
|
function assertTransferProject(transfer) {
|
|
117
|
-
if (transfers.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
|
|
191
|
+
if (transfers.get(transfer.id) !== transfer && receipts.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
|
|
118
192
|
if (!transfer.projectId || treeOperations.getProjectId() !== transfer.projectId) {
|
|
119
193
|
throw new Error('Project changed during asset upload.');
|
|
120
194
|
}
|
|
@@ -134,6 +208,7 @@
|
|
|
134
208
|
const headers = csrf ? { 'X-CSRF-Token': csrf } : {};
|
|
135
209
|
const query = folderId ? `?folder_id=${encodeURIComponent(folderId)}` : '';
|
|
136
210
|
assertTransferProject(transfer);
|
|
211
|
+
transfer.mutationAttempted = true;
|
|
137
212
|
const response = await windowRef.fetch(`/project/${encodeURIComponent(projectId)}/upload${query}`, {
|
|
138
213
|
method: 'POST', body: form, credentials: 'same-origin', headers
|
|
139
214
|
});
|
|
@@ -156,16 +231,14 @@
|
|
|
156
231
|
|
|
157
232
|
async function uploadWithDom(file, transfer) {
|
|
158
233
|
const parentPath = transfer.path.split('/').slice(0, -1).join('/');
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
return { ok: false, reason: 'Verified folder selection is unavailable for nested asset upload.' };
|
|
162
|
-
}
|
|
163
|
-
await deps.prepareUploadParent(parentPath, {
|
|
164
|
-
createMissing: true,
|
|
165
|
-
onMutation: () => { transfer.mutationAttempted = true; },
|
|
166
|
-
isCurrent: () => treeOperations.getProjectId() === transfer.projectId && transfers.get(transfer.id) === transfer
|
|
167
|
-
});
|
|
234
|
+
if (typeof deps.prepareUploadParent !== 'function') {
|
|
235
|
+
return { ok: false, reason: 'Verified folder selection is unavailable for asset upload.' };
|
|
168
236
|
}
|
|
237
|
+
await deps.prepareUploadParent(parentPath, {
|
|
238
|
+
createMissing: true,
|
|
239
|
+
onMutation: () => { transfer.mutationAttempted = true; },
|
|
240
|
+
isCurrent: () => treeOperations.getProjectId() === transfer.projectId && transfers.get(transfer.id) === transfer
|
|
241
|
+
});
|
|
169
242
|
assertTransferProject(transfer);
|
|
170
243
|
let input = findFileInput();
|
|
171
244
|
if (!input) {
|
|
@@ -192,9 +265,10 @@
|
|
|
192
265
|
for (const name of ['uploadFile', 'uploadAsset', 'createBinaryFile', 'createFile', 'addFile']) {
|
|
193
266
|
if (typeof manager[name] !== 'function') continue;
|
|
194
267
|
try {
|
|
268
|
+
transfer.mutationAttempted = true;
|
|
195
269
|
await manager[name](transfer.path, file);
|
|
196
270
|
return { ok: true, method: `fileTreeManager.${name}`, confirmedByTransport: true };
|
|
197
|
-
} catch (
|
|
271
|
+
} catch (error) { return { ok: false, reason: error.message }; }
|
|
198
272
|
}
|
|
199
273
|
return { ok: false, reason: 'Legacy file-tree manager has no compatible asset method.' };
|
|
200
274
|
}
|
|
@@ -293,9 +367,13 @@
|
|
|
293
367
|
let lastObserved = null;
|
|
294
368
|
while (Date.now() < deadline) {
|
|
295
369
|
assertTransferProject(transfer);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
370
|
+
// A matching digest identifies bytes, not their project path. In the
|
|
371
|
+
// native tree, require the exact ancestry; the generic tree adapter
|
|
372
|
+
// can resolve a root name to an identically named nested file.
|
|
373
|
+
const nativeRoot = documentRef.querySelector?.('[data-testid="file-tree-list-root"]');
|
|
374
|
+
const node = nativeRoot ? findNativeTreeNode(projectPath, 'file')
|
|
375
|
+
: treeOperations.findFileTreeNode(projectPath, { invalidateCache: true });
|
|
376
|
+
let observed = node || (!nativeRoot && treeOperations.projectPathExists(projectPath))
|
|
299
377
|
? { path: projectPath, id: readTreeEntityId(node) } : null;
|
|
300
378
|
if (!observed?.id) {
|
|
301
379
|
try {
|
|
@@ -313,7 +391,7 @@
|
|
|
313
391
|
assertTransferProject(transfer);
|
|
314
392
|
// An existing path is not replacement evidence. Re-read identity and
|
|
315
393
|
// bytes while the native uploader and file-tree events settle.
|
|
316
|
-
if (
|
|
394
|
+
if (hashVerified || result.confirmedByTransport === true) {
|
|
317
395
|
return { observed, hashVerified };
|
|
318
396
|
}
|
|
319
397
|
}
|
|
@@ -333,7 +411,7 @@
|
|
|
333
411
|
return digest === transfer.sha256;
|
|
334
412
|
}
|
|
335
413
|
function delay(ms) { return new Promise(resolve => windowRef.setTimeout(resolve, ms)); }
|
|
336
|
-
return { abort, append, begin, commit };
|
|
414
|
+
return { abort, append, begin, commit, status };
|
|
337
415
|
}
|
|
338
416
|
|
|
339
417
|
function normalizePath(value) {
|
|
@@ -120,6 +120,7 @@
|
|
|
120
120
|
deps.getProjectId?.() || pageWindow.location.pathname || pageWindow.location.href,
|
|
121
121
|
params.preferLightweight ? 'lightweight' : 'full',
|
|
122
122
|
params.zipOnly ? 'zip-only' : 'zip-or-page',
|
|
123
|
+
params.serverOnly === true ? 'server-only' : 'editor-overlay',
|
|
123
124
|
params.allowZipFallback === false ? 'no-zip-fallback' : 'zip-fallback',
|
|
124
125
|
params.allowEditorNavigation === false ? 'no-editor-navigation' : params.allowEditorNavigation === true ? 'editor-navigation' : 'default-editor-navigation',
|
|
125
126
|
params.requireFullProject ? 'require-full-project' : 'allow-partial-project',
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafReferenceProjects = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
const MAX_CATALOG_BYTES = 8 * 1024 * 1024;
|
|
8
|
+
const MAX_PROJECT_BYTES = 4 * 1024 * 1024;
|
|
9
|
+
const validId = value => /^[a-f0-9]{24}$/i.test(value);
|
|
10
|
+
|
|
11
|
+
function failure(code, message) { return Object.assign(new Error(message), { code }); }
|
|
12
|
+
|
|
13
|
+
function parseCatalog(doc) {
|
|
14
|
+
const element = doc.querySelector('meta[name="ol-prefetchedProjectsBlob"]')
|
|
15
|
+
|| doc.querySelector('meta[name="ol-projects"]');
|
|
16
|
+
if (!element) throw failure('reference_catalog_unavailable',
|
|
17
|
+
'Overleaf did not return its project directory. Check that this account is signed in, then retry.');
|
|
18
|
+
let data;
|
|
19
|
+
try { data = JSON.parse(element.getAttribute('content') || ''); }
|
|
20
|
+
catch (_) { throw failure('reference_catalog_invalid', 'Overleaf returned an unreadable project directory. Retry loading projects.'); }
|
|
21
|
+
const rows = Array.isArray(data) ? data : data?.projects;
|
|
22
|
+
if (!Array.isArray(rows)) throw failure('reference_catalog_invalid', 'Overleaf returned an invalid project directory.');
|
|
23
|
+
const total = Array.isArray(data) ? rows.length : Number(data.totalSize);
|
|
24
|
+
if (Number.isFinite(total) && total > rows.length) {
|
|
25
|
+
throw failure('reference_catalog_incomplete', 'Overleaf returned only part of the project directory. Refresh Overleaf and retry loading projects.');
|
|
26
|
+
}
|
|
27
|
+
const result = new Map();
|
|
28
|
+
for (const row of rows) {
|
|
29
|
+
if (row?.trashed === true) continue;
|
|
30
|
+
const projectId = String(row?.id || row?._id || '');
|
|
31
|
+
if (!validId(projectId) || typeof row?.name !== 'string') {
|
|
32
|
+
throw failure('reference_catalog_invalid', 'A project entry could not be read. Retry loading the directory.');
|
|
33
|
+
}
|
|
34
|
+
result.set(projectId, {
|
|
35
|
+
projectId, name: row.name.slice(0, 300),
|
|
36
|
+
archived: row.archived === true,
|
|
37
|
+
shared: row.accessLevel !== 'owner',
|
|
38
|
+
readOnly: ['readOnly', 'read-only', 'read'].includes(row.accessLevel),
|
|
39
|
+
lastUpdated: typeof row.lastUpdated === 'string' ? row.lastUpdated : ''
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return [...result.values()].sort((a, b) =>
|
|
43
|
+
(Date.parse(b.lastUpdated) || 0) - (Date.parse(a.lastUpdated) || 0) || a.name.localeCompare(b.name));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function create(deps = {}) {
|
|
47
|
+
const pageWindow = deps.window || window;
|
|
48
|
+
const fetchImpl = deps.fetch || pageWindow.fetch.bind(pageWindow);
|
|
49
|
+
const reads = new Map();
|
|
50
|
+
|
|
51
|
+
async function catalogText(response) {
|
|
52
|
+
if (Number(response.headers.get('content-length')) > MAX_CATALOG_BYTES) {
|
|
53
|
+
throw failure('reference_catalog_too_large', 'The Overleaf project directory is too large to load.');
|
|
54
|
+
}
|
|
55
|
+
if (!response.body?.getReader) {
|
|
56
|
+
const text = await response.text();
|
|
57
|
+
if (new TextEncoder().encode(text).byteLength > MAX_CATALOG_BYTES) {
|
|
58
|
+
throw failure('reference_catalog_too_large', 'The Overleaf project directory is too large to load.');
|
|
59
|
+
}
|
|
60
|
+
return text;
|
|
61
|
+
}
|
|
62
|
+
const reader = response.body.getReader(), decoder = new TextDecoder();
|
|
63
|
+
let bytes = 0, text = '';
|
|
64
|
+
try {
|
|
65
|
+
while (true) {
|
|
66
|
+
const chunk = await reader.read();
|
|
67
|
+
if (chunk.done) break;
|
|
68
|
+
bytes += chunk.value.byteLength;
|
|
69
|
+
if (bytes > MAX_CATALOG_BYTES) {
|
|
70
|
+
await reader.cancel();
|
|
71
|
+
throw failure('reference_catalog_too_large', 'The Overleaf project directory is too large to load.');
|
|
72
|
+
}
|
|
73
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
74
|
+
}
|
|
75
|
+
return text + decoder.decode();
|
|
76
|
+
} finally { reader.releaseLock(); }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function listProjects() {
|
|
80
|
+
const controller = new AbortController();
|
|
81
|
+
const timer = setTimeout(() => controller.abort(), 20000);
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetchImpl(new URL('/project', pageWindow.location.origin).href, {
|
|
84
|
+
credentials: 'include', cache: 'no-store', headers: { accept: 'text/html' },
|
|
85
|
+
signal: controller.signal
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok) throw failure('reference_catalog_http', 'Overleaf could not load projects (HTTP ' + response.status + '). Retry loading projects.');
|
|
88
|
+
if (response.url && new URL(response.url).origin !== pageWindow.location.origin) {
|
|
89
|
+
throw failure('reference_catalog_login', 'Sign in to Overleaf on this domain, then retry loading projects.');
|
|
90
|
+
}
|
|
91
|
+
const text = await catalogText(response);
|
|
92
|
+
const parser = new pageWindow.DOMParser();
|
|
93
|
+
const projects = parseCatalog(parser.parseFromString(text, 'text/html'));
|
|
94
|
+
return { ok: true, projects, total: projects.length, capturedAt: new Date().toISOString() };
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return { ok: false, code: error.code || 'reference_catalog_failed',
|
|
97
|
+
error: controller.signal.aborted ? 'Loading Overleaf projects timed out. Retry loading projects.' : error.message };
|
|
98
|
+
} finally { clearTimeout(timer); }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function readProject(params = {}) {
|
|
102
|
+
const projectId = String(params.projectId || '');
|
|
103
|
+
const requestId = String(params.requestId || '');
|
|
104
|
+
if (!validId(projectId) || !requestId || requestId.length > 160) {
|
|
105
|
+
return { ok: false, code: 'reference_project_invalid', error: 'Reference project identity is invalid.' };
|
|
106
|
+
}
|
|
107
|
+
if (reads.has(requestId)) return { ok: false, code: 'reference_read_busy', error: 'This reference read is already running.' };
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
reads.set(requestId, controller);
|
|
110
|
+
try {
|
|
111
|
+
const snapshot = await deps.snapshotRouter.fetchProjectZipSnapshot({
|
|
112
|
+
includeBinaryFiles: false, includeContent: true, zipTimeoutMs: 30000
|
|
113
|
+
}, { projectId, signal: controller.signal });
|
|
114
|
+
if (controller.signal.aborted) throw failure('reference_read_cancelled', 'Reference reading was cancelled.');
|
|
115
|
+
if (!snapshot?.ok) throw failure('reference_project_unavailable', snapshot?.reason || 'The reference project could not be read.');
|
|
116
|
+
const files = (snapshot.files || []).filter(file => /\.tex$/i.test(file.path || ''));
|
|
117
|
+
if (!files.length) throw failure('reference_project_empty', 'This project contains no readable TeX files.');
|
|
118
|
+
if (files.length > 1000 || files.some(file => typeof file.content !== 'string')) {
|
|
119
|
+
throw failure('reference_project_incomplete', 'The reference project could not be read completely.');
|
|
120
|
+
}
|
|
121
|
+
const skipped = snapshot.skipped || [];
|
|
122
|
+
if (skipped.some(file => /\.tex$/i.test(typeof file === 'string' ? file : file?.path || ''))) {
|
|
123
|
+
throw failure('reference_project_incomplete', 'Some TeX files in the reference project could not be read.');
|
|
124
|
+
}
|
|
125
|
+
const encoder = new TextEncoder();
|
|
126
|
+
const size = files.reduce((sum, file) => sum + encoder.encode(file.content).byteLength, 0);
|
|
127
|
+
if (size > MAX_PROJECT_BYTES) throw failure('reference_project_too_large', 'Reference project text exceeds 4 MiB. Add a representative PDF instead.');
|
|
128
|
+
return {
|
|
129
|
+
ok: true, projectId, capturedAt: new Date().toISOString(),
|
|
130
|
+
files: files.map(file => ({ path: file.path, content: file.content, kind: 'text' })),
|
|
131
|
+
capabilities: { referenceTextComplete: true, method: 'overleaf-reference-zip' }
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return { ok: false, code: error.code || 'reference_project_failed', error: error.message };
|
|
135
|
+
} finally { reads.delete(requestId); }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function cancelRead(params = {}) {
|
|
139
|
+
const requestId = String(params.requestId || '');
|
|
140
|
+
const controller = reads.get(requestId);
|
|
141
|
+
if (controller) controller.abort();
|
|
142
|
+
return { ok: true, cancelled: Boolean(controller) };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { listProjects, readProject, cancelRead };
|
|
146
|
+
}
|
|
147
|
+
return { create, parseCatalog };
|
|
148
|
+
});
|
|
@@ -32,12 +32,13 @@
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
if (current.state === 'verified_saved') {
|
|
35
|
-
return { ok: true, state: 'verified_saved' };
|
|
35
|
+
return { ok: true, state: 'verified_saved', signal: 'saved' };
|
|
36
36
|
}
|
|
37
37
|
if (current.state === 'unavailable') {
|
|
38
38
|
return {
|
|
39
39
|
ok: false,
|
|
40
40
|
state: 'unavailable',
|
|
41
|
+
signal: current.signal || 'unavailable',
|
|
41
42
|
reason: current.reason || 'Overleaf save state is unavailable.'
|
|
42
43
|
};
|
|
43
44
|
}
|
|
@@ -70,11 +71,16 @@
|
|
|
70
71
|
return {
|
|
71
72
|
ok: false,
|
|
72
73
|
state: 'unknown_timeout',
|
|
74
|
+
signal: lastState.signal || 'unavailable',
|
|
75
|
+
observedState: lastState.state,
|
|
73
76
|
reason: buildSaveStateTimeoutReason(lastState)
|
|
74
77
|
};
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
function getOverleafSaveState() {
|
|
81
|
+
if (pageWindow.navigator?.onLine === false) {
|
|
82
|
+
return { state: 'unknown', signal: 'offline', reason: 'Overleaf is offline; saving cannot be confirmed.' };
|
|
83
|
+
}
|
|
78
84
|
if (!document || typeof document.querySelectorAll !== 'function') {
|
|
79
85
|
return {
|
|
80
86
|
state: 'unavailable',
|
|
@@ -86,7 +92,9 @@
|
|
|
86
92
|
'[class*="saving-status" i]',
|
|
87
93
|
'[class*="save-status" i]',
|
|
88
94
|
'[data-testid*="save" i]',
|
|
89
|
-
'[aria-label*="save" i]'
|
|
95
|
+
'[aria-label*="save" i]',
|
|
96
|
+
'[title*="saved" i]',
|
|
97
|
+
'[role="status"]'
|
|
90
98
|
];
|
|
91
99
|
let sawSaveCandidate = false;
|
|
92
100
|
let sawVerifiedSaved = false;
|
|
@@ -125,12 +133,14 @@
|
|
|
125
133
|
if (sawNegativeState) {
|
|
126
134
|
return {
|
|
127
135
|
state: 'unknown',
|
|
136
|
+
signal: 'unsaved',
|
|
128
137
|
reason: 'Overleaf save indicator reports changes are not saved.'
|
|
129
138
|
};
|
|
130
139
|
}
|
|
131
140
|
if (sawSavingState) {
|
|
132
141
|
return {
|
|
133
142
|
state: 'saving',
|
|
143
|
+
signal: 'saving',
|
|
134
144
|
reason: 'Overleaf is still saving or syncing changes.'
|
|
135
145
|
};
|
|
136
146
|
}
|
|
@@ -139,6 +149,7 @@
|
|
|
139
149
|
}
|
|
140
150
|
return {
|
|
141
151
|
state: 'unknown',
|
|
152
|
+
signal: sawSaveCandidate ? 'unrecognized' : 'missing',
|
|
142
153
|
reason: sawSaveCandidate
|
|
143
154
|
? 'Overleaf save indicator was present, but did not verify that all changes are saved.'
|
|
144
155
|
: 'Overleaf save indicator was not found.'
|
|
@@ -149,6 +160,7 @@
|
|
|
149
160
|
if (!node) {
|
|
150
161
|
return true;
|
|
151
162
|
}
|
|
163
|
+
if (node.closest?.('#codex-overleaf-panel')) return true;
|
|
152
164
|
if (node.hidden === true || node.inert === true) {
|
|
153
165
|
return true;
|
|
154
166
|
}
|
|
@@ -288,5 +300,48 @@
|
|
|
288
300
|
};
|
|
289
301
|
}
|
|
290
302
|
|
|
291
|
-
|
|
303
|
+
async function confirmReviewWriteback({ params, result, readSnapshot, getProjectId, delay,
|
|
304
|
+
now = Date.now, timeoutMs = 45000 }) {
|
|
305
|
+
if (result?.ok !== true || result.skipped?.length) return result;
|
|
306
|
+
const expected = new Map((params.expectedFiles || [])
|
|
307
|
+
.filter(file => file?.path && typeof file.content === 'string').map(file => [file.path, file.content]));
|
|
308
|
+
for (const entry of result.applied || []) {
|
|
309
|
+
const path = entry.trackedChange?.path || entry.operation?.path;
|
|
310
|
+
if (expected.has(path) && typeof entry.result?.verifiedContent === 'string') {
|
|
311
|
+
expected.set(path, entry.result.verifiedContent);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const deadline = now() + Math.max(0, timeoutMs);
|
|
315
|
+
let attempts = 0;
|
|
316
|
+
if (expected.size && typeof readSnapshot === 'function') {
|
|
317
|
+
do {
|
|
318
|
+
if (!params.runProjectId || getProjectId() !== params.runProjectId) break;
|
|
319
|
+
try {
|
|
320
|
+
const snapshot = await readSnapshot({ force: true, maxAgeMs: 0,
|
|
321
|
+
zipTimeoutMs: Math.max(1, Math.min(8000, deadline - now())) });
|
|
322
|
+
attempts++;
|
|
323
|
+
if (getProjectId() !== params.runProjectId) break;
|
|
324
|
+
if (snapshot?.ok === true && Array.from(expected).every(([path, content]) =>
|
|
325
|
+
snapshot.files?.some(file => file.path === path && file.source === 'overleaf-zip'
|
|
326
|
+
&& file.content === content))) {
|
|
327
|
+
return { ...result, saveVerification: { ok: true, state: 'verified_saved',
|
|
328
|
+
source: 'overleaf-zip', attempts } };
|
|
329
|
+
}
|
|
330
|
+
} catch (_error) { /* A transient download failure grants no save proof. */ }
|
|
331
|
+
if (now() >= deadline) break;
|
|
332
|
+
await delay(Math.min(300, deadline - now()));
|
|
333
|
+
} while (now() < deadline);
|
|
334
|
+
}
|
|
335
|
+
const reason = 'Undo changed the editor, but its saved server content could not be confirmed. Keep this page open and check Overleaf before retrying.';
|
|
336
|
+
return { ...result, ok: false, saveVerification: { ok: false, state: 'unknown_timeout', attempts },
|
|
337
|
+
skipped: [...(result.skipped || []), { trackedChange: { path: expected.keys().next().value || '' },
|
|
338
|
+
result: { ok: false, code: 'undo_not_verified', reason, failure: {
|
|
339
|
+
code: 'undo_not_verified', stage: 'verification', severity: 'needs_review',
|
|
340
|
+
terminalState: 'needs_review', changedDocument: true, retryable: true, userMessage: reason
|
|
341
|
+
} } }] };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const api = { create, confirmReviewWriteback };
|
|
345
|
+
root.CodexOverleafSaveState = api;
|
|
346
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
292
347
|
})(typeof window !== 'undefined' ? window : globalThis);
|