dsh-config-manager 0.1.71 → 0.1.73
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 +149 -98
- package/README.zh-CN.md +245 -88
- package/lib/adapters/agent-instructions.js +1 -1
- package/lib/adapters/file-collection.js +1 -1
- package/lib/adapters/index.d.ts +2 -1
- package/lib/adapters/index.js +6 -1
- package/lib/adapters/link-report.d.ts +1 -0
- package/lib/adapters/link-report.js +27 -3
- package/lib/adapters/messages.d.ts +21 -0
- package/lib/adapters/messages.js +57 -0
- package/lib/adapters/plugin-data.d.ts +114 -0
- package/lib/adapters/plugin-data.js +309 -0
- package/lib/adapters/plugin-files.js +3 -2
- package/lib/adapters/plugins.js +4 -3
- package/lib/adapters/prompts.d.ts +1 -0
- package/lib/adapters/prompts.js +7 -1
- package/lib/adapters/self.d.ts +8 -2
- package/lib/adapters/self.js +3 -2
- package/lib/adapters/sessions.d.ts +1 -0
- package/lib/adapters/sessions.js +4 -2
- package/lib/adapters/test-helpers.d.ts +9 -0
- package/lib/adapters/test-helpers.js +14 -0
- package/lib/adapters/ui.d.ts +2 -2
- package/lib/adapters/ui.js +25 -25
- package/lib/adapters/workspaces.d.ts +1 -0
- package/lib/adapters/workspaces.js +7 -3
- package/lib/cli/actions.d.ts +9 -1
- package/lib/cli/actions.js +5 -2
- package/lib/cli/help.js +23 -10
- package/lib/cli/index.d.ts +23 -0
- package/lib/cli/index.js +90 -32
- package/lib/cli/sessions-inspect.js +3 -1
- package/lib/cli/sessions-repair.js +25 -6
- package/lib/cli/web/page.d.ts +12 -0
- package/lib/cli/web/page.js +21 -12
- package/lib/cli/web/routes.js +2 -2
- package/lib/cli/web/server.js +4 -4
- package/lib/client.d.ts +1442 -494
- package/lib/client.js +15326 -8520
- package/lib/core/analyzer.d.ts +11 -2
- package/lib/core/analyzer.js +31 -8
- package/lib/core/backup-plan.d.ts +10 -0
- package/lib/core/backup-plan.js +51 -20
- package/lib/core/backup-verify.js +24 -23
- package/lib/core/backup.d.ts +25 -1
- package/lib/core/backup.js +99 -28
- package/lib/core/boot-rescue.js +4 -3
- package/lib/core/boot-safety.js +2 -1
- package/lib/core/command-rollback.d.ts +182 -0
- package/lib/core/command-rollback.js +293 -0
- package/lib/core/consult-source.js +7 -6
- package/lib/core/disk-usage.d.ts +74 -10
- package/lib/core/disk-usage.js +67 -4
- package/lib/core/engine-locale.d.ts +87 -0
- package/lib/core/engine-locale.js +125 -0
- package/lib/core/exporter.js +3 -3
- package/lib/core/fork-reconcile.d.ts +128 -0
- package/lib/core/fork-reconcile.js +150 -0
- package/lib/core/journal.js +6 -5
- package/lib/core/local-plugin-host.js +3 -2
- package/lib/core/local-plugin-pack.js +11 -6
- package/lib/core/messages-core.d.ts +294 -0
- package/lib/core/messages-core.js +619 -0
- package/lib/core/messages.d.ts +61 -2
- package/lib/core/messages.js +118 -13
- package/lib/core/migration-consult.js +21 -20
- package/lib/core/migration-history.js +19 -5
- package/lib/core/model-tools.d.ts +0 -22
- package/lib/core/model-tools.js +50 -27
- package/lib/core/phase3-host.js +30 -5
- package/lib/core/plugin-cli.js +18 -19
- package/lib/core/plugin-data-registry.d.ts +124 -0
- package/lib/core/plugin-data-registry.js +198 -0
- package/lib/core/reconcile.d.ts +2 -14
- package/lib/core/reconcile.js +87 -17
- package/lib/core/recovery-orchestrator.js +19 -18
- package/lib/core/reinstall.d.ts +4 -0
- package/lib/core/reinstall.js +26 -21
- package/lib/core/restore.js +20 -17
- package/lib/core/rollback-gate.d.ts +33 -0
- package/lib/core/rollback-gate.js +57 -0
- package/lib/core/rollback-ignore-rules.d.ts +31 -0
- package/lib/core/rollback-ignore-rules.js +140 -0
- package/lib/core/rollback-ledger.d.ts +248 -0
- package/lib/core/rollback-ledger.js +1282 -0
- package/lib/core/rollback-point.d.ts +1391 -0
- package/lib/core/rollback-point.js +3503 -0
- package/lib/core/rollback.js +6 -4
- package/lib/core/run-registry.js +6 -5
- package/lib/core/session-health.d.ts +15 -0
- package/lib/core/session-meta.d.ts +42 -2
- package/lib/core/session-meta.js +140 -1
- package/lib/core/session-title.d.ts +28 -0
- package/lib/core/session-title.js +185 -0
- package/lib/core/startup-barrier.d.ts +24 -0
- package/lib/core/startup-barrier.js +22 -0
- package/lib/core/transaction-coordinator.js +4 -3
- package/lib/core/types.d.ts +37 -1
- package/lib/core/validator.js +5 -4
- package/lib/core/verify-recovery.js +31 -30
- package/lib/foreign/bundle.js +3 -2
- package/lib/foreign/continue.js +2 -1
- package/lib/foreign/crush.js +2 -1
- package/lib/foreign/gemini.js +2 -1
- package/lib/foreign/goose.js +2 -1
- package/lib/foreign/kilocode.js +2 -1
- package/lib/foreign/kimi.js +2 -1
- package/lib/foreign/messages.d.ts +15 -0
- package/lib/foreign/messages.js +40 -0
- package/lib/foreign/mimocode.js +2 -1
- package/lib/foreign/openclaw.js +2 -1
- package/lib/foreign/opencode.js +2 -1
- package/lib/foreign/pi.js +2 -1
- package/lib/foreign/qoder.js +2 -1
- package/lib/foreign/qwen.js +2 -1
- package/lib/foreign/registry.d.ts +1 -1
- package/lib/foreign/registry.js +8 -7
- package/lib/foreign/teleagent.js +2 -1
- package/lib/foreign/workbuddy.js +2 -1
- package/lib/foreign/zcode.js +2 -1
- package/lib/foreign/zed.js +2 -1
- package/lib/index.d.ts +227 -7
- package/lib/index.js +799 -54
- package/lib/market/git-file-writer.js +20 -19
- package/lib/market/github-repos.js +35 -19
- package/lib/market/index-parser.js +31 -30
- package/lib/market/messages-market.d.ts +138 -0
- package/lib/market/messages-market.js +305 -0
- package/lib/market/my-repo.d.ts +2 -2
- package/lib/market/my-repo.js +32 -29
- package/lib/market/prepare.js +18 -15
- package/lib/market/security.js +20 -19
- package/lib/market/types.js +2 -1
- package/lib/market/url.js +2 -1
- package/lib/migrations/index.js +6 -5
- package/lib/migrations/messages-migrations.d.ts +32 -0
- package/lib/migrations/messages-migrations.js +58 -0
- package/lib/profiles/dsh-profile-launcher.js +2 -1
- package/lib/profiles/dsh-profile-runtime.js +2 -1
- package/lib/profiles/messages.d.ts +7 -0
- package/lib/profiles/messages.js +27 -0
- package/lib/routes/backup.js +18 -2
- package/lib/routes/import.js +3 -3
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/kit.d.ts +3 -2
- package/lib/routes/kit.js +6 -1
- package/lib/routes/market.js +1 -1
- package/lib/routes/messages.d.ts +43 -0
- package/lib/routes/messages.js +102 -0
- package/lib/routes/prefs.js +26 -3
- package/lib/routes/profiles.js +12 -6
- package/lib/routes/recovery.d.ts +21 -0
- package/lib/routes/recovery.js +478 -94
- package/lib/routes/snapshots.js +51 -4
- package/lib/routes/sync.js +79 -17
- package/lib/schema/config.d.ts +11 -1
- package/lib/schema/config.js +52 -20
- package/lib/schema/index.d.ts +8 -6
- package/lib/schema/index.js +8 -6
- package/lib/schema/manifest.js +19 -18
- package/lib/schema/messages.d.ts +47 -0
- package/lib/schema/messages.js +110 -0
- package/lib/schema/section-registry.d.ts +42 -15
- package/lib/schema/section-registry.js +67 -32
- package/lib/schema/types.d.ts +1 -1
- package/lib/schema/versions.js +6 -5
- package/lib/security/encryption.js +16 -15
- package/lib/security/integrity.js +8 -7
- package/lib/security/messages.d.ts +30 -0
- package/lib/security/messages.js +77 -0
- package/lib/security/redaction.js +12 -0
- package/lib/security/secret-scanner.js +16 -13
- package/lib/security/vault.js +2 -1
- package/lib/security/zip-security.js +4 -3
- package/lib/sync/ancestor.js +2 -1
- package/lib/sync/autosync-config.d.ts +11 -0
- package/lib/sync/autosync-config.js +12 -0
- package/lib/sync/autosync-scheduler.d.ts +11 -1
- package/lib/sync/autosync-scheduler.js +115 -76
- package/lib/sync/backup-files.js +3 -2
- package/lib/sync/backup-scheduler.js +6 -5
- package/lib/sync/blob-store.js +2 -1
- package/lib/sync/git/git-transport.d.ts +55 -0
- package/lib/sync/git/git-transport.js +152 -2
- package/lib/sync/github-auth.js +10 -9
- package/lib/sync/layout.js +13 -12
- package/lib/sync/messages.d.ts +63 -0
- package/lib/sync/messages.js +149 -0
- package/lib/sync/review-queue.js +6 -5
- package/lib/sync/s3/messages.d.ts +7 -0
- package/lib/sync/s3/messages.js +16 -0
- package/lib/sync/s3/s3-providers.d.ts +4 -1
- package/lib/sync/s3/s3-providers.js +5 -5
- package/lib/sync/s3/s3-transport.js +1 -1
- package/lib/sync/s3/sigv4.js +2 -1
- package/lib/sync/snapshot-crypto.js +6 -5
- package/lib/sync/snapshot-json.js +3 -2
- package/lib/sync/sync-engine.d.ts +58 -22
- package/lib/sync/sync-engine.js +119 -48
- package/lib/sync/sync-history.js +7 -6
- package/lib/sync/sync-selection.d.ts +15 -4
- package/lib/sync/sync-selection.js +17 -5
- package/lib/sync/ui-prefs.d.ts +22 -0
- package/lib/sync/ui-prefs.js +28 -0
- package/lib/sync/webdav/webdav-transport.d.ts +1 -0
- package/lib/sync/webdav/webdav-transport.js +4 -1
- package/lib/ui/artifact-view.js +4 -3
- package/lib/ui/commands.d.ts +1 -1
- package/lib/ui/commands.js +10 -0
- package/lib/ui/diff-view.d.ts +10 -0
- package/lib/ui/diff-view.js +17 -0
- package/lib/ui/disk-usage-view.d.ts +33 -4
- package/lib/ui/disk-usage-view.js +29 -1
- package/lib/ui/errors.d.ts +1 -1
- package/lib/ui/errors.js +4 -3
- package/lib/ui/export-flow.js +26 -20
- package/lib/ui/foreign-view.d.ts +5 -1
- package/lib/ui/foreign-view.js +26 -2
- package/lib/ui/i18n.d.ts +68 -1
- package/lib/ui/i18n.js +120 -2
- package/lib/ui/import-wizard.d.ts +7 -0
- package/lib/ui/import-wizard.js +13 -10
- package/lib/ui/progress.d.ts +2 -2
- package/lib/ui/progress.js +1 -2
- package/lib/ui/report.d.ts +5 -5
- package/lib/ui/report.js +6 -6
- package/lib/ui/rollback-view.d.ts +815 -0
- package/lib/ui/rollback-view.js +738 -0
- package/lib/ui/selection-model.d.ts +41 -0
- package/lib/ui/selection-model.js +30 -0
- package/lib/ui/session-inventory-view.d.ts +97 -3
- package/lib/ui/session-inventory-view.js +76 -2
- package/lib/ui/session-issue-labels.d.ts +43 -0
- package/lib/ui/session-issue-labels.js +48 -0
- package/lib/ui/sync-settings-view.d.ts +33 -0
- package/lib/ui/sync-settings-view.js +40 -0
- package/lib/ui/test-helpers.js +2 -1
- package/lib/ui/types.d.ts +8 -3
- package/lib/ui/types.js +1 -1
- package/lib/utils/atomic-write.js +6 -5
- package/lib/utils/bundle-scan.d.ts +11 -1
- package/lib/utils/bundle-scan.js +12 -1
- package/lib/utils/env-lock.d.ts +10 -0
- package/lib/utils/env-lock.js +73 -46
- package/lib/utils/json.js +6 -5
- package/lib/utils/messages-utils.d.ts +96 -0
- package/lib/utils/messages-utils.js +216 -0
- package/lib/utils/proxy.js +18 -5
- package/lib/utils/recursive-walk.d.ts +22 -0
- package/lib/utils/recursive-walk.js +75 -1
- package/lib/utils/session-log-repair.js +10 -9
- package/lib/utils/zip.js +27 -26
- package/package.json +12 -2
- package/src/adapters/agent-instructions.ts +1 -1
- package/src/adapters/file-collection.test.ts +1 -0
- package/src/adapters/file-collection.ts +1 -1
- package/src/adapters/files.test.ts +27 -0
- package/src/adapters/index.ts +6 -1
- package/src/adapters/link-report.test.ts +22 -2
- package/src/adapters/link-report.ts +29 -4
- package/src/adapters/messages.ts +61 -0
- package/src/adapters/plugin-data.test.ts +225 -0
- package/src/adapters/plugin-data.ts +351 -0
- package/src/adapters/plugin-files.ts +3 -2
- package/src/adapters/plugins.ts +4 -3
- package/src/adapters/prompts.ts +7 -1
- package/src/adapters/self.test.ts +26 -0
- package/src/adapters/self.ts +10 -3
- package/src/adapters/sessions.ts +4 -2
- package/src/adapters/settings.test.ts +38 -1
- package/src/adapters/test-helpers.ts +15 -0
- package/src/adapters/ui.test.ts +2 -2
- package/src/adapters/ui.ts +27 -26
- package/src/adapters/workspaces.ts +7 -3
- package/src/cli/actions.test.ts +28 -0
- package/src/cli/actions.ts +11 -2
- package/src/cli/help.ts +23 -10
- package/src/cli/index-guards.test.ts +193 -0
- package/src/cli/index.ts +106 -31
- package/src/cli/sessions-inspect.test.ts +15 -1
- package/src/cli/sessions-inspect.ts +3 -1
- package/src/cli/sessions-repair.test.ts +84 -0
- package/src/cli/sessions-repair.ts +33 -7
- package/src/cli/web/console-route-parity.test.ts +115 -0
- package/src/cli/web/page.ts +23 -12
- package/src/cli/web/routes.ts +2 -1
- package/src/cli/web/server.ts +4 -4
- package/src/cli/web/web.test.ts +2 -2
- package/src/client/ConfigManagerSection.tsx +30 -2
- package/src/client/about/AboutPanel.tsx +2 -2
- package/src/client/about/PluginUpdateDialog.tsx +1 -1
- package/src/client/about/ReleaseNotesDialog.tsx +2 -2
- package/src/client/about/about-view.test.ts +13 -3
- package/src/client/about/about-view.ts +10 -6
- package/src/client/about/release-notes-view.test.ts +14 -4
- package/src/client/about/release-notes-view.ts +4 -2
- package/src/client/api.ts +18 -6
- package/src/client/chat/ChatEntryView.tsx +411 -0
- package/src/client/chat/RollbackDiffTab.tsx +108 -0
- package/src/client/chat/RollbackFileIcon.tsx +96 -0
- package/src/client/chat/RollbackReviewDialog.tsx +607 -0
- package/src/client/chat/chat-entry-view.test.ts +1174 -0
- package/src/client/chat/chat-entry-view.ts +758 -0
- package/src/client/chat/chat-locales.ts +152 -0
- package/src/client/chat/chat-node-registration.ts +64 -0
- package/src/client/chat/chat-point-window.ts +183 -0
- package/src/client/chat/chat-slots.ts +107 -0
- package/src/client/chat/rollback-diff-tab.test.ts +105 -0
- package/src/client/chat/rollback-diff-tab.ts +174 -0
- package/src/client/chat/rollback-entry-view.test.ts +1469 -0
- package/src/client/chat/rollback-entry-view.ts +946 -0
- package/src/client/chat/sidebar-slots.ts +59 -0
- package/src/client/common/ConfirmDialog.tsx +7 -3
- package/src/client/common/ContentPicker.tsx +48 -10
- package/src/client/common/DiffTable.tsx +133 -0
- package/src/client/common/ErrorBanner.tsx +4 -4
- package/src/client/common/Icon.tsx +2 -0
- package/src/client/common/Modal.tsx +8 -1
- package/src/client/common/ProgressBar.tsx +8 -2
- package/src/client/common/ReportView.tsx +4 -4
- package/src/client/common/RunsCenter.tsx +1 -0
- package/src/client/common/client-route-parity.test.ts +84 -0
- package/src/client/common/info-hint-guard.test.ts +46 -13
- package/src/client/common/picker-lazy-units.test.ts +42 -0
- package/src/client/common/plan-text-redaction.test.ts +17 -6
- package/src/client/common/progress-view-edge.test.ts +8 -7
- package/src/client/common/progress-view.test.ts +9 -9
- package/src/client/common/progress-view.ts +2 -2
- package/src/client/common/routes.ts +38 -0
- package/src/client/common/section-labels.test.ts +1 -0
- package/src/client/common/section-labels.ts +2 -0
- package/src/client/common/ui.tsx +73 -10
- package/src/client/config-manager.module.css +1109 -31
- package/src/client/css-scale-guard.test.ts +30 -0
- package/src/client/environment/EnvironmentPanel.tsx +11 -4
- package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
- package/src/client/export/ExportView.tsx +13 -8
- package/src/client/history/HistoryPanel.tsx +4 -3
- package/src/client/history/history-api.ts +2 -2
- package/src/client/history/history-locales.ts +5 -1
- package/src/client/home/HomePanel.tsx +29 -8
- package/src/client/import/ForeignImportView.tsx +4 -5
- package/src/client/import/ImportWizardView.tsx +5 -3
- package/src/client/import/import-wizard-split.test.ts +3 -1
- package/src/client/import/import-wizard-steps.tsx +9 -9
- package/src/client/index.ts +232 -3
- package/src/client/library/LibraryActions.tsx +32 -7
- package/src/client/library/LibraryPanel.tsx +23 -3
- package/src/client/library/RestorePlanView.tsx +9 -66
- package/src/client/locales.ts +62 -9
- package/src/client/lucide-icons.d.ts +1 -0
- package/src/client/market/MarketPanel.tsx +13 -2
- package/src/client/market/MyConfigsView.tsx +5 -4
- package/src/client/market/MyConfigsWizard.tsx +10 -8
- package/src/client/market/market-api.test.ts +9 -8
- package/src/client/market/market-api.ts +2 -2
- package/src/client/market/market-locales.ts +3 -0
- package/src/client/market/market-view.test.ts +6 -6
- package/src/client/market/market-view.ts +3 -3
- package/src/client/market/my-configs-api.ts +2 -2
- package/src/client/market/my-configs-view.test.ts +23 -22
- package/src/client/market/my-configs-view.ts +8 -8
- package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
- package/src/client/recovery/RecoveryPanel.tsx +442 -283
- package/src/client/recovery/RollbackCard.tsx +859 -0
- package/src/client/recovery/RollbackDiffPane.tsx +69 -0
- package/src/client/recovery/RollbackFileChanges.tsx +163 -0
- package/src/client/recovery/incident-api.ts +2 -2
- package/src/client/recovery/recovery-api.ts +144 -6
- package/src/client/recovery/recovery-locales.test.ts +299 -0
- package/src/client/recovery/recovery-locales.ts +520 -149
- package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
- package/src/client/recovery/rollback-enabled.ts +41 -0
- package/src/client/recovery/rollback-file-changes.test.ts +114 -0
- package/src/client/recovery/session-dialog-mount.test.ts +54 -0
- package/src/client/run-store.ts +84 -21
- package/src/client/sync/AutosyncCard.tsx +25 -1
- package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
- package/src/client/sync/ChannelConfigDialog.tsx +10 -10
- package/src/client/sync/DecryptPasswordCard.tsx +2 -2
- package/src/client/sync/SecurityOptionsCard.tsx +27 -17
- package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
- package/src/client/sync/SyncConfirmView.tsx +46 -8
- package/src/client/sync/SyncLogList.tsx +1 -1
- package/src/client/sync/SyncPanel.tsx +31 -23
- package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
- package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
- package/src/client/sync/SyncSettingsView.test.ts +20 -1
- package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
- package/src/client/sync/WorkDirCard.tsx +112 -0
- package/src/client/sync/history-model.test.ts +42 -9
- package/src/client/sync/history-model.ts +30 -15
- package/src/client/sync/sync-api.test.ts +57 -33
- package/src/client/sync/sync-api.ts +80 -6
- package/src/client/sync/sync-locales.ts +104 -3
- package/src/client/sync/sync-view-v2.test.ts +65 -13
- package/src/client/sync/sync-view.test.ts +107 -52
- package/src/client/sync/sync-view.ts +124 -41
- package/src/core/analyzer.ts +31 -9
- package/src/core/backup-plan.test.ts +48 -5
- package/src/core/backup-plan.ts +51 -20
- package/src/core/backup-verify.ts +24 -23
- package/src/core/backup.test.ts +76 -0
- package/src/core/backup.ts +105 -29
- package/src/core/boot-rescue.ts +4 -3
- package/src/core/boot-safety.ts +2 -1
- package/src/core/command-rollback.test.ts +536 -0
- package/src/core/command-rollback.ts +406 -0
- package/src/core/consult-source.ts +7 -6
- package/src/core/disk-usage.test.ts +257 -1
- package/src/core/disk-usage.ts +134 -14
- package/src/core/engine-locale.test.ts +593 -0
- package/src/core/engine-locale.ts +175 -0
- package/src/core/exporter.ts +3 -3
- package/src/core/fork-reconcile.test.ts +279 -0
- package/src/core/fork-reconcile.ts +234 -0
- package/src/core/journal.ts +6 -5
- package/src/core/local-plugin-host.ts +3 -2
- package/src/core/local-plugin-pack.ts +11 -6
- package/src/core/messages-core.ts +661 -0
- package/src/core/messages.ts +125 -17
- package/src/core/migration-consult.ts +21 -20
- package/src/core/migration-history.ts +19 -5
- package/src/core/model-tools.ts +28 -27
- package/src/core/phase3-host.ts +21 -5
- package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
- package/src/core/plugin-cli.ts +18 -19
- package/src/core/plugin-data-registry.test.ts +113 -0
- package/src/core/plugin-data-registry.ts +261 -0
- package/src/core/reconcile.test.ts +117 -0
- package/src/core/reconcile.ts +85 -17
- package/src/core/recovery-orchestrator.ts +19 -18
- package/src/core/reinstall.ts +26 -21
- package/src/core/restore.ts +20 -17
- package/src/core/rollback-file-diff.test.ts +159 -0
- package/src/core/rollback-gate.test.ts +66 -0
- package/src/core/rollback-gate.ts +61 -0
- package/src/core/rollback-ignore-rules.ts +126 -0
- package/src/core/rollback-ledger.test.ts +655 -0
- package/src/core/rollback-ledger.ts +1314 -0
- package/src/core/rollback-point.test.ts +2853 -0
- package/src/core/rollback-point.ts +4340 -0
- package/src/core/rollback.ts +5 -3
- package/src/core/run-registry.ts +6 -5
- package/src/core/session-health.ts +15 -0
- package/src/core/session-meta.test.ts +117 -1
- package/src/core/session-meta.ts +149 -2
- package/src/core/session-title.test.ts +127 -0
- package/src/core/session-title.ts +189 -0
- package/src/core/startup-barrier.test.ts +69 -0
- package/src/core/startup-barrier.ts +27 -0
- package/src/core/transaction-coordinator.ts +4 -3
- package/src/core/types.ts +37 -1
- package/src/core/validator.ts +5 -4
- package/src/core/verify-recovery.ts +31 -30
- package/src/foreign/bundle.ts +3 -2
- package/src/foreign/continue.ts +2 -1
- package/src/foreign/crush.ts +2 -1
- package/src/foreign/file-budget.test.ts +2 -0
- package/src/foreign/gemini.ts +2 -1
- package/src/foreign/goose.ts +2 -1
- package/src/foreign/kilocode.ts +2 -1
- package/src/foreign/kimi.ts +2 -1
- package/src/foreign/messages.ts +44 -0
- package/src/foreign/mimocode.ts +2 -1
- package/src/foreign/openclaw.ts +2 -1
- package/src/foreign/opencode.ts +2 -1
- package/src/foreign/pi.ts +2 -1
- package/src/foreign/qoder.ts +2 -1
- package/src/foreign/qwen.ts +2 -1
- package/src/foreign/registry.test.ts +1 -1
- package/src/foreign/registry.ts +8 -7
- package/src/foreign/teleagent.ts +2 -1
- package/src/foreign/workbuddy.ts +2 -1
- package/src/foreign/zcode.ts +2 -1
- package/src/foreign/zed.ts +2 -1
- package/src/index.facade.test.ts +340 -1
- package/src/index.ts +876 -57
- package/src/market/git-file-writer.ts +20 -19
- package/src/market/github-repos.test.ts +23 -0
- package/src/market/github-repos.ts +37 -19
- package/src/market/index-parser.ts +31 -30
- package/src/market/messages-market.ts +325 -0
- package/src/market/my-repo.ts +32 -29
- package/src/market/prepare.ts +18 -15
- package/src/market/security.ts +20 -19
- package/src/market/types.ts +2 -1
- package/src/market/url.ts +2 -1
- package/src/migrations/index.ts +6 -5
- package/src/migrations/messages-migrations.ts +59 -0
- package/src/profiles/dsh-profile-launcher.ts +2 -1
- package/src/profiles/dsh-profile-runtime.ts +2 -1
- package/src/profiles/messages.ts +31 -0
- package/src/routes/backup.ts +18 -2
- package/src/routes/import.ts +3 -3
- package/src/routes/index.ts +1 -1
- package/src/routes/kit.ts +9 -3
- package/src/routes/market.ts +1 -1
- package/src/routes/messages.ts +106 -0
- package/src/routes/prefs.ts +31 -2
- package/src/routes/profiles.ts +12 -6
- package/src/routes/recovery.test.ts +405 -1
- package/src/routes/recovery.ts +477 -94
- package/src/routes/snapshots.ts +51 -4
- package/src/routes/sync.ts +82 -17
- package/src/schema/config.ts +53 -20
- package/src/schema/index.ts +7 -5
- package/src/schema/manifest-schema.test.ts +4 -4
- package/src/schema/manifest.ts +19 -18
- package/src/schema/messages.ts +120 -0
- package/src/schema/registry.test.ts +28 -7
- package/src/schema/section-registry.ts +96 -42
- package/src/schema/types.ts +2 -1
- package/src/schema/versions.ts +6 -5
- package/src/security/encryption.ts +16 -15
- package/src/security/integrity.ts +8 -7
- package/src/security/messages.ts +87 -0
- package/src/security/redaction.test.ts +17 -1
- package/src/security/redaction.ts +13 -0
- package/src/security/secret-scanner.ts +19 -12
- package/src/security/security.test.ts +47 -0
- package/src/security/vault.ts +2 -1
- package/src/security/zip-security.ts +4 -3
- package/src/sync/ancestor.ts +2 -1
- package/src/sync/autosync-config.ts +21 -0
- package/src/sync/autosync-scheduler.test.ts +63 -7
- package/src/sync/autosync-scheduler.ts +118 -78
- package/src/sync/backup-files.ts +3 -2
- package/src/sync/backup-scheduler.ts +6 -5
- package/src/sync/blob-store.ts +2 -1
- package/src/sync/git/git-transport.test.ts +170 -8
- package/src/sync/git/git-transport.ts +176 -2
- package/src/sync/github-auth.ts +10 -9
- package/src/sync/layout.ts +13 -12
- package/src/sync/messages.ts +153 -0
- package/src/sync/review-queue.ts +6 -5
- package/src/sync/s3/messages.ts +16 -0
- package/src/sync/s3/s3-providers.ts +9 -6
- package/src/sync/s3/s3-transport.ts +1 -1
- package/src/sync/s3/sigv4.ts +3 -1
- package/src/sync/snapshot-crypto.ts +6 -5
- package/src/sync/snapshot-json.ts +3 -2
- package/src/sync/sync-credentials.test.ts +119 -0
- package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
- package/src/sync/sync-engine.test.ts +226 -8
- package/src/sync/sync-engine.ts +154 -52
- package/src/sync/sync-history.ts +7 -6
- package/src/sync/sync-selection.test.ts +12 -2
- package/src/sync/sync-selection.ts +18 -5
- package/src/sync/transport.test.ts +1 -0
- package/src/sync/ui-prefs.test.ts +44 -1
- package/src/sync/ui-prefs.ts +39 -0
- package/src/sync/webdav/webdav-transport.ts +4 -1
- package/src/ui/artifact-view.ts +4 -3
- package/src/ui/commands.test.ts +1 -0
- package/src/ui/commands.ts +11 -1
- package/src/ui/diff-view.test.ts +9 -1
- package/src/ui/diff-view.ts +22 -0
- package/src/ui/disk-usage-view.test.ts +113 -6
- package/src/ui/disk-usage-view.ts +63 -7
- package/src/ui/errors.ts +4 -3
- package/src/ui/export-flow.ts +26 -20
- package/src/ui/foreign-view.test.ts +43 -0
- package/src/ui/foreign-view.ts +30 -3
- package/src/ui/i18n.ts +131 -2
- package/src/ui/import-wizard.ts +18 -10
- package/src/ui/progress.ts +2 -2
- package/src/ui/report.ts +7 -6
- package/src/ui/rollback-view.test.ts +836 -0
- package/src/ui/rollback-view.ts +1405 -0
- package/src/ui/selection-model.test.ts +33 -1
- package/src/ui/selection-model.ts +53 -0
- package/src/ui/session-inventory-view.test.ts +109 -0
- package/src/ui/session-inventory-view.ts +138 -3
- package/src/ui/session-issue-labels.test.ts +51 -0
- package/src/ui/session-issue-labels.ts +70 -0
- package/src/ui/sync-settings-view.test.ts +36 -0
- package/src/ui/sync-settings-view.ts +49 -0
- package/src/ui/test-helpers.ts +2 -1
- package/src/ui/types.ts +10 -4
- package/src/utils/atomic-write.ts +6 -5
- package/src/utils/bundle-scan.test.ts +2 -2
- package/src/utils/bundle-scan.ts +12 -1
- package/src/utils/env-lock.ts +83 -49
- package/src/utils/json.ts +7 -5
- package/src/utils/messages-utils.ts +232 -0
- package/src/utils/proxy.test.ts +25 -0
- package/src/utils/proxy.ts +19 -5
- package/src/utils/recursive-walk.test.ts +43 -0
- package/src/utils/recursive-walk.ts +75 -1
- package/src/utils/session-log-repair.ts +10 -9
- package/src/utils/zip.ts +27 -26
- package/lib/core/checkpoint.d.ts +0 -362
- package/lib/core/checkpoint.js +0 -1309
- package/src/core/checkpoint.test.ts +0 -665
- package/src/core/checkpoint.ts +0 -1614
|
@@ -19,7 +19,7 @@ import { Banner } from '../common/ui.tsx'
|
|
|
19
19
|
import { toast } from '../common/toast-store.ts'
|
|
20
20
|
import { redact } from '../../security/redaction.ts'
|
|
21
21
|
import type { SyncApi } from './sync-api.ts'
|
|
22
|
-
import type
|
|
22
|
+
import { SYNC_CHANNEL_LABEL_KEY, type SyncChannel } from './sync-view.ts'
|
|
23
23
|
|
|
24
24
|
export function ChannelClearConfirmDialog({ open, channel, api, t, onCancel, onCleared }: {
|
|
25
25
|
open: boolean
|
|
@@ -30,7 +30,10 @@ export function ChannelClearConfirmDialog({ open, channel, api, t, onCancel, onC
|
|
|
30
30
|
/** 成功回调:调用方据此清表单残留、撤销在途自动保存并重拉 status(本组件不碰页面状态) */
|
|
31
31
|
onCleared: () => void
|
|
32
32
|
}) {
|
|
33
|
-
|
|
33
|
+
// 四个通道一律走穷尽映射(SYNC_CHANNEL_LABEL_KEY: Record<SyncChannel, SyncKey>):
|
|
34
|
+
// 此前写成「webdav ? webdav : git」,在 s3/gist 卡上点「断开配置」时,危险确认框与成功回执
|
|
35
|
+
// 都会把该通道说成「Git 私有仓库」——用户会以为断错了通道而取消(或反之误判已断的通道)。
|
|
36
|
+
const channelLabel = t(SYNC_CHANNEL_LABEL_KEY[channel])
|
|
34
37
|
return (
|
|
35
38
|
<ConfirmDialog
|
|
36
39
|
open={open}
|
|
@@ -45,7 +48,7 @@ export function ChannelClearConfirmDialog({ open, channel, api, t, onCancel, onC
|
|
|
45
48
|
toast.ok(t('toast.channelCleared', { channel: channelLabel }))
|
|
46
49
|
onCleared()
|
|
47
50
|
} catch (err) {
|
|
48
|
-
toast.error(
|
|
51
|
+
toast.error(t('toast.channelClearFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
49
52
|
}
|
|
50
53
|
}}
|
|
51
54
|
onCancel={onCancel}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
cloudIssueKey, cloudSecretRefName, presetById, presetIdForUrl, privateRepoHint,
|
|
19
19
|
S3_PROVIDER_LABEL_KEY, SYNC_CHANNEL_LABEL_KEY, WEBDAV_PRESETS,
|
|
20
20
|
} from './sync-view.ts'
|
|
21
|
+
import { GIST_LARGE_SECTION_HINT_KEY } from './sync-view.ts'
|
|
21
22
|
import type { GithubLoginView } from './sync-view.ts'
|
|
22
23
|
import { S3_PROVIDERS } from '../../ui/sync-settings-view.ts'
|
|
23
24
|
import type { CloudFormIssue, SyncFormSnapshot } from '../../ui/sync-settings-view.ts'
|
|
@@ -137,6 +138,7 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
137
138
|
{t('config.token')}
|
|
138
139
|
{' '}
|
|
139
140
|
{statusInfo?.credentialConfigured === true && <Badge kind="ok">{t('config.tokenSaved')}</Badge>}
|
|
141
|
+
<InfoHint text={t('config.tokenHint', { ref: SYNC_CREDENTIAL_REF })} label={t('common.infoHint')} />
|
|
140
142
|
</span>
|
|
141
143
|
<input
|
|
142
144
|
type="password"
|
|
@@ -149,7 +151,6 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
149
151
|
onFormChange({ token: e.target.value })
|
|
150
152
|
}}
|
|
151
153
|
/>
|
|
152
|
-
<span className={css.hint}>{t('config.tokenHint', { ref: SYNC_CREDENTIAL_REF })}</span>
|
|
153
154
|
</label>
|
|
154
155
|
|
|
155
156
|
{/* GitHub OAuth 登录(device flow):弹窗内仅 git 通道显示;已登录(token 有效)时整块隐藏 */}
|
|
@@ -161,7 +162,7 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
161
162
|
<span className={css.groupLabel}>{t('github.title')} <InfoHint text={t('github.description')} label={t('common.infoHint')} /></span>
|
|
162
163
|
{githubView.showCode && (
|
|
163
164
|
<div className={css.statRow}>
|
|
164
|
-
<Badge kind="info">{t('github.userCode')}
|
|
165
|
+
<Badge kind="info">{t('github.userCode')}{t('common.labelSeparator')}<strong>{githubView.userCode}</strong></Badge>
|
|
165
166
|
<a
|
|
166
167
|
className={css.ghostButton}
|
|
167
168
|
href={githubView.verificationUri}
|
|
@@ -192,9 +193,6 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
192
193
|
{githubView.statusText}
|
|
193
194
|
</Badge>
|
|
194
195
|
</div>
|
|
195
|
-
{githubView.phase === 'error' && (
|
|
196
|
-
<span className={css.hint}>{t('config.tokenHint', { ref: SYNC_CREDENTIAL_REF })}</span>
|
|
197
|
-
)}
|
|
198
196
|
</>
|
|
199
197
|
)}
|
|
200
198
|
</>
|
|
@@ -215,7 +213,7 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
215
213
|
const preset = presetById(next)
|
|
216
214
|
onFormChange({ webdavUrl: preset.url })
|
|
217
215
|
}}
|
|
218
|
-
options={WEBDAV_PRESETS.map((preset) => ({ value: preset.id, label: preset.
|
|
216
|
+
options={WEBDAV_PRESETS.map((preset) => ({ value: preset.id, label: t(preset.labelKey) }))}
|
|
219
217
|
/>
|
|
220
218
|
<InfoHint text={t('webdav.presetHint')} label={t('common.infoHint')} />
|
|
221
219
|
</div>
|
|
@@ -257,6 +255,7 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
257
255
|
{t('webdav.password')}
|
|
258
256
|
{' '}
|
|
259
257
|
{statusInfo?.webdav?.passwordConfigured === true && <Badge kind="ok">{t('webdav.passwordSaved')}</Badge>}
|
|
258
|
+
<InfoHint text={t('webdav.passwordHint', { ref: SYNC_WEBDAV_CREDENTIAL_REF })} label={t('common.infoHint')} />
|
|
260
259
|
</span>
|
|
261
260
|
<input
|
|
262
261
|
type="password"
|
|
@@ -269,7 +268,6 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
269
268
|
onFormChange({ webdavPassword: e.target.value })
|
|
270
269
|
}}
|
|
271
270
|
/>
|
|
272
|
-
<span className={css.hint}>{t('webdav.passwordHint', { ref: SYNC_WEBDAV_CREDENTIAL_REF })}</span>
|
|
273
271
|
</label>
|
|
274
272
|
</>
|
|
275
273
|
)}
|
|
@@ -359,6 +357,7 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
359
357
|
{t('cloud.secret')}
|
|
360
358
|
{' '}
|
|
361
359
|
{statusInfo?.s3?.secretStored === true && <Badge kind="ok">{t('cloud.secretSaved')}</Badge>}
|
|
360
|
+
<InfoHint text={t('cloud.secretHint', { ref: cloudSecretRef })} label={t('common.infoHint')} />
|
|
362
361
|
</span>
|
|
363
362
|
<input
|
|
364
363
|
type="password"
|
|
@@ -369,7 +368,6 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
369
368
|
disabled={busy}
|
|
370
369
|
onChange={(e: ChangeEvent<HTMLInputElement>) => { onFormChange({ s3Secret: e.target.value }) }}
|
|
371
370
|
/>
|
|
372
|
-
<span className={css.hint}>{t('cloud.secretHint', { ref: cloudSecretRef })}</span>
|
|
373
371
|
</label>
|
|
374
372
|
</>
|
|
375
373
|
)}
|
|
@@ -377,7 +375,9 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
377
375
|
{/* gist 通道分支(GitHub Gist:走 REST,token 只写 DSH 凭据槽位) */}
|
|
378
376
|
{channel === 'gist' && (
|
|
379
377
|
<>
|
|
380
|
-
|
|
378
|
+
{/* G-34:gist 刻意不外置 blob(一次 GET 全量 / 单文件超限截断 / 文件数上限)⇒ 大分区不适合走 gist;
|
|
379
|
+
该说明属机制类,收进标题 ⓘ(2026-10-12 用户要求) */}
|
|
380
|
+
<span className={css.groupLabel}>{t('cloud.gistTitle')} <InfoHint text={t(GIST_LARGE_SECTION_HINT_KEY)} label={t('common.infoHint')} /></span>
|
|
381
381
|
<label className={css.field}>
|
|
382
382
|
<span className={css.fieldLabel}>{t('cloud.gistId')} <InfoHint text={t('cloud.gistIdHint')} label={t('common.infoHint')} /></span>
|
|
383
383
|
<input
|
|
@@ -417,6 +417,7 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
417
417
|
{t('cloud.token')}
|
|
418
418
|
{' '}
|
|
419
419
|
{statusInfo?.gist?.secretStored === true && <Badge kind="ok">{t('cloud.tokenSaved')}</Badge>}
|
|
420
|
+
<InfoHint text={t('cloud.tokenHint', { ref: cloudSecretRef })} label={t('common.infoHint')} />
|
|
420
421
|
</span>
|
|
421
422
|
<input
|
|
422
423
|
type="password"
|
|
@@ -427,7 +428,6 @@ export function ChannelConfigDialog({ open, onClose, api, t, uiT, form, cloudIss
|
|
|
427
428
|
disabled={busy}
|
|
428
429
|
onChange={(e: ChangeEvent<HTMLInputElement>) => { onFormChange({ gistToken: e.target.value }) }}
|
|
429
430
|
/>
|
|
430
|
-
<span className={css.hint}>{t('cloud.tokenHint', { ref: cloudSecretRef })}</span>
|
|
431
431
|
</label>
|
|
432
432
|
</>
|
|
433
433
|
)}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { ChangeEvent } from 'react'
|
|
8
8
|
import type { TranslateNS } from '../client-types.ts'
|
|
9
9
|
import { Button, Card } from '../common/ui.tsx'
|
|
10
|
+
import { InfoHint } from '../common/InfoHint.tsx'
|
|
10
11
|
import type { ChannelSyncState } from './sync-view.ts'
|
|
11
12
|
import type { SyncChannelSettingsPatch } from '../../ui/sync-settings-view.ts'
|
|
12
13
|
import css from '../config-manager.module.css'
|
|
@@ -22,7 +23,7 @@ export function DecryptPasswordCard({ t, settings, onPatchSettings, onPersistDec
|
|
|
22
23
|
return (
|
|
23
24
|
<Card>
|
|
24
25
|
<label className={css.field}>
|
|
25
|
-
<span className={css.fieldLabel}>{t('mode.decryptPassword')}
|
|
26
|
+
<span className={css.fieldLabel}>{t('mode.decryptPassword')} <InfoHint text={t('mode.decryptPasswordHint')} label={t('common.infoHint')} /></span>
|
|
26
27
|
<input
|
|
27
28
|
type="password"
|
|
28
29
|
className={css.input}
|
|
@@ -32,7 +33,6 @@ export function DecryptPasswordCard({ t, settings, onPatchSettings, onPersistDec
|
|
|
32
33
|
onChange={(e: ChangeEvent<HTMLInputElement>) => { onPatchSettings({ decryptPassword: e.target.value }) }}
|
|
33
34
|
onBlur={onPersistDecryptPassword}
|
|
34
35
|
/>
|
|
35
|
-
<span className={css.hint}>{t('mode.decryptPasswordHint')}</span>
|
|
36
36
|
</label>
|
|
37
37
|
{settings.decryptPasswordSaved && (
|
|
38
38
|
<div className={css.actionRow}>
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { ChangeEvent } from 'react'
|
|
8
8
|
import type { TranslateNS } from '../client-types.ts'
|
|
9
9
|
import { Card, Checkbox } from '../common/ui.tsx'
|
|
10
|
+
import { InfoHint } from '../common/InfoHint.tsx'
|
|
10
11
|
import type { ChannelSyncState } from './sync-view.ts'
|
|
11
12
|
import type { SyncChannelSettingsPatch } from '../../ui/sync-settings-view.ts'
|
|
12
13
|
import css from '../config-manager.module.css'
|
|
@@ -26,19 +27,27 @@ export function SecurityOptionsCard({ t, settings, onToggleEncrypt, onToggleIncl
|
|
|
26
27
|
return (
|
|
27
28
|
<Card>
|
|
28
29
|
<span className={css.groupLabel}>{t('mode.security')}</span>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
{/* 说明性文案一律进 ⓘ(2026-10-12 用户要求:卡里只留勾选项与控件)。
|
|
31
|
+
「加密方式 + 密码存哪 + 自动同步跳过加密快照」两句合成同一颗 ⓘ。 */}
|
|
32
|
+
<div className={css.controlRow}>
|
|
33
|
+
<Checkbox
|
|
34
|
+
checked={settings.encrypt}
|
|
35
|
+
onChange={onToggleEncrypt}
|
|
36
|
+
label={<span className={css.categoryName}>{t('mode.encrypt')}</span>}
|
|
37
|
+
/>
|
|
38
|
+
<InfoHint
|
|
39
|
+
text={t('mode.encryptHint') + ' ' + t('mode.encryptAutosyncNotice')}
|
|
40
|
+
label={t('common.infoHint')}
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
38
43
|
{settings.encrypt && (
|
|
39
44
|
<div className={css.secretFields}>
|
|
40
45
|
<label className={css.field}>
|
|
41
|
-
<span className={css.fieldLabel}>
|
|
46
|
+
<span className={css.fieldLabel}>
|
|
47
|
+
{t('mode.password')}
|
|
48
|
+
{' '}
|
|
49
|
+
{settings.encryptPasswordSaved && <InfoHint text={t('mode.passwordSavedHint')} label={t('common.infoHint')} />}
|
|
50
|
+
</span>
|
|
42
51
|
<input
|
|
43
52
|
type="password"
|
|
44
53
|
className={css.input}
|
|
@@ -71,13 +80,14 @@ export function SecurityOptionsCard({ t, settings, onToggleEncrypt, onToggleIncl
|
|
|
71
80
|
<span className={css.hint}>{t('mode.passwordClearNotice')}</span>
|
|
72
81
|
</div>
|
|
73
82
|
)}
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
<div className={css.controlRow}>
|
|
84
|
+
<Checkbox
|
|
85
|
+
checked={settings.includeSecrets}
|
|
86
|
+
onChange={onToggleIncludeSecrets}
|
|
87
|
+
label={<span className={css.categoryName}>{t('mode.includeSecrets')}</span>}
|
|
88
|
+
/>
|
|
89
|
+
<InfoHint text={t('mode.includeSecretsHint')} label={t('common.infoHint')} />
|
|
90
|
+
</div>
|
|
81
91
|
</Card>
|
|
82
92
|
)
|
|
83
93
|
}
|
|
@@ -10,7 +10,7 @@ import { ExpandChevron } from '../common/Icon.tsx'
|
|
|
10
10
|
import { InfoHint } from '../common/InfoHint.tsx'
|
|
11
11
|
import type { SyncStatusResponse } from './sync-api.ts'
|
|
12
12
|
import type { SyncChannel } from './sync-view.ts'
|
|
13
|
-
import { formatSyncStatusTimestamp, formatSyncUrlPreview } from '../../ui/sync-settings-view.ts'
|
|
13
|
+
import { formatSyncStatusTimestamp, formatSyncUrlPreview, workdirStateKey, workdirStateBadgeKind } from '../../ui/sync-settings-view.ts'
|
|
14
14
|
import { needsGitToken, SYNC_CHANNEL_LABEL_KEY } from './sync-view.ts'
|
|
15
15
|
import css from '../config-manager.module.css'
|
|
16
16
|
|
|
@@ -38,6 +38,11 @@ export function SyncChannelEntryCard({ t, channel, statusInfo, remoteReady, repo
|
|
|
38
38
|
contentId?: string
|
|
39
39
|
onToggleExpanded?: () => void
|
|
40
40
|
}) {
|
|
41
|
+
// issue #79:工作副本状态(git 专属)。只在**需要人管**的状态(仍指旧地址 / 读不出)挂徽章 ——
|
|
42
|
+
// 「尚未创建」是首次同步前的正常态,不值得占一个徽章位;完整事实在展开区的「工作副本」卡里。
|
|
43
|
+
const workdirKey = channel === 'git' ? workdirStateKey(statusInfo?.workDir) : null
|
|
44
|
+
const workdirKind = workdirStateBadgeKind(workdirKey)
|
|
45
|
+
const showWorkdirBadge = workdirKind === 'warn'
|
|
41
46
|
/** 事实行里的「当前远端」预览:按通道取那个真正标识远端的字段(gist 用 id,没有 URL) */
|
|
42
47
|
const previewRemote = channel === 'webdav'
|
|
43
48
|
? webdavUrl
|
|
@@ -73,13 +78,20 @@ export function SyncChannelEntryCard({ t, channel, statusInfo, remoteReady, repo
|
|
|
73
78
|
{channel === 'gist' && statusInfo?.gist?.secretStored === true && (
|
|
74
79
|
<Badge kind="ok">{t('cloud.tokenSaved')}</Badge>
|
|
75
80
|
)}
|
|
81
|
+
{/* issue #79:改仓库地址后工作副本仍指旧地址(下一轮同步会自动重指向)—— 在卡上直接可见 */}
|
|
82
|
+
{showWorkdirBadge && workdirKey !== null && workdirKind !== null && (
|
|
83
|
+
<Badge kind={workdirKind}>{t(workdirKey)}</Badge>
|
|
84
|
+
)}
|
|
76
85
|
</div>
|
|
77
86
|
{/* 状态事实行(Workbench:配置状态/上次同步/可同步分区——未配置时也要给硬事实) */}
|
|
78
87
|
<div className={css.factGrid} style={{ marginTop: 8 }}>
|
|
79
88
|
<div className={css.factCell}>
|
|
80
89
|
<span className={css.factLabel}>{t('syncStatus.state')}</span>
|
|
81
90
|
<span className={css.factValue}>
|
|
82
|
-
{
|
|
91
|
+
{/* 判据必须与徽章同源(本卡自己的 remoteReady):statusInfo.configured 是**全局**语义
|
|
92
|
+
(任一通道配置过即为 true),照它渲染会让「只配了 git」时另外三张卡同时出现
|
|
93
|
+
「未配置」徽章 +「已配置」事实行。 */}
|
|
94
|
+
{remoteReady ? t('channel.configured') : t('channel.notConfigured')}
|
|
83
95
|
</span>
|
|
84
96
|
</div>
|
|
85
97
|
<div className={css.factCell}>
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
buildAdoptions, confirmListSummary, hasBulkDecidable, keepLocalAll, kindLabel, reviewItems, severityLabel,
|
|
23
23
|
summarizeConfirmItems, useRemoteAll, type BulkDecision, type SyncConflictResolution,
|
|
24
24
|
} from './sync-view.ts'
|
|
25
|
-
import type { ApplyItemsResponse } from './sync-api.ts'
|
|
25
|
+
import type { ApplyItemsResponse, SyncRollbackResponse } from './sync-api.ts'
|
|
26
26
|
import type { TranslateNS } from '../client-types.ts'
|
|
27
27
|
import type { SyncConfirmDecisions } from '../run-store.ts'
|
|
28
28
|
import { compatibilityBadgeKind, compatibilityLevel, type CompatibilityLevel } from '../../ui/import-wizard.ts'
|
|
@@ -86,6 +86,8 @@ export function SyncConfirmView(props: SyncConfirmViewProps): ReactNode {
|
|
|
86
86
|
// 执行阶段:'idle' | 'applying' | 'done' | 'failed' | 'cancelling'
|
|
87
87
|
const [phase, setPhase] = useState<'idle' | 'applying' | 'done' | 'failed' | 'cancelling'>('idle');
|
|
88
88
|
const [applyResult, setApplyResult] = useState<ApplyItemsResponse | null>(null);
|
|
89
|
+
/** 一键回滚的诚实结果:full=false 时留在界面列出未恢复项(绝不只弹一句「已回滚」) */
|
|
90
|
+
const [rollbackResult, setRollbackResult] = useState<SyncRollbackResponse | null>(null);
|
|
89
91
|
const [error, setError] = useState<string | null>(null);
|
|
90
92
|
/** Phase 7 迁移前咨询:远端快照咨询报告(本地 state,非敏感) */
|
|
91
93
|
const [consultReport, setConsultReport] = useState<ConsultReport | null>(null);
|
|
@@ -155,7 +157,7 @@ export function SyncConfirmView(props: SyncConfirmViewProps): ReactNode {
|
|
|
155
157
|
const res = states[it.itemId]?.resolution;
|
|
156
158
|
if (res !== undefined) resolutions.set(it.itemId, res);
|
|
157
159
|
}
|
|
158
|
-
const adoptions = buildAdoptions(items, adoptedMap, resolutions);
|
|
160
|
+
const adoptions = buildAdoptions(items, adoptedMap, resolutions, api.t);
|
|
159
161
|
// T1:一键同步的确认页尚无「中止 / 跳过 / 引导」三选一控件(服务端已支持三态校验),
|
|
160
162
|
// 因此显式传 `guide`:不改变既有写入行为,也避免缺省 abort 把用户挡在门外。
|
|
161
163
|
const result = await api.applyItems({ syncSessionId, adoptions, sessionFormatDisposition: 'guide' });
|
|
@@ -196,13 +198,23 @@ export function SyncConfirmView(props: SyncConfirmViewProps): ReactNode {
|
|
|
196
198
|
setPhase('applying');
|
|
197
199
|
setError(null);
|
|
198
200
|
try {
|
|
199
|
-
await api.rollback({ restoreId });
|
|
201
|
+
const result = await api.rollback({ restoreId });
|
|
202
|
+
const failed = result.failed ?? [];
|
|
200
203
|
setPhase('done');
|
|
201
204
|
setApplyResult(null);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
if (result.full === false) {
|
|
206
|
+
// 诚实性(2026-10):部分回滚**绝不**弹「已回滚」Toast —— 留在卡里列出未恢复项
|
|
207
|
+
// 与人工提示(凭据这类无法自动恢复的项会带 manualHint)。
|
|
208
|
+
setRollbackResult(result);
|
|
209
|
+
toast.warn(t('syncflow.rollbackPartialToast', { n: String(failed.length) }));
|
|
210
|
+
// 回滚没完成 ⇒ 不通知上层清空 lastRestoreId:那个回滚点仍然相关,用户可能想重试/排查。
|
|
211
|
+
} else {
|
|
212
|
+
setRollbackResult(null);
|
|
213
|
+
// R-04:回滚成功改走 Toast(原 ok Banner 只在「回滚成功」这一条路径出现;
|
|
214
|
+
// 操作已完成、用户可离开,不需要停留阅读)。下方「关闭」按钮保留。
|
|
215
|
+
toast.ok(t('syncflow.rollbackDone'));
|
|
216
|
+
onRollbackDone?.();
|
|
217
|
+
}
|
|
206
218
|
} catch (err) {
|
|
207
219
|
setPhase('failed');
|
|
208
220
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -336,6 +348,28 @@ export function SyncConfirmView(props: SyncConfirmViewProps): ReactNode {
|
|
|
336
348
|
)}
|
|
337
349
|
{(phase === 'done' || phase === 'failed') && applyResult === null && (
|
|
338
350
|
<>
|
|
351
|
+
{/* 诚实性(2026-10):回滚未完成(full=false)时逐条列出未恢复项与人工提示,
|
|
352
|
+
绝不用「已回滚」这一句话盖过去。完整回滚仍走 Toast(见 runRollback)。 */}
|
|
353
|
+
{rollbackResult !== null && rollbackResult.full === false && (
|
|
354
|
+
<>
|
|
355
|
+
<Banner kind="error">
|
|
356
|
+
{t('syncflow.rollbackIncomplete', { n: String((rollbackResult.failed ?? []).length) })}
|
|
357
|
+
</Banner>
|
|
358
|
+
{(rollbackResult.failed ?? []).length > 0 && (
|
|
359
|
+
<div>
|
|
360
|
+
<span className={css.fieldLabel}>{t('syncflow.rollbackFailedItems')}</span>
|
|
361
|
+
<ul className={css.warnList}>
|
|
362
|
+
{(rollbackResult.failed ?? []).map((f, i) => (
|
|
363
|
+
<li key={i}>
|
|
364
|
+
{redact(f.item)} —— {redact(f.reason)}
|
|
365
|
+
{f.manualHint !== undefined && f.manualHint !== '' ? t('common.parens', { text: redact(f.manualHint) }) : ''}
|
|
366
|
+
</li>
|
|
367
|
+
))}
|
|
368
|
+
</ul>
|
|
369
|
+
</div>
|
|
370
|
+
)}
|
|
371
|
+
</>
|
|
372
|
+
)}
|
|
339
373
|
{/* R-04:回滚成功的 ok Banner 已改为 Toast(见 runRollback)。此处仅保留收尾「关闭」按钮;
|
|
340
374
|
一并消除了原实现在 runApply 抛错(phase=failed 且 applyResult 仍为 null)时
|
|
341
375
|
误显示「已回滚到应用前」的问题 —— 那种情况下方 error Banner 才是唯一事实。 */}
|
|
@@ -418,7 +452,11 @@ function ApplyResultCard({ result, busy, t, onRollback }: ApplyResultCardProps):
|
|
|
418
452
|
{/* R-05:成功分支已改走 Toast(见 runApply);此处只保留失败 Banner ——
|
|
419
453
|
K-06 要求失败后用户停留本卡阅读 warnings 明细并决定是否回滚,
|
|
420
454
|
自动消失的 Toast 无法承载这个决策入口。 */}
|
|
421
|
-
{!ok &&
|
|
455
|
+
{!ok && (result.rollbackFull === false
|
|
456
|
+
// 诚实性(2026-10):rolledBack=true 但整体回滚未完成时,'syncflow.importFailed'
|
|
457
|
+
//(文案断言「已整体回滚」)是错的 —— 换专用措辞,未恢复项见下方 warnings 列表。
|
|
458
|
+
? <Banner kind="error">{t('syncflow.importRollbackIncomplete')}</Banner>
|
|
459
|
+
: <Banner kind="error">{t('syncflow.importFailed')}</Banner>)}
|
|
422
460
|
{/* issue #56:部分成功必须**留在卡里**(Toast 会消失,而用户需要据此判断
|
|
423
461
|
「我这次到底缺了什么、要不要回滚」)。措辞与事实同强度:不是失败,是 N 项没生效。 */}
|
|
424
462
|
{ok && ineffective.length > 0 && (
|
|
@@ -83,7 +83,7 @@ export function SyncLogList({ api, t }: SyncLogListProps): ReactNode {
|
|
|
83
83
|
|
|
84
84
|
return (
|
|
85
85
|
<Card>
|
|
86
|
-
<SectionTitle title={t('history.title') + '
|
|
86
|
+
<SectionTitle title={t('history.title') + t('common.parens', { text: String(rows.length) })} />
|
|
87
87
|
{/* 头部统计摘要(先扫结论);失败/跳过仅在存在时出现,并给语义色 */}
|
|
88
88
|
<div className={css.statRow} aria-label={t('history.stats.summary')}>
|
|
89
89
|
<Badge kind="info">{t('history.stats.total', { count: String(stats.total) })}</Badge>
|
|
@@ -62,6 +62,7 @@ import type {
|
|
|
62
62
|
import {
|
|
63
63
|
channelMapOf, channelStateMap, computeGithubLoginView, computeSyncButtons, defaultChannelSyncState,
|
|
64
64
|
githubPollMessage, isClientChannel, kindLabel, pullReportView, pushPreviewView, pushReportView, readStoredChannel,
|
|
65
|
+
gistSessionsRiskKey,
|
|
65
66
|
initialSyncSections, normalizeSessionsLimit, severityLabel, syncSectionOptions, writeStoredChannel,
|
|
66
67
|
} from './sync-view.ts'
|
|
67
68
|
import type {
|
|
@@ -87,6 +88,7 @@ import type { ExportPreviewResponse } from '../api.ts'
|
|
|
87
88
|
import { SecurityOptionsCard } from './SecurityOptionsCard.tsx'
|
|
88
89
|
import { DecryptPasswordCard } from './DecryptPasswordCard.tsx'
|
|
89
90
|
import { AutosyncCard } from './AutosyncCard.tsx'
|
|
91
|
+
import { WorkDirCard } from './WorkDirCard.tsx'
|
|
90
92
|
import { Collapse } from '../common/Motion.tsx'
|
|
91
93
|
import { SyncLogList } from './SyncLogList.tsx'
|
|
92
94
|
import { UnreadableSnapshotsBanner } from './UnreadableSnapshotsBanner.tsx'
|
|
@@ -444,7 +446,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
444
446
|
// 未接线的值一律按「无记录」处理并回落 localStorage 记忆 —— 不把未接线通道混进 UI 状态机。
|
|
445
447
|
const remembered: SyncChannel | null = isClientChannel(info.lastSyncChannel) ? info.lastSyncChannel : readStoredChannel()
|
|
446
448
|
// 可同步分区目录回填(host adapters 唯一事实源;仅 portable;两通道共用)
|
|
447
|
-
const catalog = info.syncSections !== undefined ? syncSectionOptions(info.syncSections) : []
|
|
449
|
+
const catalog = info.syncSections !== undefined ? syncSectionOptions(info.syncSections, uiT) : []
|
|
448
450
|
// 每通道回填:优先该通道的持久化配置(syncSelectionByChannel / autosyncByChannel);
|
|
449
451
|
// 无持久化 → 默认模式 + 推荐分区
|
|
450
452
|
const selByCh = info.syncSelectionByChannel
|
|
@@ -505,7 +507,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
505
507
|
})
|
|
506
508
|
} catch (err) {
|
|
507
509
|
// R-20:读取自动同步状态失败(此前写入页面最底部的共享 error Banner,常滚出视野)
|
|
508
|
-
toast.error(
|
|
510
|
+
toast.error(t('toast.autosyncLoadFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
509
511
|
}
|
|
510
512
|
}
|
|
511
513
|
|
|
@@ -547,7 +549,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
547
549
|
} catch (err) {
|
|
548
550
|
// M-18/M-21:拉取远端快照失败原先完全静默(下拉留空),用户无法区分
|
|
549
551
|
// 「远端确实没有快照」与「读取失败」。不阻断主流程,仅以 Toast 如实告知。
|
|
550
|
-
toast.error(
|
|
552
|
+
toast.error(t('toast.snapshotsLoadFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
551
553
|
} finally {
|
|
552
554
|
loadingSnapshotsRef.current[ch] = false
|
|
553
555
|
patchChannelState(ch, { loadingSnapshots: false })
|
|
@@ -664,7 +666,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
664
666
|
}
|
|
665
667
|
} catch (err) {
|
|
666
668
|
// R-20:保存失败**始终**提示(无论手动还是自动)——用户的改动没有落盘
|
|
667
|
-
toast.error(
|
|
669
|
+
toast.error(t('toast.configSaveFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
668
670
|
patch({ savingConfig: false })
|
|
669
671
|
} finally {
|
|
670
672
|
// 保存期间又排入的新改动 → 立即补发(保底,不丢输入)
|
|
@@ -871,7 +873,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
871
873
|
} catch (err) {
|
|
872
874
|
// R-20:预览失败(此前写共享 error Banner,渲染在页面最底部而按钮在中部)
|
|
873
875
|
patch({ busy: null })
|
|
874
|
-
toast.error(
|
|
876
|
+
toast.error(t('toast.pushPreviewFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
875
877
|
}
|
|
876
878
|
}
|
|
877
879
|
|
|
@@ -901,7 +903,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
901
903
|
}
|
|
902
904
|
} catch (err) {
|
|
903
905
|
patch({ busy: null })
|
|
904
|
-
toast.error(
|
|
906
|
+
toast.error(t('toast.pushFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
905
907
|
}
|
|
906
908
|
}
|
|
907
909
|
|
|
@@ -916,11 +918,12 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
916
918
|
const report = await api.pull({ ...payload(), ...decrypt })
|
|
917
919
|
patch({ busy: null, pullReport: report, token: '', webdavPassword: '' })
|
|
918
920
|
patchChannel({ decryptPassword: '' })
|
|
919
|
-
//
|
|
920
|
-
toast.ok(t('toast.pullDone'))
|
|
921
|
+
// 必须按 report.ok 分流(与 runPush 同口径):校验不通过时宿主仍回 HTTP 200 + ok:false
|
|
922
|
+
if (report.ok) toast.ok(t('toast.pullDone'))
|
|
923
|
+
else toast.error(t('toast.pullFailed'))
|
|
921
924
|
} catch (err) {
|
|
922
925
|
patch({ busy: null })
|
|
923
|
-
toast.error(
|
|
926
|
+
toast.error(t('toast.pullFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
924
927
|
}
|
|
925
928
|
}
|
|
926
929
|
|
|
@@ -947,7 +950,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
947
950
|
})
|
|
948
951
|
} catch (err) {
|
|
949
952
|
// R-20/M-22:同步设置持久化失败(此前写共享 error Banner,与刚点的按钮相距整屏)
|
|
950
|
-
toast.error(t('toast.selectionSaveFailed') + '
|
|
953
|
+
toast.error(t('toast.selectionSaveFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
951
954
|
}
|
|
952
955
|
}
|
|
953
956
|
|
|
@@ -1036,7 +1039,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1036
1039
|
if (!session.ok) {
|
|
1037
1040
|
// R-20:一键同步启动失败(宿主明确回报 message,无异常抛出)
|
|
1038
1041
|
patch({ busy: null })
|
|
1039
|
-
toast.error(
|
|
1042
|
+
toast.error(t('toast.syncStartFailed') + t('common.labelSeparator') + redact(session.message ?? t('syncflow.syncFailed')))
|
|
1040
1043
|
return
|
|
1041
1044
|
}
|
|
1042
1045
|
patch({ busy: null, confirmSession: session, confirmDecisions: null, token: '', webdavPassword: '' })
|
|
@@ -1044,7 +1047,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1044
1047
|
void loadSnapshots()
|
|
1045
1048
|
} catch (err) {
|
|
1046
1049
|
patch({ busy: null })
|
|
1047
|
-
toast.error(
|
|
1050
|
+
toast.error(t('toast.syncStartFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
1048
1051
|
}
|
|
1049
1052
|
}
|
|
1050
1053
|
|
|
@@ -1081,7 +1084,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1081
1084
|
toast.ok(t('toast.autosyncUpdated'))
|
|
1082
1085
|
} catch (err) {
|
|
1083
1086
|
// R-20/M-20:失败必须提示 —— 界面是乐观更新,用户会以为开关已生效
|
|
1084
|
-
toast.error(
|
|
1087
|
+
toast.error(t('toast.autosyncUpdateFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
1085
1088
|
}
|
|
1086
1089
|
}
|
|
1087
1090
|
|
|
@@ -1092,7 +1095,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1092
1095
|
patchChannelOf(channel, { autosync: updated, autosyncEnabled: updated.enabled, autosyncInterval: updated.interval })
|
|
1093
1096
|
toast.ok(t('toast.autosyncUpdated'))
|
|
1094
1097
|
} catch (err) {
|
|
1095
|
-
toast.error(
|
|
1098
|
+
toast.error(t('toast.autosyncUpdateFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
|
|
1096
1099
|
}
|
|
1097
1100
|
}
|
|
1098
1101
|
|
|
@@ -1172,9 +1175,9 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1172
1175
|
的断行。标题内联沿用 AboutPanel / EnvironmentPanel 的既有写法(零新增样式)。 */}
|
|
1173
1176
|
<span className={css.groupLabel}>{t('mode.title')} <InfoHint text={t('mode.hint')} label={t('common.infoHint')} /></span>
|
|
1174
1177
|
<span className={css.categoryDesc}>
|
|
1175
|
-
{state.
|
|
1176
|
-
? t('common.loading')
|
|
1177
|
-
|
|
1178
|
+
{state.loadError !== null ? t('load.failed')
|
|
1179
|
+
: state.catalog.length === 0 ? t('common.loading')
|
|
1180
|
+
: t('mode.selectedCount', { n: String(cs.syncSections.length) })}
|
|
1178
1181
|
</span>
|
|
1179
1182
|
<div className={css.actionRowTop}>
|
|
1180
1183
|
<Button onClick={() => { openSectionPickerFor(channel) }} disabled={busy}>
|
|
@@ -1183,6 +1186,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1183
1186
|
<InfoHint text={t('mode.persistHint')} label={t('common.infoHint')} />
|
|
1184
1187
|
</div>
|
|
1185
1188
|
{cs.syncSections.length === 0 && <Banner kind="warn">{t('mode.atLeastOne')}</Banner>}
|
|
1189
|
+
{gistSessionsRiskKey(channel, cs.syncSections) !== null && <Banner kind="warn">{t(gistSessionsRiskKey(channel, cs.syncSections)!)}</Banner>}
|
|
1186
1190
|
</Card>
|
|
1187
1191
|
)}
|
|
1188
1192
|
{ready && (
|
|
@@ -1279,6 +1283,8 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1279
1283
|
onClearSavedPassword={() => { clearSavedDecryptPassword(channel) }}
|
|
1280
1284
|
/>
|
|
1281
1285
|
)}
|
|
1286
|
+
{/* issue #79:工作副本(git 专属;确认弹窗/宿主调用/回执全在 WorkDirCard.tsx) */}
|
|
1287
|
+
{ready && channel === 'git' && <WorkDirCard t={t} api={api} workDir={state.statusInfo?.workDir} busy={busy} onChanged={() => { void loadStatus() }} />}
|
|
1282
1288
|
{/* issue #59:远端快照列表不完整时的常驻告知(push 报成功但列表为空时,这里是唯一线索)。
|
|
1283
1289
|
v3:跟着**它所属的通道**走(此前挂页面级 = 显示的是隐式当前通道的列表)。 */}
|
|
1284
1290
|
{ready && <UnreadableSnapshotsBanner items={cs.unreadableSnapshots} t={t} />}
|
|
@@ -1290,14 +1296,14 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1290
1296
|
/* ------------------------------------------------ 渲染 */
|
|
1291
1297
|
return (
|
|
1292
1298
|
<div className={css.viewBody}>
|
|
1293
|
-
<SectionTitle title={t('section.label')}
|
|
1299
|
+
<SectionTitle title={t('section.label')} info={<InfoHint text={t('section.description')} label={t('common.infoHint')} />} />
|
|
1294
1300
|
|
|
1295
1301
|
{/* M-16:页面级加载失败就地提示(此前 loadError 只写不读,宿主不可达时整页显示
|
|
1296
1302
|
「未配置」假象)。此处保留可就地重试的 ErrorBanner —— 它是**页面是否可用**
|
|
1297
1303
|
的前提条件,必须常驻到用户重试成功,不能交给会自动消失的 Toast。 */}
|
|
1298
1304
|
{state.loadError !== null && (
|
|
1299
1305
|
<ErrorBanner
|
|
1300
|
-
error={
|
|
1306
|
+
error={t('load.failed') + t('common.labelSeparator') + redact(state.loadError)}
|
|
1301
1307
|
onRetry={() => { void loadStatus() }}
|
|
1302
1308
|
retrying={state.loading}
|
|
1303
1309
|
t={api.t}
|
|
@@ -1350,6 +1356,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1350
1356
|
open={sectionPickerOpen}
|
|
1351
1357
|
t={t}
|
|
1352
1358
|
cmT={cmT}
|
|
1359
|
+
uiT={uiT}
|
|
1353
1360
|
sectionName={sectionName}
|
|
1354
1361
|
catalog={state.catalog}
|
|
1355
1362
|
sections={chState.syncSections}
|
|
@@ -1390,7 +1397,7 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1390
1397
|
closeDisabled={state.busy === 'push'}
|
|
1391
1398
|
/>
|
|
1392
1399
|
<Modal.Body scroll>
|
|
1393
|
-
<PushPreviewCard preview={state.pushPreview.preview} t={t} uiT={uiT} />
|
|
1400
|
+
<PushPreviewCard preview={state.pushPreview.preview} t={t} uiT={uiT} sectionName={sectionName} />
|
|
1394
1401
|
</Modal.Body>
|
|
1395
1402
|
<Modal.Footer>
|
|
1396
1403
|
<Button
|
|
@@ -1538,10 +1545,11 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
|
|
|
1538
1545
|
}
|
|
1539
1546
|
|
|
1540
1547
|
/** P0-②:push 预览内容(绑 src/ui/i18n.ts 的 UiT 文案;纯展示,无敏感字段)。 */
|
|
1541
|
-
function PushPreviewCard({ preview, t, uiT }: {
|
|
1548
|
+
function PushPreviewCard({ preview, t, uiT, sectionName }: {
|
|
1542
1549
|
preview: SyncPushPreview | null
|
|
1543
1550
|
t: TranslateNS<'config-manager-sync'>
|
|
1544
1551
|
uiT: UiT
|
|
1552
|
+
sectionName: (id: SectionId) => string
|
|
1545
1553
|
}) {
|
|
1546
1554
|
const view = pushPreviewView(preview, uiT)
|
|
1547
1555
|
if (view === null) return null
|
|
@@ -1561,8 +1569,8 @@ function PushPreviewCard({ preview, t, uiT }: {
|
|
|
1561
1569
|
<ul className={css.reportList}>
|
|
1562
1570
|
{view.rows.map((row) => (
|
|
1563
1571
|
<li key={row.section}>
|
|
1564
|
-
<span className={css.kindTag}>{row.changed ? '
|
|
1565
|
-
{' '}{row.section} · {row.count}
|
|
1572
|
+
<span className={css.kindTag}>{row.changed ? t('syncflow.pushChanged') : t('syncflow.pushUnchanged')}</span>
|
|
1573
|
+
{' '}{sectionName(row.section)} · {row.count}
|
|
1566
1574
|
</li>
|
|
1567
1575
|
))}
|
|
1568
1576
|
</ul>
|
|
@@ -123,7 +123,7 @@ export function SyncRepositoryPicker({ open, api, t, repoUrl, busy, onPick }: Sy
|
|
|
123
123
|
} catch (err) {
|
|
124
124
|
const detail = err instanceof Error ? err.message : String(err)
|
|
125
125
|
setCreateError(detail)
|
|
126
|
-
toast.error(t('repoPicker.createFailed') + '
|
|
126
|
+
toast.error(t('repoPicker.createFailed') + t('common.labelSeparator') + redact(detail))
|
|
127
127
|
} finally {
|
|
128
128
|
setCreating(false)
|
|
129
129
|
}
|
|
@@ -166,12 +166,12 @@ export function SyncRepositoryPicker({ open, api, t, repoUrl, busy, onPick }: Sy
|
|
|
166
166
|
{/* 空态 / 失败说明留在原地(不进 ⓘ):用户正卡在这里,说明必须看得见 */}
|
|
167
167
|
{loaded && privateRepos.length === 0 && <span className={css.hint}>{t('repoPicker.empty')}</span>}
|
|
168
168
|
{loadError !== null && (
|
|
169
|
-
<span className={css.formError}>{t('repoPicker.loadFailed')}
|
|
169
|
+
<span className={css.formError}>{t('repoPicker.loadFailed')}{t('common.parens', { text: redact(loadError) })}</span>
|
|
170
170
|
)}
|
|
171
171
|
</div>
|
|
172
172
|
{createOpen && (
|
|
173
173
|
<div className={css.field}>
|
|
174
|
-
<span className={css.groupLabel}>{t('repoPicker.createTitle')}
|
|
174
|
+
<span className={css.groupLabel}>{t('repoPicker.createTitle')} <InfoHint text={t('repoPicker.privateOnly')} label={t('common.infoHint')} /></span>
|
|
175
175
|
<label className={css.field}>
|
|
176
176
|
<span className={css.fieldLabel}>{t('repoPicker.name')}</span>
|
|
177
177
|
<input
|
|
@@ -197,8 +197,6 @@ export function SyncRepositoryPicker({ open, api, t, repoUrl, busy, onPick }: Sy
|
|
|
197
197
|
onChange={(e: ChangeEvent<HTMLInputElement>) => { setDescription(e.target.value) }}
|
|
198
198
|
/>
|
|
199
199
|
</label>
|
|
200
|
-
{/* 安全说明常驻可见(不进 ⓘ):新建的仓库恒为私有 */}
|
|
201
|
-
<span className={css.hint}>{t('repoPicker.privateOnly')}</span>
|
|
202
200
|
{nameHint !== null && <span className={css.formError}>{nameHint}</span>}
|
|
203
201
|
{createError !== null && <span className={css.formError}>{redact(createError)}</span>}
|
|
204
202
|
<div className={css.actionRowTop}>
|