codex-overleaf-link 2.4.1 → 2.5.1
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 +275 -305
- package/README.zh-CN.md +290 -320
- package/extension/bootstrap/background.js +99 -19
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +11 -16
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/update.js +25 -2
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +234 -56
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +440 -0
- package/extension/src/backgroundUpdateCoordinator.js +541 -381
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +16 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +244 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +117 -67
- package/extension/src/content/composerPanel.js +41 -8
- package/extension/src/content/contentRuntime.js +1084 -1099
- package/extension/src/content/diagnosticsController.js +122 -97
- package/extension/src/content/diagnosticsExport.js +236 -0
- package/extension/src/content/diagnosticsPanel.js +78 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +14680 -3606
- package/extension/src/content/generated/content.bundle.meta.json +30 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +25 -1
- package/extension/src/content/localSkillsPanel.js +141 -16
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +8 -5
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +33 -5
- package/extension/src/content/panelMaintenance.js +87 -132
- package/extension/src/content/panelRenderer.js +10 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +63 -14
- package/extension/src/content/providerSettingsDialog.js +724 -113
- package/extension/src/content/recentProjects.js +21 -6
- package/extension/src/content/runActivitySummary.js +665 -0
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +40 -15
- package/extension/src/content/runFailureNotice.js +34 -12
- package/extension/src/content/runGuidanceController.js +76 -30
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +150 -43
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +11 -8
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +315 -290
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/sessionPersistence.js +6 -1
- package/extension/src/content/settingsPanel.js +84 -55
- package/extension/src/content/settingsWorkbench.js +379 -0
- package/extension/src/content/sharedSessionViewSync.js +109 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +179 -56
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +308 -2
- package/extension/src/content/writebackOrchestrator.js +213 -199
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +600 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +223 -23
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +149 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +20 -6
- package/extension/src/page/snapshotRouter.js +124 -48
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +442 -71
- package/extension/src/page/trackedChangeCapture.js +53 -57
- package/extension/src/page/trackedChangeOwnership.js +216 -0
- package/extension/src/page/trackedChangeReplay.js +184 -0
- package/extension/src/page/trackedChangesLifecycle.js +343 -339
- package/extension/src/page/treeOperations.js +33 -12
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +114 -0
- package/extension/src/page/writebackRouter.js +242 -201
- package/extension/src/pageBridge.js +73 -102
- package/extension/src/popup.js +261 -65
- package/extension/src/shared/agentTranscript.js +41 -4
- package/extension/src/shared/auditRecords.js +73 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +101 -37
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +329 -3
- package/extension/src/shared/globalPreferences.js +31 -14
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +389 -25
- package/extension/src/shared/managedUpdateProjection.js +8 -5
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +9 -2
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +114 -3
- 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/runInputQueue.js +48 -5
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +37 -0
- package/extension/src/shared/sessionState.js +166 -30
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +311 -0
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +77 -78
- package/extension/src/shared/storageMigration.js +74 -111
- package/extension/src/shared/storageRunActions.js +211 -22
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +216 -6
- package/extension/src/shared/updateConsent.js +42 -11
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +78 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +67 -48
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +1987 -385
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +6 -3
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +33 -12
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +211 -318
- package/native-host/src/codexSessionTiming.js +21 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +115 -39
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeEnvironment.js +70 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +115 -0
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +17 -3
- package/native-host/src/providerReasoning.js +10 -11
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +76 -14
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +86 -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/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +176 -159
- package/native-host/src/subagentTelemetry.js +325 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +34 -0
- package/native-host/src/taskRunnerRuntime.js +130 -57
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +406 -38
- 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 +475 -0
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -0
|
@@ -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,14 +42,26 @@
|
|
|
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
|
+
if (params.overwrite === true && !/^[a-f0-9]{64}$/i.test(String(params.previousSha256 || ''))) {
|
|
46
|
+
return failure('binary_asset_baseline_missing', 'The original binary version is unavailable. Read the project again before replacing this file.');
|
|
47
|
+
}
|
|
48
|
+
pruneReceipts();
|
|
49
|
+
if (transfers.has(transferId) || receipts.has(transferId)) {
|
|
50
|
+
return failure('binary_upload_transfer_conflict', 'Asset transfer ID is already in use.');
|
|
51
|
+
}
|
|
52
|
+
if (transfers.size >= 32 || receipts.size >= 32) {
|
|
53
|
+
return failure('binary_upload_busy', 'Too many asset transfers are pending.');
|
|
54
|
+
}
|
|
33
55
|
transfers.set(transferId, {
|
|
34
56
|
id: transferId,
|
|
35
57
|
path: projectPath,
|
|
36
58
|
size,
|
|
37
59
|
sha256: String(params.sha256).toLowerCase(),
|
|
60
|
+
previousSha256: params.overwrite === true ? String(params.previousSha256).toLowerCase() : '',
|
|
38
61
|
mimeType: String(params.mimeType || 'application/octet-stream'),
|
|
39
62
|
projectId: treeOperations.getProjectId(),
|
|
40
63
|
overwrite: params.overwrite === true,
|
|
64
|
+
createdDirectories: [], folderChanged: false, fileUploadAttempted: false,
|
|
41
65
|
chunks: [],
|
|
42
66
|
received: 0
|
|
43
67
|
});
|
|
@@ -62,7 +86,59 @@
|
|
|
62
86
|
return { ok: true, transferId: transfer.id, received: transfer.received };
|
|
63
87
|
}
|
|
64
88
|
|
|
65
|
-
|
|
89
|
+
function commit(params = {}) {
|
|
90
|
+
const id = String(params.transferId || '');
|
|
91
|
+
const existing = receipts.get(id);
|
|
92
|
+
if (existing) {
|
|
93
|
+
if (existing.projectId !== treeOperations.getProjectId()) {
|
|
94
|
+
return Promise.resolve(failure('binary_upload_scope_changed', 'Asset project changed.'));
|
|
95
|
+
}
|
|
96
|
+
return existing.promise;
|
|
97
|
+
}
|
|
98
|
+
const transfer = transfers.get(id);
|
|
99
|
+
if (!transfer) return Promise.resolve(failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.'));
|
|
100
|
+
receipts.set(id, transfer);
|
|
101
|
+
transfer.promise = commitOnce(params).catch(error => ({
|
|
102
|
+
...failure(error.code || 'binary_upload_failed', error.message),
|
|
103
|
+
changedDocument: transfer.mutationAttempted === true
|
|
104
|
+
})).then(async result => {
|
|
105
|
+
if (result.ok !== true && transfer.createdDirectories.length && typeof deps.cleanupCreatedDirectories === 'function') {
|
|
106
|
+
try {
|
|
107
|
+
const cleanup = await deps.cleanupCreatedDirectories(transfer.createdDirectories, {
|
|
108
|
+
projectId: transfer.projectId, isCurrent: () => treeOperations.getProjectId() === transfer.projectId && receipts.get(id) === transfer
|
|
109
|
+
});
|
|
110
|
+
transfer.createdDirectories = cleanup.remaining || transfer.createdDirectories;
|
|
111
|
+
} catch (_error) { /* Retain ownership proofs when cleanup cannot be confirmed. */ }
|
|
112
|
+
}
|
|
113
|
+
transfer.result = { ...result, ...directoryFacts(transfer, result.ok !== true) };
|
|
114
|
+
transfer.finishedAt = Date.now();
|
|
115
|
+
transfer.chunks = [];
|
|
116
|
+
return transfer.result;
|
|
117
|
+
});
|
|
118
|
+
return transfer.promise;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function status(params = {}) {
|
|
122
|
+
pruneReceipts();
|
|
123
|
+
const transfer = receipts.get(String(params.transferId || ''));
|
|
124
|
+
if (!transfer || !params.runProjectId || transfer.projectId !== params.runProjectId
|
|
125
|
+
|| treeOperations.getProjectId() !== transfer.projectId) {
|
|
126
|
+
return failure('binary_upload_receipt_missing', 'Asset upload receipt is unavailable for this project.');
|
|
127
|
+
}
|
|
128
|
+
if (transfer.result?.ok !== true && transfer.fileUploadAttempted === true) {
|
|
129
|
+
const evidence = await waitForPath(transfer, {}, 2500).catch(() => null);
|
|
130
|
+
if (evidence?.hashVerified === true) {
|
|
131
|
+
transfer.result = { ok: true, written: true, path: transfer.path,
|
|
132
|
+
changedDocument: true, method: 'remote-hash-reconciliation', ...directoryFacts(transfer),
|
|
133
|
+
entityId: evidence.observed?.id || readTreeEntityId(evidence.observed), sha256: transfer.sha256 };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { ok: true, transferId: transfer.id, runProjectId: transfer.projectId,
|
|
137
|
+
state: transfer.finishedAt || transfer.result?.ok === true ? 'completed' : 'running',
|
|
138
|
+
result: transfer.result || null, changedDocument: transfer.mutationAttempted === true };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function commitOnce(params = {}) {
|
|
66
142
|
const transferId = String(params.transferId || '');
|
|
67
143
|
const transfer = transfers.get(transferId);
|
|
68
144
|
if (!transfer) return failure('binary_upload_transfer_missing', 'Asset upload transfer was not found.');
|
|
@@ -92,15 +168,30 @@
|
|
|
92
168
|
const observed = evidence?.observed;
|
|
93
169
|
const hashVerified = evidence?.hashVerified === true;
|
|
94
170
|
assertTransferProject(transfer);
|
|
95
|
-
const overwriteVerified =
|
|
171
|
+
const overwriteVerified = hashVerified || result?.confirmedByTransport === true;
|
|
96
172
|
if (result?.ok && observed && overwriteVerified) {
|
|
97
|
-
return { ok: true, written: true, path: transfer.path, method: result.method, changedDocument: true
|
|
173
|
+
return { ok: true, written: true, path: transfer.path, method: result.method, changedDocument: true,
|
|
174
|
+
entityId: observed.id || observed._id || observed.entityId || observed.fileId || '', sha256: transfer.sha256 };
|
|
98
175
|
}
|
|
99
176
|
if (result?.ok && observed && !overwriteVerified) errors.push('Overleaf did not expose enough evidence to verify the binary replacement.');
|
|
100
177
|
if (result?.reason) errors.push(result.reason);
|
|
101
178
|
} catch (error) {
|
|
179
|
+
if (error?.code?.startsWith('binary_asset_baseline_')) return {
|
|
180
|
+
...failure(error.code, error.message), changedDocument: transfer.mutationAttempted === true
|
|
181
|
+
};
|
|
102
182
|
errors.push(error.message);
|
|
103
183
|
}
|
|
184
|
+
// Once submission may have happened, changing transports can duplicate
|
|
185
|
+
// or overwrite a successful upload. Reconcile the same attempt instead.
|
|
186
|
+
if (transfer.mutationAttempted === true) {
|
|
187
|
+
const evidence = transfer.fileUploadAttempted === true
|
|
188
|
+
? await waitForPath(transfer, {}, 12000).catch(() => null) : null;
|
|
189
|
+
if (evidence?.hashVerified === true) {
|
|
190
|
+
return { ok: true, written: true, path: transfer.path,
|
|
191
|
+
method: 'remote-hash-reconciliation', changedDocument: true };
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
104
195
|
}
|
|
105
196
|
return { ...failure('binary_upload_unavailable', errors.filter(Boolean).join('; ') || 'No supported Overleaf asset upload path succeeded.'),
|
|
106
197
|
changedDocument: transfer.mutationAttempted === true };
|
|
@@ -110,16 +201,24 @@
|
|
|
110
201
|
}
|
|
111
202
|
|
|
112
203
|
function abort(params = {}) {
|
|
113
|
-
|
|
204
|
+
const id = String(params.transferId || '');
|
|
205
|
+
if (receipts.has(id)) return { ok: true, aborted: false, committed: true };
|
|
206
|
+
return { ok: true, aborted: transfers.delete(id) };
|
|
114
207
|
}
|
|
115
208
|
|
|
116
209
|
function assertTransferProject(transfer) {
|
|
117
|
-
if (transfers.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
|
|
210
|
+
if (transfers.get(transfer.id) !== transfer && receipts.get(transfer.id) !== transfer) throw new Error('Asset upload was cancelled or replaced.');
|
|
118
211
|
if (!transfer.projectId || treeOperations.getProjectId() !== transfer.projectId) {
|
|
119
212
|
throw new Error('Project changed during asset upload.');
|
|
120
213
|
}
|
|
121
214
|
}
|
|
122
215
|
|
|
216
|
+
function directoryFacts(transfer, requireCleanup = false) {
|
|
217
|
+
return { createdDirectories: transfer.createdDirectories.map(proof => ({ ...proof })),
|
|
218
|
+
folderChanged: transfer.folderChanged === true, fileUploadAttempted: transfer.fileUploadAttempted === true,
|
|
219
|
+
directoryCleanupRequired: requireCleanup && transfer.createdDirectories.length > 0 };
|
|
220
|
+
}
|
|
221
|
+
|
|
123
222
|
async function uploadWithMultipart(file, transfer) {
|
|
124
223
|
const projectId = treeOperations.getProjectId();
|
|
125
224
|
if (!projectId || typeof windowRef.fetch !== 'function') return { ok: false, reason: 'Project upload endpoint is unavailable.' };
|
|
@@ -134,6 +233,11 @@
|
|
|
134
233
|
const headers = csrf ? { 'X-CSRF-Token': csrf } : {};
|
|
135
234
|
const query = folderId ? `?folder_id=${encodeURIComponent(folderId)}` : '';
|
|
136
235
|
assertTransferProject(transfer);
|
|
236
|
+
await assertBinaryBaseline(transfer);
|
|
237
|
+
if (parentPath && await findFolderId(parentPath) !== folderId) throw baselineError('The target folder changed before replacement.');
|
|
238
|
+
assertTransferProject(transfer);
|
|
239
|
+
transfer.mutationAttempted = true;
|
|
240
|
+
transfer.fileUploadAttempted = true;
|
|
137
241
|
const response = await windowRef.fetch(`/project/${encodeURIComponent(projectId)}/upload${query}`, {
|
|
138
242
|
method: 'POST', body: form, credentials: 'same-origin', headers
|
|
139
243
|
});
|
|
@@ -156,17 +260,24 @@
|
|
|
156
260
|
|
|
157
261
|
async function uploadWithDom(file, transfer) {
|
|
158
262
|
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
|
-
});
|
|
263
|
+
if (typeof deps.prepareUploadParent !== 'function' || typeof deps.isUploadParentCurrent !== 'function') {
|
|
264
|
+
return { ok: false, reason: 'Verified folder selection is unavailable for asset upload.' };
|
|
168
265
|
}
|
|
266
|
+
transfer.uploadParent = await deps.prepareUploadParent(parentPath, {
|
|
267
|
+
createMissing: true,
|
|
268
|
+
onMutation: () => { transfer.mutationAttempted = true; transfer.folderChanged = true; },
|
|
269
|
+
onCreatedDirectory: proof => {
|
|
270
|
+
if (proof?.v !== 1 || proof.projectId !== transfer.projectId || typeof proof.path !== 'string'
|
|
271
|
+
|| !transfer.path.startsWith(proof.path + '/')) return;
|
|
272
|
+
const next = { v: 1, projectId: proof.projectId, path: proof.path, nativeFolderId: String(proof.nativeFolderId || ''),
|
|
273
|
+
ownershipConfirmed: proof.ownershipConfirmed === true, observedAfterCreate: proof.observedAfterCreate === true };
|
|
274
|
+
const index = transfer.createdDirectories.findIndex(value => value.path === proof.path);
|
|
275
|
+
if (index >= 0) transfer.createdDirectories[index] = next; else transfer.createdDirectories.push(next);
|
|
276
|
+
},
|
|
277
|
+
isCurrent: () => treeOperations.getProjectId() === transfer.projectId && transfers.get(transfer.id) === transfer
|
|
278
|
+
});
|
|
169
279
|
assertTransferProject(transfer);
|
|
280
|
+
transfer.uploadParentPath = parentPath;
|
|
170
281
|
let input = findFileInput();
|
|
171
282
|
if (!input) {
|
|
172
283
|
const button = findUploadButton();
|
|
@@ -178,8 +289,13 @@
|
|
|
178
289
|
const data = new windowRef.DataTransfer();
|
|
179
290
|
data.items.add(file);
|
|
180
291
|
assertTransferProject(transfer);
|
|
292
|
+
await assertBinaryBaseline(transfer);
|
|
293
|
+
assertTransferProject(transfer);
|
|
294
|
+
if (deps.isUploadParentCurrent(parentPath, transfer.uploadParent) !== true) throw baselineError('The selected upload folder changed before submission.');
|
|
295
|
+
if (input.isConnected === false || findFileInput() !== input) throw baselineError('The upload input changed before submission.');
|
|
181
296
|
assignFilesToInput(input, data.files, windowRef);
|
|
182
297
|
transfer.mutationAttempted = true;
|
|
298
|
+
transfer.fileUploadAttempted = true;
|
|
183
299
|
input.dispatchEvent(new windowRef.Event('input', { bubbles: true }));
|
|
184
300
|
input.dispatchEvent(new windowRef.Event('change', { bubbles: true }));
|
|
185
301
|
await clickReplaceIfShown(transfer);
|
|
@@ -192,9 +308,14 @@
|
|
|
192
308
|
for (const name of ['uploadFile', 'uploadAsset', 'createBinaryFile', 'createFile', 'addFile']) {
|
|
193
309
|
if (typeof manager[name] !== 'function') continue;
|
|
194
310
|
try {
|
|
311
|
+
await assertBinaryBaseline(transfer);
|
|
312
|
+
assertTransferProject(transfer);
|
|
313
|
+
if (treeOperations.findFileTreeManager() !== manager) throw baselineError('The upload manager changed before submission.');
|
|
314
|
+
transfer.mutationAttempted = true;
|
|
315
|
+
transfer.fileUploadAttempted = true;
|
|
195
316
|
await manager[name](transfer.path, file);
|
|
196
317
|
return { ok: true, method: `fileTreeManager.${name}`, confirmedByTransport: true };
|
|
197
|
-
} catch (
|
|
318
|
+
} catch (error) { if (error?.code?.startsWith('binary_asset_baseline_')) throw error; return { ok: false, reason: error.message }; }
|
|
198
319
|
}
|
|
199
320
|
return { ok: false, reason: 'Legacy file-tree manager has no compatible asset method.' };
|
|
200
321
|
}
|
|
@@ -248,9 +369,84 @@
|
|
|
248
369
|
await delay(180);
|
|
249
370
|
assertTransferProject(transfer);
|
|
250
371
|
if (!transfer.overwrite) return;
|
|
251
|
-
const
|
|
252
|
-
&&
|
|
253
|
-
|
|
372
|
+
const candidates = () => Array.from(documentRef.querySelectorAll('[role="dialog"] button')).filter(node => !isCodexOwnedNode(node)
|
|
373
|
+
&& /^(replace|overwrite|替换|覆盖)(?:\b|\s|$)/i.test(String(node.textContent || '').trim()));
|
|
374
|
+
const buttons = candidates();
|
|
375
|
+
if (!buttons.length) return;
|
|
376
|
+
const matchesTarget = node => {
|
|
377
|
+
const dialog = node.closest?.('[role="dialog"]');
|
|
378
|
+
const text = String(dialog?.textContent || '');
|
|
379
|
+
return [transfer.path, transfer.path.split('/').pop()].some(path =>
|
|
380
|
+
new RegExp('(^|[^\\w./-])' + path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '($|[^\\w./-])').test(text));
|
|
381
|
+
};
|
|
382
|
+
const intended = buttons.filter(matchesTarget);
|
|
383
|
+
if (intended.length !== 1) throw baselineError('The replacement dialog could not be bound to the target file.');
|
|
384
|
+
const button = intended[0], dialog = button.closest('[role="dialog"]');
|
|
385
|
+
await assertBinaryBaseline(transfer);
|
|
386
|
+
assertTransferProject(transfer);
|
|
387
|
+
if (deps.isUploadParentCurrent(transfer.uploadParentPath, transfer.uploadParent) !== true) throw baselineError('The selected upload folder changed before replacement confirmation.');
|
|
388
|
+
const current = candidates().filter(matchesTarget);
|
|
389
|
+
if (current.length !== 1 || current[0] !== button || button.isConnected === false
|
|
390
|
+
|| button.closest('[role="dialog"]') !== dialog) throw baselineError('The replacement dialog changed before confirmation.');
|
|
391
|
+
button.click();
|
|
392
|
+
}
|
|
393
|
+
async function binaryIdentity(transfer) {
|
|
394
|
+
assertTransferProject(transfer);
|
|
395
|
+
const nativeRoot = documentRef.querySelector?.('[data-testid="file-tree-list-root"]');
|
|
396
|
+
// The generic adapter may resolve a root basename to another folder.
|
|
397
|
+
// Only the native ancestry or an exact metadata path can bind old bytes.
|
|
398
|
+
const node = nativeRoot ? findNativeTreeNode(transfer.path, 'file') : null;
|
|
399
|
+
let id = readTreeEntityId(node);
|
|
400
|
+
if (!id) {
|
|
401
|
+
const list = await snapshotRouter?.buildProjectFileList({ force: true, maxAgeMs: 0, preferLightweight: true, allowZipFallback: false });
|
|
402
|
+
const file = list?.files?.find(file => file.path === transfer.path);
|
|
403
|
+
id = file?.id || file?._id || file?.entityId || file?.fileId || '';
|
|
404
|
+
}
|
|
405
|
+
assertTransferProject(transfer);
|
|
406
|
+
return id;
|
|
407
|
+
}
|
|
408
|
+
async function assertBinaryBaseline(transfer) {
|
|
409
|
+
if (!transfer.overwrite) return;
|
|
410
|
+
const { id, digest } = await readBaselineDigest(transfer);
|
|
411
|
+
assertTransferProject(transfer);
|
|
412
|
+
if (digest !== transfer.previousSha256) throw baselineError('The binary file changed since Codex read it. Read the project again before replacing it.');
|
|
413
|
+
transfer.previousEntityId = id;
|
|
414
|
+
}
|
|
415
|
+
function baselineError(message) { return Object.assign(new Error(message), { code: 'binary_asset_baseline_changed' }); }
|
|
416
|
+
async function readBaselineDigest(transfer) {
|
|
417
|
+
const controller = new AbortController();
|
|
418
|
+
let timer, reader;
|
|
419
|
+
const timeout = new Promise((_resolve, reject) => { timer = setTimeout(() => {
|
|
420
|
+
controller.abort(); reader?.cancel().catch(() => {});
|
|
421
|
+
reject(baselineError('Reading the original binary file timed out.'));
|
|
422
|
+
}, 8000); });
|
|
423
|
+
try {
|
|
424
|
+
return await Promise.race([timeout, (async () => {
|
|
425
|
+
const id = await binaryIdentity(transfer);
|
|
426
|
+
if (controller.signal.aborted) throw baselineError('Reading the original binary file timed out.');
|
|
427
|
+
if (!id || (transfer.previousEntityId && transfer.previousEntityId !== id)) throw baselineError('The binary file identity changed before replacement.');
|
|
428
|
+
const response = await windowRef.fetch(`/project/${encodeURIComponent(transfer.projectId)}/file/${encodeURIComponent(id)}`, {
|
|
429
|
+
credentials: 'same-origin', cache: 'no-store', signal: controller.signal
|
|
430
|
+
});
|
|
431
|
+
if (!response.ok || Number(response.headers?.get?.('content-length')) > MAX_ASSET_BYTES) throw baselineError('The original binary file is unavailable or exceeds the read limit.');
|
|
432
|
+
let bytes;
|
|
433
|
+
if (response.body?.getReader) {
|
|
434
|
+
reader = response.body.getReader(); const chunks = []; let size = 0;
|
|
435
|
+
for (;;) { const chunk = await reader.read(); if (chunk.done) break;
|
|
436
|
+
size += chunk.value.byteLength;
|
|
437
|
+
if (size > MAX_ASSET_BYTES) throw baselineError('The original binary file exceeds the read limit.');
|
|
438
|
+
chunks.push(chunk.value);
|
|
439
|
+
}
|
|
440
|
+
bytes = joinChunks(chunks, size);
|
|
441
|
+
} else bytes = new Uint8Array(await response.arrayBuffer());
|
|
442
|
+
if (bytes.byteLength > MAX_ASSET_BYTES) throw baselineError('The original binary file exceeds the read limit.');
|
|
443
|
+
const digest = await sha256Hex(bytes, windowRef.crypto);
|
|
444
|
+
if (await binaryIdentity(transfer) !== id) throw baselineError('The binary file identity changed while its original bytes were read.');
|
|
445
|
+
assertTransferProject(transfer);
|
|
446
|
+
if (controller.signal.aborted) throw baselineError('Reading the original binary file timed out.');
|
|
447
|
+
return { id, digest };
|
|
448
|
+
})()]);
|
|
449
|
+
} finally { clearTimeout(timer); controller.abort(); reader?.cancel().catch(() => {}); }
|
|
254
450
|
}
|
|
255
451
|
function readTreeEntityId(node) {
|
|
256
452
|
const owner = node?.closest?.('[data-entity-id], [data-file-id], [data-folder-id], [data-id]');
|
|
@@ -293,9 +489,13 @@
|
|
|
293
489
|
let lastObserved = null;
|
|
294
490
|
while (Date.now() < deadline) {
|
|
295
491
|
assertTransferProject(transfer);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
492
|
+
// A matching digest identifies bytes, not their project path. In the
|
|
493
|
+
// native tree, require the exact ancestry; the generic tree adapter
|
|
494
|
+
// can resolve a root name to an identically named nested file.
|
|
495
|
+
const nativeRoot = documentRef.querySelector?.('[data-testid="file-tree-list-root"]');
|
|
496
|
+
const node = nativeRoot ? findNativeTreeNode(projectPath, 'file')
|
|
497
|
+
: treeOperations.findFileTreeNode(projectPath, { invalidateCache: true });
|
|
498
|
+
let observed = node || (!nativeRoot && treeOperations.projectPathExists(projectPath))
|
|
299
499
|
? { path: projectPath, id: readTreeEntityId(node) } : null;
|
|
300
500
|
if (!observed?.id) {
|
|
301
501
|
try {
|
|
@@ -313,7 +513,7 @@
|
|
|
313
513
|
assertTransferProject(transfer);
|
|
314
514
|
// An existing path is not replacement evidence. Re-read identity and
|
|
315
515
|
// bytes while the native uploader and file-tree events settle.
|
|
316
|
-
if (
|
|
516
|
+
if (hashVerified || result.confirmedByTransport === true) {
|
|
317
517
|
return { observed, hashVerified };
|
|
318
518
|
}
|
|
319
519
|
}
|
|
@@ -333,7 +533,7 @@
|
|
|
333
533
|
return digest === transfer.sha256;
|
|
334
534
|
}
|
|
335
535
|
function delay(ms) { return new Promise(resolve => windowRef.setTimeout(resolve, ms)); }
|
|
336
|
-
return { abort, append, begin, commit };
|
|
536
|
+
return { abort, append, begin, commit, status };
|
|
337
537
|
}
|
|
338
538
|
|
|
339
539
|
function normalizePath(value) {
|