@lesliechan721/aw 0.1.0-beta.3
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/dist/authoring/assets/aw-config.d.mts +2 -0
- package/dist/authoring/assets/aw-config.mjs +556 -0
- package/dist/authoring/assets/aw-config.mjs.map +1 -0
- package/dist/authoring/scripts/add-skill.d.mts +2 -0
- package/dist/authoring/scripts/add-skill.mjs +355 -0
- package/dist/authoring/scripts/add-skill.mjs.map +1 -0
- package/dist/authoring/scripts/check-plugin-versions.d.mts +2 -0
- package/dist/authoring/scripts/check-plugin-versions.mjs +5 -0
- package/dist/authoring/scripts/check-plugin-versions.mjs.map +1 -0
- package/dist/authoring/scripts/init-plugin.d.mts +2 -0
- package/dist/authoring/scripts/init-plugin.mjs +512 -0
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -0
- package/dist/authoring/scripts/lib/javascript-import-scanner.d.mts +4 -0
- package/dist/authoring/scripts/lib/javascript-import-scanner.mjs +261 -0
- package/dist/authoring/scripts/lib/javascript-import-scanner.mjs.map +1 -0
- package/dist/authoring/scripts/lib/plugin-root.d.mts +26 -0
- package/dist/authoring/scripts/lib/plugin-root.mjs +125 -0
- package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -0
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +55 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +777 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -0
- package/dist/authoring/scripts/lib/semver.d.mts +9 -0
- package/dist/authoring/scripts/lib/semver.mjs +63 -0
- package/dist/authoring/scripts/lib/semver.mjs.map +1 -0
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +124 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +374 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -0
- package/dist/authoring/scripts/plugin-status.d.mts +2 -0
- package/dist/authoring/scripts/plugin-status.mjs +96 -0
- package/dist/authoring/scripts/plugin-status.mjs.map +1 -0
- package/dist/authoring/scripts/validate-plugin.d.mts +2 -0
- package/dist/authoring/scripts/validate-plugin.mjs +4442 -0
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -0
- package/dist/authoring/scripts/validate-skill.d.mts +2 -0
- package/dist/authoring/scripts/validate-skill.mjs +126 -0
- package/dist/authoring/scripts/validate-skill.mjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/scripts/aw-skill-eval-engine.d.ts +13 -0
- package/dist/scripts/aw-skill-eval-engine.js +31 -0
- package/dist/scripts/aw-skill-eval-engine.js.map +1 -0
- package/dist/scripts/bundle-plugins.d.ts +18 -0
- package/dist/scripts/bundle-plugins.js +123 -0
- package/dist/scripts/bundle-plugins.js.map +1 -0
- package/dist/scripts/codex-eval-app-server.d.ts +89 -0
- package/dist/scripts/codex-eval-app-server.js +1228 -0
- package/dist/scripts/codex-eval-app-server.js.map +1 -0
- package/dist/scripts/codex-eval-isolation.d.ts +61 -0
- package/dist/scripts/codex-eval-isolation.js +318 -0
- package/dist/scripts/codex-eval-isolation.js.map +1 -0
- package/dist/scripts/codex-eval-process-management.d.ts +8 -0
- package/dist/scripts/codex-eval-process-management.js +69 -0
- package/dist/scripts/codex-eval-process-management.js.map +1 -0
- package/dist/scripts/codex-eval-process-tree.d.ts +38 -0
- package/dist/scripts/codex-eval-process-tree.js +803 -0
- package/dist/scripts/codex-eval-process-tree.js.map +1 -0
- package/dist/scripts/codex-eval-runtime-config.d.ts +62 -0
- package/dist/scripts/codex-eval-runtime-config.js +843 -0
- package/dist/scripts/codex-eval-runtime-config.js.map +1 -0
- package/dist/scripts/codex-trigger-eval.d.ts +84 -0
- package/dist/scripts/codex-trigger-eval.js +968 -0
- package/dist/scripts/codex-trigger-eval.js.map +1 -0
- package/dist/scripts/eval-plugin-skills-audited.d.ts +6 -0
- package/dist/scripts/eval-plugin-skills-audited.js +261 -0
- package/dist/scripts/eval-plugin-skills-audited.js.map +1 -0
- package/dist/scripts/eval-plugin-skills.d.ts +45 -0
- package/dist/scripts/eval-plugin-skills.js +1586 -0
- package/dist/scripts/eval-plugin-skills.js.map +1 -0
- package/dist/scripts/eval-run-layout.d.ts +17 -0
- package/dist/scripts/eval-run-layout.js +26 -0
- package/dist/scripts/eval-run-layout.js.map +1 -0
- package/dist/scripts/plugin-eval-contract.d.ts +185 -0
- package/dist/scripts/plugin-eval-contract.js +695 -0
- package/dist/scripts/plugin-eval-contract.js.map +1 -0
- package/dist/scripts/plugin-eval-materializer.d.ts +13 -0
- package/dist/scripts/plugin-eval-materializer.js +421 -0
- package/dist/scripts/plugin-eval-materializer.js.map +1 -0
- package/dist/scripts/plugin-staging.d.ts +43 -0
- package/dist/scripts/plugin-staging.js +196 -0
- package/dist/scripts/plugin-staging.js.map +1 -0
- package/dist/scripts/skill-reporting.d.ts +212 -0
- package/dist/scripts/skill-reporting.js +922 -0
- package/dist/scripts/skill-reporting.js.map +1 -0
- package/dist/scripts/test-plugin-scripts.d.ts +7 -0
- package/dist/scripts/test-plugin-scripts.js +674 -0
- package/dist/scripts/test-plugin-scripts.js.map +1 -0
- package/dist/src/adapters/helpers.d.ts +44 -0
- package/dist/src/adapters/helpers.js +95 -0
- package/dist/src/adapters/helpers.js.map +1 -0
- package/dist/src/authoring/workspace-commands.d.ts +46 -0
- package/dist/src/authoring/workspace-commands.js +133 -0
- package/dist/src/authoring/workspace-commands.js.map +1 -0
- package/dist/src/commands/catalog-publish.d.ts +33 -0
- package/dist/src/commands/catalog-publish.js +374 -0
- package/dist/src/commands/catalog-publish.js.map +1 -0
- package/dist/src/commands/catalog-source.d.ts +111 -0
- package/dist/src/commands/catalog-source.js +266 -0
- package/dist/src/commands/catalog-source.js.map +1 -0
- package/dist/src/commands/console.d.ts +3 -0
- package/dist/src/commands/console.js +44 -0
- package/dist/src/commands/console.js.map +1 -0
- package/dist/src/commands/dependency-operation-plan.d.ts +28 -0
- package/dist/src/commands/dependency-operation-plan.js +15 -0
- package/dist/src/commands/dependency-operation-plan.js.map +1 -0
- package/dist/src/commands/init.d.ts +31 -0
- package/dist/src/commands/init.js +63 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/options.d.ts +68 -0
- package/dist/src/commands/options.js +146 -0
- package/dist/src/commands/options.js.map +1 -0
- package/dist/src/commands/rebuild-command.d.ts +77 -0
- package/dist/src/commands/rebuild-command.js +1681 -0
- package/dist/src/commands/rebuild-command.js.map +1 -0
- package/dist/src/commands/rebuild-integration.d.ts +94 -0
- package/dist/src/commands/rebuild-integration.js +3289 -0
- package/dist/src/commands/rebuild-integration.js.map +1 -0
- package/dist/src/commands/rebuild-operations.d.ts +110 -0
- package/dist/src/commands/rebuild-operations.js +791 -0
- package/dist/src/commands/rebuild-operations.js.map +1 -0
- package/dist/src/commands/review-publication.d.ts +99 -0
- package/dist/src/commands/review-publication.js +904 -0
- package/dist/src/commands/review-publication.js.map +1 -0
- package/dist/src/commands/runtime.d.ts +176 -0
- package/dist/src/commands/runtime.js +746 -0
- package/dist/src/commands/runtime.js.map +1 -0
- package/dist/src/commands/status-v4.d.ts +102 -0
- package/dist/src/commands/status-v4.js +844 -0
- package/dist/src/commands/status-v4.js.map +1 -0
- package/dist/src/commands/status.d.ts +93 -0
- package/dist/src/commands/status.js +442 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +57 -0
- package/dist/src/commands/wizard.js +58 -0
- package/dist/src/commands/wizard.js.map +1 -0
- package/dist/src/commands/write-commands.d.ts +7 -0
- package/dist/src/commands/write-commands.js +167 -0
- package/dist/src/commands/write-commands.js.map +1 -0
- package/dist/src/constants.d.ts +25 -0
- package/dist/src/constants.js +26 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/generators/agents.d.ts +3 -0
- package/dist/src/generators/agents.js +46 -0
- package/dist/src/generators/agents.js.map +1 -0
- package/dist/src/generators/hooks.d.ts +15 -0
- package/dist/src/generators/hooks.js +76 -0
- package/dist/src/generators/hooks.js.map +1 -0
- package/dist/src/generators/instructions.d.ts +13 -0
- package/dist/src/generators/instructions.js +66 -0
- package/dist/src/generators/instructions.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +53 -0
- package/dist/src/generators/local-plugin.js +1268 -0
- package/dist/src/generators/local-plugin.js.map +1 -0
- package/dist/src/generators/permissions.d.ts +10 -0
- package/dist/src/generators/permissions.js +37 -0
- package/dist/src/generators/permissions.js.map +1 -0
- package/dist/src/generators/shared.d.ts +2 -0
- package/dist/src/generators/shared.js +15 -0
- package/dist/src/generators/shared.js.map +1 -0
- package/dist/src/index.d.ts +35 -0
- package/dist/src/index.js +2344 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/output/command-receipt.d.ts +90 -0
- package/dist/src/output/command-receipt.js +129 -0
- package/dist/src/output/command-receipt.js.map +1 -0
- package/dist/src/output/debug-log.d.ts +4 -0
- package/dist/src/output/debug-log.js +97 -0
- package/dist/src/output/debug-log.js.map +1 -0
- package/dist/src/output/equivalent-command.d.ts +9 -0
- package/dist/src/output/equivalent-command.js +39 -0
- package/dist/src/output/equivalent-command.js.map +1 -0
- package/dist/src/output/model.d.ts +117 -0
- package/dist/src/output/model.js +145 -0
- package/dist/src/output/model.js.map +1 -0
- package/dist/src/output/rebuild-progress.d.ts +21 -0
- package/dist/src/output/rebuild-progress.js +138 -0
- package/dist/src/output/rebuild-progress.js.map +1 -0
- package/dist/src/output/redact.d.ts +3 -0
- package/dist/src/output/redact.js +39 -0
- package/dist/src/output/redact.js.map +1 -0
- package/dist/src/output/reporter.d.ts +9 -0
- package/dist/src/output/reporter.js +133 -0
- package/dist/src/output/reporter.js.map +1 -0
- package/dist/src/portable/commands.d.ts +120 -0
- package/dist/src/portable/commands.js +506 -0
- package/dist/src/portable/commands.js.map +1 -0
- package/dist/src/portable/materializer.d.ts +50 -0
- package/dist/src/portable/materializer.js +408 -0
- package/dist/src/portable/materializer.js.map +1 -0
- package/dist/src/portable/skills-cli.d.ts +43 -0
- package/dist/src/portable/skills-cli.js +148 -0
- package/dist/src/portable/skills-cli.js.map +1 -0
- package/dist/src/tui/components.d.ts +32 -0
- package/dist/src/tui/components.js +42 -0
- package/dist/src/tui/components.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +20 -0
- package/dist/src/tui/console-app-v4.js +2613 -0
- package/dist/src/tui/console-app-v4.js.map +1 -0
- package/dist/src/tui/controller-runtime.d.ts +11 -0
- package/dist/src/tui/controller-runtime.js +902 -0
- package/dist/src/tui/controller-runtime.js.map +1 -0
- package/dist/src/tui/controller.d.ts +367 -0
- package/dist/src/tui/controller.js +1724 -0
- package/dist/src/tui/controller.js.map +1 -0
- package/dist/src/tui/index.d.ts +13 -0
- package/dist/src/tui/index.js +14 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/keys.d.ts +12 -0
- package/dist/src/tui/keys.js +22 -0
- package/dist/src/tui/keys.js.map +1 -0
- package/dist/src/tui/layout.d.ts +12 -0
- package/dist/src/tui/layout.js +19 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/model.d.ts +279 -0
- package/dist/src/tui/model.js +652 -0
- package/dist/src/tui/model.js.map +1 -0
- package/dist/src/tui/prompt.d.ts +8 -0
- package/dist/src/tui/prompt.js +242 -0
- package/dist/src/tui/prompt.js.map +1 -0
- package/dist/src/tui/router.d.ts +153 -0
- package/dist/src/tui/router.js +102 -0
- package/dist/src/tui/router.js.map +1 -0
- package/dist/src/tui/runner.d.ts +11 -0
- package/dist/src/tui/runner.js +71 -0
- package/dist/src/tui/runner.js.map +1 -0
- package/dist/src/tui/theme.d.ts +18 -0
- package/dist/src/tui/theme.js +20 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/views.d.ts +57 -0
- package/dist/src/tui/views.js +173 -0
- package/dist/src/tui/views.js.map +1 -0
- package/dist/src/tui/wizard.d.ts +11 -0
- package/dist/src/tui/wizard.js +321 -0
- package/dist/src/tui/wizard.js.map +1 -0
- package/dist/src/types.d.ts +313 -0
- package/dist/src/types.js +33 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/active-marketplace-runtime-lock.d.ts +12 -0
- package/dist/src/utils/active-marketplace-runtime-lock.js +133 -0
- package/dist/src/utils/active-marketplace-runtime-lock.js.map +1 -0
- package/dist/src/utils/agent-version.d.ts +13 -0
- package/dist/src/utils/agent-version.js +172 -0
- package/dist/src/utils/agent-version.js.map +1 -0
- package/dist/src/utils/artifact-plan.d.ts +26 -0
- package/dist/src/utils/artifact-plan.js +444 -0
- package/dist/src/utils/artifact-plan.js.map +1 -0
- package/dist/src/utils/artifact-renderer.d.ts +124 -0
- package/dist/src/utils/artifact-renderer.js +442 -0
- package/dist/src/utils/artifact-renderer.js.map +1 -0
- package/dist/src/utils/artifact-sources.d.ts +8 -0
- package/dist/src/utils/artifact-sources.js +10 -0
- package/dist/src/utils/artifact-sources.js.map +1 -0
- package/dist/src/utils/aw-home-mutation-lock.d.ts +10 -0
- package/dist/src/utils/aw-home-mutation-lock.js +55 -0
- package/dist/src/utils/aw-home-mutation-lock.js.map +1 -0
- package/dist/src/utils/bundle-integrity.d.ts +3 -0
- package/dist/src/utils/bundle-integrity.js +88 -0
- package/dist/src/utils/bundle-integrity.js.map +1 -0
- package/dist/src/utils/bundle-layout.d.ts +4 -0
- package/dist/src/utils/bundle-layout.js +546 -0
- package/dist/src/utils/bundle-layout.js.map +1 -0
- package/dist/src/utils/catalog-digests.d.ts +8 -0
- package/dist/src/utils/catalog-digests.js +108 -0
- package/dist/src/utils/catalog-digests.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v3.d.ts +2 -0
- package/dist/src/utils/catalog-manifest-v3.js +3 -0
- package/dist/src/utils/catalog-manifest-v3.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v4.d.ts +5 -0
- package/dist/src/utils/catalog-manifest-v4.js +106 -0
- package/dist/src/utils/catalog-manifest-v4.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +15 -0
- package/dist/src/utils/catalog-public-error.js +84 -0
- package/dist/src/utils/catalog-public-error.js.map +1 -0
- package/dist/src/utils/catalog-runtime-projection.d.ts +36 -0
- package/dist/src/utils/catalog-runtime-projection.js +172 -0
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -0
- package/dist/src/utils/catalog-snapshot.d.ts +8 -0
- package/dist/src/utils/catalog-snapshot.js +12 -0
- package/dist/src/utils/catalog-snapshot.js.map +1 -0
- package/dist/src/utils/catalog-source-cache.d.ts +61 -0
- package/dist/src/utils/catalog-source-cache.js +276 -0
- package/dist/src/utils/catalog-source-cache.js.map +1 -0
- package/dist/src/utils/catalog-source-identity.d.ts +4 -0
- package/dist/src/utils/catalog-source-identity.js +118 -0
- package/dist/src/utils/catalog-source-identity.js.map +1 -0
- package/dist/src/utils/catalog-source-presentation.d.ts +1 -0
- package/dist/src/utils/catalog-source-presentation.js +32 -0
- package/dist/src/utils/catalog-source-presentation.js.map +1 -0
- package/dist/src/utils/catalog-source-references.d.ts +65 -0
- package/dist/src/utils/catalog-source-references.js +1418 -0
- package/dist/src/utils/catalog-source-references.js.map +1 -0
- package/dist/src/utils/catalog-sources.d.ts +39 -0
- package/dist/src/utils/catalog-sources.js +293 -0
- package/dist/src/utils/catalog-sources.js.map +1 -0
- package/dist/src/utils/codex-config.d.ts +124 -0
- package/dist/src/utils/codex-config.js +1011 -0
- package/dist/src/utils/codex-config.js.map +1 -0
- package/dist/src/utils/codex-project-surface.d.ts +3 -0
- package/dist/src/utils/codex-project-surface.js +49 -0
- package/dist/src/utils/codex-project-surface.js.map +1 -0
- package/dist/src/utils/codex-roles.d.ts +71 -0
- package/dist/src/utils/codex-roles.js +257 -0
- package/dist/src/utils/codex-roles.js.map +1 -0
- package/dist/src/utils/codex-skill-staging.d.ts +9 -0
- package/dist/src/utils/codex-skill-staging.js +8 -0
- package/dist/src/utils/codex-skill-staging.js.map +1 -0
- package/dist/src/utils/config-value.d.ts +3 -0
- package/dist/src/utils/config-value.js +23 -0
- package/dist/src/utils/config-value.js.map +1 -0
- package/dist/src/utils/dynamic-skill-tree.d.ts +32 -0
- package/dist/src/utils/dynamic-skill-tree.js +436 -0
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -0
- package/dist/src/utils/file-rules.d.ts +1 -0
- package/dist/src/utils/file-rules.js +2 -0
- package/dist/src/utils/file-rules.js.map +1 -0
- package/dist/src/utils/git-catalog-provider.d.ts +7 -0
- package/dist/src/utils/git-catalog-provider.js +510 -0
- package/dist/src/utils/git-catalog-provider.js.map +1 -0
- package/dist/src/utils/hooks-contract.d.ts +1 -0
- package/dist/src/utils/hooks-contract.js +2 -0
- package/dist/src/utils/hooks-contract.js.map +1 -0
- package/dist/src/utils/json.d.ts +4 -0
- package/dist/src/utils/json.js +29 -0
- package/dist/src/utils/json.js.map +1 -0
- package/dist/src/utils/lifecycle-active-store.d.ts +175 -0
- package/dist/src/utils/lifecycle-active-store.js +1276 -0
- package/dist/src/utils/lifecycle-active-store.js.map +1 -0
- package/dist/src/utils/local-marketplace.d.ts +41 -0
- package/dist/src/utils/local-marketplace.js +317 -0
- package/dist/src/utils/local-marketplace.js.map +1 -0
- package/dist/src/utils/manifest.d.ts +14 -0
- package/dist/src/utils/manifest.js +36 -0
- package/dist/src/utils/manifest.js.map +1 -0
- package/dist/src/utils/marketplace.d.ts +3 -0
- package/dist/src/utils/marketplace.js +76 -0
- package/dist/src/utils/marketplace.js.map +1 -0
- package/dist/src/utils/multi-source-catalog.d.ts +72 -0
- package/dist/src/utils/multi-source-catalog.js +402 -0
- package/dist/src/utils/multi-source-catalog.js.map +1 -0
- package/dist/src/utils/native-cli.d.ts +75 -0
- package/dist/src/utils/native-cli.js +435 -0
- package/dist/src/utils/native-cli.js.map +1 -0
- package/dist/src/utils/orphan-marketplace-cleanup.d.ts +18 -0
- package/dist/src/utils/orphan-marketplace-cleanup.js +202 -0
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -0
- package/dist/src/utils/ownership.d.ts +130 -0
- package/dist/src/utils/ownership.js +766 -0
- package/dist/src/utils/ownership.js.map +1 -0
- package/dist/src/utils/path-catalog-provider.d.ts +7 -0
- package/dist/src/utils/path-catalog-provider.js +87 -0
- package/dist/src/utils/path-catalog-provider.js.map +1 -0
- package/dist/src/utils/paths.d.ts +41 -0
- package/dist/src/utils/paths.js +138 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/dist/src/utils/platform-entries.d.ts +1 -0
- package/dist/src/utils/platform-entries.js +2 -0
- package/dist/src/utils/platform-entries.js.map +1 -0
- package/dist/src/utils/platform-support.d.ts +1 -0
- package/dist/src/utils/platform-support.js +2 -0
- package/dist/src/utils/platform-support.js.map +1 -0
- package/dist/src/utils/plugin-authoring-capabilities.d.ts +23 -0
- package/dist/src/utils/plugin-authoring-capabilities.js +37 -0
- package/dist/src/utils/plugin-authoring-capabilities.js.map +1 -0
- package/dist/src/utils/plugin-authoring-capabilities.json +92 -0
- package/dist/src/utils/plugin-config-write.d.ts +107 -0
- package/dist/src/utils/plugin-config-write.js +565 -0
- package/dist/src/utils/plugin-config-write.js.map +1 -0
- package/dist/src/utils/plugin-config.d.ts +396 -0
- package/dist/src/utils/plugin-config.js +1898 -0
- package/dist/src/utils/plugin-config.js.map +1 -0
- package/dist/src/utils/plugin-dependencies.d.ts +12 -0
- package/dist/src/utils/plugin-dependencies.js +162 -0
- package/dist/src/utils/plugin-dependencies.js.map +1 -0
- package/dist/src/utils/plugin-release.d.ts +2 -0
- package/dist/src/utils/plugin-release.js +2 -0
- package/dist/src/utils/plugin-release.js.map +1 -0
- package/dist/src/utils/plugin-script-contract.d.ts +1 -0
- package/dist/src/utils/plugin-script-contract.js +2 -0
- package/dist/src/utils/plugin-script-contract.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +172 -0
- package/dist/src/utils/plugin-scripts.js +585 -0
- package/dist/src/utils/plugin-scripts.js.map +1 -0
- package/dist/src/utils/process-lock.d.ts +47 -0
- package/dist/src/utils/process-lock.js +532 -0
- package/dist/src/utils/process-lock.js.map +1 -0
- package/dist/src/utils/process-tree.d.ts +2 -0
- package/dist/src/utils/process-tree.js +2 -0
- package/dist/src/utils/process-tree.js.map +1 -0
- package/dist/src/utils/project-boundary.d.ts +21 -0
- package/dist/src/utils/project-boundary.js +95 -0
- package/dist/src/utils/project-boundary.js.map +1 -0
- package/dist/src/utils/rendered-contract.d.ts +8 -0
- package/dist/src/utils/rendered-contract.js +134 -0
- package/dist/src/utils/rendered-contract.js.map +1 -0
- package/dist/src/utils/runtime-skill-closure.d.ts +13 -0
- package/dist/src/utils/runtime-skill-closure.js +863 -0
- package/dist/src/utils/runtime-skill-closure.js.map +1 -0
- package/dist/src/utils/skill-authoring.d.ts +1 -0
- package/dist/src/utils/skill-authoring.js +2 -0
- package/dist/src/utils/skill-authoring.js.map +1 -0
- package/dist/src/utils/skill-frontmatter-capabilities.d.ts +201 -0
- package/dist/src/utils/skill-frontmatter-capabilities.js +5 -0
- package/dist/src/utils/skill-frontmatter-capabilities.js.map +1 -0
- package/dist/src/utils/skill-frontmatter-capabilities.json +200 -0
- package/dist/src/utils/skill-validation.d.ts +37 -0
- package/dist/src/utils/skill-validation.js +326 -0
- package/dist/src/utils/skill-validation.js.map +1 -0
- package/dist/src/utils/slash-commands.d.ts +1 -0
- package/dist/src/utils/slash-commands.js +2 -0
- package/dist/src/utils/slash-commands.js.map +1 -0
- package/dist/src/utils/state-paths.d.ts +13 -0
- package/dist/src/utils/state-paths.js +129 -0
- package/dist/src/utils/state-paths.js.map +1 -0
- package/dist/src/utils/target-eligibility.d.ts +7 -0
- package/dist/src/utils/target-eligibility.js +26 -0
- package/dist/src/utils/target-eligibility.js.map +1 -0
- package/dist/src/utils/target-order.d.ts +7 -0
- package/dist/src/utils/target-order.js +10 -0
- package/dist/src/utils/target-order.js.map +1 -0
- package/dist/src/utils/template-engine.d.ts +1 -0
- package/dist/src/utils/template-engine.js +2 -0
- package/dist/src/utils/template-engine.js.map +1 -0
- package/dist/src/utils/test-path-fence.d.ts +12 -0
- package/dist/src/utils/test-path-fence.js +31 -0
- package/dist/src/utils/test-path-fence.js.map +1 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +16 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,2613 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput, useWindowSize } from 'ink';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { CATALOG_SOURCE_ID_PATTERN } from '../utils/catalog-sources.js';
|
|
6
|
+
import { canonicalizeCatalogGitUrl } from '../utils/catalog-source-identity.js';
|
|
7
|
+
import { normalizePortableAgents } from '../portable/commands.js';
|
|
8
|
+
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
9
|
+
import { ScreenFrame } from './components.js';
|
|
10
|
+
import { extractPortableError, userIssueKey } from './controller.js';
|
|
11
|
+
import { resolveTuiSizePolicy } from './layout.js';
|
|
12
|
+
import { hasInstalledPlacement } from './model.js';
|
|
13
|
+
import { tuiTheme } from './theme.js';
|
|
14
|
+
export const CONSOLE_V4_SCREEN_IDS = [
|
|
15
|
+
'G01', 'G02', 'G03', 'G04', 'G05',
|
|
16
|
+
'P01', 'P02', 'P03', 'P04', 'P05', 'P06', 'P07', 'P08', 'P09', 'P10',
|
|
17
|
+
'P11', 'P12', 'P13', 'P14', 'P15', 'P16', 'P17', 'P18', 'P19', 'P20', 'P21',
|
|
18
|
+
'C01', 'C02', 'C03', 'C04', 'C05', 'C06', 'C07', 'C08', 'C09', 'C10',
|
|
19
|
+
'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17',
|
|
20
|
+
'S01', 'S02', 'S03', 'S04', 'S05', 'S06', 'S07', 'S08', 'S09',
|
|
21
|
+
'S10', 'S11', 'S12', 'S13', 'S14', 'S15', 'S16', 'S17', 'S18',
|
|
22
|
+
'I01', 'I02', 'I03', 'I04', 'I05', 'I06', 'I07', 'I08', 'I09',
|
|
23
|
+
'I10', 'I11', 'I12', 'I13',
|
|
24
|
+
'D01', 'D02', 'D03', 'D04', 'D05', 'D06', 'D07', 'D08', 'D09',
|
|
25
|
+
'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17',
|
|
26
|
+
'U01', 'U02', 'U03', 'U04', 'U05', 'U06', 'U07', 'U08', 'U09', 'U10', 'U11',
|
|
27
|
+
];
|
|
28
|
+
function configOwnerKey(route) {
|
|
29
|
+
return `${route.pluginId}\0${route.layer.scope}\0${route.layer.projectLayer}`;
|
|
30
|
+
}
|
|
31
|
+
function configPageMatches(page, route) {
|
|
32
|
+
return Boolean(page && route
|
|
33
|
+
&& page.pluginId === route.pluginId
|
|
34
|
+
&& page.layer.scope === route.layer.scope
|
|
35
|
+
&& page.layer.projectLayer === route.layer.projectLayer);
|
|
36
|
+
}
|
|
37
|
+
function platformLabel(platform) {
|
|
38
|
+
return platform === 'codex' ? 'Codex' : 'Claude Code';
|
|
39
|
+
}
|
|
40
|
+
function partialRetryAction(operationKind, originalAction, targetState) {
|
|
41
|
+
if (!targetState)
|
|
42
|
+
return undefined;
|
|
43
|
+
if (operationKind === 'configSave' || operationKind === 'configReset') {
|
|
44
|
+
return targetState === 'absent' ? undefined : 'reinstall';
|
|
45
|
+
}
|
|
46
|
+
if (originalAction === 'uninstall') {
|
|
47
|
+
return targetState === 'present' || targetState === 'partial' ? 'uninstall' : undefined;
|
|
48
|
+
}
|
|
49
|
+
if (originalAction === 'cleanup')
|
|
50
|
+
return targetState === 'absent' ? undefined : 'cleanup';
|
|
51
|
+
if (targetState === 'absent')
|
|
52
|
+
return 'install';
|
|
53
|
+
if (targetState === 'partial')
|
|
54
|
+
return 'reinstall';
|
|
55
|
+
if (targetState === 'error')
|
|
56
|
+
return undefined;
|
|
57
|
+
return originalAction === 'upgrade' ? 'upgrade' : 'reinstall';
|
|
58
|
+
}
|
|
59
|
+
function scopeLabel(scope) {
|
|
60
|
+
return scope === 'user' ? '所有项目' : '当前项目';
|
|
61
|
+
}
|
|
62
|
+
function statusText(plugin) {
|
|
63
|
+
return {
|
|
64
|
+
available: '未添加',
|
|
65
|
+
installed: '可用',
|
|
66
|
+
needsAttention: '需要处理',
|
|
67
|
+
unavailable: '来源不可用',
|
|
68
|
+
}[plugin.aggregateStatus];
|
|
69
|
+
}
|
|
70
|
+
function statusColor(status) {
|
|
71
|
+
if (status === 'success')
|
|
72
|
+
return tuiTheme.statusColors.success;
|
|
73
|
+
if (status === 'warning')
|
|
74
|
+
return tuiTheme.statusColors.warning;
|
|
75
|
+
if (status === 'error')
|
|
76
|
+
return tuiTheme.statusColors.error;
|
|
77
|
+
if (status === 'info')
|
|
78
|
+
return tuiTheme.statusColors.info;
|
|
79
|
+
if (status === 'muted')
|
|
80
|
+
return tuiTheme.colors.muted;
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function moveFocus(actions, current, direction) {
|
|
84
|
+
if (actions.length === 0)
|
|
85
|
+
return 0;
|
|
86
|
+
for (let offset = 1; offset <= actions.length; offset += 1) {
|
|
87
|
+
const next = (current + direction * offset + actions.length) % actions.length;
|
|
88
|
+
if (!actions[next]?.disabled)
|
|
89
|
+
return next;
|
|
90
|
+
}
|
|
91
|
+
return current;
|
|
92
|
+
}
|
|
93
|
+
function actionLimitForLineBudget(actions, lineBudget) {
|
|
94
|
+
let used = 0;
|
|
95
|
+
let count = 0;
|
|
96
|
+
for (const action of actions) {
|
|
97
|
+
const height = action.description || action.disabled ? 2 : 1;
|
|
98
|
+
if (count > 0 && used + height > lineBudget)
|
|
99
|
+
break;
|
|
100
|
+
used += height;
|
|
101
|
+
count += 1;
|
|
102
|
+
}
|
|
103
|
+
return Math.max(1, count);
|
|
104
|
+
}
|
|
105
|
+
function lineBudgetViewport(actions, focus, requestedOffset, lineBudget, groupKeys) {
|
|
106
|
+
const height = (start, end) => {
|
|
107
|
+
let lines = 0;
|
|
108
|
+
let previousGroup;
|
|
109
|
+
for (let index = start; index < end; index += 1) {
|
|
110
|
+
const group = groupKeys?.[index];
|
|
111
|
+
if (group && group !== previousGroup)
|
|
112
|
+
lines += 1;
|
|
113
|
+
lines += actions[index]?.description || actions[index]?.disabled ? 2 : 1;
|
|
114
|
+
previousGroup = group;
|
|
115
|
+
}
|
|
116
|
+
return lines;
|
|
117
|
+
};
|
|
118
|
+
let offset = Math.min(requestedOffset, Math.max(0, actions.length - 1));
|
|
119
|
+
if (focus < offset)
|
|
120
|
+
offset = focus;
|
|
121
|
+
while (offset < focus && height(offset, focus + 1) > lineBudget)
|
|
122
|
+
offset += 1;
|
|
123
|
+
let end = Math.max(offset + 1, focus + 1);
|
|
124
|
+
while (end < actions.length && height(offset, end + 1) <= lineBudget)
|
|
125
|
+
end += 1;
|
|
126
|
+
return { offset, limit: Math.max(1, end - offset) };
|
|
127
|
+
}
|
|
128
|
+
function actionViewport(routeName, actions, mode, focus, requestedOffset) {
|
|
129
|
+
if (routeName === 'portableDependencies') {
|
|
130
|
+
return lineBudgetViewport(actions, focus, requestedOffset, mode === 'compact' ? 7 : 12);
|
|
131
|
+
}
|
|
132
|
+
const limit = routeName === 'pluginList' || routeName === 'issueList' || routeName === 'sourceList'
|
|
133
|
+
? actionLimitForLineBudget(actions, mode === 'compact' ? 8 : 12)
|
|
134
|
+
: routeName === 'pluginDependencies' ? mode === 'compact' ? 2 : 7
|
|
135
|
+
: mode === 'compact' ? 4
|
|
136
|
+
: routeName === 'configFields' || routeName === 'review' || routeName === 'issueDetail' ? 7 : undefined;
|
|
137
|
+
if (limit === undefined)
|
|
138
|
+
return { offset: 0 };
|
|
139
|
+
const maximumOffset = Math.max(0, actions.length - limit);
|
|
140
|
+
let offset = Math.min(requestedOffset, maximumOffset);
|
|
141
|
+
if (focus < offset)
|
|
142
|
+
offset = focus;
|
|
143
|
+
else if (focus >= offset + limit)
|
|
144
|
+
offset = focus - limit + 1;
|
|
145
|
+
return { limit, offset: Math.max(0, offset) };
|
|
146
|
+
}
|
|
147
|
+
function portableSkillCandidateViewport(actions, candidates, mode, focus, requestedOffset) {
|
|
148
|
+
const candidateFocus = focus < candidates.length ? focus : requestedOffset;
|
|
149
|
+
return lineBudgetViewport(actions, candidateFocus, requestedOffset, mode === 'compact' ? 10 : 12, candidates.map((candidate) => candidate.providerPluginId));
|
|
150
|
+
}
|
|
151
|
+
function portableScopeVariantsDiffer(candidate) {
|
|
152
|
+
const project = candidate.scopeDetails.project;
|
|
153
|
+
const user = candidate.scopeDetails.user;
|
|
154
|
+
return Boolean(project && user && (project.description !== user.description
|
|
155
|
+
|| project.providerPluginId !== user.providerPluginId
|
|
156
|
+
|| project.providerDisplayName !== user.providerDisplayName
|
|
157
|
+
|| project.dynamic !== user.dynamic
|
|
158
|
+
|| project.filesDigest !== user.filesDigest));
|
|
159
|
+
}
|
|
160
|
+
function portableScopeVariantLabel(scope, detail) {
|
|
161
|
+
if (!detail)
|
|
162
|
+
return '';
|
|
163
|
+
return `${scope === 'project' ? '当前项目' : '所有项目'}:${detail.description} · ${detail.providerDisplayName}${detail.dynamic ? ' · 按设置生成' : ''}`;
|
|
164
|
+
}
|
|
165
|
+
function privatePathInput(value) {
|
|
166
|
+
if (!value)
|
|
167
|
+
return '';
|
|
168
|
+
const basename = value.split(/[\\/]/u).filter(Boolean).at(-1) ?? '已选择';
|
|
169
|
+
return `已选择 / … / ${basename}`;
|
|
170
|
+
}
|
|
171
|
+
function ActionRows(props) {
|
|
172
|
+
const offset = props.offset ?? 0;
|
|
173
|
+
const actions = props.limit === undefined
|
|
174
|
+
? props.actions
|
|
175
|
+
: props.actions.slice(offset, offset + props.limit);
|
|
176
|
+
return (_jsx(Box, { flexDirection: "column", children: actions.map((action, visibleIndex) => {
|
|
177
|
+
const index = visibleIndex + offset;
|
|
178
|
+
const focused = index === props.focus && !action.disabled;
|
|
179
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, backgroundColor: focused ? tuiTheme.colors.selectionBackground : undefined, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { bold: focused, color: action.disabled ? tuiTheme.colors.muted : statusColor(action.status), wrap: "truncate-end", children: [focused ? '› ' : ' ', action.label] }), action.meta ? _jsx(Text, { color: statusColor(action.status) ?? tuiTheme.colors.muted, children: action.meta }) : null] }), action.description || action.disabled
|
|
180
|
+
? _jsxs(Text, { color: tuiTheme.colors.muted, wrap: "truncate-end", children: [' ', action.disabled ?? action.description] })
|
|
181
|
+
: null] }, `${action.label}:${index}`));
|
|
182
|
+
}) }));
|
|
183
|
+
}
|
|
184
|
+
function PortableSkillRows(props) {
|
|
185
|
+
const end = props.limit === undefined ? props.actions.length : props.offset + props.limit;
|
|
186
|
+
const indexes = props.actions.map((_, index) => index)
|
|
187
|
+
.filter((index) => index >= props.offset && index < end);
|
|
188
|
+
return _jsx(Box, { flexDirection: "column", children: indexes.map((index) => {
|
|
189
|
+
const candidate = props.candidates[index];
|
|
190
|
+
const previous = props.candidates[index - 1];
|
|
191
|
+
const showHeading = candidate && (index === props.offset
|
|
192
|
+
|| candidate.providerPluginId !== previous?.providerPluginId);
|
|
193
|
+
const heading = candidate?.relation === 'requested'
|
|
194
|
+
? '当前插件'
|
|
195
|
+
: candidate ? `来自依赖插件 · ${candidate.providerDisplayName}` : undefined;
|
|
196
|
+
return _jsxs(Box, { flexDirection: "column", children: [showHeading && heading ? _jsx(Text, { bold: true, color: tuiTheme.colors.muted, children: heading }) : null, _jsx(ActionRows, { actions: props.actions, focus: props.focus, offset: index, limit: 1 })] }, index);
|
|
197
|
+
}) });
|
|
198
|
+
}
|
|
199
|
+
function Bordered(props) {
|
|
200
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: tuiTheme.colors.border, paddingX: 1, children: [props.title ? _jsx(Text, { bold: true, children: props.title }) : null, props.children] }));
|
|
201
|
+
}
|
|
202
|
+
function SourceTabs(props) {
|
|
203
|
+
const tabs = [{ id: 'all', label: '全部', count: props.controller.listPlugins({ mode: props.mode }).length },
|
|
204
|
+
...props.controller.listSources().map((source) => ({
|
|
205
|
+
...source,
|
|
206
|
+
count: props.controller.listPlugins({ mode: props.mode, sourceId: source.id }).length,
|
|
207
|
+
}))];
|
|
208
|
+
const index = Math.max(0, tabs.findIndex((tab) => tab.id === props.current));
|
|
209
|
+
if (props.compact) {
|
|
210
|
+
const tab = tabs[index];
|
|
211
|
+
return _jsxs(Text, { children: ["\u6765\u6E90 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", tab.label, "]"] }), " ", index + 1, " / ", tabs.length, " \u2190 \u2192"] });
|
|
212
|
+
}
|
|
213
|
+
return (_jsxs(Text, { children: ["\u6765\u6E90 ", tabs.map((tab) => {
|
|
214
|
+
const active = tab.id === props.current;
|
|
215
|
+
const state = 'state' in tab ? tab.state : 'ready';
|
|
216
|
+
return (_jsxs(Text, { bold: active, color: active ? tuiTheme.colors.accent
|
|
217
|
+
: state === 'error' ? tuiTheme.statusColors.error
|
|
218
|
+
: state === 'cache' ? tuiTheme.statusColors.warning : tuiTheme.colors.muted, children: [active ? '[' : '', tab.label, state === 'error' ? ' !' : ` ${tab.count ?? ''}`, active ? ']' : '', ' '] }, tab.id));
|
|
219
|
+
})] }));
|
|
220
|
+
}
|
|
221
|
+
function placementStatus(placement) {
|
|
222
|
+
if (placement.state === 'notInstalled') {
|
|
223
|
+
return { value: '未添加', status: 'muted', description: '选择使用位置后添加' };
|
|
224
|
+
}
|
|
225
|
+
if (placement.state === 'scopeConflict') {
|
|
226
|
+
return { value: '位置冲突', status: 'warning', description: '需要先处理两个安装位置' };
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
value: `${scopeLabel(placement.scope)} · ${placement.health === 'ready' ? '可用' : placement.health === 'incomplete' ? '需要处理' : '不可用'}`,
|
|
230
|
+
status: placement.health === 'ready' ? 'success' : placement.health === 'incomplete' ? 'warning' : 'error',
|
|
231
|
+
description: placement.health === 'ready' ? '当前检查正常' : '查看影响和建议操作',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function dependencyStatus(item) {
|
|
235
|
+
if (item.aggregateStatus === 'ready')
|
|
236
|
+
return { value: '已可用', status: 'success' };
|
|
237
|
+
if (item.aggregateStatus === 'notInstalled')
|
|
238
|
+
return { value: '未添加', status: 'muted' };
|
|
239
|
+
if (item.aggregateStatus === 'needsAttention')
|
|
240
|
+
return { value: '需要处理', status: 'warning' };
|
|
241
|
+
return { value: '不可用', status: 'error' };
|
|
242
|
+
}
|
|
243
|
+
function dependencyPlacementLabel(item) {
|
|
244
|
+
const placement = item.placements.find((entry) => entry.state === 'installed');
|
|
245
|
+
if (placement?.state === 'installed') {
|
|
246
|
+
const state = placement.health === 'ready' ? '可用'
|
|
247
|
+
: placement.health === 'incomplete' ? '需要处理' : '不可用';
|
|
248
|
+
return `${scopeLabel(placement.scope)} · ${state}`;
|
|
249
|
+
}
|
|
250
|
+
if (item.placements.some((entry) => entry.state === 'scopeConflict'))
|
|
251
|
+
return '位置冲突';
|
|
252
|
+
return dependencyStatus(item).value;
|
|
253
|
+
}
|
|
254
|
+
function dependentTargetLabel(item) {
|
|
255
|
+
const consumer = item.placementReferences?.[0]?.consumer;
|
|
256
|
+
return consumer
|
|
257
|
+
? `${platformLabel(consumer.platform)} · ${scopeLabel(consumer.scope)}`
|
|
258
|
+
: dependencyPlacementLabel(item);
|
|
259
|
+
}
|
|
260
|
+
function dependencyRelationDescription(item) {
|
|
261
|
+
if (item.relation === 'dependent')
|
|
262
|
+
return '使用这个依赖的插件';
|
|
263
|
+
const requiredBy = item.requiredBy.map((entry) => entry.displayName).join('、');
|
|
264
|
+
return `${item.relation === 'direct' ? '直接依赖' : '间接依赖'}${requiredBy ? ` · ${requiredBy} 需要它` : ''}`;
|
|
265
|
+
}
|
|
266
|
+
function dependencySummaryText(summary) {
|
|
267
|
+
return `${summary.directCount} 个直接 · ${summary.totalCount} 个总计`;
|
|
268
|
+
}
|
|
269
|
+
function operationTargetStateLabel(state) {
|
|
270
|
+
const labels = {
|
|
271
|
+
completed: { label: '已完成', status: 'success' },
|
|
272
|
+
kept: { label: '已保留', status: 'success' },
|
|
273
|
+
failed: { label: '失败', status: 'error' },
|
|
274
|
+
skipped: { label: '未执行 · 依赖未完成', status: 'warning' },
|
|
275
|
+
};
|
|
276
|
+
return labels[state];
|
|
277
|
+
}
|
|
278
|
+
function reviewSymbol(change) {
|
|
279
|
+
return { add: '+', change: '~', remove: '−', keep: '✓', warning: '!' }[change.kind];
|
|
280
|
+
}
|
|
281
|
+
function currentPlugin(controller, route) {
|
|
282
|
+
return 'pluginId' in route ? controller.getPluginOverview(route.pluginId) : undefined;
|
|
283
|
+
}
|
|
284
|
+
export function resolveConsoleV4ScreenId(snapshot, controller, overlay, tooSmall = false, searchMode = false) {
|
|
285
|
+
if (tooSmall)
|
|
286
|
+
return 'G05';
|
|
287
|
+
if (overlay === 'help')
|
|
288
|
+
return 'G03';
|
|
289
|
+
if (overlay === 'discard')
|
|
290
|
+
return 'G04';
|
|
291
|
+
if (snapshot.phase !== 'ready')
|
|
292
|
+
return 'G01';
|
|
293
|
+
const route = snapshot.router.current.route;
|
|
294
|
+
if (route.name === 'home')
|
|
295
|
+
return 'G02';
|
|
296
|
+
if (route.name === 'pluginList') {
|
|
297
|
+
const items = controller.listPlugins({ mode: route.mode, sourceId: route.sourceId });
|
|
298
|
+
if (route.mode === 'installed' && items.length === 0)
|
|
299
|
+
return 'P12';
|
|
300
|
+
if (route.sourceId && controller.getSourceOverview(route.sourceId).source?.state === 'error')
|
|
301
|
+
return 'P03';
|
|
302
|
+
return route.mode === 'installed' ? 'P11' : searchMode ? 'P02' : 'P01';
|
|
303
|
+
}
|
|
304
|
+
if (route.name === 'pluginPreview') {
|
|
305
|
+
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D01' : 'P04';
|
|
306
|
+
}
|
|
307
|
+
if (route.name === 'portableSkillSelect')
|
|
308
|
+
return 'U01';
|
|
309
|
+
if (route.name === 'portableDependencies')
|
|
310
|
+
return 'U02';
|
|
311
|
+
if (route.name === 'portableAgentMode')
|
|
312
|
+
return 'U03';
|
|
313
|
+
if (route.name === 'portableAgentInput')
|
|
314
|
+
return 'U04';
|
|
315
|
+
if (route.name === 'portableScope')
|
|
316
|
+
return 'U05';
|
|
317
|
+
if (route.name === 'portableBlocker') {
|
|
318
|
+
if (route.code === 'requiredConfigMissing')
|
|
319
|
+
return 'U09';
|
|
320
|
+
if (route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible')
|
|
321
|
+
return 'U10';
|
|
322
|
+
return 'U11';
|
|
323
|
+
}
|
|
324
|
+
if (route.name === 'pluginDependencies') {
|
|
325
|
+
if (route.view === 'removeBlocked')
|
|
326
|
+
return 'D12';
|
|
327
|
+
const plugin = controller.getPluginOverview(route.pluginId);
|
|
328
|
+
return plugin && hasInstalledPlacement(plugin) ? 'D10' : 'D02';
|
|
329
|
+
}
|
|
330
|
+
if (route.name === 'installPlacement') {
|
|
331
|
+
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D03' : 'P05';
|
|
332
|
+
}
|
|
333
|
+
if (route.name === 'installTools') {
|
|
334
|
+
if (route.view === 'blocked')
|
|
335
|
+
return 'D16';
|
|
336
|
+
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D04' : 'P06';
|
|
337
|
+
}
|
|
338
|
+
if (route.name === 'pluginDetail') {
|
|
339
|
+
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D09' : 'P13';
|
|
340
|
+
}
|
|
341
|
+
if (route.name === 'platform') {
|
|
342
|
+
const plugin = controller.getPluginOverview(route.pluginId);
|
|
343
|
+
if (plugin?.aggregateStatus === 'unavailable')
|
|
344
|
+
return 'P17';
|
|
345
|
+
const placement = controller.getPlatformPlacement(route.pluginId, route.platform);
|
|
346
|
+
if (!placement || placement.state === 'notInstalled')
|
|
347
|
+
return 'P15';
|
|
348
|
+
if (placement.state === 'installed' && placement.health === 'ready')
|
|
349
|
+
return 'P14';
|
|
350
|
+
return 'P16';
|
|
351
|
+
}
|
|
352
|
+
const dependencyOperation = snapshot.activeReview?.operationKind === 'pluginWrite'
|
|
353
|
+
&& Boolean(snapshot.activeReview.changes.some((change) => change.group));
|
|
354
|
+
if (route.name === 'review' && dependencyOperation) {
|
|
355
|
+
return snapshot.activeReview?.pluginAction === 'uninstall' || snapshot.activeReview?.pluginAction === 'cleanup'
|
|
356
|
+
? 'D11' : 'D05';
|
|
357
|
+
}
|
|
358
|
+
if (route.name === 'progress' && snapshot.progressTargets.length > 0)
|
|
359
|
+
return 'D06';
|
|
360
|
+
if (route.name === 'result' && (snapshot.result?.state === 'succeeded' || snapshot.result?.state === 'partial')
|
|
361
|
+
&& snapshot.result.summary.targets?.some((target) => target.relation)) {
|
|
362
|
+
return snapshot.result.state === 'partial' ? 'D08' : 'D07';
|
|
363
|
+
}
|
|
364
|
+
if (route.name === 'review' && snapshot.activeReview?.originIssue)
|
|
365
|
+
return 'I10';
|
|
366
|
+
if (route.name === 'progress' && snapshot.activeReview?.originIssue)
|
|
367
|
+
return 'I11';
|
|
368
|
+
if (route.name === 'result' && snapshot.result?.originIssue) {
|
|
369
|
+
return snapshot.result.state === 'succeeded' ? 'I12' : 'I13';
|
|
370
|
+
}
|
|
371
|
+
if (route.name === 'review' && snapshot.activeReview?.operationKind === 'sourceMutation') {
|
|
372
|
+
return { add: 'S10', update: 'S15', setPrimary: 'S16', remove: 'S18' }[snapshot.activeReview.sourceAction];
|
|
373
|
+
}
|
|
374
|
+
if (route.name === 'progress' && snapshot.activeReview?.operationKind === 'sourceMutation')
|
|
375
|
+
return 'S11';
|
|
376
|
+
if (route.name === 'result' && snapshot.result && 'operationKind' in snapshot.result
|
|
377
|
+
&& snapshot.result.operationKind === 'sourceMutation') {
|
|
378
|
+
return snapshot.result.state === 'succeeded' ? 'S12' : 'S13';
|
|
379
|
+
}
|
|
380
|
+
if (route.name === 'review')
|
|
381
|
+
return snapshot.activeReview?.operationKind === 'portableInstall' ? 'U06'
|
|
382
|
+
: snapshot.activeReview?.operationKind === 'configSave'
|
|
383
|
+
? 'C12'
|
|
384
|
+
: snapshot.activeReview?.operationKind === 'configReset'
|
|
385
|
+
? 'C16'
|
|
386
|
+
: snapshot.activeReview?.operationKind === 'pluginWrite'
|
|
387
|
+
&& (snapshot.activeReview.pluginAction === 'upgrade' || snapshot.activeReview.pluginAction === 'reinstall')
|
|
388
|
+
? 'P18'
|
|
389
|
+
: snapshot.activeReview?.operationKind === 'pluginWrite'
|
|
390
|
+
&& (snapshot.activeReview.pluginAction === 'uninstall' || snapshot.activeReview.pluginAction === 'cleanup')
|
|
391
|
+
? 'P19' : 'P07';
|
|
392
|
+
if (route.name === 'progress')
|
|
393
|
+
return snapshot.activeReview?.operationKind === 'portableInstall' ? 'U07'
|
|
394
|
+
: snapshot.activeReview?.operationKind === 'configSave'
|
|
395
|
+
|| snapshot.activeReview?.operationKind === 'configReset' ? 'C13' : 'P08';
|
|
396
|
+
if (route.name === 'result')
|
|
397
|
+
return snapshot.result?.operationKind === 'portableInstall' ? 'U08'
|
|
398
|
+
: snapshot.result?.operationKind === 'configSave'
|
|
399
|
+
|| snapshot.result?.operationKind === 'configReset'
|
|
400
|
+
? snapshot.result.state === 'succeeded' ? 'C14' : 'C15'
|
|
401
|
+
: snapshot.result?.state === 'partial' ? 'P10' : 'P09';
|
|
402
|
+
if (route.name === 'pluginAbout')
|
|
403
|
+
return 'P20';
|
|
404
|
+
if (route.name === 'pluginTechnical')
|
|
405
|
+
return 'P21';
|
|
406
|
+
if (route.name === 'configLayer')
|
|
407
|
+
return 'C01';
|
|
408
|
+
if (route.name === 'configFields')
|
|
409
|
+
return route.layer.scope === 'effective'
|
|
410
|
+
? 'C02' : route.groupId ? 'C04' : 'C03';
|
|
411
|
+
if (route.name === 'configFieldEditor')
|
|
412
|
+
return {
|
|
413
|
+
enum: 'C05', boolean: 'C06', number: 'C07', string: 'C08', path: 'C09', sensitive: 'C10',
|
|
414
|
+
}[route.editor];
|
|
415
|
+
if (route.name === 'configPresets')
|
|
416
|
+
return 'C11';
|
|
417
|
+
if (route.name === 'configUnavailable')
|
|
418
|
+
return 'C17';
|
|
419
|
+
if (route.name === 'sourceList')
|
|
420
|
+
return 'S01';
|
|
421
|
+
if (route.name === 'sourceDetail')
|
|
422
|
+
return 'S02';
|
|
423
|
+
if (route.name === 'sourceTechnical')
|
|
424
|
+
return 'S03';
|
|
425
|
+
if (route.name === 'sourceAdd')
|
|
426
|
+
return {
|
|
427
|
+
type: 'S04', id: 'S05', location: 'S06', refPolicy: 'S07', ref: 'S08', subdir: 'S09',
|
|
428
|
+
}[route.step];
|
|
429
|
+
if (route.name === 'sourceEdit')
|
|
430
|
+
return 'S14';
|
|
431
|
+
if (route.name === 'sourceRemoveBlocked')
|
|
432
|
+
return 'S17';
|
|
433
|
+
if (route.name === 'issueList') {
|
|
434
|
+
const issues = controller.listUserIssues();
|
|
435
|
+
return issues.length === 0 ? 'I02' : issues.some((issue) => issue.category.startsWith('dependency')) ? 'D13' : 'I01';
|
|
436
|
+
}
|
|
437
|
+
if (route.name === 'issueDetail') {
|
|
438
|
+
const category = controller.listUserIssues().find((issue) => issue.issueId === route.issueId)?.category;
|
|
439
|
+
return {
|
|
440
|
+
incompleteInstallation: 'I03',
|
|
441
|
+
sourceDegraded: 'I04',
|
|
442
|
+
sourceUnavailable: 'I04',
|
|
443
|
+
sourceMissing: 'I05',
|
|
444
|
+
sourceIdentityChanged: 'I06',
|
|
445
|
+
requiredConfigMissing: 'I07',
|
|
446
|
+
scopeConflict: 'I08',
|
|
447
|
+
dependencyMissing: 'D14',
|
|
448
|
+
dependencyNotReady: 'D15',
|
|
449
|
+
dependencyTargetUnsupported: 'D16',
|
|
450
|
+
dependencyStillRequired: 'D12',
|
|
451
|
+
dependencyGraphInvalid: 'D17',
|
|
452
|
+
legacyState: 'I09',
|
|
453
|
+
diagnostic: 'I09',
|
|
454
|
+
}[category ?? 'diagnostic'];
|
|
455
|
+
}
|
|
456
|
+
return 'G02';
|
|
457
|
+
}
|
|
458
|
+
export function ConsoleAppV4(props) {
|
|
459
|
+
const windowSize = useWindowSize();
|
|
460
|
+
const size = props.size ?? windowSize;
|
|
461
|
+
const policy = resolveTuiSizePolicy(size.columns, size.rows);
|
|
462
|
+
const [snapshot, setSnapshot] = useState(props.controller.snapshot);
|
|
463
|
+
const [overlay, setOverlay] = useState();
|
|
464
|
+
const [overlayFocus, setOverlayFocus] = useState(0);
|
|
465
|
+
const [discardForExit, setDiscardForExit] = useState(false);
|
|
466
|
+
const [focus, setFocus] = useState(0);
|
|
467
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
468
|
+
const [searchMode, setSearchMode] = useState(false);
|
|
469
|
+
const [query, setQuery] = useState('');
|
|
470
|
+
const [scope, setScope] = useState('user');
|
|
471
|
+
const [tools, setTools] = useState([]);
|
|
472
|
+
const [technicalRouteKey, setTechnicalRouteKey] = useState();
|
|
473
|
+
const [exitAfterBusy, setExitAfterBusy] = useState(false);
|
|
474
|
+
const [, setClockTick] = useState(0);
|
|
475
|
+
const [dirty, setDirty] = useState(false);
|
|
476
|
+
const [configPage, setConfigPage] = useState();
|
|
477
|
+
const [configLayerCounts, setConfigLayerCounts] = useState({});
|
|
478
|
+
const [configLoadKey, setConfigLoadKey] = useState(0);
|
|
479
|
+
const [draftUpdates, setDraftUpdates] = useState({});
|
|
480
|
+
const [draftRemoves, setDraftRemoves] = useState([]);
|
|
481
|
+
const [inputValue, setInputValue] = useState('');
|
|
482
|
+
const [inputError, setInputError] = useState();
|
|
483
|
+
const [configActionError, setConfigActionError] = useState();
|
|
484
|
+
const [configResetAvailability, setConfigResetAvailability] = useState();
|
|
485
|
+
const [actionError, setActionError] = useState();
|
|
486
|
+
const [sourceDraft, setSourceDraft] = useState();
|
|
487
|
+
const [sourceInput, setSourceInput] = useState('');
|
|
488
|
+
const [sourceInputError, setSourceInputError] = useState();
|
|
489
|
+
const [sourceReferenceSummary, setSourceReferenceSummary] = useState();
|
|
490
|
+
const [sourceDependenciesExpanded, setSourceDependenciesExpanded] = useState(false);
|
|
491
|
+
const [portableAgentInput, setPortableAgentInput] = useState('');
|
|
492
|
+
const [draftOwner, setDraftOwner] = useState();
|
|
493
|
+
const exitSent = useRef(false);
|
|
494
|
+
const operationBusy = useRef(false);
|
|
495
|
+
const route = snapshot.router.current.route;
|
|
496
|
+
const routeKey = JSON.stringify(route);
|
|
497
|
+
const showTechnical = technicalRouteKey === routeKey;
|
|
498
|
+
const setShowTechnical = (next) => {
|
|
499
|
+
const visible = typeof next === 'function' ? next(showTechnical) : next;
|
|
500
|
+
setTechnicalRouteKey(visible ? routeKey : undefined);
|
|
501
|
+
};
|
|
502
|
+
const plugin = currentPlugin(props.controller, route);
|
|
503
|
+
const issue = route.name === 'issueDetail'
|
|
504
|
+
? props.controller.listUserIssues().find((entry) => entry.issueId === route.issueId)
|
|
505
|
+
: undefined;
|
|
506
|
+
const busy = route.name === 'progress';
|
|
507
|
+
useEffect(() => {
|
|
508
|
+
if (!busy)
|
|
509
|
+
return;
|
|
510
|
+
const timer = setInterval(() => setClockTick((value) => value + 1), 1_000);
|
|
511
|
+
return () => clearInterval(timer);
|
|
512
|
+
}, [busy, route.name === 'progress' ? route.operationId : '']);
|
|
513
|
+
const tooSmall = policy.mode === 'too-small';
|
|
514
|
+
const requestExit = (request) => {
|
|
515
|
+
if (exitSent.current)
|
|
516
|
+
return;
|
|
517
|
+
exitSent.current = true;
|
|
518
|
+
props.onExit?.(request);
|
|
519
|
+
};
|
|
520
|
+
useEffect(() => {
|
|
521
|
+
const unsubscribe = props.controller.subscribe(setSnapshot);
|
|
522
|
+
if (props.controller.snapshot.phase === 'idle') {
|
|
523
|
+
void props.controller.load().catch(() => {
|
|
524
|
+
setActionError('操作未完成。请重新检查后重试。');
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return () => {
|
|
528
|
+
unsubscribe();
|
|
529
|
+
if (props.ownsController)
|
|
530
|
+
props.controller.dispose();
|
|
531
|
+
};
|
|
532
|
+
}, [props.controller, props.ownsController]);
|
|
533
|
+
useEffect(() => {
|
|
534
|
+
if (!exitAfterBusy || !snapshot.result)
|
|
535
|
+
return;
|
|
536
|
+
const state = snapshot.result.state;
|
|
537
|
+
requestExit({
|
|
538
|
+
code: state === 'blocked' || state === 'partial' ? 1 : 0,
|
|
539
|
+
reason: state === 'blocked' ? 'blocked' : state === 'partial' ? 'partial'
|
|
540
|
+
: state === 'stale' ? 'stale' : 'completed',
|
|
541
|
+
});
|
|
542
|
+
}, [exitAfterBusy, snapshot.result]);
|
|
543
|
+
useEffect(() => {
|
|
544
|
+
if (route.name !== 'result'
|
|
545
|
+
|| !snapshot.result
|
|
546
|
+
|| (snapshot.result.state !== 'succeeded' && snapshot.result.state !== 'partial')
|
|
547
|
+
|| !snapshot.result.operationKind)
|
|
548
|
+
return;
|
|
549
|
+
setDirty(false);
|
|
550
|
+
setDraftUpdates({});
|
|
551
|
+
setDraftRemoves([]);
|
|
552
|
+
if (snapshot.result.operationKind === 'sourceMutation')
|
|
553
|
+
setSourceDraft(undefined);
|
|
554
|
+
}, [route.name, snapshot.result]);
|
|
555
|
+
useEffect(() => {
|
|
556
|
+
if (route.name !== 'sourceAdd')
|
|
557
|
+
setSourceInput('');
|
|
558
|
+
}, [route.name]);
|
|
559
|
+
useEffect(() => {
|
|
560
|
+
if (route.name === 'portableAgentInput') {
|
|
561
|
+
setPortableAgentInput(snapshot.portableDraft?.requestedAgents.join(' ') ?? '');
|
|
562
|
+
}
|
|
563
|
+
else
|
|
564
|
+
setPortableAgentInput('');
|
|
565
|
+
}, [route.name, route.name === 'portableAgentInput' ? snapshot.portableDraft?.requestedAgents.join('\0') : '']);
|
|
566
|
+
useEffect(() => {
|
|
567
|
+
if (route.name !== 'configFieldEditor')
|
|
568
|
+
setInputValue('');
|
|
569
|
+
}, [route.name]);
|
|
570
|
+
const configRoute = route.name === 'configFields'
|
|
571
|
+
|| route.name === 'configFieldEditor'
|
|
572
|
+
|| route.name === 'configPresets'
|
|
573
|
+
? route
|
|
574
|
+
: undefined;
|
|
575
|
+
const currentConfigPage = configPageMatches(configPage, configRoute) ? configPage : undefined;
|
|
576
|
+
useEffect(() => {
|
|
577
|
+
if (route.name !== 'configLayer')
|
|
578
|
+
return;
|
|
579
|
+
const abort = new AbortController();
|
|
580
|
+
const layers = [
|
|
581
|
+
{ key: 'effective', layer: { scope: 'effective', projectLayer: 'shared' } },
|
|
582
|
+
{ key: 'user', layer: { scope: 'user', projectLayer: 'shared' } },
|
|
583
|
+
{ key: 'project', layer: { scope: 'project', projectLayer: 'shared' } },
|
|
584
|
+
{ key: 'local', layer: { scope: 'project', projectLayer: 'local' } },
|
|
585
|
+
];
|
|
586
|
+
void Promise.allSettled(layers.map(async ({ key, layer }) => ({
|
|
587
|
+
key,
|
|
588
|
+
count: (await props.controller.getConfigPage(route.pluginId, layer, abort.signal)).storedCount,
|
|
589
|
+
}))).then((results) => {
|
|
590
|
+
if (abort.signal.aborted)
|
|
591
|
+
return;
|
|
592
|
+
setConfigLayerCounts(Object.fromEntries(results.flatMap((result) => result.status === 'fulfilled' ? [[result.value.key, result.value.count]] : [])));
|
|
593
|
+
});
|
|
594
|
+
return () => abort.abort();
|
|
595
|
+
}, [props.controller, route.name, route.name === 'configLayer' ? route.pluginId : '']);
|
|
596
|
+
useEffect(() => {
|
|
597
|
+
if (!configRoute)
|
|
598
|
+
return;
|
|
599
|
+
const abort = new AbortController();
|
|
600
|
+
const expectedPluginId = configRoute.pluginId;
|
|
601
|
+
const expectedLayer = JSON.stringify(configRoute.layer);
|
|
602
|
+
const owner = configOwnerKey(configRoute);
|
|
603
|
+
setConfigPage(undefined);
|
|
604
|
+
if (draftOwner !== owner) {
|
|
605
|
+
setDraftOwner(owner);
|
|
606
|
+
setDraftUpdates({});
|
|
607
|
+
setDraftRemoves([]);
|
|
608
|
+
setDirty(false);
|
|
609
|
+
setConfigActionError(undefined);
|
|
610
|
+
}
|
|
611
|
+
void props.controller.getConfigPage(expectedPluginId, configRoute.layer, abort.signal)
|
|
612
|
+
.then((page) => {
|
|
613
|
+
if (abort.signal.aborted
|
|
614
|
+
|| expectedPluginId !== configRoute.pluginId
|
|
615
|
+
|| expectedLayer !== JSON.stringify(configRoute.layer))
|
|
616
|
+
return;
|
|
617
|
+
setConfigPage(page);
|
|
618
|
+
})
|
|
619
|
+
.catch(() => {
|
|
620
|
+
if (!abort.signal.aborted) {
|
|
621
|
+
const layer = configRoute.layer.scope === 'effective'
|
|
622
|
+
? undefined
|
|
623
|
+
: { scope: configRoute.layer.scope, projectLayer: configRoute.layer.projectLayer };
|
|
624
|
+
props.controller.replace({
|
|
625
|
+
name: 'configUnavailable',
|
|
626
|
+
pluginId: expectedPluginId,
|
|
627
|
+
...(layer ? { layer } : {}),
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
return () => abort.abort();
|
|
632
|
+
}, [configLoadKey, configRoute?.pluginId, configRoute?.layer.scope, configRoute?.layer.projectLayer, props.controller]);
|
|
633
|
+
useEffect(() => {
|
|
634
|
+
if (route.name !== 'configUnavailable' || !route.layer) {
|
|
635
|
+
setConfigResetAvailability(undefined);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
const abort = new AbortController();
|
|
639
|
+
void props.controller.getConfigResetAvailability(route.pluginId, route.layer)
|
|
640
|
+
.then((availability) => {
|
|
641
|
+
if (!abort.signal.aborted)
|
|
642
|
+
setConfigResetAvailability(availability);
|
|
643
|
+
})
|
|
644
|
+
.catch(() => {
|
|
645
|
+
if (!abort.signal.aborted)
|
|
646
|
+
setConfigResetAvailability({ exists: false, sourceIdentityPresent: false });
|
|
647
|
+
});
|
|
648
|
+
return () => abort.abort();
|
|
649
|
+
}, [props.controller, route.name, route.name === 'configUnavailable' ? route.pluginId : '', route.name === 'configUnavailable' ? JSON.stringify(route.layer) : '']);
|
|
650
|
+
useEffect(() => {
|
|
651
|
+
let count;
|
|
652
|
+
if (route.name === 'sourceList')
|
|
653
|
+
count = props.controller.listSources().length + 1;
|
|
654
|
+
else if (route.name === 'issueList') {
|
|
655
|
+
const issueCount = props.controller.listUserIssues().length;
|
|
656
|
+
count = issueCount || 2;
|
|
657
|
+
}
|
|
658
|
+
else if (route.name === 'pluginList') {
|
|
659
|
+
const itemCount = props.controller.listPlugins({
|
|
660
|
+
mode: route.mode,
|
|
661
|
+
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
662
|
+
...(query ? { query } : {}),
|
|
663
|
+
}).length;
|
|
664
|
+
count = itemCount || 1;
|
|
665
|
+
}
|
|
666
|
+
if (count === undefined)
|
|
667
|
+
return;
|
|
668
|
+
setFocus((value) => Math.min(value, Math.max(0, count - 1)));
|
|
669
|
+
const maximumVisible = policy.mode === 'compact' ? 4 : 6;
|
|
670
|
+
setScrollOffset((value) => Math.min(value, Math.max(0, count - maximumVisible)));
|
|
671
|
+
}, [
|
|
672
|
+
policy.mode,
|
|
673
|
+
props.controller,
|
|
674
|
+
query,
|
|
675
|
+
route.name,
|
|
676
|
+
route.name === 'pluginList' ? route.mode : '',
|
|
677
|
+
route.name === 'pluginList' ? route.sourceId : '',
|
|
678
|
+
snapshot.viewModel,
|
|
679
|
+
]);
|
|
680
|
+
const activeField = route.name === 'configFieldEditor'
|
|
681
|
+
? currentConfigPage?.groups.flatMap((group) => group.fields).find((field) => field.key === route.fieldKey)
|
|
682
|
+
: undefined;
|
|
683
|
+
useEffect(() => {
|
|
684
|
+
if (route.name !== 'configFieldEditor' || !activeField)
|
|
685
|
+
return;
|
|
686
|
+
const draft = draftUpdates[activeField.key];
|
|
687
|
+
setInputValue(route.editor === 'sensitive' || route.editor === 'path'
|
|
688
|
+
? draft === undefined ? '' : String(draft)
|
|
689
|
+
: String(draft ?? activeField.value ?? ''));
|
|
690
|
+
setInputError(undefined);
|
|
691
|
+
}, [route.name, route.name === 'configFieldEditor' ? route.fieldKey : '', activeField?.key]);
|
|
692
|
+
useEffect(() => {
|
|
693
|
+
if (route.name !== 'sourceAdd')
|
|
694
|
+
return;
|
|
695
|
+
const draft = sourceDraft?.owner === route.owner ? sourceDraft : undefined;
|
|
696
|
+
setSourceInput(route.step === 'id' ? draft?.sourceId ?? ''
|
|
697
|
+
: route.step === 'location' ? draft?.location ?? ''
|
|
698
|
+
: route.step === 'ref' ? draft?.ref ?? ''
|
|
699
|
+
: route.step === 'subdir' ? draft?.subdir ?? '' : '');
|
|
700
|
+
setSourceInputError(undefined);
|
|
701
|
+
}, [route.name, route.name === 'sourceAdd' ? `${route.owner}:${route.step}` : '', sourceDraft?.owner]);
|
|
702
|
+
useEffect(() => {
|
|
703
|
+
if (route.name !== 'sourceRemoveBlocked')
|
|
704
|
+
return;
|
|
705
|
+
const abort = new AbortController();
|
|
706
|
+
setSourceReferenceSummary(undefined);
|
|
707
|
+
setSourceDependenciesExpanded(false);
|
|
708
|
+
void props.controller.inspectSourceRemoval(route.sourceId)
|
|
709
|
+
.then((summary) => { if (!abort.signal.aborted)
|
|
710
|
+
setSourceReferenceSummary(summary); })
|
|
711
|
+
.catch(() => { if (!abort.signal.aborted)
|
|
712
|
+
setSourceReferenceSummary({ blocked: true, inventoryComplete: false, dependencies: [] }); });
|
|
713
|
+
return () => abort.abort();
|
|
714
|
+
}, [props.controller, route.name, route.name === 'sourceRemoveBlocked' ? route.sourceId : '']);
|
|
715
|
+
const screenId = resolveConsoleV4ScreenId(snapshot, props.controller, overlay, tooSmall, searchMode);
|
|
716
|
+
useEffect(() => {
|
|
717
|
+
props.onScreenChange?.(screenId);
|
|
718
|
+
}, [props.onScreenChange, screenId]);
|
|
719
|
+
const selectedPlatforms = (candidate, selectedScope) => (candidate?.supportedPlatforms ?? []).map((platform) => {
|
|
720
|
+
const placement = props.controller.getPlatformPlacement(candidate.pluginId, platform);
|
|
721
|
+
if (!placement)
|
|
722
|
+
return { platform, disabled: '当前没有可信的平台能力信息' };
|
|
723
|
+
if (placement.state === 'scopeConflict')
|
|
724
|
+
return { platform, disabled: '安装位置冲突;请先处理问题' };
|
|
725
|
+
if (placement.state === 'installed') {
|
|
726
|
+
return { platform, disabled: `已安装到${scopeLabel(placement.scope)};请先移除现有安装` };
|
|
727
|
+
}
|
|
728
|
+
if (!placement.supportedScopes.includes(selectedScope))
|
|
729
|
+
return { platform, disabled: '不支持所选位置' };
|
|
730
|
+
const overview = props.controller.getPluginDependencyOverview(candidate.pluginId);
|
|
731
|
+
for (const dependency of overview?.dependencies ?? []) {
|
|
732
|
+
const dependentPlugin = props.controller.getPluginOverview(dependency.pluginId);
|
|
733
|
+
if (!dependentPlugin || !dependentPlugin.supportedPlatforms.includes(platform)) {
|
|
734
|
+
return { platform, disabled: `${dependency.displayName} 不支持${platformLabel(platform)}` };
|
|
735
|
+
}
|
|
736
|
+
const dependencyPlacement = props.controller.getPlatformPlacement(dependency.pluginId, platform);
|
|
737
|
+
if (dependencyPlacement?.state === 'scopeConflict') {
|
|
738
|
+
return { platform, disabled: `${dependency.displayName} 的安装位置冲突` };
|
|
739
|
+
}
|
|
740
|
+
if (dependencyPlacement?.state === 'installed') {
|
|
741
|
+
if (dependencyPlacement.health !== 'ready') {
|
|
742
|
+
return { platform, disabled: `${dependency.displayName} 需要先处理` };
|
|
743
|
+
}
|
|
744
|
+
if (selectedScope === 'user' && dependencyPlacement.scope === 'project') {
|
|
745
|
+
return { platform, disabled: `${dependency.displayName} 只在当前项目可用` };
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (dependencyPlacement?.state === 'notInstalled'
|
|
749
|
+
&& !dependencyPlacement.supportedScopes.includes(selectedScope)) {
|
|
750
|
+
return { platform, disabled: `${dependency.displayName} 不支持所选位置` };
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return { platform };
|
|
754
|
+
});
|
|
755
|
+
const dependencyOverview = (candidate) => candidate ? props.controller.getPluginDependencyOverview(candidate.pluginId) : undefined;
|
|
756
|
+
const installIntent = (candidate) => ({
|
|
757
|
+
kind: 'pluginWrite',
|
|
758
|
+
action: 'install',
|
|
759
|
+
pluginId: candidate.pluginId,
|
|
760
|
+
platform: tools.length === 2 ? 'both' : tools[0],
|
|
761
|
+
scope,
|
|
762
|
+
});
|
|
763
|
+
let title = '管理你的 Agent 工作流插件';
|
|
764
|
+
let description = '为 Codex 和 Claude Code 添加、设置或移除开发能力。';
|
|
765
|
+
let breadcrumb = '首页';
|
|
766
|
+
let context;
|
|
767
|
+
let help = '↑↓ 移动 · Enter 选择 · ? 帮助 · q 退出';
|
|
768
|
+
let content;
|
|
769
|
+
let actions = [];
|
|
770
|
+
const go = (next) => {
|
|
771
|
+
props.controller.updatePageContext({ focusIndex: focus, scrollOffset, query, sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined });
|
|
772
|
+
props.controller.push(next);
|
|
773
|
+
setFocus(0);
|
|
774
|
+
setScrollOffset(0);
|
|
775
|
+
setSearchMode(false);
|
|
776
|
+
setShowTechnical(false);
|
|
777
|
+
setActionError(undefined);
|
|
778
|
+
};
|
|
779
|
+
const back = () => {
|
|
780
|
+
if (route.name === 'result')
|
|
781
|
+
props.controller.leaveResult();
|
|
782
|
+
else
|
|
783
|
+
props.controller.back();
|
|
784
|
+
const restored = props.controller.snapshot.router.current.context;
|
|
785
|
+
setFocus(restored.focusIndex ?? 0);
|
|
786
|
+
setScrollOffset(restored.scrollOffset ?? 0);
|
|
787
|
+
setQuery(restored.query ?? '');
|
|
788
|
+
setSearchMode(Boolean(restored.query));
|
|
789
|
+
setActionError(undefined);
|
|
790
|
+
};
|
|
791
|
+
const leaveResult = (target) => {
|
|
792
|
+
props.controller.leaveResult(target);
|
|
793
|
+
const restored = props.controller.snapshot.router.current.context;
|
|
794
|
+
setFocus(restored.focusIndex ?? 0);
|
|
795
|
+
setScrollOffset(restored.scrollOffset ?? 0);
|
|
796
|
+
setQuery(restored.query ?? '');
|
|
797
|
+
setSearchMode(Boolean(restored.query));
|
|
798
|
+
setShowTechnical(false);
|
|
799
|
+
setActionError(undefined);
|
|
800
|
+
};
|
|
801
|
+
const beginReview = async (intent) => {
|
|
802
|
+
const underlyingResultRoute = route.name === 'result' && snapshot.result && 'returnRoute' in snapshot.result
|
|
803
|
+
? snapshot.result.returnRoute
|
|
804
|
+
: undefined;
|
|
805
|
+
await props.controller.buildOperationReview(intent, underlyingResultRoute);
|
|
806
|
+
setFocus(0);
|
|
807
|
+
setScrollOffset(0);
|
|
808
|
+
};
|
|
809
|
+
const beginPortable = async (pluginId) => {
|
|
810
|
+
try {
|
|
811
|
+
await props.controller.preparePortableInstall(pluginId, route.name === 'portableBlocker' ? 'replace' : 'push');
|
|
812
|
+
setFocus(0);
|
|
813
|
+
setScrollOffset(0);
|
|
814
|
+
setActionError(undefined);
|
|
815
|
+
}
|
|
816
|
+
catch (error) {
|
|
817
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
818
|
+
return;
|
|
819
|
+
const blocker = extractPortableError(error);
|
|
820
|
+
const target = { name: 'portableBlocker', pluginId, ...blocker };
|
|
821
|
+
if (props.controller.snapshot.router.current.route.name === 'portableBlocker') {
|
|
822
|
+
props.controller.replace(target);
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
go(target);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
const beginPortableReview = async (pluginId, selectedScope) => {
|
|
830
|
+
props.controller.setPortableScope(selectedScope);
|
|
831
|
+
try {
|
|
832
|
+
await props.controller.buildPortableInstallReview();
|
|
833
|
+
setFocus(0);
|
|
834
|
+
setActionError(undefined);
|
|
835
|
+
}
|
|
836
|
+
catch (error) {
|
|
837
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
838
|
+
return;
|
|
839
|
+
const blocker = extractPortableError(error);
|
|
840
|
+
go({ name: 'portableBlocker', pluginId, ...blocker });
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
const refreshPortableDraft = async (pluginId) => {
|
|
844
|
+
try {
|
|
845
|
+
await props.controller.refreshPortableInstallDraft(pluginId);
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
850
|
+
return false;
|
|
851
|
+
const blocker = extractPortableError(error);
|
|
852
|
+
const target = { name: 'portableBlocker', pluginId, ...blocker };
|
|
853
|
+
if (route.name === 'result')
|
|
854
|
+
leaveResult(target);
|
|
855
|
+
else
|
|
856
|
+
props.controller.replace(target);
|
|
857
|
+
setFocus(0);
|
|
858
|
+
setScrollOffset(0);
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
const beginPluginRemoval = (candidate, platform, selectedScope) => {
|
|
863
|
+
if (props.controller.getBlockingDependents(candidate.pluginId, platform, selectedScope).length > 0) {
|
|
864
|
+
go({ name: 'pluginDependencies', pluginId: candidate.pluginId, view: 'removeBlocked',
|
|
865
|
+
platform, scope: selectedScope });
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
void beginReview({
|
|
869
|
+
kind: 'pluginWrite',
|
|
870
|
+
action: 'uninstall',
|
|
871
|
+
pluginId: candidate.pluginId,
|
|
872
|
+
platform,
|
|
873
|
+
scope: selectedScope,
|
|
874
|
+
});
|
|
875
|
+
};
|
|
876
|
+
const beginAddSource = () => {
|
|
877
|
+
const owner = `source-add:${Date.now()}`;
|
|
878
|
+
setSourceDraft({ owner, mode: 'add', sourceId: '', type: 'git', location: '', refPolicy: 'cliChannel', ref: '', subdir: '' });
|
|
879
|
+
setDirty(true);
|
|
880
|
+
go({ name: 'sourceAdd', step: 'type', owner });
|
|
881
|
+
};
|
|
882
|
+
const beginIssueSourceAdd = (sourceId) => {
|
|
883
|
+
const owner = `source-readd:${sourceId}:${Date.now()}`;
|
|
884
|
+
setSourceDraft({ owner, mode: 'add', sourceId, type: 'git', location: '', refPolicy: 'cliChannel', ref: '', subdir: '' });
|
|
885
|
+
setDirty(true);
|
|
886
|
+
go({ name: 'sourceAdd', step: 'type', owner });
|
|
887
|
+
};
|
|
888
|
+
const issueWriteIntent = (selectedIssue, action) => {
|
|
889
|
+
const pluginId = action.pluginId ?? selectedIssue.pluginId;
|
|
890
|
+
if (!pluginId || !selectedIssue.platform)
|
|
891
|
+
return undefined;
|
|
892
|
+
const selectedScope = action.id === 'keepUserScope'
|
|
893
|
+
? 'project'
|
|
894
|
+
: action.id === 'keepProjectScope' ? 'user' : action.scope ?? selectedIssue.scope;
|
|
895
|
+
const selectedPlatform = action.platform ?? selectedIssue.platform;
|
|
896
|
+
const writeAction = action.writeAction ?? (action.id === 'reinstall'
|
|
897
|
+
? 'reinstall'
|
|
898
|
+
: action.id === 'remove' || action.id === 'keepUserScope' || action.id === 'keepProjectScope'
|
|
899
|
+
? 'uninstall'
|
|
900
|
+
: undefined);
|
|
901
|
+
if (!selectedScope || !selectedPlatform || !writeAction)
|
|
902
|
+
return undefined;
|
|
903
|
+
return {
|
|
904
|
+
kind: 'pluginWrite',
|
|
905
|
+
action: writeAction,
|
|
906
|
+
pluginId,
|
|
907
|
+
platform: selectedPlatform,
|
|
908
|
+
scope: selectedScope,
|
|
909
|
+
originIssue: {
|
|
910
|
+
issueId: selectedIssue.issueId,
|
|
911
|
+
issueKey: userIssueKey(selectedIssue),
|
|
912
|
+
category: selectedIssue.category,
|
|
913
|
+
actionId: action.id,
|
|
914
|
+
...(selectedIssue.category === 'dependencyMissing' || selectedIssue.category === 'dependencyNotReady'
|
|
915
|
+
? {
|
|
916
|
+
consumerPluginId: selectedIssue.pluginId,
|
|
917
|
+
consumerSourceIdentity: selectedIssue.sourceIdentity,
|
|
918
|
+
consumerPlatform: selectedIssue.platform,
|
|
919
|
+
consumerScope: selectedIssue.scope,
|
|
920
|
+
dependencyPluginId: selectedIssue.dependencyPluginId,
|
|
921
|
+
dependencyPlatform: selectedIssue.dependencyPlatform,
|
|
922
|
+
dependencyScope: selectedIssue.dependencyScope,
|
|
923
|
+
dependencyProject: selectedIssue.dependencyProject,
|
|
924
|
+
}
|
|
925
|
+
: {}),
|
|
926
|
+
},
|
|
927
|
+
};
|
|
928
|
+
};
|
|
929
|
+
const sourceFromDraft = (draft) => draft.type === 'git'
|
|
930
|
+
? {
|
|
931
|
+
id: draft.sourceId,
|
|
932
|
+
type: 'git',
|
|
933
|
+
primary: false,
|
|
934
|
+
url: draft.location,
|
|
935
|
+
refPolicy: draft.refPolicy === 'cliChannel' ? { type: 'cliChannel' } : { type: 'explicit', ref: draft.ref },
|
|
936
|
+
...(draft.subdir ? { subdir: draft.subdir } : {}),
|
|
937
|
+
}
|
|
938
|
+
: { id: draft.sourceId, type: 'path', primary: false, path: draft.location };
|
|
939
|
+
const beginSourceAddReviewWith = async (draft) => {
|
|
940
|
+
const source = sourceFromDraft(draft);
|
|
941
|
+
await beginReview({
|
|
942
|
+
kind: 'sourceMutation', action: 'add', sourceId: source.id, source,
|
|
943
|
+
changes: [
|
|
944
|
+
{ title: `添加来源 ${source.id}`, after: source.type === 'git' ? 'Git 仓库' : '本地目录' },
|
|
945
|
+
{ title: source.type === 'git' ? 'Git URL' : '本地目录', ...(source.type === 'git' ? { after: projectCatalogSourceLocation(source.url) } : { private: true }) },
|
|
946
|
+
...(source.type === 'git' ? [{ title: '版本引用', after: source.refPolicy.type === 'cliChannel' ? '跟随 AW 版本通道' : source.refPolicy.ref }] : []),
|
|
947
|
+
...(source.type === 'git' && source.subdir ? [{ title: 'Catalog 子目录', after: source.subdir }] : []),
|
|
948
|
+
],
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
const beginSourceUpdateReview = async (draft) => {
|
|
952
|
+
const detail = props.controller.getSourceOverview(draft.sourceId).detail;
|
|
953
|
+
if (!detail || !draft.editField)
|
|
954
|
+
return;
|
|
955
|
+
const update = draft.type === 'path'
|
|
956
|
+
? { type: 'path', path: draft.location }
|
|
957
|
+
: draft.editField === 'location'
|
|
958
|
+
? { type: 'git', url: draft.location }
|
|
959
|
+
: draft.editField === 'refPolicy'
|
|
960
|
+
? { type: 'git', refPolicy: draft.refPolicy === 'cliChannel' ? { type: 'cliChannel' } : { type: 'explicit', ref: draft.ref } }
|
|
961
|
+
: { type: 'git', subdir: draft.subdir || null };
|
|
962
|
+
const change = draft.editField === 'location'
|
|
963
|
+
? { title: draft.type === 'git' ? 'Git URL' : '本地目录', before: detail.locationSummary, ...(draft.type === 'git' ? { after: projectCatalogSourceLocation(draft.location) } : { private: true }) }
|
|
964
|
+
: draft.editField === 'refPolicy'
|
|
965
|
+
? { title: '版本引用', before: detail.refPolicy === 'explicit' ? detail.explicitRef : '跟随 AW 版本通道', after: draft.refPolicy === 'explicit' ? draft.ref : '跟随 AW 版本通道' }
|
|
966
|
+
: { title: 'Catalog 子目录', before: detail.subdir ?? '未设置', after: draft.subdir || '未设置' };
|
|
967
|
+
await beginReview({ kind: 'sourceMutation', action: 'update', sourceId: draft.sourceId, update, changes: [change] });
|
|
968
|
+
};
|
|
969
|
+
const configLayerLabel = (layer) => layer.scope === 'effective' ? '有效设置'
|
|
970
|
+
: layer.scope === 'user' ? '个人默认'
|
|
971
|
+
: layer.projectLayer === 'local' ? '当前项目(仅本机)' : '当前项目';
|
|
972
|
+
const currentFieldDisplay = (field) => {
|
|
973
|
+
if (draftRemoves.includes(field.key))
|
|
974
|
+
return '恢复继承';
|
|
975
|
+
if (Object.hasOwn(draftUpdates, field.key)) {
|
|
976
|
+
const value = draftUpdates[field.key];
|
|
977
|
+
if (field.sensitive || field.type === 'file' || field.type === 'directory')
|
|
978
|
+
return '将更新';
|
|
979
|
+
if (field.type === 'enum')
|
|
980
|
+
return field.valueLabels?.[String(value)] ?? String(value);
|
|
981
|
+
if (field.type === 'boolean')
|
|
982
|
+
return value ? '开启' : '关闭';
|
|
983
|
+
return `${String(value)}${field.unit ? ` ${field.unit}` : ''}`;
|
|
984
|
+
}
|
|
985
|
+
if (field.type === 'enum' && field.value !== undefined) {
|
|
986
|
+
return field.valueLabels?.[String(field.value)] ?? field.displayValue;
|
|
987
|
+
}
|
|
988
|
+
if (field.type === 'boolean' && field.value !== undefined)
|
|
989
|
+
return field.value ? '开启' : '关闭';
|
|
990
|
+
return `${field.displayValue}${field.unit && field.displayValue !== '未设置' ? ` ${field.unit}` : ''}`;
|
|
991
|
+
};
|
|
992
|
+
const fieldEditor = (field) => {
|
|
993
|
+
if (field.sensitive)
|
|
994
|
+
return 'sensitive';
|
|
995
|
+
if (field.type === 'file' || field.type === 'directory')
|
|
996
|
+
return 'path';
|
|
997
|
+
return field.type;
|
|
998
|
+
};
|
|
999
|
+
const updateDraft = (key, value) => {
|
|
1000
|
+
setConfigActionError(undefined);
|
|
1001
|
+
const field = currentConfigPage?.groups.flatMap((group) => group.fields)
|
|
1002
|
+
.find((candidate) => candidate.key === key);
|
|
1003
|
+
const unchanged = field?.configured === true && field.value === value;
|
|
1004
|
+
setDraftUpdates((current) => {
|
|
1005
|
+
const next = { ...current };
|
|
1006
|
+
if (unchanged)
|
|
1007
|
+
delete next[key];
|
|
1008
|
+
else
|
|
1009
|
+
next[key] = value;
|
|
1010
|
+
const nextRemoves = draftRemoves.filter((candidate) => candidate !== key);
|
|
1011
|
+
setDraftRemoves(nextRemoves);
|
|
1012
|
+
setDirty(Object.keys(next).length + nextRemoves.length > 0);
|
|
1013
|
+
return next;
|
|
1014
|
+
});
|
|
1015
|
+
};
|
|
1016
|
+
const removeDraft = (key) => {
|
|
1017
|
+
setConfigActionError(undefined);
|
|
1018
|
+
setDraftUpdates((current) => {
|
|
1019
|
+
const next = { ...current };
|
|
1020
|
+
delete next[key];
|
|
1021
|
+
const field = currentConfigPage?.groups.flatMap((group) => group.fields)
|
|
1022
|
+
.find((candidate) => candidate.key === key);
|
|
1023
|
+
const nextRemoves = field?.configured
|
|
1024
|
+
? [...new Set([...draftRemoves, key])]
|
|
1025
|
+
: draftRemoves.filter((candidate) => candidate !== key);
|
|
1026
|
+
setDraftRemoves(nextRemoves);
|
|
1027
|
+
setDirty(Object.keys(next).length + nextRemoves.length > 0);
|
|
1028
|
+
return next;
|
|
1029
|
+
});
|
|
1030
|
+
};
|
|
1031
|
+
const configChanges = () => {
|
|
1032
|
+
const fields = currentConfigPage?.groups.flatMap((group) => group.fields) ?? [];
|
|
1033
|
+
return [...Object.keys(draftUpdates), ...draftRemoves.filter((key) => !Object.hasOwn(draftUpdates, key))]
|
|
1034
|
+
.map((key) => {
|
|
1035
|
+
const field = fields.find((candidate) => candidate.key === key);
|
|
1036
|
+
return {
|
|
1037
|
+
key,
|
|
1038
|
+
title: field?.title ?? key,
|
|
1039
|
+
before: field?.displayValue ?? '未设置',
|
|
1040
|
+
after: draftRemoves.includes(key) ? '未设置' : currentFieldDisplay(field),
|
|
1041
|
+
};
|
|
1042
|
+
});
|
|
1043
|
+
};
|
|
1044
|
+
const beginConfigSave = async () => {
|
|
1045
|
+
if (!configRoute || configRoute.layer.scope === 'effective' || !currentConfigPage || !dirty)
|
|
1046
|
+
return;
|
|
1047
|
+
const fields = currentConfigPage.groups.flatMap((group) => group.fields);
|
|
1048
|
+
try {
|
|
1049
|
+
await beginReview({
|
|
1050
|
+
kind: 'configSave',
|
|
1051
|
+
pluginId: configRoute.pluginId,
|
|
1052
|
+
scope: configRoute.layer.scope,
|
|
1053
|
+
projectLayer: configRoute.layer.projectLayer,
|
|
1054
|
+
updates: draftUpdates,
|
|
1055
|
+
removeKeys: draftRemoves,
|
|
1056
|
+
sensitiveKeys: fields.filter((field) => field.sensitive).map((field) => field.key),
|
|
1057
|
+
privateKeys: fields.filter((field) => field.type === 'file' || field.type === 'directory').map((field) => field.key),
|
|
1058
|
+
changes: configChanges(),
|
|
1059
|
+
});
|
|
1060
|
+
setConfigActionError(undefined);
|
|
1061
|
+
}
|
|
1062
|
+
catch {
|
|
1063
|
+
setConfigActionError('设置值未通过验证。请重新检查修改。');
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
if (snapshot.phase === 'error') {
|
|
1067
|
+
breadcrumb = '启动失败';
|
|
1068
|
+
title = '无法读取插件状态';
|
|
1069
|
+
description = snapshot.loadError?.message ?? '当前不能安全显示插件状态。';
|
|
1070
|
+
help = 'q 退出 · Ctrl-C 退出';
|
|
1071
|
+
content = _jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", snapshot.loadError?.code ?? 'statusLoadFailed'] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u6267\u884C\u4EFB\u4F55\u5199\u64CD\u4F5C\u3002" })] });
|
|
1072
|
+
}
|
|
1073
|
+
else if (snapshot.phase !== 'ready') {
|
|
1074
|
+
breadcrumb = '启动';
|
|
1075
|
+
title = '正在检查插件状态';
|
|
1076
|
+
description = '单个来源失败不会阻止其他来源继续加载。';
|
|
1077
|
+
help = 'Ctrl-C 完成后退出';
|
|
1078
|
+
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: tuiTheme.statusColors.success, children: "\u2713 \u8BFB\u53D6\u63D2\u4EF6\u6765\u6E90" }), _jsx(Text, { color: tuiTheme.colors.accent, children: "\u25CF \u68C0\u67E5\u5F53\u524D\u9879\u76EE" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u25CB \u6C47\u603B\u53EF\u7528\u64CD\u4F5C" })] }));
|
|
1079
|
+
}
|
|
1080
|
+
else if (route.name === 'home') {
|
|
1081
|
+
const home = snapshot.viewModel.home;
|
|
1082
|
+
context = '当前项目';
|
|
1083
|
+
actions = [
|
|
1084
|
+
{ label: '添加插件', description: '浏览多个来源中的 Agent 能力', meta: String(home.availablePlugins), run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
1085
|
+
{ label: '我的插件', description: '查看状态、设置和移除已添加插件', meta: String(home.installedPlugins), run: () => go({ name: 'pluginList', mode: 'installed' }) },
|
|
1086
|
+
{ label: '需要处理', description: '解决安装、来源或设置问题', meta: String(home.issueCount), status: 'warning', run: () => go({ name: 'issueList' }) },
|
|
1087
|
+
{ label: '插件来源', description: '管理 Git 仓库和本地目录', meta: String(home.sourceCount), run: () => go({ name: 'sourceList' }) },
|
|
1088
|
+
];
|
|
1089
|
+
content = (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsxs(Text, { color: tuiTheme.statusColors.success, children: ["\u2713 ", home.installedPlugins, " \u4E2A\u63D2\u4EF6\u53EF\u7528"] }), ' ', _jsxs(Text, { color: tuiTheme.statusColors.warning, children: ["! ", home.issueCount, " \u9879\u9700\u8981\u5904\u7406"] }), home.cachedSourceCount > 0 ? _jsxs(_Fragment, { children: [' ', _jsxs(Text, { color: tuiTheme.statusColors.warning, children: ["\u25D0 ", home.cachedSourceCount, " \u4E2A\u6765\u6E90\u4F7F\u7528\u7F13\u5B58"] })] }) : null] }), home.unavailableSourceCount > 0 ? _jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", home.unavailableSourceCount, " \u4E2A\u6765\u6E90\u4E0D\u53EF\u7528"] }) : null, _jsx(Box, { marginTop: 1, children: _jsx(ActionRows, { actions: actions, focus: focus }) })] }));
|
|
1090
|
+
}
|
|
1091
|
+
else if (route.name === 'pluginList') {
|
|
1092
|
+
const sourceId = route.sourceId ?? 'all';
|
|
1093
|
+
const items = props.controller.listPlugins({
|
|
1094
|
+
mode: route.mode,
|
|
1095
|
+
...(sourceId !== 'all' ? { sourceId } : {}),
|
|
1096
|
+
...(query ? { query } : {}),
|
|
1097
|
+
});
|
|
1098
|
+
breadcrumb = route.mode === 'browse' ? '添加插件' : '我的插件';
|
|
1099
|
+
title = breadcrumb;
|
|
1100
|
+
description = route.mode === 'browse' ? '选择希望 Agent 获得的能力。' : '查看状态,或选择一个插件进行管理。';
|
|
1101
|
+
context = `${items.length} 个插件`;
|
|
1102
|
+
help = '↑↓ 移动 · Enter 查看 · ←→ 来源 · / 搜索 · Esc 返回';
|
|
1103
|
+
const sourceError = sourceId !== 'all' && props.controller.getSourceOverview(sourceId).source?.state === 'error';
|
|
1104
|
+
actions = sourceError
|
|
1105
|
+
? [{ label: '查看来源问题', description: '没有可用缓存;当前不能浏览这个来源', run: () => go({ name: 'issueDetail', issueId: `source:${sourceId}:unavailable` }) }]
|
|
1106
|
+
: items.map((item) => ({
|
|
1107
|
+
label: item.displayName,
|
|
1108
|
+
description: `${item.description}${sourceId === 'all' ? ` · ${item.sourceLabel}` : ''}`,
|
|
1109
|
+
meta: statusText(item),
|
|
1110
|
+
status: item.aggregateStatus === 'needsAttention' ? 'warning'
|
|
1111
|
+
: item.aggregateStatus === 'installed' ? 'success'
|
|
1112
|
+
: item.aggregateStatus === 'unavailable' ? 'error' : 'muted',
|
|
1113
|
+
run: () => go(route.mode === 'browse'
|
|
1114
|
+
? { name: 'pluginPreview', pluginId: item.pluginId }
|
|
1115
|
+
: { name: 'pluginDetail', pluginId: item.pluginId }),
|
|
1116
|
+
}));
|
|
1117
|
+
if (route.mode === 'installed' && items.length === 0 && !sourceError) {
|
|
1118
|
+
title = '还没有添加插件';
|
|
1119
|
+
description = `从已配置的 ${props.controller.listSources().length} 个来源中浏览可用能力。`;
|
|
1120
|
+
actions = [{ label: '浏览插件', description: '开始添加第一个插件', run: () => go({ name: 'pluginList', mode: 'browse' }) }];
|
|
1121
|
+
}
|
|
1122
|
+
const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
|
|
1123
|
+
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(SourceTabs, { controller: props.controller, current: sourceId, compact: policy.mode === 'compact', mode: route.mode }), _jsxs(Text, { color: searchMode ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["/ ", searchMode ? query || '输入搜索内容' : '搜索名称、用途或来源…'] }), sourceError
|
|
1124
|
+
? _jsxs(Bordered, { children: [_jsxs(Text, { bold: true, children: [sourceId, " \u6682\u65F6\u65E0\u6CD5\u63D0\u4F9B\u63D2\u4EF6"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u53EF\u7528\u7F13\u5B58\uFF0C\u56E0\u6B64\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })] })
|
|
1125
|
+
: route.mode === 'installed' && items.length === 0
|
|
1126
|
+
? _jsxs(Bordered, { children: [_jsx(Text, { bold: true, children: "\u5F00\u59CB\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u63D2\u4EF6" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u63D2\u4EF6\u53EF\u4EE5\u5B89\u88C5\u5230 Codex\u3001Claude Code \u6216\u4E24\u8005\u3002" })] })
|
|
1127
|
+
: null, _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit })] }));
|
|
1128
|
+
}
|
|
1129
|
+
else if (route.name === 'pluginPreview' && plugin) {
|
|
1130
|
+
breadcrumb = `添加插件 / ${plugin.displayName}`;
|
|
1131
|
+
context = plugin.sourceLabel;
|
|
1132
|
+
title = `${plugin.displayName}${plugin.version ? ` ${plugin.version}` : ''}`;
|
|
1133
|
+
description = plugin.description;
|
|
1134
|
+
const overview = dependencyOverview(plugin);
|
|
1135
|
+
const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
|
|
1136
|
+
const beginInstall = () => {
|
|
1137
|
+
const initialScope = ['user', 'project'].find((candidateScope) => selectedPlatforms(plugin, candidateScope).some((entry) => !entry.disabled)) ?? scope;
|
|
1138
|
+
const available = selectedPlatforms(plugin, initialScope)
|
|
1139
|
+
.filter((entry) => !entry.disabled).map((entry) => entry.platform);
|
|
1140
|
+
setScope(initialScope);
|
|
1141
|
+
setTools(available);
|
|
1142
|
+
if (available.length > 0)
|
|
1143
|
+
go({ name: 'installPlacement', pluginId: plugin.pluginId });
|
|
1144
|
+
else
|
|
1145
|
+
go({ name: 'installTools', pluginId: plugin.pluginId, view: 'blocked' });
|
|
1146
|
+
};
|
|
1147
|
+
const portableAction = plugin.portableSkills
|
|
1148
|
+
? [{ label: '安装通用 Skills…', description: '选择 Skill、Agent 和使用范围', run: () => beginPortable(plugin.pluginId) }]
|
|
1149
|
+
: [];
|
|
1150
|
+
actions = hasDependencies && (overview?.blockers.length ?? 0) > 0
|
|
1151
|
+
? [
|
|
1152
|
+
{ label: '查看依赖问题', description: overview.blockers[0].explanation, status: 'warning', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
|
|
1153
|
+
...portableAction,
|
|
1154
|
+
{ label: '查看依赖关系', description: '查看直接、间接依赖和当前状态', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
|
|
1155
|
+
{ label: '查看插件设置定义', description: `${plugin.configFieldCount} 个配置字段`, run: () => go({ name: 'configLayer', pluginId: plugin.pluginId }) },
|
|
1156
|
+
]
|
|
1157
|
+
: [
|
|
1158
|
+
{ label: '添加插件', description: '选择使用位置并确认变化', run: beginInstall },
|
|
1159
|
+
...portableAction,
|
|
1160
|
+
...(hasDependencies ? [{ label: '查看依赖关系', description: '查看直接、间接依赖和当前状态', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) }] : []),
|
|
1161
|
+
{ label: '查看插件设置定义', description: `${plugin.configFieldCount} 个配置字段`, run: () => go({ name: 'configLayer', pluginId: plugin.pluginId }) },
|
|
1162
|
+
{ label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) },
|
|
1163
|
+
];
|
|
1164
|
+
const summaryRows = (relation) => {
|
|
1165
|
+
const dependencies = overview?.dependencies.filter((entry) => entry.relation === relation) ?? [];
|
|
1166
|
+
const visible = dependencies.slice(0, 3);
|
|
1167
|
+
return [
|
|
1168
|
+
...visible.map((entry) => _jsxs(Text, { children: [_jsxs(Text, { color: relation === 'direct' ? tuiTheme.colors.text : tuiTheme.colors.muted, children: ["\u2022 ", entry.displayName] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: [" ", relation === 'direct' ? '直接依赖' : '间接依赖'] })] }, entry.pluginId)),
|
|
1169
|
+
...(dependencies.length > visible.length
|
|
1170
|
+
? [_jsxs(Text, { color: tuiTheme.colors.muted, children: [" \u53E6\u6709 ", dependencies.length - visible.length, " \u4E2A", relation === 'direct' ? '直接' : '间接', "\u4F9D\u8D56"] }, `${relation}:more`)]
|
|
1171
|
+
: []),
|
|
1172
|
+
];
|
|
1173
|
+
};
|
|
1174
|
+
content = hasDependencies
|
|
1175
|
+
? _jsxs(Box, { flexDirection: "column", children: [plugin.portableSkills ? _jsx(Bordered, { title: "\u53EF\u7528\u65B9\u5F0F", children: _jsx(Text, { children: "\u901A\u7528 Skills \u63D0\u4F9B\u901A\u7528 Skills" }) }) : null, _jsxs(Bordered, { title: "\u5B89\u88C5\u5185\u5BB9", children: [_jsxs(Text, { children: [_jsxs(Text, { color: tuiTheme.colors.accent, children: ["\u2022 ", plugin.displayName] }), _jsx(Text, { color: tuiTheme.colors.muted, children: " \u4F60\u9009\u62E9\u7684\u63D2\u4EF6" })] }), summaryRows('direct'), summaryRows('transitive'), _jsxs(Text, { color: tuiTheme.colors.muted, children: [plugin.dependencySummary.totalCount + 1, " \u4E2A\u63D2\u4EF6\u5C06\u4F5C\u4E3A\u4E00\u4E2A\u5B89\u88C5\u8BA1\u5212\u68C0\u67E5\u3002"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u4F9D\u8D56\u4ECE\u540C\u4E00\u4E2A\u6765\u6E90\u5FEB\u7167\u89E3\u6790\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] })
|
|
1176
|
+
: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { title: "\u53EF\u7528\u5DE5\u5177", children: [_jsx(Text, { children: plugin.supportedPlatforms.map(platformLabel).join(' · ') || '暂无可用工具' }), plugin.portableSkills ? _jsx(Text, { children: "\u901A\u7528 Skills \u63D0\u4F9B\u901A\u7528 Skills" }) : null] }), _jsxs(Bordered, { title: "\u4F7F\u7528\u8BF4\u660E", children: [_jsx(Text, { children: "\u2713 \u5728 Agent \u4F1A\u8BDD\u4E2D\u63D0\u4F9B\u80FD\u529B" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5B89\u88C5\u4E0D\u4F1A\u81EA\u52A8\u4FEE\u6539\u9879\u76EE\u6E90\u7801\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1177
|
+
}
|
|
1178
|
+
else if (route.name === 'portableSkillSelect' && plugin && snapshot.portableDraft) {
|
|
1179
|
+
const draft = snapshot.portableDraft;
|
|
1180
|
+
const focusedCandidate = draft.candidates[focus];
|
|
1181
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills`;
|
|
1182
|
+
context = `${draft.selectedCount} / ${draft.totalCount} 已选择`;
|
|
1183
|
+
title = '选择要安装的 Skills';
|
|
1184
|
+
description = '已根据插件依赖和当前设置准备候选;默认全部选择。';
|
|
1185
|
+
const candidateActions = draft.candidates.map((candidate) => ({
|
|
1186
|
+
label: `${candidate.selected ? '◆' : '◇'} ${candidate.name}`,
|
|
1187
|
+
description: portableScopeVariantsDiffer(candidate)
|
|
1188
|
+
? '当前项目与所有项目提供不同内容;下方显示两种范围变体'
|
|
1189
|
+
: `${candidate.description} · ${candidate.providerDisplayName} · ${candidate.relation === 'requested' ? '当前插件' : candidate.relation === 'direct' ? '直接依赖' : '间接依赖'}${candidate.dynamic ? ' · 按设置生成' : ''}`,
|
|
1190
|
+
run: () => props.controller.togglePortableSkill(candidate.name),
|
|
1191
|
+
}));
|
|
1192
|
+
actions = [
|
|
1193
|
+
...candidateActions,
|
|
1194
|
+
{ label: '继续', disabled: draft.canContinue ? undefined : '至少选择一个 Skill', run: () => go({ name: 'portableAgentMode', pluginId: plugin.pluginId }) },
|
|
1195
|
+
...(plugin.dependencySummary?.totalCount ? [{ label: '查看依赖关系', run: () => go({ name: 'portableDependencies', pluginId: plugin.pluginId }) }] : []),
|
|
1196
|
+
];
|
|
1197
|
+
const viewport = portableSkillCandidateViewport(candidateActions, draft.candidates, policy.mode, focus, scrollOffset);
|
|
1198
|
+
help = '↑↓ 移动 · Space 切换 · a 全选 · Enter 继续 · Esc 返回';
|
|
1199
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PortableSkillRows, { actions: candidateActions, candidates: draft.candidates, focus: focus, offset: viewport.offset, limit: viewport.limit }), focusedCandidate && portableScopeVariantsDiffer(focusedCandidate) ? _jsxs(Bordered, { title: "\u8303\u56F4\u53D8\u4F53", children: [_jsx(Text, { children: portableScopeVariantLabel('project', focusedCandidate.scopeDetails.project) }), _jsx(Text, { children: portableScopeVariantLabel('user', focusedCandidate.scopeDetails.user) })] }) : null, _jsx(ActionRows, { actions: actions, focus: focus, offset: draft.candidates.length, limit: actions.length - draft.candidates.length })] });
|
|
1200
|
+
}
|
|
1201
|
+
else if (route.name === 'portableDependencies' && plugin && snapshot.portableDraft) {
|
|
1202
|
+
const overview = dependencyOverview(plugin);
|
|
1203
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills / 依赖`;
|
|
1204
|
+
context = `${(overview?.summary.totalCount ?? 0) + 1} 个插件`;
|
|
1205
|
+
title = '生成这些候选 Skills 需要的插件';
|
|
1206
|
+
description = '插件依赖用于生成,不能在这里取消。';
|
|
1207
|
+
actions = [
|
|
1208
|
+
...(overview?.dependencies ?? []).map((dependency) => ({
|
|
1209
|
+
label: dependency.displayName,
|
|
1210
|
+
description: `${dependency.relation === 'direct' ? '直接依赖' : '间接依赖'} · 贡献 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.providerPluginId === dependency.pluginId).length} 个候选 Skill`,
|
|
1211
|
+
})),
|
|
1212
|
+
{ label: '返回 Skill 选择', run: back },
|
|
1213
|
+
{ label: '技术详情', run: () => setShowTechnical((value) => !value) },
|
|
1214
|
+
];
|
|
1215
|
+
const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
|
|
1216
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u5F53\u524D\u63D2\u4EF6", children: _jsxs(Text, { children: [plugin.displayName, " \u00B7 \u8D21\u732E ", snapshot.portableDraft.candidates.filter((candidate) => candidate.relation === 'requested').length, " \u4E2A\u5019\u9009 Skills"] }) }), _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit }), showTechnical ? _jsx(Text, { color: tuiTheme.colors.muted, children: overview?.dependencies.map((entry) => entry.pluginId).join('\n') }) : null] });
|
|
1217
|
+
}
|
|
1218
|
+
else if (route.name === 'portableAgentMode' && plugin && snapshot.portableDraft) {
|
|
1219
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
|
|
1220
|
+
title = '安装到哪些 Agent?';
|
|
1221
|
+
description = '可以自动选择检测到的 Agent,也可以限制到指定 Agent。';
|
|
1222
|
+
actions = [
|
|
1223
|
+
{ label: '自动选择', description: '自动选择检测到的 Agent;未检测到时可能选择全部 Agent', meta: '默认', run: () => { props.controller.setPortableAgentMode('automatic'); go({ name: 'portableScope', pluginId: plugin.pluginId }); } },
|
|
1224
|
+
{ label: '指定 Agent…', description: '输入一个或多个 Skills CLI ID', run: () => go({ name: 'portableAgentInput', pluginId: plugin.pluginId }) },
|
|
1225
|
+
];
|
|
1226
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1227
|
+
}
|
|
1228
|
+
else if (route.name === 'portableAgentInput' && plugin) {
|
|
1229
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
|
|
1230
|
+
context = '指定';
|
|
1231
|
+
title = '输入 Agent ID';
|
|
1232
|
+
description = inputError ?? '多个 Agent 使用空格分隔。';
|
|
1233
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: _jsx(Text, { children: portableAgentInput || ' ' }) }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u793A\u4F8B\uFF1Acursor claude-code codex gemini-cli" })] });
|
|
1234
|
+
}
|
|
1235
|
+
else if (route.name === 'portableScope' && plugin && snapshot.portableDraft) {
|
|
1236
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills / 范围`;
|
|
1237
|
+
title = '在哪里使用这些 Skills?';
|
|
1238
|
+
description = `已选择 ${snapshot.portableDraft.selectedCount} 个 Skills · Agent:${snapshot.portableDraft.agentMode === 'automatic' ? '自动选择' : snapshot.portableDraft.requestedAgents.join('、')}`;
|
|
1239
|
+
actions = [
|
|
1240
|
+
{ label: '当前项目', description: '安装到当前项目的 Skill 目录', meta: '推荐', run: () => beginPortableReview(plugin.pluginId, 'project') },
|
|
1241
|
+
{ label: '所有项目', description: '安装到用户级 Skill 目录', run: () => beginPortableReview(plugin.pluginId, 'user') },
|
|
1242
|
+
];
|
|
1243
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1244
|
+
}
|
|
1245
|
+
else if (route.name === 'portableBlocker' && plugin) {
|
|
1246
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills`;
|
|
1247
|
+
title = route.code === 'requiredConfigMissing' ? '无法准备通用 Skills'
|
|
1248
|
+
: route.code === 'portableScopeUnavailable' ? '所选范围不可用'
|
|
1249
|
+
: route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '暂时无法安装通用 Skills' : '无法准备通用 Skills';
|
|
1250
|
+
description = route.code === 'requiredConfigMissing' ? '插件缺少生成内容所需的设置。'
|
|
1251
|
+
: route.code === 'portableScopeUnavailable' ? '至少一个所选 Skill 不适用于这个范围。请返回 Skill 选择或更改范围。'
|
|
1252
|
+
: route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '没有找到兼容的 Skills CLI。' : '候选 Skill 没有通过 Portable 校验。';
|
|
1253
|
+
actions = [
|
|
1254
|
+
...(route.code === 'requiredConfigMissing' ? [{ label: '完成插件设置', run: () => {
|
|
1255
|
+
props.controller.replace({ name: 'configLayer', pluginId: plugin.pluginId });
|
|
1256
|
+
setFocus(0);
|
|
1257
|
+
setScrollOffset(0);
|
|
1258
|
+
} }] : []),
|
|
1259
|
+
...(route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible'
|
|
1260
|
+
? [{ label: '查看安装说明', run: () => setShowTechnical((value) => !value) }] : []),
|
|
1261
|
+
...(route.code === 'portableScopeUnavailable' ? [{ label: '返回 Skill 选择', run: () => {
|
|
1262
|
+
props.controller.resetPortableFlowToPreview(plugin.pluginId, { name: 'portableSkillSelect', pluginId: plugin.pluginId }, true);
|
|
1263
|
+
setFocus(0);
|
|
1264
|
+
setScrollOffset(0);
|
|
1265
|
+
} }] : []),
|
|
1266
|
+
{ label: '重新检查', run: () => beginPortable(plugin.pluginId) },
|
|
1267
|
+
{ label: '返回插件预览', run: () => props.controller.resetPortableFlowToPreview(plugin.pluginId) },
|
|
1268
|
+
];
|
|
1269
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", route.code] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u8C03\u7528 Skills CLI \u5B89\u88C5\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus }), showTechnical ? _jsx(Bordered, { title: route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '安装说明' : '技术详情', children: _jsx(Text, { color: tuiTheme.colors.muted, children: route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '安装或升级兼容的 Skills CLI(>=1.5.23 <2.0.0),然后重新检查。' : `诊断代码:${route.diagnosticCode}` }) }) : _jsx(Text, { color: tuiTheme.colors.muted, children: "d \u67E5\u770B\u8131\u654F\u8BCA\u65AD" })] });
|
|
1270
|
+
}
|
|
1271
|
+
else if (route.name === 'pluginDependencies' && plugin) {
|
|
1272
|
+
const overview = dependencyOverview(plugin);
|
|
1273
|
+
const visibleDependents = route.view === 'removeBlocked' && route.platform && route.scope
|
|
1274
|
+
? props.controller.getBlockingDependents(plugin.pluginId, route.platform, route.scope)
|
|
1275
|
+
: overview?.dependents ?? [];
|
|
1276
|
+
const relationActions = [
|
|
1277
|
+
...(overview?.dependencies ?? []).map((entry) => {
|
|
1278
|
+
const state = dependencyStatus(entry);
|
|
1279
|
+
return {
|
|
1280
|
+
label: entry.displayName,
|
|
1281
|
+
description: dependencyRelationDescription(entry),
|
|
1282
|
+
meta: dependencyPlacementLabel(entry),
|
|
1283
|
+
status: state.status,
|
|
1284
|
+
run: () => go({ name: 'pluginDetail', pluginId: entry.pluginId }),
|
|
1285
|
+
};
|
|
1286
|
+
}),
|
|
1287
|
+
...visibleDependents.map((entry) => {
|
|
1288
|
+
const state = dependencyStatus(entry);
|
|
1289
|
+
return {
|
|
1290
|
+
label: entry.displayName,
|
|
1291
|
+
description: dependencyRelationDescription(entry),
|
|
1292
|
+
meta: route.view === 'removeBlocked' ? dependentTargetLabel(entry) : dependencyPlacementLabel(entry),
|
|
1293
|
+
status: state.status,
|
|
1294
|
+
run: () => go({ name: 'pluginDetail', pluginId: entry.pluginId }),
|
|
1295
|
+
};
|
|
1296
|
+
}),
|
|
1297
|
+
];
|
|
1298
|
+
const dependencyCount = overview?.dependencies.length ?? 0;
|
|
1299
|
+
const dependentCount = visibleDependents.length;
|
|
1300
|
+
if (route.view === 'removeBlocked') {
|
|
1301
|
+
breadcrumb = `${plugin.displayName} / 移除`;
|
|
1302
|
+
context = '已阻止';
|
|
1303
|
+
title = `无法移除 ${plugin.displayName}`;
|
|
1304
|
+
description = '仍有插件依赖它。请先处理依赖者,再重新尝试。';
|
|
1305
|
+
actions = [
|
|
1306
|
+
...relationActions.slice(dependencyCount),
|
|
1307
|
+
...(route.platform && route.scope ? [{
|
|
1308
|
+
label: '强制清理…',
|
|
1309
|
+
description: '依赖者会变为不完整;下一页仍需明确确认',
|
|
1310
|
+
status: 'error',
|
|
1311
|
+
run: () => beginReview({
|
|
1312
|
+
kind: 'pluginWrite',
|
|
1313
|
+
action: 'cleanup',
|
|
1314
|
+
pluginId: plugin.pluginId,
|
|
1315
|
+
platform: route.platform,
|
|
1316
|
+
scope: route.scope,
|
|
1317
|
+
force: true,
|
|
1318
|
+
}),
|
|
1319
|
+
}] : []),
|
|
1320
|
+
{ label: '查看完整依赖关系', run: () => {
|
|
1321
|
+
props.controller.replace({ name: 'pluginDependencies', pluginId: plugin.pluginId });
|
|
1322
|
+
setFocus(0);
|
|
1323
|
+
setScrollOffset(0);
|
|
1324
|
+
} },
|
|
1325
|
+
{ label: '返回插件详情', run: back },
|
|
1326
|
+
];
|
|
1327
|
+
const consumerActions = actions.slice(0, dependentCount);
|
|
1328
|
+
const blockedViewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
|
|
1329
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", dependentCount, " \u4E2A\u5DF2\u5B89\u88C5\u63D2\u4EF6\u4ECD\u5728\u4F7F\u7528"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u4FEE\u6539\u4EFB\u4F55\u5B89\u88C5\u3002" })] }), _jsx(Bordered, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", children: _jsx(ActionRows, { actions: consumerActions, focus: focus, offset: blockedViewport.offset, limit: blockedViewport.limit }) }), _jsx(ActionRows, { actions: actions.slice(dependentCount), focus: focus - dependentCount })] });
|
|
1330
|
+
}
|
|
1331
|
+
else {
|
|
1332
|
+
breadcrumb = `${plugin.displayName} / 依赖关系`;
|
|
1333
|
+
context = overview ? `${overview.summary.totalCount + 1} 个插件` : '只读';
|
|
1334
|
+
title = hasInstalledPlacement(plugin) ? '依赖与被使用情况' : '安装所需插件';
|
|
1335
|
+
description = '依赖从同一个来源快照解析;关系页不会修改安装。';
|
|
1336
|
+
actions = [
|
|
1337
|
+
...relationActions,
|
|
1338
|
+
{ label: '技术详情', description: '显示 canonical pluginId 和安全错误代码', run: () => setShowTechnical((value) => !value) },
|
|
1339
|
+
];
|
|
1340
|
+
const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
|
|
1341
|
+
const visibleRows = (start, end) => relationActions
|
|
1342
|
+
.map((_, index) => index)
|
|
1343
|
+
.filter((index) => index >= start && index < end
|
|
1344
|
+
&& index >= viewport.offset
|
|
1345
|
+
&& (viewport.limit === undefined || index < viewport.offset + viewport.limit))
|
|
1346
|
+
.map((index) => _jsx(ActionRows, { actions: actions, focus: focus, offset: index, limit: 1 }, index));
|
|
1347
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u4F60\u9009\u62E9\u7684\u63D2\u4EF6", children: _jsxs(Text, { children: [plugin.displayName, " ", _jsx(Text, { color: tuiTheme.colors.muted, children: statusText(plugin) })] }) }), dependencyCount > 0 ? _jsx(Bordered, { title: "\u5FC5\u9700\u4F9D\u8D56", children: visibleRows(0, dependencyCount) }) : null, dependentCount > 0 ? _jsx(Bordered, { title: "\u4F7F\u7528\u5B83\u7684\u63D2\u4EF6", children: visibleRows(dependencyCount, dependencyCount + dependentCount) }) : null, overview?.blockers.map((blocker) => _jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", blocker.title] }), _jsx(Text, { color: tuiTheme.colors.muted, children: blocker.explanation })] }, blocker.code)), relationActions.length === 0 ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u53EF\u663E\u793A\u7684\u4F9D\u8D56\u5173\u7CFB\u3002" }) : null, viewport.limit !== undefined && viewport.offset + viewport.limit > relationActions.length
|
|
1348
|
+
? _jsx(ActionRows, { actions: actions, focus: focus, offset: relationActions.length, limit: 1 })
|
|
1349
|
+
: null, showTechnical ? _jsxs(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["canonical pluginId ", plugin.pluginId] }), overview?.dependencies.map((entry) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [entry.pluginId, " \u00B7 ", dependencyRelationDescription(entry)] }, entry.pluginId))] }) : null] });
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
else if (route.name === 'installPlacement' && plugin) {
|
|
1353
|
+
breadcrumb = `${plugin.displayName} / 添加`;
|
|
1354
|
+
context = '第 1 步,共 2 步';
|
|
1355
|
+
title = `你希望在哪里使用 ${plugin.displayName}?`;
|
|
1356
|
+
const overview = dependencyOverview(plugin);
|
|
1357
|
+
const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
|
|
1358
|
+
description = hasDependencies
|
|
1359
|
+
? '根插件和缺失依赖使用同一个位置;健康的所有项目依赖可在当前项目复用。'
|
|
1360
|
+
: '工具已根据插件能力自动选择;需要时可以调整。';
|
|
1361
|
+
const availableScopes = ['user', 'project'].filter((candidateScope) => selectedPlatforms(plugin, candidateScope).some((entry) => !entry.disabled));
|
|
1362
|
+
const reusableDependencies = overview?.dependencies.filter((dependency) => dependency.placements.some((placement) => placement.state === 'installed' && placement.health === 'ready'
|
|
1363
|
+
&& (scope === 'project' || placement.scope === 'user'))).length ?? 0;
|
|
1364
|
+
const missingDependencies = Math.max(0, (overview?.summary.totalCount ?? 0) - reusableDependencies);
|
|
1365
|
+
actions = [
|
|
1366
|
+
...availableScopes.map((candidateScope) => ({
|
|
1367
|
+
label: `${candidateScope === scope ? '●' : '○'} ${scopeLabel(candidateScope)}`,
|
|
1368
|
+
description: candidateScope === 'user'
|
|
1369
|
+
? hasDependencies ? '根插件和缺失依赖都可在任意项目中使用' : '在任意项目中可使用,适合个人常用能力'
|
|
1370
|
+
: hasDependencies ? '缺失依赖添加到当前项目;已有所有项目依赖会复用' : '仅在当前项目中使用,适合团队共享',
|
|
1371
|
+
meta: candidateScope === 'user' ? '推荐' : undefined,
|
|
1372
|
+
run: () => {
|
|
1373
|
+
setScope(candidateScope);
|
|
1374
|
+
setTools(selectedPlatforms(plugin, candidateScope).filter((entry) => !entry.disabled).map((entry) => entry.platform));
|
|
1375
|
+
},
|
|
1376
|
+
})),
|
|
1377
|
+
{ label: '继续', disabled: tools.length === 0 ? '没有可安装的工具' : undefined, run: async () => beginReview(installIntent(plugin)) },
|
|
1378
|
+
{ label: '调整工具…', run: () => {
|
|
1379
|
+
const choices = selectedPlatforms(plugin, scope);
|
|
1380
|
+
go({ name: 'installTools', pluginId: plugin.pluginId,
|
|
1381
|
+
...(choices.every((choice) => choice.disabled) ? { view: 'blocked' } : {}) });
|
|
1382
|
+
} },
|
|
1383
|
+
];
|
|
1384
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ActionRows, { actions: actions.slice(0, availableScopes.length), focus: focus }), hasDependencies ? _jsxs(Bordered, { title: "\u4F9D\u8D56\u5904\u7406", children: [_jsxs(Text, { color: reusableDependencies > 0 ? tuiTheme.statusColors.success : tuiTheme.colors.muted, children: ["\u2713 ", reusableDependencies, " \u4E2A\u4F9D\u8D56\u4F1A\u590D\u7528\u73B0\u6709\u5B89\u88C5"] }), _jsxs(Text, { color: missingDependencies > 0 ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["+ ", missingDependencies, " \u4E2A\u4F9D\u8D56\u5C06\u5B89\u88C5\u5230\u6240\u9009\u4F4D\u7F6E"] })] }) : null, _jsx(Bordered, { title: "\u53EF\u7528\u5DE5\u5177", children: _jsxs(Text, { children: [tools.map(platformLabel).join('、') || '当前没有可用工具', "\uFF08\u5DF2\u81EA\u52A8\u9009\u62E9\uFF09"] }) }), _jsx(ActionRows, { actions: actions.slice(availableScopes.length), focus: focus - availableScopes.length })] });
|
|
1385
|
+
}
|
|
1386
|
+
else if (route.name === 'installTools' && plugin) {
|
|
1387
|
+
breadcrumb = `${plugin.displayName} / 添加 / 工具`;
|
|
1388
|
+
context = '可选';
|
|
1389
|
+
title = '选择要添加到哪些工具';
|
|
1390
|
+
const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
|
|
1391
|
+
description = hasDependencies
|
|
1392
|
+
? '只有根插件和所有依赖都支持所选位置的工具才可选择。'
|
|
1393
|
+
: '同一插件、同一平台只能安装到一个位置。';
|
|
1394
|
+
const choices = selectedPlatforms(plugin, scope);
|
|
1395
|
+
const allUnavailable = choices.length > 0 && choices.every((choice) => choice.disabled);
|
|
1396
|
+
if (route.view === 'blocked' || allUnavailable) {
|
|
1397
|
+
context = '依赖限制';
|
|
1398
|
+
title = '无法添加到所选工具';
|
|
1399
|
+
description = '根插件或必需依赖不支持当前工具组合,因此不会进入确认页。';
|
|
1400
|
+
actions = [
|
|
1401
|
+
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
|
|
1402
|
+
{ label: '返回调整位置', run: back },
|
|
1403
|
+
];
|
|
1404
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: choices.map((choice) => _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u25C7 ", platformLabel(choice.platform), " \u4E0D\u53EF\u9009", choice.disabled ? ` · ${choice.disabled}` : ''] }, choice.platform)) }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1405
|
+
}
|
|
1406
|
+
else {
|
|
1407
|
+
actions = choices.map((choice) => ({
|
|
1408
|
+
label: `${tools.includes(choice.platform) ? '◆' : '◇'} ${platformLabel(choice.platform)}`,
|
|
1409
|
+
description: choice.disabled ?? '当前未安装,支持所选位置',
|
|
1410
|
+
meta: tools.includes(choice.platform) ? '已选择' : undefined,
|
|
1411
|
+
disabled: choice.disabled,
|
|
1412
|
+
run: () => {
|
|
1413
|
+
setDirty(true);
|
|
1414
|
+
setTools((current) => current.includes(choice.platform)
|
|
1415
|
+
? current.filter((platform) => platform !== choice.platform)
|
|
1416
|
+
: [...current, choice.platform]);
|
|
1417
|
+
},
|
|
1418
|
+
}));
|
|
1419
|
+
help = '↑↓ 移动 · Space 切换 · Enter 完成 · Esc 返回';
|
|
1420
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ActionRows, { actions: actions, focus: focus }), hasDependencies ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u4E0D\u517C\u5BB9\u7684\u5DE5\u5177\u4E0D\u80FD\u9009\u62E9\uFF1B\u8BF7\u67E5\u770B\u4F9D\u8D56\u5173\u7CFB\u4E86\u89E3\u539F\u56E0\u3002" }) : null, _jsxs(Text, { color: tools.length > 0 ? tuiTheme.colors.accent : tuiTheme.statusColors.warning, children: ["Enter ", tools.length > 0 ? '完成选择' : '至少选择一个工具'] })] });
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
else if (route.name === 'pluginDetail' && plugin) {
|
|
1424
|
+
breadcrumb = `我的插件 / ${plugin.displayName}`;
|
|
1425
|
+
context = `${plugin.sourceLabel}${plugin.version ? ` · ${plugin.version}` : ''}`;
|
|
1426
|
+
title = plugin.displayName;
|
|
1427
|
+
description = plugin.description;
|
|
1428
|
+
const dependencySummary = plugin.dependencySummary;
|
|
1429
|
+
const placementActions = plugin.installedPlacements.map((placement) => {
|
|
1430
|
+
const projected = placementStatus(placement);
|
|
1431
|
+
return {
|
|
1432
|
+
label: platformLabel(placement.platform),
|
|
1433
|
+
description: projected.description,
|
|
1434
|
+
meta: projected.value,
|
|
1435
|
+
status: projected.status,
|
|
1436
|
+
run: () => placement.state === 'scopeConflict'
|
|
1437
|
+
? go({ name: 'issueDetail', issueId: `scope:${plugin.pluginId}:${placement.platform}` })
|
|
1438
|
+
: go({ name: 'platform', pluginId: plugin.pluginId, platform: placement.platform }),
|
|
1439
|
+
};
|
|
1440
|
+
});
|
|
1441
|
+
const dependencyActions = dependencySummary ? [{
|
|
1442
|
+
label: '必需依赖',
|
|
1443
|
+
description: dependencySummary.issueCount > 0 ? `${dependencySummary.issueCount} 项需要处理` : '查看直接、间接依赖和被使用情况',
|
|
1444
|
+
meta: dependencySummaryText(dependencySummary),
|
|
1445
|
+
status: dependencySummary.issueCount > 0 ? 'warning' : undefined,
|
|
1446
|
+
run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }),
|
|
1447
|
+
}] : [];
|
|
1448
|
+
const portableActions = plugin.portableSkills ? [{
|
|
1449
|
+
label: '通用 Skills',
|
|
1450
|
+
description: '由插件提供;安装状态未知',
|
|
1451
|
+
meta: '已提供',
|
|
1452
|
+
run: () => beginPortable(plugin.pluginId),
|
|
1453
|
+
}] : [];
|
|
1454
|
+
actions = [
|
|
1455
|
+
...placementActions,
|
|
1456
|
+
...dependencyActions,
|
|
1457
|
+
...portableActions,
|
|
1458
|
+
{ label: '设置', description: '个人默认、当前项目和本机覆盖', meta: `${plugin.configFieldCount} 项配置`, run: () => go({ name: 'configLayer', pluginId: plugin.pluginId }) },
|
|
1459
|
+
{ label: '关于与技术详情', description: '版本、来源和受管内容', run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }) },
|
|
1460
|
+
];
|
|
1461
|
+
const dependencyOffset = placementActions.length;
|
|
1462
|
+
const portableOffset = dependencyOffset + dependencyActions.length;
|
|
1463
|
+
const settingOffset = portableOffset + portableActions.length;
|
|
1464
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u4F7F\u7528\u72B6\u6001", children: _jsx(ActionRows, { actions: placementActions, focus: focus }) }), dependencySummary ? _jsx(Bordered, { title: "\u4F9D\u8D56\u5173\u7CFB", children: _jsx(ActionRows, { actions: dependencyActions, focus: focus - dependencyOffset }) }) : null, plugin.portableSkills ? _jsx(Bordered, { title: "\u901A\u7528 Skills", children: _jsx(ActionRows, { actions: portableActions, focus: focus - portableOffset }) }) : null, _jsx(Bordered, { title: "\u63D2\u4EF6\u8BBE\u7F6E", children: _jsx(ActionRows, { actions: actions.slice(settingOffset, -1), focus: focus - settingOffset }) }), _jsx(ActionRows, { actions: actions.slice(-1), focus: focus - actions.length + 1 })] });
|
|
1465
|
+
}
|
|
1466
|
+
else if (route.name === 'platform' && plugin) {
|
|
1467
|
+
const placement = props.controller.getPlatformPlacement(plugin.pluginId, route.platform);
|
|
1468
|
+
breadcrumb = `${plugin.displayName} / ${platformLabel(route.platform)}`;
|
|
1469
|
+
if (plugin.aggregateStatus === 'unavailable') {
|
|
1470
|
+
title = `暂时无法管理 ${platformLabel(route.platform)}`;
|
|
1471
|
+
description = '插件来源不可用,当前状态不能安全确认。';
|
|
1472
|
+
actions = [{ label: '查看来源问题', description: `${plugin.sourceId} 无可用缓存`, run: () => go({ name: 'issueDetail', issueId: `source:${plugin.sourceId}:unavailable` }) }, { label: '重新检查状态', description: '不会修改现场内容', run: () => props.controller.refreshSource(plugin.sourceId) }];
|
|
1473
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.error, children: "\u00D7 \u6765\u6E90\u4E0D\u53EF\u7528" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u63D2\u4EF6\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1474
|
+
}
|
|
1475
|
+
else if (!placement || placement.state === 'notInstalled') {
|
|
1476
|
+
title = `尚未添加到 ${platformLabel(route.platform)}`;
|
|
1477
|
+
description = '此插件支持所列使用位置。';
|
|
1478
|
+
actions = [{ label: `添加到 ${platformLabel(route.platform)}…`, description: '选择使用位置', run: () => {
|
|
1479
|
+
const initialScope = placement?.state === 'notInstalled'
|
|
1480
|
+
? placement.supportedScopes[0] ?? scope
|
|
1481
|
+
: scope;
|
|
1482
|
+
setScope(initialScope);
|
|
1483
|
+
setTools([route.platform]);
|
|
1484
|
+
go({ name: 'installPlacement', pluginId: plugin.pluginId });
|
|
1485
|
+
} }, { label: '查看支持信息', description: 'manifest 与平台能力', run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }) }];
|
|
1486
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.colors.muted, children: "\u25CB \u672A\u6DFB\u52A0" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u63D2\u4EF6\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1487
|
+
}
|
|
1488
|
+
else if (placement.state === 'scopeConflict') {
|
|
1489
|
+
title = '安装位置冲突';
|
|
1490
|
+
description = '正常状态只能保留一个位置。';
|
|
1491
|
+
actions = [{ label: '查看位置冲突问题', run: () => go({ name: 'issueDetail', issueId: `scope:${plugin.pluginId}:${route.platform}` }) }];
|
|
1492
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1493
|
+
}
|
|
1494
|
+
else if (placement.health === 'error') {
|
|
1495
|
+
const typedIssue = props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId
|
|
1496
|
+
&& candidate.platform === route.platform
|
|
1497
|
+
&& candidate.scope === placement.scope);
|
|
1498
|
+
title = `${platformLabel(route.platform)} 暂时不可管理`;
|
|
1499
|
+
description = typedIssue?.explanation ?? '平台状态检查失败,当前不能安全选择写操作。';
|
|
1500
|
+
actions = [
|
|
1501
|
+
...(typedIssue ? [{ label: '查看对应问题', description: typedIssue.title, run: () => go({ name: 'issueDetail', issueId: typedIssue.issueId }) }] : []),
|
|
1502
|
+
{ label: '查看安全技术详情', description: '只显示类型化状态和错误码', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
1503
|
+
{ label: '返回插件', run: back },
|
|
1504
|
+
];
|
|
1505
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.error, children: "\u00D7 \u72B6\u6001\u68C0\u67E5\u5931\u8D25" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5F53\u524D\u53EA\u63D0\u4F9B\u53EA\u8BFB\u64CD\u4F5C" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1506
|
+
}
|
|
1507
|
+
else if (placement.health === 'incomplete') {
|
|
1508
|
+
title = `${platformLabel(route.platform)} ${placement.health === 'incomplete' ? '安装不完整' : '暂时不可用'}`;
|
|
1509
|
+
description = '缺少一项必要内容,插件可能无法稳定启动。';
|
|
1510
|
+
actions = [
|
|
1511
|
+
{ label: '重新连接缺失内容…', description: '保留现有设置', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: route.platform, scope: placement.scope }) },
|
|
1512
|
+
{ label: '查看影响', description: '缺少的内容和范围', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
1513
|
+
{ label: `从${scopeLabel(placement.scope)}移除…`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, route.platform, placement.scope) },
|
|
1514
|
+
];
|
|
1515
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.warning, children: "! \u9700\u8981\u5904\u7406" }), _jsxs(Text, { color: tuiTheme.colors.muted, children: [scopeLabel(placement.scope), " \u00B7 ", plugin.sourceLabel] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1516
|
+
}
|
|
1517
|
+
else {
|
|
1518
|
+
title = `已安装到${scopeLabel(placement.scope)}`;
|
|
1519
|
+
description = `当前检查正常${plugin.version ? `;版本 ${plugin.version}` : ''}。`;
|
|
1520
|
+
actions = [
|
|
1521
|
+
{ label: '运行升级…', description: '使用当前来源中的版本重建', run: () => beginReview({ kind: 'pluginWrite', action: 'upgrade', pluginId: plugin.pluginId, platform: route.platform, scope: placement.scope }) },
|
|
1522
|
+
{ label: '重新安装…', description: '重新生成并验证受管内容', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: route.platform, scope: placement.scope }) },
|
|
1523
|
+
{ label: `从${scopeLabel(placement.scope)}移除…`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, route.platform, placement.scope) },
|
|
1524
|
+
{ label: '查看安装详情', description: '组件与诊断信息', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
1525
|
+
];
|
|
1526
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.success, children: "\u2713 \u72B6\u6001\u6B63\u5E38" }), _jsxs(Text, { color: tuiTheme.colors.muted, children: [scopeLabel(placement.scope), plugin.version ? ` · 当前版本 ${plugin.version}` : ''] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
else if (route.name === 'configLayer' && plugin) {
|
|
1530
|
+
breadcrumb = `${plugin.displayName} / 设置`;
|
|
1531
|
+
title = '选择要查看或修改的设置';
|
|
1532
|
+
description = '安装位置与设置保存层是两个不同概念。';
|
|
1533
|
+
help = '↑↓ 移动 · Enter 打开 · Esc 返回';
|
|
1534
|
+
actions = [
|
|
1535
|
+
{ label: '有效设置', description: '合并后的实际结果 · 只读', meta: '查看', run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'effective', projectLayer: 'shared' } }) },
|
|
1536
|
+
{ label: '个人默认', description: '在所有项目中作为默认值', meta: configLayerCounts.user === undefined ? undefined : `${configLayerCounts.user} 个值`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'user', projectLayer: 'shared' } }) },
|
|
1537
|
+
{ label: '当前项目', description: '随仓库共享,适合团队约定', meta: configLayerCounts.project === undefined ? undefined : `${configLayerCounts.project} 个值`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'project', projectLayer: 'shared' } }) },
|
|
1538
|
+
{ label: '当前项目(仅本机)', description: '只保存在本机,不提交到仓库', meta: configLayerCounts.local === undefined ? undefined : configLayerCounts.local === 0 ? '未设置' : `${configLayerCounts.local} 个值`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'project', projectLayer: 'local' } }) },
|
|
1539
|
+
];
|
|
1540
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1541
|
+
}
|
|
1542
|
+
else if (route.name === 'configFields' && plugin) {
|
|
1543
|
+
const readonly = route.layer.scope === 'effective';
|
|
1544
|
+
const selectedGroups = route.groupId
|
|
1545
|
+
? currentConfigPage?.groups.filter((group) => group.id === route.groupId) ?? []
|
|
1546
|
+
: currentConfigPage?.groups ?? [];
|
|
1547
|
+
breadcrumb = `${plugin.displayName} / 设置 / ${configLayerLabel(route.layer)}`;
|
|
1548
|
+
context = readonly ? '只读' : dirty ? `${Object.keys(draftUpdates).length + draftRemoves.length} 项未保存` : undefined;
|
|
1549
|
+
title = readonly ? '当前实际使用的设置' : route.groupId ? selectedGroups[0]?.title ?? '高级设置' : configLayerLabel(route.layer);
|
|
1550
|
+
description = readonly ? '由默认值、个人默认、当前项目和本机覆盖合并。' : route.groupId ? '这些字段来自 manifest 的高级设置分组。' : '字段来自插件 manifest;修改先保存在本地草稿。';
|
|
1551
|
+
if (!readonly && configActionError)
|
|
1552
|
+
description = configActionError;
|
|
1553
|
+
help = readonly ? '↑↓ 查看 · Esc 返回' : '↑↓ 移动 · Enter 修改 · s 保存 · u 恢复继承 · Esc 返回';
|
|
1554
|
+
if (!currentConfigPage) {
|
|
1555
|
+
content = _jsx(Text, { color: tuiTheme.colors.accent, children: "\u25CF \u6B63\u5728\u8BFB\u53D6\u53EF\u4FE1\u8BBE\u7F6E\u5B9A\u4E49" });
|
|
1556
|
+
}
|
|
1557
|
+
else if (readonly) {
|
|
1558
|
+
actions = selectedGroups.flatMap((group) => group.fields.map((field) => ({
|
|
1559
|
+
label: field.title,
|
|
1560
|
+
description: `来自${field.sourceLabel}`,
|
|
1561
|
+
meta: currentFieldDisplay(field),
|
|
1562
|
+
})));
|
|
1563
|
+
const limit = policy.mode === 'compact' ? 4 : 7;
|
|
1564
|
+
content = _jsx(Bordered, { title: "\u6709\u6548\u8BBE\u7F6E", children: _jsx(ActionRows, { actions: actions, focus: focus, offset: scrollOffset, limit: limit }) });
|
|
1565
|
+
}
|
|
1566
|
+
else {
|
|
1567
|
+
const mutableLayer = {
|
|
1568
|
+
scope: route.layer.scope,
|
|
1569
|
+
projectLayer: route.layer.projectLayer,
|
|
1570
|
+
};
|
|
1571
|
+
const rowGroups = [];
|
|
1572
|
+
if (!route.groupId && currentConfigPage.presets.length > 0) {
|
|
1573
|
+
rowGroups.push({ id: 'presets', title: '快速方案', rows: [{ label: '当前方案', description: '应用 manifest 声明的预设', meta: `${currentConfigPage.presets.length} 个`, run: () => go({ name: 'configPresets', pluginId: plugin.pluginId, layer: mutableLayer }) }] });
|
|
1574
|
+
}
|
|
1575
|
+
for (const group of selectedGroups) {
|
|
1576
|
+
const rows = group.advanced && !route.groupId
|
|
1577
|
+
? [{ label: group.title, description: group.description ?? '渐进展开高级字段', meta: `${group.fields.length} 项`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: route.layer, groupId: group.id }) }]
|
|
1578
|
+
: group.fields.map((field) => ({
|
|
1579
|
+
label: field.title,
|
|
1580
|
+
description: field.description,
|
|
1581
|
+
meta: currentFieldDisplay(field),
|
|
1582
|
+
configKey: field.key,
|
|
1583
|
+
run: () => go({ name: 'configFieldEditor', pluginId: plugin.pluginId, layer: mutableLayer, fieldKey: field.key, editor: fieldEditor(field) }),
|
|
1584
|
+
}));
|
|
1585
|
+
rowGroups.push({ id: group.id, title: group.advanced && !route.groupId ? '高级设置' : group.title, rows });
|
|
1586
|
+
}
|
|
1587
|
+
actions = rowGroups.flatMap((group) => group.rows);
|
|
1588
|
+
if (!route.groupId)
|
|
1589
|
+
actions.push({ label: '保存修改', description: dirty ? '查看安全变更汇总' : '当前没有未保存修改', disabled: dirty ? undefined : '当前没有未保存修改', run: beginConfigSave }, {
|
|
1590
|
+
label: '重置此设置层…',
|
|
1591
|
+
description: '删除本层全部值并恢复继承',
|
|
1592
|
+
status: 'error',
|
|
1593
|
+
disabled: currentConfigPage.storedCount === 0 ? '本层没有已存值' : undefined,
|
|
1594
|
+
run: () => beginReview({ kind: 'configReset', pluginId: plugin.pluginId, scope: route.layer.scope, projectLayer: route.layer.projectLayer }),
|
|
1595
|
+
});
|
|
1596
|
+
let rowOffset = 0;
|
|
1597
|
+
content = policy.mode === 'compact' || scrollOffset > 0
|
|
1598
|
+
? _jsx(Bordered, { title: "\u8BBE\u7F6E\u5B57\u6BB5", children: _jsx(ActionRows, { actions: actions, focus: focus, offset: scrollOffset, limit: policy.mode === 'compact' ? 4 : 7 }) })
|
|
1599
|
+
: _jsxs(Box, { flexDirection: "column", children: [rowGroups.map((group) => {
|
|
1600
|
+
const offset = rowOffset;
|
|
1601
|
+
rowOffset += group.rows.length;
|
|
1602
|
+
return _jsx(Bordered, { title: group.title, children: _jsx(ActionRows, { actions: actions, focus: focus, offset: offset, limit: group.rows.length }) }, group.id);
|
|
1603
|
+
}), !route.groupId ? _jsx(ActionRows, { actions: actions, focus: focus, offset: rowOffset }) : null] });
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
else if (route.name === 'configFieldEditor' && plugin && activeField) {
|
|
1607
|
+
breadcrumb = `${plugin.displayName} / 设置 / ${activeField.title}`;
|
|
1608
|
+
help = route.editor === 'enum' || route.editor === 'boolean'
|
|
1609
|
+
? '↑↓ 移动 · Enter 使用 · Esc 取消'
|
|
1610
|
+
: '输入 · Enter 继续 · Ctrl-U 删除本层值 · Esc 取消';
|
|
1611
|
+
if (route.editor === 'enum') {
|
|
1612
|
+
title = `选择${activeField.title}`;
|
|
1613
|
+
description = '保存值仍是 manifest 中的原始 enum token。';
|
|
1614
|
+
actions = (activeField.enumValues ?? []).map((value) => ({
|
|
1615
|
+
label: `${activeField.valueLabels?.[value] ?? value}${activeField.recommendedValue === value ? '(推荐)' : ''}`,
|
|
1616
|
+
meta: draftUpdates[activeField.key] === value || (!Object.hasOwn(draftUpdates, activeField.key) && activeField.value === value) ? '●' : '○',
|
|
1617
|
+
run: () => { updateDraft(activeField.key, value); back(); },
|
|
1618
|
+
}));
|
|
1619
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1620
|
+
}
|
|
1621
|
+
else if (route.editor === 'boolean') {
|
|
1622
|
+
title = `${activeField.title}?`;
|
|
1623
|
+
description = activeField.description ?? '选择开启或关闭。';
|
|
1624
|
+
actions = [{ label: '开启', run: () => { updateDraft(activeField.key, true); back(); } }, { label: '关闭', run: () => { updateDraft(activeField.key, false); back(); } }];
|
|
1625
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
title = route.editor === 'number' ? `输入${activeField.title}` : route.editor === 'path' ? `选择${activeField.title}` : route.editor === 'sensitive' ? '输入敏感值' : `输入${activeField.title}`;
|
|
1629
|
+
description = route.editor === 'number'
|
|
1630
|
+
? `允许范围:${activeField.min ?? '不限'}–${activeField.max ?? '不限'}${activeField.unit ? ` ${activeField.unit}` : ''}。`
|
|
1631
|
+
: route.editor === 'path' ? `${activeField.type === 'file' ? '文件' : '目录'}路径会在保存前由现有 schema 路径验证。`
|
|
1632
|
+
: route.editor === 'sensitive' ? '当前值不会回显;留空不会自动删除旧值。' : activeField.description ?? '';
|
|
1633
|
+
const visibleInput = route.editor === 'sensitive'
|
|
1634
|
+
? '•'.repeat(inputValue.length)
|
|
1635
|
+
: route.editor === 'path' ? privatePathInput(inputValue) : inputValue;
|
|
1636
|
+
content = _jsxs(Bordered, { children: [_jsx(Text, { children: visibleInput || activeField.placeholder || ' ' }), _jsx(Text, { color: inputError ? tuiTheme.statusColors.error : tuiTheme.colors.muted, children: inputError ?? (route.editor === 'sensitive' ? '输入已隐藏。' : route.editor === 'path' ? '已隐藏父目录;Backspace 仍会编辑原始输入。' : activeField.placeholder ?? '按 Enter 使用输入值。') })] });
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
else if (route.name === 'configPresets' && plugin) {
|
|
1640
|
+
breadcrumb = `${plugin.displayName} / 设置 / 预设`;
|
|
1641
|
+
title = '选择快速方案';
|
|
1642
|
+
description = '预设由插件 manifest 声明;应用后仍可逐项修改。';
|
|
1643
|
+
actions = (currentConfigPage?.presets ?? []).map((preset) => ({
|
|
1644
|
+
label: preset.title,
|
|
1645
|
+
description: (preset.privateFieldTitles?.length ?? 0) > 0
|
|
1646
|
+
? `${preset.description ? `${preset.description} · ` : ''}${preset.privateFieldTitles.join('、')}需手动输入`
|
|
1647
|
+
: preset.description,
|
|
1648
|
+
run: () => {
|
|
1649
|
+
const fields = new Map(currentConfigPage?.groups.flatMap((group) => group.fields)
|
|
1650
|
+
.map((field) => [field.key, field]));
|
|
1651
|
+
const nextUpdates = { ...draftUpdates };
|
|
1652
|
+
for (const [key, value] of Object.entries(preset.values)) {
|
|
1653
|
+
const field = fields.get(key);
|
|
1654
|
+
if (field?.configured && field.value === value)
|
|
1655
|
+
delete nextUpdates[key];
|
|
1656
|
+
else
|
|
1657
|
+
nextUpdates[key] = value;
|
|
1658
|
+
}
|
|
1659
|
+
const nextRemoves = draftRemoves.filter((key) => !Object.hasOwn(preset.values, key));
|
|
1660
|
+
setDraftUpdates(nextUpdates);
|
|
1661
|
+
setDraftRemoves(nextRemoves);
|
|
1662
|
+
setDirty(Object.keys(nextUpdates).length + nextRemoves.length > 0);
|
|
1663
|
+
back();
|
|
1664
|
+
},
|
|
1665
|
+
}));
|
|
1666
|
+
content = currentConfigPage ? _jsx(ActionRows, { actions: actions, focus: focus }) : _jsx(Text, { color: tuiTheme.colors.accent, children: "\u25CF \u6B63\u5728\u8BFB\u53D6\u9884\u8BBE" });
|
|
1667
|
+
}
|
|
1668
|
+
else if (route.name === 'configUnavailable' && plugin) {
|
|
1669
|
+
breadcrumb = `${plugin.displayName} / 设置`;
|
|
1670
|
+
context = '暂时不可编辑';
|
|
1671
|
+
title = '暂时无法编辑设置';
|
|
1672
|
+
description = '当前无法取得可信的字段定义,因此不能验证新值。';
|
|
1673
|
+
actions = [
|
|
1674
|
+
{ label: '重新检查来源', description: '刷新当前来源后重新读取可信定义', run: async () => {
|
|
1675
|
+
await props.controller.refreshSource(plugin.sourceId);
|
|
1676
|
+
setConfigLoadKey((value) => value + 1);
|
|
1677
|
+
props.controller.replace({ name: 'configFields', pluginId: plugin.pluginId, layer: route.layer ?? { scope: 'user', projectLayer: 'shared' } });
|
|
1678
|
+
} },
|
|
1679
|
+
...(configResetAvailability?.sourceIdentityPresent
|
|
1680
|
+
? [{ label: '技术详情', description: '查看可信身份和设置层摘要', run: () => setShowTechnical((value) => !value) }]
|
|
1681
|
+
: []),
|
|
1682
|
+
{ label: '返回', run: back },
|
|
1683
|
+
];
|
|
1684
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.error, children: "\u00D7 Manifest \u6216\u6765\u6E90\u4E0D\u53EF\u7528" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5DF2\u4FDD\u5B58\u7684\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8\u3002\u6062\u590D\u6765\u6E90\u540E\u624D\u80FD\u7F16\u8F91\u6216\u91CD\u7F6E\u3002" })] }), showTechnical && configResetAvailability?.sourceIdentityPresent ? _jsxs(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: [_jsxs(Text, { children: ["\u6765\u6E90\u8EAB\u4EFD\uFF1A", configResetAvailability.sourceIdentity] }), _jsxs(Text, { children: ["\u8BBE\u7F6E\u6458\u8981\uFF1A", configResetAvailability.storeDigest] })] }) : null, _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1685
|
+
}
|
|
1686
|
+
else if (route.name === 'sourceList') {
|
|
1687
|
+
breadcrumb = '插件来源';
|
|
1688
|
+
title = '插件来源';
|
|
1689
|
+
description = '单个来源失败不会阻止其他来源继续工作。';
|
|
1690
|
+
context = `${props.controller.listSources().length} 个来源`;
|
|
1691
|
+
help = '↑↓ 移动 · Enter 打开 · r 重新检查 · Esc 返回';
|
|
1692
|
+
actions = [
|
|
1693
|
+
...props.controller.listSources().map((source) => {
|
|
1694
|
+
const overview = props.controller.getSourceOverview(source.id);
|
|
1695
|
+
const health = overview.health?.state ?? source.state;
|
|
1696
|
+
const status = health === 'ready' ? '已从远端加载'
|
|
1697
|
+
: health === 'path' ? '本地目录'
|
|
1698
|
+
: health === 'checking' ? '正在检查'
|
|
1699
|
+
: health === 'cache' || health === 'degraded' ? '使用缓存' : '无法读取';
|
|
1700
|
+
return {
|
|
1701
|
+
label: `${source.id}${source.primary ? ' 主来源' : ''}`,
|
|
1702
|
+
description: `${overview.detail?.type === 'path' ? '本地目录' : 'Git 仓库'} · ${source.count ?? 0} 个插件`,
|
|
1703
|
+
meta: status,
|
|
1704
|
+
status: health === 'unavailable' ? 'error'
|
|
1705
|
+
: health === 'cache' || health === 'degraded' ? 'warning'
|
|
1706
|
+
: source.primary ? 'success' : undefined,
|
|
1707
|
+
run: () => go({ name: 'sourceDetail', sourceId: source.id }),
|
|
1708
|
+
};
|
|
1709
|
+
}),
|
|
1710
|
+
{ label: '+ 添加来源', description: 'Git 仓库或本地目录', run: beginAddSource },
|
|
1711
|
+
];
|
|
1712
|
+
const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
|
|
1713
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit });
|
|
1714
|
+
}
|
|
1715
|
+
else if (route.name === 'sourceDetail') {
|
|
1716
|
+
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
1717
|
+
const detail = overview.detail;
|
|
1718
|
+
const state = overview.health?.state ?? overview.source?.state;
|
|
1719
|
+
breadcrumb = `插件来源 / ${route.sourceId}`;
|
|
1720
|
+
context = detail?.type === 'path' ? '本地目录' : 'Git';
|
|
1721
|
+
title = route.sourceId;
|
|
1722
|
+
description = state === 'cache' || state === 'degraded' ? '当前使用已缓存的 Catalog 数据。'
|
|
1723
|
+
: state === 'unavailable' ? '当前无法读取这个来源。' : '来源状态可用。';
|
|
1724
|
+
help = '↑↓ 移动 · Enter 选择 · r 重新检查 · Esc 返回';
|
|
1725
|
+
actions = [
|
|
1726
|
+
{ label: '重新检查来源', description: '读取远端并更新缓存', run: () => props.controller.refreshSource(route.sourceId) },
|
|
1727
|
+
{ label: '编辑来源…', description: 'ID 与类型创建后不可修改', run: () => go({ name: 'sourceEdit', sourceId: route.sourceId }) },
|
|
1728
|
+
{ label: '设为主来源…', description: '只改变默认 marketplace', disabled: detail?.primary ? '当前已经是主来源' : undefined, run: () => beginReview({ kind: 'sourceMutation', action: 'setPrimary', sourceId: route.sourceId, changes: [{ title: '主来源', before: snapshot.viewModel?.sourceTabs.find((source) => source.primary)?.id, after: route.sourceId }] }) },
|
|
1729
|
+
{ label: '移除来源…', description: '移除来源配置', status: 'error', disabled: detail?.primary ? '主来源不能直接移除' : undefined, run: () => beginReview({ kind: 'sourceMutation', action: 'remove', sourceId: route.sourceId, changes: [{ title: `移除来源 ${route.sourceId}` }] }) },
|
|
1730
|
+
{ label: '技术详情', description: '只读身份与解析证据', run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }) },
|
|
1731
|
+
];
|
|
1732
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u5F53\u524D\u72B6\u6001", children: _jsx(Text, { color: state === 'unavailable' ? tuiTheme.statusColors.error : state === 'cache' || state === 'degraded' ? tuiTheme.statusColors.warning : tuiTheme.statusColors.success, children: state === 'checking' ? '● 正在检查' : state === 'unavailable' ? '× 无法读取' : state === 'cache' || state === 'degraded' ? '◐ 使用缓存' : '✓ 可用' }) }), _jsxs(Bordered, { title: "\u6765\u6E90\u914D\u7F6E", children: [_jsxs(Text, { children: ["\u4F4D\u7F6E ", detail?.locationSummary ?? '不可用'] }), _jsxs(Text, { children: ["\u7248\u672C\u9009\u62E9 ", detail?.type === 'path' ? '本地目录' : detail?.refPolicy === 'explicit' ? '固定 Git 引用' : '跟随 AW 版本通道'] }), detail?.explicitRef ? _jsxs(Text, { children: ["\u5F15\u7528 ", detail.explicitRef] }) : null, _jsxs(Text, { children: ["\u63D2\u4EF6 ", detail?.pluginCount ?? 0, " \u4E2A"] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1733
|
+
}
|
|
1734
|
+
else if (route.name === 'sourceTechnical') {
|
|
1735
|
+
const detail = props.controller.getSourceOverview(route.sourceId).detail;
|
|
1736
|
+
breadcrumb = `插件来源 / ${route.sourceId} / 技术详情`;
|
|
1737
|
+
context = '只读';
|
|
1738
|
+
title = '来源身份与解析详情';
|
|
1739
|
+
description = '用于诊断和安全审查;不显示认证信息或绝对路径。';
|
|
1740
|
+
content = _jsxs(Bordered, { children: [_jsxs(Text, { children: ["sourceId ", route.sourceId] }), _jsxs(Text, { children: ["sourceIdentity ", detail?.sourceIdentity ?? '不可用'] }), _jsxs(Text, { children: ["requestedRef ", detail?.requestedRef ?? '不可用'] }), detail?.resolvedCommit ? _jsxs(Text, { children: ["resolvedCommit ", detail.resolvedCommit] }) : null, _jsxs(Text, { children: ["freshness ", detail?.freshness ?? '不可用'] }), _jsxs(Text, { children: ["configDigest ", detail?.configDigest ?? '不可用'] })] });
|
|
1741
|
+
}
|
|
1742
|
+
else if (route.name === 'sourceAdd' && sourceDraft?.owner === route.owner) {
|
|
1743
|
+
breadcrumb = sourceDraft.mode === 'add' ? '插件来源 / 添加' : `插件来源 / ${sourceDraft.sourceId} / 编辑`;
|
|
1744
|
+
const stepLabel = { type: '第 1 步,共 6 步', id: '第 2 步,共 6 步', location: '第 3 步,共 6 步', refPolicy: '第 4 步,共 6 步', ref: '第 5 步,共 6 步', subdir: '可选高级步骤' }[route.step];
|
|
1745
|
+
context = stepLabel;
|
|
1746
|
+
help = route.step === 'type' || route.step === 'refPolicy' ? '↑↓ 移动 · Enter 继续 · Esc 返回' : '输入 · Enter 继续 · Esc 返回';
|
|
1747
|
+
if (route.step === 'type') {
|
|
1748
|
+
title = '选择来源类型';
|
|
1749
|
+
description = '来源类型创建后不能修改。';
|
|
1750
|
+
actions = [
|
|
1751
|
+
{ label: 'Git 仓库', description: '从远端 Git 仓库读取 Catalog', run: () => { setSourceDraft({ ...sourceDraft, type: 'git' }); go({ name: 'sourceAdd', step: sourceDraft.sourceId ? 'location' : 'id', owner: route.owner }); } },
|
|
1752
|
+
{ label: '本地目录', description: '用于本地开发与测试', run: () => { setSourceDraft({ ...sourceDraft, type: 'path' }); go({ name: 'sourceAdd', step: sourceDraft.sourceId ? 'location' : 'id', owner: route.owner }); } },
|
|
1753
|
+
];
|
|
1754
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1755
|
+
}
|
|
1756
|
+
else if (route.step === 'refPolicy') {
|
|
1757
|
+
title = '选择版本引用方式';
|
|
1758
|
+
description = '跟随 AW 通道适合常规来源;固定 ref 适合测试来源。';
|
|
1759
|
+
actions = [
|
|
1760
|
+
{ label: '跟随 AW 版本通道', description: '自动选择 catalog/latest 或 catalog/beta', run: async () => { const next = { ...sourceDraft, refPolicy: 'cliChannel', ref: '' }; setSourceDraft(next); if (next.mode === 'update')
|
|
1761
|
+
await beginSourceUpdateReview(next);
|
|
1762
|
+
else
|
|
1763
|
+
go({ name: 'sourceAdd', step: 'subdir', owner: route.owner }); } },
|
|
1764
|
+
{ label: '固定 Git 引用', description: 'branch、tag 或 commit', run: () => { setSourceDraft({ ...sourceDraft, refPolicy: 'explicit' }); go({ name: 'sourceAdd', step: 'ref', owner: route.owner }); } },
|
|
1765
|
+
];
|
|
1766
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1767
|
+
}
|
|
1768
|
+
else {
|
|
1769
|
+
title = route.step === 'id' ? '输入来源 ID' : route.step === 'location' ? `输入 ${sourceDraft.type === 'git' ? 'Git URL' : '绝对路径'}` : route.step === 'ref' ? '输入固定 Git 引用' : '输入 Catalog 子目录';
|
|
1770
|
+
description = route.step === 'subdir' ? '仓库根目录是 Catalog 时保持为空。' : '输入会在继续前按来源规范验证。';
|
|
1771
|
+
const displayedInput = route.step === 'location' && sourceDraft.type === 'git' && sourceInput
|
|
1772
|
+
? projectCatalogSourceLocation(sourceInput)
|
|
1773
|
+
: route.step === 'location' && sourceDraft.type === 'path'
|
|
1774
|
+
? privatePathInput(sourceInput)
|
|
1775
|
+
: sourceInput;
|
|
1776
|
+
content = _jsxs(Bordered, { children: [_jsx(Text, { children: displayedInput || ' ' }), _jsx(Text, { color: sourceInputError ? tuiTheme.statusColors.error : tuiTheme.colors.muted, children: sourceInputError ?? (route.step === 'location' && sourceDraft.type === 'path' ? '已隐藏父目录;Backspace 仍会编辑原始输入。' : '按 Enter 继续。') })] });
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
else if (route.name === 'sourceEdit') {
|
|
1780
|
+
const detail = props.controller.getSourceOverview(route.sourceId).detail;
|
|
1781
|
+
breadcrumb = `插件来源 / ${route.sourceId} / 编辑`;
|
|
1782
|
+
title = '选择要修改的内容';
|
|
1783
|
+
description = '来源 ID 和类型只读。';
|
|
1784
|
+
const startEdit = (field) => {
|
|
1785
|
+
if (!detail)
|
|
1786
|
+
return;
|
|
1787
|
+
const owner = `source-update:${route.sourceId}:${field}:${Date.now()}`;
|
|
1788
|
+
setSourceDraft({ owner, mode: 'update', sourceId: route.sourceId, type: detail.type, location: '', refPolicy: detail.refPolicy ?? 'cliChannel', ref: detail.explicitRef ?? '', subdir: detail.subdir ?? '', editField: field });
|
|
1789
|
+
setDirty(true);
|
|
1790
|
+
go({ name: 'sourceAdd', step: field === 'location' ? 'location' : field === 'subdir' ? 'subdir' : 'refPolicy', owner });
|
|
1791
|
+
};
|
|
1792
|
+
actions = detail?.type === 'git' ? [
|
|
1793
|
+
{ label: 'Git URL', description: detail.locationSummary, run: () => startEdit('location') },
|
|
1794
|
+
{ label: '版本引用', description: detail.refPolicy === 'explicit' ? `固定:${detail.explicitRef}` : '跟随 AW 版本通道', run: () => startEdit('refPolicy') },
|
|
1795
|
+
{ label: 'Catalog 子目录', description: detail.subdir ?? '未设置', run: () => startEdit('subdir') },
|
|
1796
|
+
] : [{ label: '本地目录', description: '已选择本地目录', run: () => startEdit('location') }];
|
|
1797
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
1798
|
+
}
|
|
1799
|
+
else if (route.name === 'sourceRemoveBlocked') {
|
|
1800
|
+
breadcrumb = `插件来源 / ${route.sourceId} / 移除`;
|
|
1801
|
+
context = '已阻止';
|
|
1802
|
+
title = `无法移除 ${route.sourceId}`;
|
|
1803
|
+
description = sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整,不能把未知引用当作零。' : '仍有内容依赖这个来源。';
|
|
1804
|
+
actions = [{ label: sourceDependenciesExpanded ? '收起依赖内容' : '查看依赖内容', description: '只显示插件、来源、平台、位置和类别聚合', run: () => setSourceDependenciesExpanded((value) => !value) }, { label: '返回来源详情', run: back }];
|
|
1805
|
+
const countLabel = (value, unit) => value === undefined ? '未知' : `${value} ${unit}`;
|
|
1806
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: _jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整' : '发现活动引用'] }) }), _jsxs(Bordered, { children: [_jsxs(Text, { children: ["\u5B89\u88C5\u4F4D\u7F6E ", countLabel(sourceReferenceSummary?.installationCount, '个')] }), _jsxs(Text, { children: ["\u63D2\u4EF6\u8BBE\u7F6E ", countLabel(sourceReferenceSummary?.configCount, '份')] }), _jsxs(Text, { children: ["\u751F\u547D\u5468\u671F\u72B6\u6001 ", countLabel(sourceReferenceSummary?.lifecycleCount, '个')] }), _jsxs(Text, { children: ["Marketplace ", countLabel(sourceReferenceSummary?.marketplaceCount, '个')] }), sourceDependenciesExpanded ? sourceReferenceSummary?.dependencies.slice(0, policy.mode === 'compact' ? 2 : 5).map((dependency, index) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [dependency.category, " \u00B7 ", dependency.pluginId ?? dependency.sourceId ?? '身份未知', dependency.platform ? ` · ${platformLabel(dependency.platform)}` : '', dependency.scope ? ` · ${scopeLabel(dependency.scope)}` : ''] }, `${dependency.category}:${dependency.pluginId ?? dependency.sourceId ?? index}`)) : null] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1807
|
+
}
|
|
1808
|
+
else if (route.name === 'issueList') {
|
|
1809
|
+
const issues = props.controller.listUserIssues();
|
|
1810
|
+
breadcrumb = '需要处理';
|
|
1811
|
+
context = issues.length > 0 ? `${issues.length} 项` : '没有问题';
|
|
1812
|
+
title = issues.length > 0 ? '需要处理' : '当前没有需要处理的问题';
|
|
1813
|
+
description = issues.length > 0
|
|
1814
|
+
? '选择一个问题,查看影响和真实可用的处理路径。'
|
|
1815
|
+
: '当前状态检查没有发现需要处理的安装、来源或设置问题。';
|
|
1816
|
+
if (issues.length === 0) {
|
|
1817
|
+
actions = [
|
|
1818
|
+
{ label: '返回首页', run: () => go({ name: 'home' }) },
|
|
1819
|
+
{ label: '浏览插件', run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
1820
|
+
];
|
|
1821
|
+
}
|
|
1822
|
+
else {
|
|
1823
|
+
actions = issues.map((entry) => ({
|
|
1824
|
+
label: entry.category.startsWith('dependency')
|
|
1825
|
+
? `${props.controller.getPluginOverview(entry.pluginId ?? '')?.displayName ?? entry.pluginId ?? entry.title}${entry.platform ? ` · ${platformLabel(entry.platform)}` : ''}`
|
|
1826
|
+
: entry.title,
|
|
1827
|
+
description: entry.category.startsWith('dependency')
|
|
1828
|
+
? `${props.controller.getPluginOverview(entry.dependencyPluginId ?? '')?.displayName ?? entry.dependencyPluginId ?? entry.explanation} · ${entry.impact[0] ?? ''}`
|
|
1829
|
+
: `${entry.explanation} · ${entry.impact[0] ?? ''}`,
|
|
1830
|
+
status: entry.category === 'sourceDegraded' ? 'warning' : 'error',
|
|
1831
|
+
run: () => entry.category === 'dependencyStillRequired' && entry.pluginId
|
|
1832
|
+
? go({ name: 'pluginDependencies', pluginId: entry.pluginId, view: 'removeBlocked',
|
|
1833
|
+
...(entry.platform ? { platform: entry.platform } : {}),
|
|
1834
|
+
...(entry.scope ? { scope: entry.scope } : {}) })
|
|
1835
|
+
: go({ name: 'issueDetail', issueId: entry.issueId }),
|
|
1836
|
+
}));
|
|
1837
|
+
}
|
|
1838
|
+
const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
|
|
1839
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit });
|
|
1840
|
+
}
|
|
1841
|
+
else if (route.name === 'issueDetail' && issue) {
|
|
1842
|
+
breadcrumb = `需要处理 / ${issue.pluginId ?? issue.sourceId ?? issue.entity.id}`;
|
|
1843
|
+
context = issue.platform ? `${platformLabel(issue.platform)}${issue.scope ? ` · ${scopeLabel(issue.scope)}` : ''}` : issue.sourceId;
|
|
1844
|
+
title = issue.title;
|
|
1845
|
+
description = issue.explanation;
|
|
1846
|
+
const runIssueAction = async (entry) => {
|
|
1847
|
+
const intent = issueWriteIntent(issue, entry);
|
|
1848
|
+
if (intent) {
|
|
1849
|
+
await beginReview(intent);
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
if (entry.id === 'refreshSource' && issue.sourceId)
|
|
1853
|
+
await props.controller.refreshSource(issue.sourceId);
|
|
1854
|
+
else if (entry.id === 'continueWithCache' || entry.id === 'retryLater')
|
|
1855
|
+
go({ name: 'issueList' });
|
|
1856
|
+
else if (entry.id === 'editSource' && issue.sourceId)
|
|
1857
|
+
go({ name: 'sourceDetail', sourceId: issue.sourceId });
|
|
1858
|
+
else if (entry.id === 'reAddSource' && issue.sourceId)
|
|
1859
|
+
beginIssueSourceAdd(issue.sourceId);
|
|
1860
|
+
else if (entry.id === 'viewSourceChange' && issue.sourceId)
|
|
1861
|
+
go({ name: 'sourceTechnical', sourceId: issue.sourceId });
|
|
1862
|
+
else if (entry.id === 'openConfig' && issue.pluginId) {
|
|
1863
|
+
go(issue.configLayer
|
|
1864
|
+
? { name: 'configFields', pluginId: issue.pluginId, layer: issue.configLayer }
|
|
1865
|
+
: { name: 'configLayer', pluginId: issue.pluginId });
|
|
1866
|
+
}
|
|
1867
|
+
else if (entry.id === 'viewDependencies' && issue.pluginId) {
|
|
1868
|
+
go({ name: 'pluginDependencies', pluginId: issue.pluginId });
|
|
1869
|
+
}
|
|
1870
|
+
else if (entry.id === 'viewDependencyIssue' && entry.pluginId) {
|
|
1871
|
+
const dependencyIssue = props.controller.listUserIssues().find((candidate) => candidate.pluginId === entry.pluginId
|
|
1872
|
+
&& candidate.issueId !== issue.issueId);
|
|
1873
|
+
if (dependencyIssue)
|
|
1874
|
+
go({ name: 'issueDetail', issueId: dependencyIssue.issueId });
|
|
1875
|
+
else
|
|
1876
|
+
go({ name: 'pluginDependencies', pluginId: entry.pluginId });
|
|
1877
|
+
}
|
|
1878
|
+
else
|
|
1879
|
+
setShowTechnical((value) => !value);
|
|
1880
|
+
};
|
|
1881
|
+
actions = issue.actions.map((entry) => ({
|
|
1882
|
+
label: entry.label,
|
|
1883
|
+
description: entry.description,
|
|
1884
|
+
status: entry.id === 'remove'
|
|
1885
|
+
|| entry.id === 'keepUserScope' || entry.id === 'keepProjectScope' ? 'error' : undefined,
|
|
1886
|
+
run: () => runIssueAction(entry),
|
|
1887
|
+
}));
|
|
1888
|
+
const issuePlugin = issue.pluginId ? props.controller.getPluginOverview(issue.pluginId) : undefined;
|
|
1889
|
+
const issueOverview = issue.pluginId ? props.controller.getPluginDependencyOverview(issue.pluginId) : undefined;
|
|
1890
|
+
const manualMigration = issue.category === 'legacyState' && showTechnical;
|
|
1891
|
+
if (issue.category === 'dependencyStillRequired' && issuePlugin) {
|
|
1892
|
+
const blockingDependents = issue.platform && issue.scope
|
|
1893
|
+
? props.controller.getBlockingDependents(issuePlugin.pluginId, issue.platform, issue.scope)
|
|
1894
|
+
: issueOverview?.dependents ?? [];
|
|
1895
|
+
title = `无法移除 ${issuePlugin.displayName}`;
|
|
1896
|
+
description = '仍有插件依赖它。没有提供危险绕过操作。';
|
|
1897
|
+
actions = [
|
|
1898
|
+
{ label: '查看完整依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
|
|
1899
|
+
{ label: '返回插件详情', run: () => go({ name: 'pluginDetail', pluginId: issuePlugin.pluginId }) },
|
|
1900
|
+
];
|
|
1901
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: _jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", blockingDependents.length, " \u4E2A\u5DF2\u5B89\u88C5\u63D2\u4EF6\u4ECD\u5728\u4F7F\u7528"] }) }), _jsx(Bordered, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", children: blockingDependents.map((dependent) => _jsxs(Text, { children: [dependent.displayName, " ", _jsx(Text, { color: tuiTheme.colors.muted, children: dependentTargetLabel(dependent) })] }, dependent.pluginId)) }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1902
|
+
}
|
|
1903
|
+
else if (issue.category === 'dependencyTargetUnsupported' && issuePlugin) {
|
|
1904
|
+
const compatible = selectedPlatforms(issuePlugin, issue.scope ?? scope).filter((entry) => !entry.disabled);
|
|
1905
|
+
title = `无法添加到 ${issue.platform ? platformLabel(issue.platform) : '所选工具'}`;
|
|
1906
|
+
description = issue.dependencyPluginId
|
|
1907
|
+
? `${props.controller.getPluginOverview(issue.dependencyPluginId)?.displayName ?? issue.dependencyPluginId} 不支持所选工具,因此完整插件计划不能运行。`
|
|
1908
|
+
: '完整插件计划不支持所选工具。';
|
|
1909
|
+
actions = [
|
|
1910
|
+
...compatible.map((entry) => ({ label: `只添加到 ${platformLabel(entry.platform)}`, description: '返回位置页并使用完整依赖检查', run: () => {
|
|
1911
|
+
setScope(issue.scope ?? scope);
|
|
1912
|
+
setTools([entry.platform]);
|
|
1913
|
+
go({ name: 'installPlacement', pluginId: issuePlugin.pluginId });
|
|
1914
|
+
} })),
|
|
1915
|
+
{ label: '返回调整工具', run: () => go({ name: 'installTools', pluginId: issuePlugin.pluginId }) },
|
|
1916
|
+
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
|
|
1917
|
+
];
|
|
1918
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", issue.dependencyPluginId ? `${props.controller.getPluginOverview(issue.dependencyPluginId)?.displayName ?? issue.dependencyPluginId} 不支持所选工具` : '工具不兼容'] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u53EF\u7528\u5DE5\u5177\u4ECD\u4FDD\u6301\u53EF\u9009\uFF0C\u4E0D\u517C\u5BB9\u5DE5\u5177\u4E0D\u4F1A\u8FDB\u5165\u786E\u8BA4\u9875\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
1919
|
+
}
|
|
1920
|
+
else if (issue.category === 'dependencyGraphInvalid') {
|
|
1921
|
+
title = `暂时无法添加 ${issuePlugin?.displayName ?? issue.pluginId ?? '插件'}`;
|
|
1922
|
+
description = '来源中的依赖关系无效或安装记录不能安全验证。没有修改任何安装。';
|
|
1923
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", issue.rootCode ?? issue.category] }), _jsx(Text, { color: tuiTheme.colors.muted, children: issue.explanation })] }), _jsx(ActionRows, { actions: actions, focus: focus }), showTechnical ? _jsx(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: issue.technicalDetails.map((detail, index) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [detail.code, " \u00B7 ", detail.message] }, `${detail.code}:${index}`)) }) : _jsx(Text, { color: tuiTheme.colors.muted, children: "d \u67E5\u770B\u5B89\u5168\u9519\u8BEF\u4EE3\u7801" })] });
|
|
1924
|
+
}
|
|
1925
|
+
else {
|
|
1926
|
+
const sourceMissingCleanup = issue.category === 'sourceMissing' && issue.pluginId && issue.platform && issue.scope
|
|
1927
|
+
? `aw cleanup ${issue.pluginId} --platform ${issue.platform} --scope ${issue.scope}`
|
|
1928
|
+
: undefined;
|
|
1929
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { title: "\u5F71\u54CD\u8303\u56F4", children: [issue.impact.map((line) => _jsx(Text, { children: line }, line)), sourceMissingCleanup ? _jsxs(_Fragment, { children: [_jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u53EF\u6E05\u7406\u53EF\u4FE1\u7684 AW \u73B0\u573A\uFF1A" }), _jsx(Text, { children: sourceMissingCleanup }), _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u5982\u9700\u8DF3\u8FC7 dependent \u4FDD\u62A4\uFF0C\u5728\u547D\u4EE4\u672B\u5C3E\u6DFB\u52A0 --force\u3002" })] }) : null, issue.category === 'sourceIdentityChanged' ? _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u8BF7\u786E\u8BA4\u6765\u6E90\u53D8\u66F4\uFF0C\u6216\u6062\u590D\u539F identity\uFF1B\u5F53\u524D\u4E0D\u81EA\u52A8\u8FC1\u79FB\u6216\u6E05\u7406\u3002" }) : null, issue.placements ? _jsxs(Text, { children: ["\u4F4D\u7F6E\uFF1A", issue.placements.map(scopeLabel).join('、')] }) : null, issue.requiredFields ? _jsxs(Text, { children: ["\u5B57\u6BB5\uFF1A", issue.requiredFields.map((field) => field.title).join('、')] }) : null] }), _jsx(ActionRows, { actions: actions, focus: focus, offset: policy.mode === 'compact' ? scrollOffset : undefined, limit: policy.mode === 'compact' ? 4 : 7 }), manualMigration ? _jsxs(Bordered, { title: "\u624B\u52A8\u5904\u7406\u8BF4\u660E", children: [_jsx(Text, { children: "1. \u4E0D\u81EA\u52A8\u8FC1\u79FB\u65E7\u7248\u72B6\u6001\u3002" }), _jsx(Text, { children: "2. \u8FD0\u884C aw status --json\uFF0C\u8BB0\u5F55\u516C\u5F00\u9519\u8BEF code\u3002" }), _jsx(Text, { children: "3. \u5907\u4EFD\u76F8\u5173 AW \u914D\u7F6E\u3002" }), _jsx(Text, { children: "4. \u6309\u6280\u672F\u8BE6\u60C5\u4F7F\u7528\u53D7\u652F\u6301\u547D\u4EE4\u6216\u4EBA\u5DE5\u5904\u7406\u3002" }), _jsx(Text, { children: "5. \u518D\u6B21\u8FD0\u884C\u72B6\u6001\u68C0\u67E5\u3002" })] }) : showTechnical ? _jsx(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: issue.technicalDetails.map((detail, index) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [detail.code, " \u00B7 ", detail.message] }, `${detail.code}:${index}`)) }) : _jsx(Text, { color: tuiTheme.colors.muted, children: "d \u6216\u201C\u6280\u672F\u8BE6\u60C5\u201D\u67E5\u770B\u5B89\u5168\u9519\u8BEF\u4EE3\u7801" })] });
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
else if (route.name === 'review' && snapshot.activeReview) {
|
|
1933
|
+
const review = snapshot.activeReview;
|
|
1934
|
+
breadcrumb = `${review.subject.name} / ${review.title}`;
|
|
1935
|
+
title = `${snapshot.result?.state === 'stale' ? '状态已变化 · ' : ''}确认${review.title} ${review.subject.name}`;
|
|
1936
|
+
description = snapshot.result?.state === 'stale' ? '当前状态已变化。请检查新的变化后再次确认。' : review.description;
|
|
1937
|
+
actions = [
|
|
1938
|
+
...review.changes.map((change) => ({
|
|
1939
|
+
label: `${reviewSymbol(change)} ${change.title}`,
|
|
1940
|
+
description: change.description,
|
|
1941
|
+
status: change.kind === 'remove' ? 'error'
|
|
1942
|
+
: change.kind === 'warning' ? 'warning'
|
|
1943
|
+
: change.kind === 'keep' || change.kind === 'add' ? 'success' : undefined,
|
|
1944
|
+
})),
|
|
1945
|
+
{ label: review.primaryLabel, description: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, run: () => {
|
|
1946
|
+
if (operationBusy.current)
|
|
1947
|
+
return;
|
|
1948
|
+
operationBusy.current = true;
|
|
1949
|
+
return props.controller.executeOperation(review.reviewToken).then(() => {
|
|
1950
|
+
setFocus(0);
|
|
1951
|
+
setScrollOffset(0);
|
|
1952
|
+
}).finally(() => {
|
|
1953
|
+
operationBusy.current = false;
|
|
1954
|
+
});
|
|
1955
|
+
} },
|
|
1956
|
+
{ label: '返回修改', run: review.operationKind === 'portableInstall' && snapshot.portableDraft
|
|
1957
|
+
? async () => {
|
|
1958
|
+
if (await refreshPortableDraft(snapshot.portableDraft.pluginId))
|
|
1959
|
+
back();
|
|
1960
|
+
}
|
|
1961
|
+
: back },
|
|
1962
|
+
];
|
|
1963
|
+
const reviewViewport = lineBudgetViewport(actions, focus, scrollOffset, policy.mode === 'compact' ? 8 : 15, [...review.changes.map((change) => change.group), undefined, undefined]);
|
|
1964
|
+
const reviewRows = (indexes) => indexes
|
|
1965
|
+
.filter((index) => index >= reviewViewport.offset
|
|
1966
|
+
&& (reviewViewport.limit === undefined || index < reviewViewport.offset + reviewViewport.limit))
|
|
1967
|
+
.map((index) => _jsx(ActionRows, { actions: actions, focus: focus, offset: index, limit: 1 }, index));
|
|
1968
|
+
const groupedChanges = review.changes.some((change) => change.group);
|
|
1969
|
+
const groupTitle = {
|
|
1970
|
+
requested: review.operationKind === 'portableInstall' ? '已选择 Skills · 当前插件' : '你选择的插件',
|
|
1971
|
+
dependencies: review.operationKind === 'portableInstall' ? '已选择 Skills · 来自依赖插件' : '必需依赖',
|
|
1972
|
+
portableTarget: '安装目标',
|
|
1973
|
+
portableInputs: '准备内容',
|
|
1974
|
+
portableWarnings: '覆盖冲突',
|
|
1975
|
+
preserved: '保持不变',
|
|
1976
|
+
};
|
|
1977
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(Bordered, { children: [_jsxs(Text, { bold: true, children: [review.subject.name, " ", review.subject.sourceLabel, " ", review.subject.version] }), review.equivalentCommand ? _jsxs(_Fragment, { children: [_jsx(Text, { children: review.description }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] })] }) : null, groupedChanges ? Object.keys(groupTitle).map((group) => {
|
|
1978
|
+
const indexes = review.changes.map((change, index) => (change.group ?? 'requested') === group ? index : -1)
|
|
1979
|
+
.filter((index) => index >= 0);
|
|
1980
|
+
const rows = reviewRows(indexes);
|
|
1981
|
+
return rows.length > 0 ? _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: tuiTheme.colors.muted, children: groupTitle[group] }), rows] }, group) : null;
|
|
1982
|
+
}) : _jsx(_Fragment, { children: reviewRows(review.changes.map((_, index) => index)) }), reviewRows([review.changes.length, review.changes.length + 1]), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u25B8 \u6280\u672F\u8BE6\u60C5\uFF08d\uFF09" }), showTechnical ? review.technicalDetails?.map((detail) => _jsx(Text, { color: tuiTheme.colors.muted, children: detail }, detail)) : null] }) });
|
|
1983
|
+
}
|
|
1984
|
+
else if (route.name === 'progress') {
|
|
1985
|
+
const portableOperation = snapshot.activeReview?.operationKind === 'portableInstall';
|
|
1986
|
+
const configOperation = snapshot.activeReview?.operationKind === 'configSave'
|
|
1987
|
+
|| snapshot.activeReview?.operationKind === 'configReset';
|
|
1988
|
+
const sourceOperation = snapshot.activeReview?.operationKind === 'sourceMutation';
|
|
1989
|
+
const issueOperation = snapshot.activeReview?.originIssue;
|
|
1990
|
+
breadcrumb = portableOperation ? '通用 Skills / 进度' : issueOperation ? '需要处理 / 进度' : sourceOperation ? '插件来源 / 保存' : configOperation ? '设置 / 进度' : '插件操作 / 进度';
|
|
1991
|
+
title = portableOperation ? '正在安装通用 Skills' : issueOperation ? '正在处理问题' : sourceOperation ? '正在保存并刷新来源' : configOperation ? '正在保存并应用设置' : '正在执行插件操作';
|
|
1992
|
+
description = issueOperation ? '完成后会使用新的状态 generation 重新检查问题。' : sourceOperation ? '先原子保存来源配置,再尽力刷新。刷新失败时配置仍保留并显示警告。' : configOperation ? '先保存所选层,再重新应用当前可见安装位置。' : '请勿关闭当前终端。';
|
|
1993
|
+
help = 'd 技术进度 · Ctrl-C 完成后退出';
|
|
1994
|
+
const progress = snapshot.progress;
|
|
1995
|
+
const elapsedSeconds = Math.max(0, Math.floor(((progress.elapsedMs
|
|
1996
|
+
?? (progress.startedAtMs === undefined ? 0 : Date.now() - progress.startedAtMs))) / 1_000));
|
|
1997
|
+
const latestMessage = progress.messages.at(-1);
|
|
1998
|
+
const technicalMessages = progress.messages.slice(policy.mode === 'compact' ? -4 : -8);
|
|
1999
|
+
const dependencyProgress = snapshot.progressTargets.length > 0;
|
|
2000
|
+
const progressLabel = (entry) => {
|
|
2001
|
+
if (entry.state === 'kept')
|
|
2002
|
+
return { label: '已保留', status: 'success' };
|
|
2003
|
+
if (entry.state === 'completed')
|
|
2004
|
+
return { label: '已完成', status: 'success' };
|
|
2005
|
+
if (entry.state === 'running')
|
|
2006
|
+
return { label: '正在添加', status: 'info' };
|
|
2007
|
+
if (entry.state === 'failed')
|
|
2008
|
+
return { label: '失败', status: 'error' };
|
|
2009
|
+
if (entry.state === 'skipped')
|
|
2010
|
+
return { label: '未执行 · 依赖未完成', status: 'warning' };
|
|
2011
|
+
return { label: entry.relation === 'requested' ? '等待依赖' : '未开始', status: 'muted' };
|
|
2012
|
+
};
|
|
2013
|
+
content = portableOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: progress.current?.phase.includes('检查') ? tuiTheme.colors.accent : tuiTheme.statusColors.success, children: [progress.current?.phase.includes('检查') ? '●' : '✓', " \u68C0\u67E5\u63D2\u4EF6\u4F9D\u8D56"] }), _jsxs(Text, { color: progress.current?.phase.includes('检查') ? tuiTheme.colors.accent : tuiTheme.statusColors.success, children: [progress.current?.phase.includes('检查') ? '●' : '✓', " \u751F\u6210\u5019\u9009 Skills"] }), _jsxs(Text, { color: progress.current?.phase.includes('安装') ? tuiTheme.colors.accent : progress.current?.phase.includes('验证') ? tuiTheme.statusColors.success : tuiTheme.colors.muted, children: [progress.current?.phase.includes('安装') ? '●' : progress.current?.phase.includes('验证') ? '✓' : '○', " \u5B89\u88C5\u9009\u62E9"] }), _jsxs(Text, { color: progress.current?.phase.includes('验证') ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: [progress.current?.phase.includes('验证') ? '●' : '○', " \u9A8C\u8BC1"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5F53\u524D\uFF1ASkills CLI" }), showTechnical ? technicalMessages.map((message, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: message }, `${index}:${message}`)) : null] }) : _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.statusColors.success, children: ["\u2713 ", sourceOperation ? '检查来源操作输入' : configOperation ? '验证设置值' : dependencyProgress ? '检查依赖关系' : '检查兼容性'] }), _jsxs(Text, { color: tuiTheme.colors.accent, children: ["\u25CF ", progress.current?.phase ?? (sourceOperation ? '保存来源配置并刷新' : configOperation ? '保存设置层并重新应用' : dependencyProgress ? '添加必需依赖' : '连接到所选工具')] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u25CB ", dependencyProgress ? '验证完整结果' : '验证结果'] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5DF2\u7528\u65F6\uFF1A", elapsedSeconds, "s"] }), progress.current ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5F53\u524D\uFF1A", progress.current.target] }) : null, dependencyProgress ? _jsx(Bordered, { children: snapshot.progressTargets.map((entry) => {
|
|
2014
|
+
const state = progressLabel(entry);
|
|
2015
|
+
const relation = entry.relation === 'requested' ? '你选择的插件' : entry.relation === 'direct' ? '直接依赖' : '间接依赖';
|
|
2016
|
+
const title = props.controller.getPluginOverview(entry.pluginId)?.displayName
|
|
2017
|
+
?? entry.pluginId.split('/').at(-1);
|
|
2018
|
+
return _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [title, " ", _jsx(Text, { color: tuiTheme.colors.muted, children: relation })] }), _jsx(Text, { color: statusColor(state.status), children: state.label })] }, `${entry.pluginId}:${entry.platform}:${entry.scope}`);
|
|
2019
|
+
}) }) : null, !showTechnical && latestMessage ? _jsx(Text, { color: tuiTheme.colors.muted, children: latestMessage }) : null, showTechnical ? technicalMessages.map((message, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: message }, `${index}:${message}`)) : null, exitAfterBusy ? _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u64CD\u4F5C\u5B8C\u6210\u540E\u9000\u51FA\u3002" }) : null] });
|
|
2020
|
+
}
|
|
2021
|
+
else if (route.name === 'result' && snapshot.result) {
|
|
2022
|
+
const result = snapshot.result;
|
|
2023
|
+
const partial = result.state === 'partial';
|
|
2024
|
+
const blocked = result.state === 'blocked';
|
|
2025
|
+
breadcrumb = '插件操作 / 结果';
|
|
2026
|
+
const action = result.state === 'succeeded' || result.state === 'partial'
|
|
2027
|
+
? result.summary.action
|
|
2028
|
+
: undefined;
|
|
2029
|
+
const operationKind = 'operationKind' in result ? result.operationKind : undefined;
|
|
2030
|
+
const portableOperation = operationKind === 'portableInstall';
|
|
2031
|
+
const portableReceipt = result.state === 'succeeded' || result.state === 'partial'
|
|
2032
|
+
? result.summary.portable : undefined;
|
|
2033
|
+
const portableInstallFailed = portableReceipt?.failureCode === 'portableSkillsCliInstallFailed';
|
|
2034
|
+
const portableOriginIsDetail = 'returnRoute' in result
|
|
2035
|
+
&& result.returnRoute?.name === 'pluginDetail';
|
|
2036
|
+
const configOperation = operationKind === 'configSave' || operationKind === 'configReset'
|
|
2037
|
+
? operationKind : undefined;
|
|
2038
|
+
const sourceOperation = operationKind === 'sourceMutation';
|
|
2039
|
+
const sourceRefreshWarning = sourceOperation && 'summary' in result
|
|
2040
|
+
&& result.summary.messages?.some((message) => message.startsWith('警告:'));
|
|
2041
|
+
const issueOperation = result.originIssue;
|
|
2042
|
+
const completedTitle = {
|
|
2043
|
+
install: '✓ 插件添加完成',
|
|
2044
|
+
upgrade: '✓ 插件升级完成',
|
|
2045
|
+
reinstall: '✓ 插件重新安装完成',
|
|
2046
|
+
uninstall: '✓ 插件移除完成',
|
|
2047
|
+
cleanup: '✓ 插件清理完成',
|
|
2048
|
+
}[action ?? 'install'];
|
|
2049
|
+
title = portableOperation
|
|
2050
|
+
? blocked ? '× 通用 Skills 安装被阻止'
|
|
2051
|
+
: partial ? '! 通用 Skills 结果未完全完成' : '✓ 通用 Skills 已安装'
|
|
2052
|
+
: blocked && result.writeMayHaveCompleted
|
|
2053
|
+
? '× 写入结果未确认'
|
|
2054
|
+
: issueOperation
|
|
2055
|
+
? blocked || partial ? '× 问题仍未解决' : '✓ 问题已处理'
|
|
2056
|
+
: sourceOperation
|
|
2057
|
+
? blocked ? '× 来源操作未完成' : sourceRefreshWarning ? '! 来源配置已保存,但刷新失败' : '✓ 来源操作完成'
|
|
2058
|
+
: configOperation
|
|
2059
|
+
? blocked
|
|
2060
|
+
? '× 设置未确认保存'
|
|
2061
|
+
: partial ? '! 设置已保存,但部分应用失败' : '✓ 设置已保存并应用'
|
|
2062
|
+
: partial ? '! 插件操作只完成了一部分' : blocked ? '× 插件操作未完成' : completedTitle;
|
|
2063
|
+
description = portableOperation
|
|
2064
|
+
? blocked
|
|
2065
|
+
? result.code === 'operationReviewExpired' || result.code === 'reviewTicketExpired'
|
|
2066
|
+
? 'Review 已过期,写入未开始。请返回修改并重新确认。'
|
|
2067
|
+
: result.code === 'operationReviewStale'
|
|
2068
|
+
? '安装证据已变化,写入被阻止。请返回修改并重新确认。'
|
|
2069
|
+
: result.writeMayHaveCompleted
|
|
2070
|
+
? '写入结果无法确认。请检查外部 Skills 状态后重试。'
|
|
2071
|
+
: '安装在写入前被阻止。请返回修改后重试。'
|
|
2072
|
+
: portableInstallFailed ? 'Skills CLI 安装失败,可能已有部分目标写入。可以修改 Agent 或使用原选择重试。' : partial ? '无法确认全部已选择 Skills。' : '所选 Skills 已通过安装验证。'
|
|
2073
|
+
: blocked && result.writeMayHaveCompleted
|
|
2074
|
+
? result.message
|
|
2075
|
+
: issueOperation
|
|
2076
|
+
? blocked
|
|
2077
|
+
? result.code === 'issueVerificationFailed'
|
|
2078
|
+
? '写入可能已完成,但当前无法确认。请重新检查状态。'
|
|
2079
|
+
: result.code === 'issueStillPresent'
|
|
2080
|
+
? '写入已完成,但重新检查后原问题仍然存在。'
|
|
2081
|
+
: '操作被安全阻止。可返回问题详情后重试。'
|
|
2082
|
+
: '状态已重新加载;原问题已确认消失。'
|
|
2083
|
+
: sourceOperation
|
|
2084
|
+
? blocked
|
|
2085
|
+
? result.writeMayHaveCompleted
|
|
2086
|
+
? '来源配置可能已保存。请重新检查来源列表;刷新失败不会撤销配置。'
|
|
2087
|
+
: '来源操作未执行。草稿已保留,可返回修改。'
|
|
2088
|
+
: sourceRefreshWarning
|
|
2089
|
+
? '来源配置已保留。请根据警告修复来源后重试刷新。'
|
|
2090
|
+
: '来源配置已保存,刷新已完成。'
|
|
2091
|
+
: blocked ? result.message
|
|
2092
|
+
: configOperation ? '设置层已经更新。' : '状态已刷新。';
|
|
2093
|
+
const completedResult = result.state === 'succeeded' || result.state === 'partial'
|
|
2094
|
+
? result
|
|
2095
|
+
: undefined;
|
|
2096
|
+
const pluginId = completedResult?.summary.pluginId
|
|
2097
|
+
?? ('pluginId' in result ? result.pluginId : undefined);
|
|
2098
|
+
const failedTarget = completedResult?.summary.targets
|
|
2099
|
+
?.filter((target) => target.state === 'failed')
|
|
2100
|
+
.sort((left, right) => [left.pluginId ?? '', left.platform, left.scope].join('\0')
|
|
2101
|
+
.localeCompare([right.pluginId ?? '', right.platform, right.scope].join('\0'), 'en'))[0];
|
|
2102
|
+
const failedPluginId = failedTarget?.pluginId ?? pluginId;
|
|
2103
|
+
const failedIssue = failedPluginId && failedTarget
|
|
2104
|
+
? props.controller.listUserIssues().find((candidate) => (candidate.pluginId === failedPluginId || candidate.dependencyPluginId === failedPluginId)
|
|
2105
|
+
&& candidate.platform === failedTarget.platform
|
|
2106
|
+
&& (candidate.pluginId === failedPluginId
|
|
2107
|
+
? candidate.scope === failedTarget.scope
|
|
2108
|
+
: candidate.dependencyPlatform === failedTarget.platform
|
|
2109
|
+
&& candidate.dependencyScope === failedTarget.scope)
|
|
2110
|
+
&& (candidate.category === 'incompleteInstallation' || candidate.category === 'diagnostic'
|
|
2111
|
+
|| candidate.category === 'dependencyNotReady' || candidate.category === 'dependencyMissing'))
|
|
2112
|
+
: undefined;
|
|
2113
|
+
const failedTargetState = failedPluginId && failedTarget
|
|
2114
|
+
? props.controller.getTargetState(failedPluginId, failedTarget.platform, failedTarget.scope)
|
|
2115
|
+
: undefined;
|
|
2116
|
+
const retryAction = partialRetryAction(operationKind, action, failedTargetState);
|
|
2117
|
+
const retryIssueAction = retryAction
|
|
2118
|
+
? failedIssue?.actions.find((candidate) => (candidate.writeAction === retryAction || candidate.id === retryAction)
|
|
2119
|
+
&& (candidate.pluginId ?? failedIssue.pluginId) === failedPluginId
|
|
2120
|
+
&& (candidate.platform ?? failedIssue.platform) === failedTarget?.platform
|
|
2121
|
+
&& (candidate.scope ?? failedIssue.scope) === failedTarget?.scope)
|
|
2122
|
+
: undefined;
|
|
2123
|
+
actions = portableOperation
|
|
2124
|
+
? [
|
|
2125
|
+
{ label: blocked || partial ? '返回修改' : portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: async () => {
|
|
2126
|
+
if (!pluginId) {
|
|
2127
|
+
leaveResult();
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
if (!blocked && !partial) {
|
|
2131
|
+
props.controller.discardPortableDraft();
|
|
2132
|
+
leaveResult();
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
if (!await refreshPortableDraft(pluginId))
|
|
2136
|
+
return;
|
|
2137
|
+
if (portableInstallFailed && portableReceipt?.agentMode === 'explicit') {
|
|
2138
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableAgentInput', pluginId }, true);
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
|
|
2142
|
+
} },
|
|
2143
|
+
{ label: blocked || partial ? '重试' : '再安装其他 Skills…', run: async () => {
|
|
2144
|
+
if (!pluginId)
|
|
2145
|
+
return;
|
|
2146
|
+
if (blocked || partial) {
|
|
2147
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
|
|
2148
|
+
await beginPortableReview(pluginId, snapshot.portableDraft?.scope ?? 'project');
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
props.controller.resetPortableFlowToPreview(pluginId);
|
|
2152
|
+
await beginPortable(pluginId);
|
|
2153
|
+
} },
|
|
2154
|
+
{ label: portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: () => { if (pluginId)
|
|
2155
|
+
props.controller.resetPortableFlowToPreview(pluginId); } },
|
|
2156
|
+
]
|
|
2157
|
+
: issueOperation
|
|
2158
|
+
? blocked || partial
|
|
2159
|
+
? [
|
|
2160
|
+
{ label: '返回问题详情', run: () => blocked
|
|
2161
|
+
? leaveResult()
|
|
2162
|
+
: failedIssue
|
|
2163
|
+
? leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId })
|
|
2164
|
+
: pluginId && failedTarget
|
|
2165
|
+
? leaveResult({ name: 'platform', pluginId, platform: failedTarget.platform })
|
|
2166
|
+
: leaveResult() },
|
|
2167
|
+
{ label: '重新检查问题', run: () => leaveResult({ name: 'issueList' }) },
|
|
2168
|
+
]
|
|
2169
|
+
: [
|
|
2170
|
+
{ label: '查看问题列表', run: () => leaveResult({ name: 'issueList' }) },
|
|
2171
|
+
{ label: '返回首页', run: () => leaveResult({ name: 'home' }) },
|
|
2172
|
+
]
|
|
2173
|
+
: sourceOperation
|
|
2174
|
+
? [{ label: blocked ? '返回修改' : '查看来源', run: () => blocked ? leaveResult() : leaveResult({ name: 'sourceList' }) }, { label: '返回首页', run: () => leaveResult({ name: 'home' }) }]
|
|
2175
|
+
: configOperation && blocked
|
|
2176
|
+
? [
|
|
2177
|
+
{ label: '返回设置', run: () => leaveResult() },
|
|
2178
|
+
{ label: '返回首页', run: () => leaveResult({ name: 'home' }) },
|
|
2179
|
+
]
|
|
2180
|
+
: [{ label: partial ? '查看失败项' : configOperation ? '查看插件' : action === 'uninstall' || action === 'cleanup' ? '返回插件详情' : '查看插件', run: () => {
|
|
2181
|
+
if (partial && pluginId && failedTarget) {
|
|
2182
|
+
if (failedIssue)
|
|
2183
|
+
leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId });
|
|
2184
|
+
else
|
|
2185
|
+
leaveResult({ name: 'platform', pluginId, platform: failedTarget.platform });
|
|
2186
|
+
}
|
|
2187
|
+
else if (pluginId && snapshot.viewModel?.plugins.some((entry) => entry.pluginId === pluginId)) {
|
|
2188
|
+
leaveResult({ name: 'pluginDetail', pluginId });
|
|
2189
|
+
}
|
|
2190
|
+
else
|
|
2191
|
+
leaveResult({ name: 'pluginList', mode: 'installed' });
|
|
2192
|
+
} }, ...(partial && pluginId && failedTarget && retryAction ? [{
|
|
2193
|
+
label: '重试失败部分…',
|
|
2194
|
+
run: () => {
|
|
2195
|
+
const issueIntent = failedIssue && retryIssueAction
|
|
2196
|
+
? issueWriteIntent(failedIssue, retryIssueAction)
|
|
2197
|
+
: undefined;
|
|
2198
|
+
return beginReview(issueIntent ?? {
|
|
2199
|
+
kind: 'pluginWrite',
|
|
2200
|
+
action: retryAction,
|
|
2201
|
+
pluginId: failedPluginId,
|
|
2202
|
+
platform: failedTarget.platform,
|
|
2203
|
+
scope: failedTarget.scope,
|
|
2204
|
+
});
|
|
2205
|
+
},
|
|
2206
|
+
}] : []), { label: '返回首页', run: () => leaveResult({ name: 'home' }) }];
|
|
2207
|
+
const visibleMessages = completedResult?.summary.messages?.slice(policy.mode === 'compact' ? -2 : -4) ?? [];
|
|
2208
|
+
const dependencyTargets = completedResult?.summary.targets?.some((target) => target.relation) ?? false;
|
|
2209
|
+
const resultTargetName = (target) => target.pluginId
|
|
2210
|
+
? props.controller.getPluginOverview(target.pluginId)?.displayName
|
|
2211
|
+
?? target.pluginId.split('/').at(-1)
|
|
2212
|
+
: `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`;
|
|
2213
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [result.state === 'blocked' ? _jsxs(Text, { color: tuiTheme.statusColors.error, children: [result.writeMayHaveCompleted ? '结果未确认' : '操作未完成', " \u00B7 ", result.code] }) : sourceOperation ? _jsx(Text, { color: tuiTheme.statusColors.success, children: "\u6765\u6E90\u914D\u7F6E\u5199\u5165\u5DF2\u5B8C\u6210\u3002" }) : completedResult && (completedResult.summary.targets?.length ?? 0) > 0 ? _jsxs(_Fragment, { children: [dependencyTargets ? _jsx(Text, { color: partial ? tuiTheme.statusColors.warning : tuiTheme.statusColors.success, children: partial ? '依赖安装没有全部完成' : '操作已完成' }) : null, completedResult.summary.targets.map((target) => { const state = operationTargetStateLabel(target.state); const relation = target.relation === 'requested' ? '你选择的插件' : target.relation === 'direct' ? '直接依赖' : target.relation === 'transitive' ? '间接依赖' : undefined; return _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [resultTargetName(target), relation ? _jsxs(Text, { color: tuiTheme.colors.muted, children: [" \u00B7 ", relation] }) : null] }), _jsx(Text, { color: statusColor(state.status), children: state.label })] }, `${target.pluginId ?? ''}:${target.platform}:${target.scope}`); }), partial && completedResult.summary.targets.some((target) => target.state === 'skipped') ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u672A\u6267\u884C\u7684\u76EE\u6807\u6CA1\u6709\u5199\u5165\uFF1B\u5DF2\u5B8C\u6210\u6216\u5DF2\u4FDD\u7559\u7684\u76EE\u6807\u4FDD\u6301\u53EF\u7528\u3002" }) : null] }) : completedResult ? _jsx(Text, { color: portableInstallFailed ? tuiTheme.statusColors.error : partial ? tuiTheme.statusColors.warning : tuiTheme.statusColors.success, children: completedResult.summary.message }) : _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u786E\u8BA4\u3002" }), portableReceipt ? _jsxs(_Fragment, { children: [_jsxs(Text, { children: ["Skills\uFF1A", portableReceipt.selectedSkills.join('、')] }), _jsxs(Text, { children: ["Agent\uFF1A", portableReceipt.agentMode === 'automatic' ? '自动选择检测到的 Agent;未检测到时可能选择全部 Agent' : portableReceipt.requestedAgents.join('、')] }), _jsxs(Text, { children: ["\u8303\u56F4\uFF1A", scopeLabel(portableReceipt.scope)] }), portableReceipt.verificationUnknown ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u672A\u77E5" }) : _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u9A8C\u8BC1\u7F3A\u9879\uFF1A", portableReceipt.missing.length] }), portableReceipt.recovery ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u6062\u590D\uFF1A", portableReceipt.recovery] }) : null, _jsxs(Text, { color: tuiTheme.colors.muted, children: ["Materialization\uFF1A", portableReceipt.materializationDigest] })] }) : null, completedResult?.summary.elapsedMs !== undefined ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5DF2\u7528\u65F6\uFF1A", Math.floor(completedResult.summary.elapsedMs / 1_000), "s"] }) : null, visibleMessages.map((message, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: message }, `${index}:${message}`))] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
2214
|
+
}
|
|
2215
|
+
else if (route.name === 'pluginAbout' && plugin) {
|
|
2216
|
+
breadcrumb = `${plugin.displayName} / 关于`;
|
|
2217
|
+
title = plugin.displayName;
|
|
2218
|
+
description = plugin.description;
|
|
2219
|
+
actions = [{ label: '查看技术详情', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) }, { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) }];
|
|
2220
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u7248\u672C ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.version ?? '未知' })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u6765\u6E90 ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.sourceLabel })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u53EF\u7528\u5DE5\u5177 ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.supportedPlatforms.map(platformLabel).join('、') })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u914D\u7F6E ", _jsxs(Text, { color: tuiTheme.colors.text, children: [plugin.configFieldCount, " \u4E2A\u5B57\u6BB5"] })] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
2221
|
+
}
|
|
2222
|
+
else if (route.name === 'pluginTechnical' && plugin) {
|
|
2223
|
+
breadcrumb = `${plugin.displayName} / 技术详情`;
|
|
2224
|
+
context = '只读';
|
|
2225
|
+
title = '安装与身份详情';
|
|
2226
|
+
description = '默认页面不会展示这些内部信息。';
|
|
2227
|
+
actions = [{ label: '返回插件', run: back }];
|
|
2228
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["canonical pluginId ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.pluginId })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u6765\u6E90 ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.sourceId })] }), plugin.installedPlacements.map((placement) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [platformLabel(placement.platform), " target ", _jsx(Text, { color: tuiTheme.colors.text, children: placementStatus(placement).value })] }, placement.platform)), plugin.technicalComponents.map((component) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [platformLabel(component.platform), " \u00B7 ", scopeLabel(component.scope), " \u00B7 ", component.key, " \u00B7 ", component.state, component.errorCode ? ` · ${component.errorCode}` : ''] }, `${component.platform}:${component.scope}:${component.key}`))] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
|
|
2229
|
+
}
|
|
2230
|
+
else {
|
|
2231
|
+
breadcrumb = '后续阶段';
|
|
2232
|
+
title = '此业务页将在后续阶段接入';
|
|
2233
|
+
description = '当前阶段只实现插件发现、安装和维护页面。';
|
|
2234
|
+
actions = [{ label: '返回上一页', run: back }];
|
|
2235
|
+
content = _jsx(ActionRows, { actions: actions, focus: focus });
|
|
2236
|
+
}
|
|
2237
|
+
const runAction = (action) => {
|
|
2238
|
+
try {
|
|
2239
|
+
const pending = action?.run?.();
|
|
2240
|
+
if (pending)
|
|
2241
|
+
void pending.catch(() => {
|
|
2242
|
+
setActionError('操作未完成。请重新检查后重试。');
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
catch {
|
|
2246
|
+
setActionError('操作未完成。请重新检查后重试。');
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
useInput((input, key) => {
|
|
2250
|
+
if (busy) {
|
|
2251
|
+
if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
2252
|
+
setExitAfterBusy(true);
|
|
2253
|
+
}
|
|
2254
|
+
else if (!tooSmall && input.toLowerCase() === 'd') {
|
|
2255
|
+
setShowTechnical((value) => !value);
|
|
2256
|
+
}
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
if (tooSmall) {
|
|
2260
|
+
if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
2261
|
+
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
2262
|
+
}
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
if (overlay) {
|
|
2266
|
+
if (key.ctrl && input.toLowerCase() === 'c') {
|
|
2267
|
+
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
if (key.escape || input.toLowerCase() === 'b' || input === '?') {
|
|
2271
|
+
setOverlay(undefined);
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
if (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow) {
|
|
2275
|
+
setOverlayFocus((value) => value === 0 ? 1 : 0);
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
if (key.return) {
|
|
2279
|
+
if (overlay === 'help' || overlayFocus === 0)
|
|
2280
|
+
setOverlay(undefined);
|
|
2281
|
+
else if (overlay === 'discard') {
|
|
2282
|
+
setOverlay(undefined);
|
|
2283
|
+
setDirty(false);
|
|
2284
|
+
setDraftUpdates({});
|
|
2285
|
+
setDraftRemoves([]);
|
|
2286
|
+
const discardedSourceDraft = sourceDraft;
|
|
2287
|
+
setSourceDraft(undefined);
|
|
2288
|
+
if (discardForExit) {
|
|
2289
|
+
requestExit({ code: 0, reason: 'user' });
|
|
2290
|
+
}
|
|
2291
|
+
else if (discardedSourceDraft) {
|
|
2292
|
+
props.controller.discardSourceDraft(discardedSourceDraft.owner, discardedSourceDraft.mode === 'update'
|
|
2293
|
+
? { name: 'sourceDetail', sourceId: discardedSourceDraft.sourceId }
|
|
2294
|
+
: { name: 'sourceList' });
|
|
2295
|
+
}
|
|
2296
|
+
else {
|
|
2297
|
+
back();
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
else
|
|
2301
|
+
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
2302
|
+
}
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
if (key.ctrl && input.toLowerCase() === 'c') {
|
|
2306
|
+
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
if (route.name === 'portableAgentInput') {
|
|
2310
|
+
if (key.escape) {
|
|
2311
|
+
back();
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
if (key.return) {
|
|
2315
|
+
try {
|
|
2316
|
+
const normalizedInput = portableAgentInput.trim();
|
|
2317
|
+
const agents = normalizePortableAgents(normalizedInput ? normalizedInput.split(/\s+/u) : []);
|
|
2318
|
+
props.controller.setPortableAgentMode('explicit', agents);
|
|
2319
|
+
setInputError(undefined);
|
|
2320
|
+
go({ name: 'portableScope', pluginId: route.pluginId });
|
|
2321
|
+
}
|
|
2322
|
+
catch (error) {
|
|
2323
|
+
setInputError(error instanceof Error ? error.message.replace(/^portableInvalidAgentSelection:\s*/u, '') : 'Agent ID 无效。');
|
|
2324
|
+
}
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
if (key.backspace || key.delete)
|
|
2328
|
+
setPortableAgentInput((value) => value.slice(0, -1));
|
|
2329
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
2330
|
+
setPortableAgentInput((value) => value + input);
|
|
2331
|
+
setInputError(undefined);
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
if (route.name === 'sourceAdd'
|
|
2335
|
+
&& route.step !== 'type'
|
|
2336
|
+
&& route.step !== 'refPolicy'
|
|
2337
|
+
&& sourceDraft?.owner === route.owner) {
|
|
2338
|
+
if (key.escape) {
|
|
2339
|
+
setOverlayFocus(0);
|
|
2340
|
+
setDiscardForExit(false);
|
|
2341
|
+
setOverlay('discard');
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
if (key.return) {
|
|
2345
|
+
let error;
|
|
2346
|
+
if (route.step === 'id') {
|
|
2347
|
+
if (!CATALOG_SOURCE_ID_PATTERN.test(sourceInput))
|
|
2348
|
+
error = '格式必须是小写字母、数字或连字符,最多 32 字符。';
|
|
2349
|
+
else if (props.controller.listSources().some((source) => source.id === sourceInput))
|
|
2350
|
+
error = '这个来源 ID 已存在。';
|
|
2351
|
+
}
|
|
2352
|
+
else if (route.step === 'location') {
|
|
2353
|
+
if (sourceDraft.type === 'path') {
|
|
2354
|
+
if (!path.isAbsolute(sourceInput))
|
|
2355
|
+
error = '必须输入绝对路径。';
|
|
2356
|
+
}
|
|
2357
|
+
else {
|
|
2358
|
+
try {
|
|
2359
|
+
canonicalizeCatalogGitUrl(sourceInput);
|
|
2360
|
+
}
|
|
2361
|
+
catch {
|
|
2362
|
+
error = 'Git URL 不符合来源规范,或包含不允许的认证信息。';
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
else if (route.step === 'ref') {
|
|
2367
|
+
if (sourceInput.trim() === '' || sourceInput !== sourceInput.trim() || /[\s\u0000-\u001f\u007f]/u.test(sourceInput))
|
|
2368
|
+
error = '必须输入不含空白或控制字符的 Git ref。';
|
|
2369
|
+
}
|
|
2370
|
+
else if (sourceInput !== '') {
|
|
2371
|
+
const normalized = path.posix.normalize(sourceInput);
|
|
2372
|
+
if (sourceInput.includes('\\') || path.posix.isAbsolute(sourceInput)
|
|
2373
|
+
|| normalized === '.' || normalized === '..' || normalized.startsWith('../')) {
|
|
2374
|
+
error = '必须输入来源内部的相对 POSIX 路径。';
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
if (error) {
|
|
2378
|
+
setSourceInputError(error);
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
const next = { ...sourceDraft };
|
|
2382
|
+
if (route.step === 'id')
|
|
2383
|
+
next.sourceId = sourceInput;
|
|
2384
|
+
else if (route.step === 'location')
|
|
2385
|
+
next.location = sourceInput;
|
|
2386
|
+
else if (route.step === 'ref')
|
|
2387
|
+
next.ref = sourceInput;
|
|
2388
|
+
else
|
|
2389
|
+
next.subdir = sourceInput === '' ? '' : path.posix.normalize(sourceInput).replace(/^\.\//u, '').replace(/\/$/u, '');
|
|
2390
|
+
setSourceDraft(next);
|
|
2391
|
+
setSourceInputError(undefined);
|
|
2392
|
+
if (route.step === 'id')
|
|
2393
|
+
go({ name: 'sourceAdd', step: 'location', owner: route.owner });
|
|
2394
|
+
else if (route.step === 'location') {
|
|
2395
|
+
if (next.mode === 'update')
|
|
2396
|
+
void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
|
|
2397
|
+
else if (next.type === 'git')
|
|
2398
|
+
go({ name: 'sourceAdd', step: 'refPolicy', owner: route.owner });
|
|
2399
|
+
else
|
|
2400
|
+
void beginSourceAddReviewWith(next).catch(() => setActionError('来源未通过验证。请检查输入后重试。'));
|
|
2401
|
+
}
|
|
2402
|
+
else if (route.step === 'ref') {
|
|
2403
|
+
if (next.mode === 'update')
|
|
2404
|
+
void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
|
|
2405
|
+
else
|
|
2406
|
+
go({ name: 'sourceAdd', step: 'subdir', owner: route.owner });
|
|
2407
|
+
}
|
|
2408
|
+
else if (next.mode === 'update')
|
|
2409
|
+
void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
|
|
2410
|
+
else
|
|
2411
|
+
void beginSourceAddReviewWith(next).catch(() => setActionError('来源未通过验证。请检查输入后重试。'));
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
if (key.backspace || key.delete)
|
|
2415
|
+
setSourceInput((value) => value.slice(0, -1));
|
|
2416
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
2417
|
+
setSourceInput((value) => value + input);
|
|
2418
|
+
setSourceInputError(undefined);
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
if (route.name === 'configFieldEditor'
|
|
2422
|
+
&& route.editor !== 'enum'
|
|
2423
|
+
&& route.editor !== 'boolean') {
|
|
2424
|
+
if (key.escape) {
|
|
2425
|
+
back();
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)) {
|
|
2429
|
+
removeDraft(route.fieldKey);
|
|
2430
|
+
back();
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
if (key.return && activeField) {
|
|
2434
|
+
if (route.editor === 'sensitive' && inputValue.length === 0) {
|
|
2435
|
+
back();
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
if (route.editor === 'number') {
|
|
2439
|
+
const number = Number(inputValue);
|
|
2440
|
+
if (!Number.isFinite(number)) {
|
|
2441
|
+
setInputError('必须输入有限数字。');
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
if (activeField.min !== undefined && number < activeField.min
|
|
2445
|
+
|| activeField.max !== undefined && number > activeField.max) {
|
|
2446
|
+
setInputError(`必须在 ${activeField.min ?? '不限'}–${activeField.max ?? '不限'} 范围内。`);
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
updateDraft(activeField.key, number);
|
|
2450
|
+
}
|
|
2451
|
+
else
|
|
2452
|
+
updateDraft(activeField.key, inputValue);
|
|
2453
|
+
back();
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
if (key.backspace || key.delete)
|
|
2457
|
+
setInputValue((value) => value.slice(0, -1));
|
|
2458
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
2459
|
+
setInputValue((value) => value + input);
|
|
2460
|
+
setInputError(undefined);
|
|
2461
|
+
return;
|
|
2462
|
+
}
|
|
2463
|
+
if (searchMode && route.name === 'pluginList') {
|
|
2464
|
+
if (key.escape) {
|
|
2465
|
+
setSearchMode(false);
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
if (key.backspace || key.delete)
|
|
2469
|
+
setQuery((value) => value.slice(0, -1));
|
|
2470
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
2471
|
+
setQuery((value) => value + input);
|
|
2472
|
+
setFocus(0);
|
|
2473
|
+
setScrollOffset(0);
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
if (key.tab && (route.name === 'configLayer'
|
|
2477
|
+
|| route.name === 'configFields'
|
|
2478
|
+
|| route.name === 'configFieldEditor'
|
|
2479
|
+
|| route.name === 'configPresets')) {
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
if (input === '?') {
|
|
2483
|
+
setOverlayFocus(0);
|
|
2484
|
+
setOverlay('help');
|
|
2485
|
+
}
|
|
2486
|
+
else if (input.toLowerCase() === 'q') {
|
|
2487
|
+
setOverlayFocus(0);
|
|
2488
|
+
setDiscardForExit(dirty);
|
|
2489
|
+
setOverlay(dirty ? 'discard' : 'exit');
|
|
2490
|
+
}
|
|
2491
|
+
else if (input.toLowerCase() === 'd' && (route.name === 'review'
|
|
2492
|
+
|| route.name === 'issueDetail'
|
|
2493
|
+
|| route.name === 'configUnavailable'
|
|
2494
|
+
|| route.name === 'pluginDependencies'
|
|
2495
|
+
|| route.name === 'portableDependencies'
|
|
2496
|
+
|| route.name === 'portableBlocker')) {
|
|
2497
|
+
setShowTechnical((value) => !value);
|
|
2498
|
+
}
|
|
2499
|
+
else if ((key.escape || input.toLowerCase() === 'b')
|
|
2500
|
+
&& (route.name === 'configFieldEditor'
|
|
2501
|
+
|| route.name === 'configPresets'
|
|
2502
|
+
|| (route.name === 'configFields' && Boolean(route.groupId)))) {
|
|
2503
|
+
back();
|
|
2504
|
+
}
|
|
2505
|
+
else if (key.escape || input.toLowerCase() === 'b') {
|
|
2506
|
+
if (dirty) {
|
|
2507
|
+
setOverlayFocus(0);
|
|
2508
|
+
setDiscardForExit(false);
|
|
2509
|
+
setOverlay('discard');
|
|
2510
|
+
}
|
|
2511
|
+
else
|
|
2512
|
+
back();
|
|
2513
|
+
}
|
|
2514
|
+
else if (route.name === 'pluginList' && input === '/') {
|
|
2515
|
+
setSearchMode(true);
|
|
2516
|
+
}
|
|
2517
|
+
else if (route.name === 'configFields' && route.layer.scope !== 'effective' && input.toLowerCase() === 's') {
|
|
2518
|
+
if (dirty)
|
|
2519
|
+
void beginConfigSave();
|
|
2520
|
+
}
|
|
2521
|
+
else if (route.name === 'configFields' && route.layer.scope !== 'effective' && input.toLowerCase() === 'u') {
|
|
2522
|
+
const key = actions[focus]?.configKey;
|
|
2523
|
+
if (key)
|
|
2524
|
+
removeDraft(key);
|
|
2525
|
+
}
|
|
2526
|
+
else if (input.toLowerCase() === 'r' && route.name === 'sourceDetail') {
|
|
2527
|
+
void props.controller.refreshSource(route.sourceId);
|
|
2528
|
+
}
|
|
2529
|
+
else if (input.toLowerCase() === 'r' && route.name === 'sourceList') {
|
|
2530
|
+
const source = props.controller.listSources()[focus];
|
|
2531
|
+
if (source)
|
|
2532
|
+
void props.controller.refreshSource(source.id);
|
|
2533
|
+
}
|
|
2534
|
+
else if (route.name === 'pluginList' && (key.leftArrow || key.rightArrow || input === '[' || input === ']')) {
|
|
2535
|
+
const tabs = ['all', ...props.controller.listSources().map((source) => source.id)];
|
|
2536
|
+
const current = route.sourceId ?? 'all';
|
|
2537
|
+
const selectedPluginId = props.controller.listPlugins({
|
|
2538
|
+
mode: route.mode,
|
|
2539
|
+
...(current !== 'all' ? { sourceId: current } : {}),
|
|
2540
|
+
...(query ? { query } : {}),
|
|
2541
|
+
})[focus]?.pluginId;
|
|
2542
|
+
const direction = key.leftArrow || input === '[' ? -1 : 1;
|
|
2543
|
+
const index = tabs.indexOf(current);
|
|
2544
|
+
const next = tabs[(index + direction + tabs.length) % tabs.length];
|
|
2545
|
+
const nextItems = props.controller.listPlugins({ mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}), ...(query ? { query } : {}) });
|
|
2546
|
+
const retained = selectedPluginId ? nextItems.findIndex((item) => item.pluginId === selectedPluginId) : -1;
|
|
2547
|
+
props.controller.replace({ name: 'pluginList', mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}) }, { ...snapshot.router.current.context, sourceTabId: next });
|
|
2548
|
+
setFocus(retained >= 0 ? retained : 0);
|
|
2549
|
+
setScrollOffset(retained >= 4 ? retained - 3 : 0);
|
|
2550
|
+
}
|
|
2551
|
+
else if (input.toLowerCase() === 'a' && route.name === 'portableSkillSelect') {
|
|
2552
|
+
props.controller.selectAllPortableSkills();
|
|
2553
|
+
}
|
|
2554
|
+
else if (input === ' ' && route.name === 'portableSkillSelect') {
|
|
2555
|
+
const candidate = snapshot.portableDraft?.candidates[focus];
|
|
2556
|
+
if (candidate)
|
|
2557
|
+
props.controller.togglePortableSkill(candidate.name);
|
|
2558
|
+
}
|
|
2559
|
+
else if (key.upArrow || input.toLowerCase() === 'k') {
|
|
2560
|
+
setFocus((value) => {
|
|
2561
|
+
const next = moveFocus(actions, value, -1);
|
|
2562
|
+
const viewport = route.name === 'portableSkillSelect' && snapshot.portableDraft
|
|
2563
|
+
? portableSkillCandidateViewport(actions.slice(0, snapshot.portableDraft.candidates.length), snapshot.portableDraft.candidates, policy.mode, next, scrollOffset)
|
|
2564
|
+
: actionViewport(route.name, actions, policy.mode, next, scrollOffset);
|
|
2565
|
+
setScrollOffset(viewport.offset);
|
|
2566
|
+
return next;
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
else if (key.downArrow || input.toLowerCase() === 'j') {
|
|
2570
|
+
setFocus((value) => {
|
|
2571
|
+
const next = moveFocus(actions, value, 1);
|
|
2572
|
+
const viewport = route.name === 'portableSkillSelect' && snapshot.portableDraft
|
|
2573
|
+
? portableSkillCandidateViewport(actions.slice(0, snapshot.portableDraft.candidates.length), snapshot.portableDraft.candidates, policy.mode, next, scrollOffset)
|
|
2574
|
+
: actionViewport(route.name, actions, policy.mode, next, scrollOffset);
|
|
2575
|
+
setScrollOffset(viewport.offset);
|
|
2576
|
+
return next;
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
else if (input === ' ' && route.name === 'installTools' && route.view !== 'blocked') {
|
|
2580
|
+
runAction(actions[focus]);
|
|
2581
|
+
}
|
|
2582
|
+
else if (key.return && route.name === 'installTools' && route.view !== 'blocked') {
|
|
2583
|
+
if (tools.length > 0) {
|
|
2584
|
+
setDirty(false);
|
|
2585
|
+
back();
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
else if (key.return) {
|
|
2589
|
+
runAction(actions[focus]);
|
|
2590
|
+
}
|
|
2591
|
+
});
|
|
2592
|
+
if (tooSmall) {
|
|
2593
|
+
return (_jsx(ScreenFrame, { title: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", breadcrumb: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", context: `${size.columns} × ${size.rows}`, help: "q \u9000\u51FA", size: size, children: _jsxs(Bordered, { children: [_jsx(Text, { bold: true, children: "\u2194 \u8BF7\u5C06\u7EC8\u7AEF\u8C03\u6574\u5230\u81F3\u5C11 60 \u00D7 18" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u8C03\u6574\u540E\u4F1A\u8FD4\u56DE\u539F\u9875\u9762\uFF0C\u4E0D\u4F1A\u4E22\u5931\u5F53\u524D\u9009\u62E9\u3002" })] }) }));
|
|
2594
|
+
}
|
|
2595
|
+
if (actionError)
|
|
2596
|
+
description = actionError;
|
|
2597
|
+
const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, help: help, size: size, children: content }));
|
|
2598
|
+
if (!overlay)
|
|
2599
|
+
return base;
|
|
2600
|
+
const helpRows = [
|
|
2601
|
+
['↑ / ↓', '移动当前唯一焦点'],
|
|
2602
|
+
['Enter', '打开、选择或继续'],
|
|
2603
|
+
['Esc / b', '返回上一页'],
|
|
2604
|
+
...(route.name === 'pluginList' ? [['← / →', '切换来源'], ['/', '进入搜索']] : []),
|
|
2605
|
+
...(route.name === 'installTools' ? [['Space', '切换工具选择']] : []),
|
|
2606
|
+
...(route.name === 'review' || route.name === 'progress' ? [['d', '打开技术详情']] : []),
|
|
2607
|
+
['?', '关闭帮助'],
|
|
2608
|
+
];
|
|
2609
|
+
return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsxs(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", borderStyle: "round", borderColor: tuiTheme.colors.accent, paddingX: 1, children: [_jsx(Text, { bold: true, children: overlay === 'help' ? '当前页快捷键' : overlay === 'discard' ? '放弃未保存修改?' : '退出控制台?' }), overlay === 'help'
|
|
2610
|
+
? _jsx(_Fragment, { children: helpRows.map(([key, value]) => _jsxs(Text, { children: [key.padEnd(9), value] }, key)) })
|
|
2611
|
+
: _jsx(Text, { color: tuiTheme.colors.muted, children: overlay === 'discard' ? '放弃后将返回上一页。' : '进行中的写操作会先完成。' }), _jsxs(Text, { bold: true, color: overlayFocus === 0 ? tuiTheme.colors.accent : undefined, children: ["\u203A ", overlay === 'help' ? '关闭' : overlay === 'discard' ? '继续编辑' : '继续使用'] }), overlay === 'exit' || overlay === 'discard' ? _jsxs(Text, { bold: true, color: overlayFocus === 1 ? tuiTheme.statusColors.error : undefined, children: [overlayFocus === 1 ? '› ' : ' ', overlay === 'discard' ? '放弃修改' : '退出'] }) : null] })] }));
|
|
2612
|
+
}
|
|
2613
|
+
//# sourceMappingURL=console-app-v4.js.map
|