@lesliechan721/aw 0.1.0-beta.64 → 0.1.0-beta.65
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-runtime-config.mjs +3 -3
- package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -1
- package/dist/authoring/scripts/add-skill.mjs +3 -17
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +1 -0
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +2 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +2 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +95 -698
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +2 -0
- package/dist/scripts/aw-skill-eval-materializer.js +74 -44
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -1
- package/dist/scripts/bundle-plugins.js +10 -2
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +1 -1
- package/dist/scripts/plugin-staging.js +1 -1
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.js +31 -31
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +2 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/commands/console.js +4 -3
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/intent-command.d.ts +30 -0
- package/dist/src/commands/intent-command.js +164 -0
- package/dist/src/commands/intent-command.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +2 -0
- package/dist/src/commands/operation-plan.js +2 -1
- package/dist/src/commands/operation-plan.js.map +1 -1
- package/dist/src/commands/options.d.ts +3 -11
- package/dist/src/commands/options.js +11 -17
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +27 -33
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +108 -6
- package/dist/src/commands/rebuild-command.js +617 -1549
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +19 -3
- package/dist/src/commands/rebuild-integration.js +500 -459
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.js +1 -2
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/review-inspection.d.ts +2 -2
- package/dist/src/commands/review-inspection.js +30 -86
- package/dist/src/commands/review-inspection.js.map +1 -1
- package/dist/src/commands/review-preparation.js +16 -7
- package/dist/src/commands/review-preparation.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +2 -0
- package/dist/src/commands/skill-eval.js +15 -9
- package/dist/src/commands/skill-eval.js.map +1 -1
- package/dist/src/commands/status-v6.d.ts +38 -5
- package/dist/src/commands/status-v6.js +269 -102
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/targets-command.d.ts +27 -0
- package/dist/src/commands/targets-command.js +77 -0
- package/dist/src/commands/targets-command.js.map +1 -0
- package/dist/src/commands/write-commands.js +5 -5
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +2 -0
- package/dist/src/eval/manifest-v1.js +7 -2
- package/dist/src/eval/manifest-v1.js.map +1 -1
- package/dist/src/generators/local-plugin.d.ts +2 -1
- package/dist/src/generators/local-plugin.js +111 -108
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +10 -17
- package/dist/src/index.js +585 -1349
- package/dist/src/index.js.map +1 -1
- package/dist/src/intent/deployment-preferences.d.ts +63 -0
- package/dist/src/intent/deployment-preferences.js +170 -0
- package/dist/src/intent/deployment-preferences.js.map +1 -0
- package/dist/src/intent/diagnostics.d.ts +11 -0
- package/dist/src/intent/diagnostics.js +14 -0
- package/dist/src/intent/diagnostics.js.map +1 -0
- package/dist/src/intent/intent-codec.d.ts +19 -0
- package/dist/src/intent/intent-codec.js +117 -0
- package/dist/src/intent/intent-codec.js.map +1 -0
- package/dist/src/intent/intent-store.d.ts +43 -0
- package/dist/src/intent/intent-store.js +118 -0
- package/dist/src/intent/intent-store.js.map +1 -0
- package/dist/src/intent/scope-context.d.ts +33 -0
- package/dist/src/intent/scope-context.js +145 -0
- package/dist/src/intent/scope-context.js.map +1 -0
- package/dist/src/intent/work-context.d.ts +13 -0
- package/dist/src/intent/work-context.js +22 -0
- package/dist/src/intent/work-context.js.map +1 -0
- package/dist/src/output/command-receipt.js +16 -9
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/portable/commands.d.ts +18 -8
- package/dist/src/portable/commands.js +46 -562
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/historical-materialization.d.ts +22 -0
- package/dist/src/portable/historical-materialization.js +124 -0
- package/dist/src/portable/historical-materialization.js.map +1 -0
- package/dist/src/portable/materializer.d.ts +8 -0
- package/dist/src/portable/materializer.js +75 -44
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/recovery-layout.d.ts +17 -0
- package/dist/src/portable/recovery-layout.js +55 -0
- package/dist/src/portable/recovery-layout.js.map +1 -0
- package/dist/src/portable/recovery.d.ts +143 -0
- package/dist/src/portable/recovery.js +339 -0
- package/dist/src/portable/recovery.js.map +1 -0
- package/dist/src/portable/writer.d.ts +64 -0
- package/dist/src/portable/writer.js +316 -0
- package/dist/src/portable/writer.js.map +1 -0
- package/dist/src/recovery/coordinator.d.ts +137 -0
- package/dist/src/recovery/coordinator.js +1198 -0
- package/dist/src/recovery/coordinator.js.map +1 -0
- package/dist/src/recovery/demand.d.ts +44 -0
- package/dist/src/recovery/demand.js +178 -0
- package/dist/src/recovery/demand.js.map +1 -0
- package/dist/src/recovery/joint-coordinator.d.ts +24 -0
- package/dist/src/recovery/joint-coordinator.js +770 -0
- package/dist/src/recovery/joint-coordinator.js.map +1 -0
- package/dist/src/recovery/joint-demand.d.ts +68 -0
- package/dist/src/recovery/joint-demand.js +166 -0
- package/dist/src/recovery/joint-demand.js.map +1 -0
- package/dist/src/recovery/receipt.d.ts +81 -0
- package/dist/src/recovery/receipt.js +2 -0
- package/dist/src/recovery/receipt.js.map +1 -0
- package/dist/src/recovery/retirement.d.ts +45 -0
- package/dist/src/recovery/retirement.js +366 -0
- package/dist/src/recovery/retirement.js.map +1 -0
- package/dist/src/recovery/status.d.ts +108 -0
- package/dist/src/recovery/status.js +417 -0
- package/dist/src/recovery/status.js.map +1 -0
- package/dist/src/recovery/types.d.ts +86 -0
- package/dist/src/recovery/types.js +2 -0
- package/dist/src/recovery/types.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +2 -1
- package/dist/src/review/review-profile.js +15 -15
- package/dist/src/review/review-profile.js.map +1 -1
- package/dist/src/review/review-selection.d.ts +1 -2
- package/dist/src/review/review-selection.js +40 -114
- package/dist/src/review/review-selection.js.map +1 -1
- package/dist/src/review/review-types.d.ts +4 -4
- package/dist/src/review/review-types.js.map +1 -1
- package/dist/src/review/skill-materialization.js +15 -6
- package/dist/src/review/skill-materialization.js.map +1 -1
- package/dist/src/review/source-binding.d.ts +14 -4
- package/dist/src/review/source-binding.js +62 -117
- package/dist/src/review/source-binding.js.map +1 -1
- package/dist/src/tui/agent-management.d.ts +23 -0
- package/dist/src/tui/agent-management.js +217 -0
- package/dist/src/tui/agent-management.js.map +1 -0
- package/dist/src/tui/agent-settings.d.ts +44 -0
- package/dist/src/tui/agent-settings.js +98 -0
- package/dist/src/tui/agent-settings.js.map +1 -0
- package/dist/src/tui/components.d.ts +3 -0
- package/dist/src/tui/components.js +10 -1
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-references.d.ts +6 -3
- package/dist/src/tui/config-references.js +17 -12
- package/dist/src/tui/config-references.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +1 -2
- package/dist/src/tui/console-app-v4.js +1533 -1047
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +2 -1
- package/dist/src/tui/controller-runtime.js +405 -1626
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +131 -81
- package/dist/src/tui/controller.js +307 -227
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +2 -2
- package/dist/src/tui/entity-cards.js +98 -20
- package/dist/src/tui/entity-cards.js.map +1 -1
- package/dist/src/tui/glossary.d.ts +2 -0
- package/dist/src/tui/glossary.js +20 -0
- package/dist/src/tui/glossary.js.map +1 -1
- package/dist/src/tui/model.d.ts +28 -10
- package/dist/src/tui/model.js +41 -41
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-review-planner.d.ts +2 -0
- package/dist/src/tui/operation-review-planner.js +58 -122
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/plugin-content.js +7 -4
- package/dist/src/tui/plugin-content.js.map +1 -1
- package/dist/src/tui/plugin-detail.d.ts +22 -3
- package/dist/src/tui/plugin-detail.js +113 -26
- package/dist/src/tui/plugin-detail.js.map +1 -1
- package/dist/src/tui/reader.d.ts +1 -0
- package/dist/src/tui/reader.js +3 -1
- package/dist/src/tui/reader.js.map +1 -1
- package/dist/src/tui/router.d.ts +23 -33
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/scoped-flow.d.ts +45 -0
- package/dist/src/tui/scoped-flow.js +96 -0
- package/dist/src/tui/scoped-flow.js.map +1 -0
- package/dist/src/tui/scoped-inventory.d.ts +122 -0
- package/dist/src/tui/scoped-inventory.js +225 -0
- package/dist/src/tui/scoped-inventory.js.map +1 -0
- package/dist/src/tui/screen-registry.d.ts +25 -25
- package/dist/src/tui/screen-registry.js +3 -7
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/dist/src/tui/session-console-app.js +1 -1
- package/dist/src/tui/session-console-app.js.map +1 -1
- package/dist/src/tui/session-host.js +12 -2
- package/dist/src/tui/session-host.js.map +1 -1
- package/dist/src/types.d.ts +15 -12
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.js +2 -0
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.js +13 -6
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +6 -3
- package/dist/src/utils/artifact-renderer.js +27 -32
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +1 -0
- package/dist/src/utils/artifact-request.js +22 -12
- package/dist/src/utils/artifact-request.js.map +1 -1
- package/dist/src/utils/artifact-sources.d.ts +2 -2
- package/dist/src/utils/bundle-layout.js +18 -16
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-digests.js +2 -0
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +4 -3
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-source-references.d.ts +5 -0
- package/dist/src/utils/catalog-source-references.js +42 -57
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/claude-native-render-receipt.d.ts +14 -0
- package/dist/src/utils/claude-native-render-receipt.js +26 -0
- package/dist/src/utils/claude-native-render-receipt.js.map +1 -0
- package/dist/src/utils/claude-skills-root.js +14 -3
- package/dist/src/utils/claude-skills-root.js.map +1 -1
- package/dist/src/utils/codex-manager.js +6 -12
- package/dist/src/utils/codex-manager.js.map +1 -1
- package/dist/src/utils/codex-native-observation.js +29 -13
- package/dist/src/utils/codex-native-observation.js.map +1 -1
- package/dist/src/utils/codex-reconciler.d.ts +2 -5
- package/dist/src/utils/codex-reconciler.js +25 -1
- package/dist/src/utils/codex-reconciler.js.map +1 -1
- package/dist/src/utils/codex-surface-analysis.js +5 -3
- package/dist/src/utils/codex-surface-analysis.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +14 -2
- package/dist/src/utils/local-marketplace.js +209 -42
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/managed-command-evidence.js +1 -1
- package/dist/src/utils/managed-command-evidence.js.map +1 -1
- package/dist/src/utils/managed-command-materialization.d.ts +4 -0
- package/dist/src/utils/managed-command-materialization.js +15 -6
- package/dist/src/utils/managed-command-materialization.js.map +1 -1
- package/dist/src/utils/managed-command-store.d.ts +15 -0
- package/dist/src/utils/managed-command-store.js +73 -8
- package/dist/src/utils/managed-command-store.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +2 -0
- package/dist/src/utils/native-cli.js +22 -0
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/native-demand.d.ts +10 -0
- package/dist/src/utils/native-demand.js +4 -0
- package/dist/src/utils/native-demand.js.map +1 -0
- package/dist/src/utils/ownership.js +2 -0
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/paths.js +1 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/platform-support.d.ts +1 -1
- package/dist/src/utils/platform-support.js +1 -1
- package/dist/src/utils/platform-support.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +3 -0
- package/dist/src/utils/plugin-config-impact.js +115 -273
- package/dist/src/utils/plugin-config-impact.js.map +1 -1
- package/dist/src/utils/plugin-config-references.d.ts +9 -2
- package/dist/src/utils/plugin-config-references.js +75 -85
- package/dist/src/utils/plugin-config-references.js.map +1 -1
- package/dist/src/utils/plugin-config-write.d.ts +58 -1
- package/dist/src/utils/plugin-config-write.js +756 -809
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +47 -46
- package/dist/src/utils/plugin-config.js +292 -833
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.d.ts +11 -6
- package/dist/src/utils/plugin-scripts.js +52 -35
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/runtime-config.js +5 -4
- package/dist/src/utils/runtime-config.js.map +1 -1
- package/dist/src/utils/runtime-skill-closure.d.ts +2 -2
- package/dist/src/utils/runtime-skill-closure.js.map +1 -1
- package/dist/src/utils/state-paths.d.ts +8 -3
- package/dist/src/utils/state-paths.js +53 -16
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/windows-npm-shim.d.ts +2 -0
- package/dist/src/utils/windows-npm-shim.js +5 -7
- package/dist/src/utils/windows-npm-shim.js.map +1 -1
- package/dist/vitest.config.js +1 -0
- package/dist/vitest.config.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/commands/sync-command.d.ts +0 -40
- package/dist/src/commands/sync-command.js +0 -254
- package/dist/src/commands/sync-command.js.map +0 -1
- package/dist/src/portable/skills-cli.d.ts +0 -51
- package/dist/src/portable/skills-cli.js +0 -202
- package/dist/src/portable/skills-cli.js.map +0 -1
- package/dist/src/tui/project-inventory.d.ts +0 -68
- package/dist/src/tui/project-inventory.js +0 -203
- package/dist/src/tui/project-inventory.js.map +0 -1
- package/dist/src/utils/plugin-intent-state.d.ts +0 -49
- package/dist/src/utils/plugin-intent-state.js +0 -320
- package/dist/src/utils/plugin-intent-state.js.map +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { configReferenceKey, configReferenceValues, configReferenceScalars, configReferenceSelection, configReferenceMessage, configReferenceSource, configCrossLayerDependencies } from './config-references.js';
|
|
3
3
|
import { arrayDraftChanged, arrayDraftValue, createConfigArrayDraft, editConfigArrayDraft } from './config-array-draft.js';
|
|
4
|
-
import { PLUGIN_CONFIG_ARRAY_MAX_ITEMS } from '@lesliechan721/aw-plugin-core';
|
|
4
|
+
import { PLATFORM_TARGETS, PLUGIN_CONFIG_ARRAY_MAX_ITEMS } from '@lesliechan721/aw-plugin-core';
|
|
5
5
|
import { presentConfigValue, safeConfigText } from './config-value-presentation.js';
|
|
6
6
|
import { ConfigLeafEditor } from './config-leaf-editor.js';
|
|
7
7
|
import { Box, Text, useInput, useWindowSize } from 'ink';
|
|
8
|
+
import { AgentManagement } from './agent-management.js';
|
|
8
9
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
9
10
|
import path from 'node:path';
|
|
10
11
|
import { getCommandPackageId, resolvePluginConfigObjectProperties, validatePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
@@ -15,7 +16,7 @@ import { normalizePortableAgents, SKILLS_CLI_SUPPORTED_VERSION_LABEL, } from '..
|
|
|
15
16
|
import { selectLifecycleRetryAction, } from '../commands/operation-planner.js';
|
|
16
17
|
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, SCRIPT_TIMEOUT_MAX_MS, SCRIPT_TIMEOUT_MIN_MS, } from '../commands/options.js';
|
|
17
18
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
18
|
-
import { ActionRow, ConfigTile, configTileHeight, HomeNavTile, KeyCap, ObjectCardFrame, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
19
|
+
import { ActionRow, AgentStatusText, ConfigTile, configTileHeight, HomeNavTile, KeyCap, ObjectCardFrame, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
19
20
|
import { safeDiagnosticCode, sanitizeDiagnosticText, } from './diagnostics.js';
|
|
20
21
|
import { extractPortableError, TuiConfigInvalidStoreError, userIssueKey, } from './controller.js';
|
|
21
22
|
import { pluginWriteTaskVersionEvidence } from './plugin-write-task.js';
|
|
@@ -25,7 +26,7 @@ import { copyToClipboard } from './clipboard.js';
|
|
|
25
26
|
import { resolveTuiSizePolicy } from './layout.js';
|
|
26
27
|
import { buildDependencyGraphProjection, buildUnifiedDependencyGraphProjection, } from './dependency-graph.js';
|
|
27
28
|
import { DependencyGraphView, projectDependencyGraphView } from './dependency-graph-view.js';
|
|
28
|
-
import { platformLabel, scopeLabel } from './glossary.js';
|
|
29
|
+
import { agentLabel, platformLabel, scopeLabel, recoveryReason } from './glossary.js';
|
|
29
30
|
import { InputDisplay, removeLastGrapheme } from './input-display.js';
|
|
30
31
|
import { QueryHeader, querySearchWidth } from './query-header.js';
|
|
31
32
|
import { Pager, Reader, paginateWrappedRows, readerPage } from './reader.js';
|
|
@@ -34,18 +35,239 @@ import { ContentItemCard, contentItemCardLayout, contentItemCardWindowHeight, co
|
|
|
34
35
|
import { SourceDetailCard, sourceDetailCardLayout } from './source-detail-card.js';
|
|
35
36
|
import { catalogRefPolicyForPreset } from './source-ref-presets.js';
|
|
36
37
|
import { buildPlatformActions, configObjectHasSensitiveChild, hasInstalledPlacement, configValuesEqual, } from './model.js';
|
|
38
|
+
import { managedCommandPlanChanges } from './operation-review-planner.js';
|
|
37
39
|
import { resolveTuiGlobalKeyAction, resolveTuiSessionNavigationKey } from './keys.js';
|
|
38
40
|
import { tuiTheme } from './theme.js';
|
|
39
41
|
import { contentRowBudget, rowViewport, wrapDisplayText } from './viewport.js';
|
|
40
42
|
import { resolveRouteScreenId, routeDefinition, TUI_SCREEN_IDS, unreachableTuiRoute, } from './screen-registry.js';
|
|
41
43
|
export const CONSOLE_V4_SCREEN_IDS = TUI_SCREEN_IDS;
|
|
44
|
+
function splitTaskLines(context) {
|
|
45
|
+
const targetLabel = (target) => `${target.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`;
|
|
46
|
+
return [
|
|
47
|
+
`完整任务请求:${context.targets.length} 个位置`,
|
|
48
|
+
...context.targets.map((target) => `请求:${targetLabel(target)}`),
|
|
49
|
+
`本次范围:${scopeLabel(context.currentScope)}`,
|
|
50
|
+
...context.targets.filter((target) => target.scope !== context.currentScope)
|
|
51
|
+
.map((target) => `其余范围未处理:${targetLabel(target)}`),
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
function scopedRiskReasons(plan) {
|
|
55
|
+
if (plan.kind === 'targets' || plan.kind === 'intent')
|
|
56
|
+
return [];
|
|
57
|
+
if (plan.kind === 'retire')
|
|
58
|
+
return [
|
|
59
|
+
...(plan.plan.options.deleteConfig ? ['本次会删除此插件的全部本机设置(通用与各 Agent),影响所有项目。'] : []),
|
|
60
|
+
...(plan.scope === 'user' && plan.plan.plannedImpact.outputs
|
|
61
|
+
? ['本次会清理个人原生输出,可能影响其他项目。'] : []),
|
|
62
|
+
];
|
|
63
|
+
const reasons = [];
|
|
64
|
+
const visit = (entry) => {
|
|
65
|
+
reasons.push(...entry.nativeGroups.flatMap((group) => (group.preview.codexReconcilePlans ?? []).filter((item) => item.review.requiresConfirmation)
|
|
66
|
+
.map((item) => item.review.sharedImpact.warning
|
|
67
|
+
?? '本次共享原生内容变更可能影响其他使用位置。')));
|
|
68
|
+
if (entry.receipt.scope === 'user' && entry.portableGroups.length > 0) {
|
|
69
|
+
reasons.push('本次可能修改所选工具的个人 Skills 目录,适用于所有项目。');
|
|
70
|
+
}
|
|
71
|
+
for (const refresh of entry.sourceRefreshes ?? []) {
|
|
72
|
+
if (!refresh.impact.inventoryComplete) {
|
|
73
|
+
reasons.push(`${refresh.sourceId} 的来源影响清单不完整;必须明确确认来源变更。`);
|
|
74
|
+
}
|
|
75
|
+
else if (refresh.impact.affectedReferences.length > 0) {
|
|
76
|
+
reasons.push(`${refresh.sourceId} 的来源变更会影响 ${refresh.impact.affectedReferences.length} 个已知引用。`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const child of entry.joint?.children ?? [])
|
|
80
|
+
visit(child);
|
|
81
|
+
};
|
|
82
|
+
visit(plan.plan);
|
|
83
|
+
if (plan.plan.command === 'cleanup' && plan.plan.options.force) {
|
|
84
|
+
reasons.push('本次受限强制清理只删除已确认的专属目标;依赖此插件的其它目标可能需要恢复。');
|
|
85
|
+
}
|
|
86
|
+
return [...new Set(reasons)];
|
|
87
|
+
}
|
|
88
|
+
function recoveryPluginLabel(id) {
|
|
89
|
+
const [source, ...name] = id.split('/');
|
|
90
|
+
return name.length ? `${name.join('/')}(${source})` : id;
|
|
91
|
+
}
|
|
92
|
+
function recoveryTargetLines(receipt, result) {
|
|
93
|
+
const groups = new Map();
|
|
94
|
+
let unchanged = 0;
|
|
95
|
+
for (const target of receipt.targetResults) {
|
|
96
|
+
let action;
|
|
97
|
+
if (result) {
|
|
98
|
+
action = target.satisfaction === 'unsupported' || target.satisfaction === 'excluded' ? '! 已跳过'
|
|
99
|
+
: target.execution === 'applied' || target.execution === 'no-op' ? '✓ 完成'
|
|
100
|
+
: target.execution === 'failed' || target.execution === 'partial' ? '× 安装失败'
|
|
101
|
+
: target.blockers.length ? '! 已跳过' : '? 尚未确认';
|
|
102
|
+
}
|
|
103
|
+
else if (target.blockers.length || target.satisfaction === 'excluded' || target.satisfaction === 'unsupported') {
|
|
104
|
+
action = '! 跳过';
|
|
105
|
+
}
|
|
106
|
+
else if (receipt.command === 'cleanup' || receipt.command === 'uninstall' || receipt.command === 'retire') {
|
|
107
|
+
action = '− 移除';
|
|
108
|
+
}
|
|
109
|
+
else if ((target.content === 'satisfied' || target.observation === 'verified') && receipt.command !== 'reinstall') {
|
|
110
|
+
unchanged += 1;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
else
|
|
114
|
+
action = target.content === 'absent' || target.observation === 'missing' ? '+ 新增安装' : '↻ 同步并修复';
|
|
115
|
+
const reason = target.blockers[0] || target.reasons?.[0] ? recoveryReason((target.blockers[0] ?? target.reasons[0]).code)
|
|
116
|
+
: target.satisfaction === 'excluded' ? '已关闭此 Agent'
|
|
117
|
+
: target.satisfaction === 'unsupported' ? '插件不支持此 Agent' : undefined;
|
|
118
|
+
const label = `${action} ${recoveryPluginLabel(target.pluginId)} ${scopeLabel(target.scope)}`;
|
|
119
|
+
const key = `${label}\0${reason ?? ''}`;
|
|
120
|
+
const group = groups.get(key) ?? { label: `${label}${reason ? ` · ${reason}` : ''}`, targets: [] };
|
|
121
|
+
const agent = agentLabel(target.agent);
|
|
122
|
+
if (!group.targets.includes(agent))
|
|
123
|
+
group.targets.push(agent);
|
|
124
|
+
groups.set(key, group);
|
|
125
|
+
}
|
|
126
|
+
return [...groups.values()].flatMap((group) => [group.label, ` → ${group.targets.join(' · ')}`])
|
|
127
|
+
.concat(unchanged ? [`✓ 另有 ${unchanged} 项已是最新`] : []);
|
|
128
|
+
}
|
|
129
|
+
function scopedPlanSummary(plan) {
|
|
130
|
+
if (plan.kind === 'targets')
|
|
131
|
+
return ['保存本机 Agent 选择',
|
|
132
|
+
`已选 ${plan.agents.map(agentLabel).join(' · ') || '无'}`, '已有输出保持不变;保存后可单独同步。'].join('\n');
|
|
133
|
+
if (plan.kind === 'intent')
|
|
134
|
+
return [
|
|
135
|
+
`${plan.action === 'add' ? '+ 加入插件清单' : '− 移出插件清单'} ${recoveryPluginLabel(plan.selector)}`,
|
|
136
|
+
scopeLabel(plan.scope), '已有安装内容保持不变。',
|
|
137
|
+
].join('\n');
|
|
138
|
+
const receipt = plan.plan.receipt;
|
|
139
|
+
const lines = [receipt.coverage?.scopes.length === 2 ? '所有项目 + 当前项目' : scopeLabel(plan.scope),
|
|
140
|
+
...recoveryTargetLines(receipt, false)];
|
|
141
|
+
if (plan.kind === 'recovery' && plan.plan.sourceRefreshes?.length) {
|
|
142
|
+
lines.unshift(...plan.plan.sourceRefreshes.map((refresh) => `↻ 启用来源 ${refresh.sourceId} 的候选版本`), '来源启用后,将重新准备插件操作汇总;本次不会直接更新插件。');
|
|
143
|
+
}
|
|
144
|
+
if (plan.plan.plannedImpact.declaration)
|
|
145
|
+
lines.push('插件清单也将保存本次变化。');
|
|
146
|
+
if (plan.plan.plannedImpact.config)
|
|
147
|
+
lines.push('− 删除所选插件的全部本机设置(通用与各 Agent)');
|
|
148
|
+
for (const change of receipt.nativeToolChanges ?? []) {
|
|
149
|
+
lines.push(`! 原生工具变更:${recoveryPluginLabel(`${change.producer.sourceId}/${change.producer.pluginName}`)}`);
|
|
150
|
+
for (const phase of ['before', 'after']) {
|
|
151
|
+
const closure = change[phase];
|
|
152
|
+
lines.push(` ${phase === 'before' ? '变更前' : '变更后'}:${closure?.definitions.map((tool) => tool.key).join(' · ') || '无'}`);
|
|
153
|
+
for (const consumer of closure?.consumers ?? [])
|
|
154
|
+
for (const tool of consumer.tools) {
|
|
155
|
+
lines.push(` ${consumer.key} 使用 ${tool.key}:${tool.policy === 'required' ? '必须可用' : '优先使用,可回退'}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (plan.kind === 'recovery') {
|
|
160
|
+
const scripts = new Set();
|
|
161
|
+
const visit = (entry) => {
|
|
162
|
+
for (const script of [...(entry.preApplyAuthorization ?? []),
|
|
163
|
+
...entry.nativeGroups.flatMap((group) => group.preview.preApplyAuthorization ?? [])]) {
|
|
164
|
+
scripts.add(`安装检查:${recoveryPluginLabel(script.pluginId)} · ${agentLabel(script.platform)} · ${scopeLabel(script.scope)}`);
|
|
165
|
+
}
|
|
166
|
+
for (const child of entry.joint?.children ?? [])
|
|
167
|
+
visit(child);
|
|
168
|
+
};
|
|
169
|
+
visit(plan.plan);
|
|
170
|
+
lines.push(...scripts);
|
|
171
|
+
}
|
|
172
|
+
lines.push(...scopedRiskReasons(plan).map((reason) => `! ${reason}`));
|
|
173
|
+
for (const diagnostic of [...receipt.errors, ...receipt.blockers])
|
|
174
|
+
lines.push(`! ${recoveryReason(diagnostic.code)}`);
|
|
175
|
+
return [...new Set(lines)].join('\n');
|
|
176
|
+
}
|
|
177
|
+
function scopedResultSummary(receipt) {
|
|
178
|
+
return [receipt.coverage?.scopes.length === 2 ? '所有项目 + 当前项目' : scopeLabel(receipt.scope), ...recoveryTargetLines(receipt, true),
|
|
179
|
+
...(receipt.declarationChanged ? ['插件清单已保存。'] : []),
|
|
180
|
+
...(receipt.preferencesChanged ? ['Agent 选择已保存。'] : []),
|
|
181
|
+
...(receipt.configChanged ? ['插件设置已保存。'] : []),
|
|
182
|
+
...[...new Set([...receipt.errors, ...receipt.blockers].map((entry) => recoveryReason(entry.code)))]
|
|
183
|
+
.map((reason) => `! ${reason}`),
|
|
184
|
+
...(receipt.commandResults?.length ? [`命令包:${receipt.commandResults.filter((entry) => entry.errors.length === 0).length} 项完成,${receipt.commandResults.filter((entry) => entry.errors.length > 0).length} 项需处理;详情中可查看恢复方法。`] : []),
|
|
185
|
+
].join('\n');
|
|
186
|
+
}
|
|
187
|
+
function scopedPlanText(plan, technical = false) {
|
|
188
|
+
if (!technical)
|
|
189
|
+
return scopedPlanSummary(plan);
|
|
190
|
+
if (plan.kind === 'targets')
|
|
191
|
+
return [
|
|
192
|
+
'偏好变更:', ...plan.changes, '需求变更:无', '配置变更:无', '输出变更:无',
|
|
193
|
+
...plan.warnings,
|
|
194
|
+
].join('\n');
|
|
195
|
+
if (plan.kind === 'intent')
|
|
196
|
+
return [
|
|
197
|
+
`范围:${plan.scope === 'user' ? '个人(所有项目)' : '当前项目'}`,
|
|
198
|
+
`完整请求:${plan.selector}`,
|
|
199
|
+
`需求变更:${plan.action === 'add' ? '添加' : '移除'} ${plan.selector}`,
|
|
200
|
+
'偏好变更:无', '配置变更:无', '输出变更:无',
|
|
201
|
+
...plan.warnings,
|
|
202
|
+
].join('\n');
|
|
203
|
+
const { receipt } = plan.plan;
|
|
204
|
+
const impact = plan.plan.plannedImpact;
|
|
205
|
+
const diagnostics = [...new Map([
|
|
206
|
+
...receipt.errors.map((entry) => [`${entry.code}:${entry.message}`, `全局错误:${entry.code} · ${entry.message}`]),
|
|
207
|
+
...receipt.blockers.map((entry) => [`${entry.code}:${entry.message}`, `全局受阻:${entry.code} · ${entry.message}`]),
|
|
208
|
+
]).values()];
|
|
209
|
+
return [
|
|
210
|
+
`范围:${plan.scope === 'user' ? '个人(所有项目)' : '当前项目'}`,
|
|
211
|
+
`完整请求:${receipt.requestedRoots.join('、') || '无'}`,
|
|
212
|
+
`本机工具:${receipt.selection.agents.map(agentLabel).join('、') || '未选择'}`,
|
|
213
|
+
`需求变更(计划):${impact.declaration
|
|
214
|
+
? plan.kind === 'retire' ? '删除当前范围声明' : '保存当前范围需求' : '无'}`,
|
|
215
|
+
`偏好变更(计划):${impact.preferences
|
|
216
|
+
? plan.kind === 'retire' ? '删除当前范围本机依赖覆盖' : '保存当前范围明确选择的依赖偏好' : '无'}`,
|
|
217
|
+
`配置变更(计划):${impact.config ? '删除所选插件的全部本机配置(common 与所有平台)' : '无'}`,
|
|
218
|
+
`输出变更(计划):${impact.outputs ? '执行所列精确目标' : '无可执行输出变更'}`,
|
|
219
|
+
...receipt.targetResults.map((target) => `${target.pluginId} · ${agentLabel(target.agent)} [${target.scope === 'user' ? '个人' : '当前项目'}] · ${target.delivery?.kind === 'native' ? '原生' : target.delivery?.kind === 'agent-skills' ? 'Skills' : '不支持'} · ${target.blockers.length
|
|
220
|
+
? `受阻:${target.blockers.map((blocker) => blocker.code).join('、')}` : target.observation}`),
|
|
221
|
+
...diagnostics,
|
|
222
|
+
...receipt.targetResults.flatMap((target) => target.reasons?.map((reason) => `${reason.code} · ${reason.message}`) ?? []),
|
|
223
|
+
...plan.plan.changes.map((change) => `影响:${change}`),
|
|
224
|
+
...plan.plan.warnings.map((warning) => `注意:${warning}`),
|
|
225
|
+
...(plan.kind === 'recovery' ? scopedCommandPlanText(plan) : []),
|
|
226
|
+
...(plan.kind === 'retire' ? [`配置删除选项:${plan.plan.options.deleteConfig
|
|
227
|
+
? impact.config ? '已选择' : '已选择;当前没有可删除配置' : '关闭(默认)'}`] : []),
|
|
228
|
+
].join('\n');
|
|
229
|
+
}
|
|
230
|
+
function scopedCommandPlanText(plan) {
|
|
231
|
+
return managedCommandPlanChanges(plan.plan.nativeGroups.flatMap((group) => group.preview.operationPlan?.actions ?? []))
|
|
232
|
+
.map((change) => `${change.title}${change.description ? `:${change.description}` : ''}`);
|
|
233
|
+
}
|
|
234
|
+
function scopedResultText(receipt, technical = false) {
|
|
235
|
+
if (!technical)
|
|
236
|
+
return scopedResultSummary(receipt);
|
|
237
|
+
const diagnostics = [...new Map([
|
|
238
|
+
...receipt.errors.map((entry) => [`${entry.code}:${entry.message}`, `全局错误:${entry.code} · ${entry.message}`]),
|
|
239
|
+
...receipt.blockers.map((entry) => [`${entry.code}:${entry.message}`, `全局受阻:${entry.code} · ${entry.message}`]),
|
|
240
|
+
]).values()];
|
|
241
|
+
const reviewStale = [...receipt.blockers, ...receipt.errors]
|
|
242
|
+
.some((entry) => entry.code === 'operationReviewStale');
|
|
243
|
+
return [
|
|
244
|
+
`范围:${receipt.scope === 'user' ? '个人(所有项目)' : '当前项目'}`,
|
|
245
|
+
`完整请求:${receipt.requestedRoots.join('、') || '无'}`,
|
|
246
|
+
`需求变更:${receipt.declarationChanged ? '是' : '无'}`,
|
|
247
|
+
`偏好变更:${receipt.preferencesChanged ? '是' : '无'}`,
|
|
248
|
+
`配置变更:${receipt.configChanged ? '是' : '无'}`,
|
|
249
|
+
`输出可能改变:${receipt.outputsMayHaveChanged ? '是' : '否'}`,
|
|
250
|
+
...diagnostics,
|
|
251
|
+
...receipt.targetResults.flatMap((target) => target.reasons?.map((reason) => `${reason.code} · ${reason.message}`) ?? []),
|
|
252
|
+
...(reviewStale ? ['本次未执行,需要重新准备。'] : []),
|
|
253
|
+
...receipt.targetResults.map((target) => `${target.pluginId} · ${agentLabel(target.agent)} [${target.scope === 'user' ? '个人' : '当前项目'}] · ${target.delivery?.kind === 'native' ? '原生' : target.delivery?.kind === 'agent-skills' ? 'Skills' : '无交付'} · ${target.execution} · ${target.observation}${target.blockers.length ? ` · 受阻 ${target.blockers.map((blocker) => blocker.code).join('、')}` : ''}`),
|
|
254
|
+
...receipt.targetResults.flatMap((target) => target.recoveryActions?.map((action) => `${target.pluginId} · ${target.agent}:${action}`) ?? []),
|
|
255
|
+
...(receipt.commandResults ?? []).flatMap((result) => [
|
|
256
|
+
`命令${result.producer.kind === 'package' ? '包' : '生产者'} ${result.producer.key} · ${result.producer.sourceId}/${result.producer.pluginName} · ${scopeLabel(result.producer.scope)}${result.producer.platform ? ` · ${platformLabel(result.producer.platform)}` : ''} · ${result.state}`,
|
|
257
|
+
...(result.recoveryActions.some((action) => action.startsWith('aw command package remove '))
|
|
258
|
+
? [result.producer.scope === 'user' ? '保留命令包:所有项目共用' : '保留命令包:当前项目内共用'] : []),
|
|
259
|
+
...result.commandIds.map((id) => `命令:${id}`),
|
|
260
|
+
...result.removedCommandIds.map((id) => `撤销命令:${id}`),
|
|
261
|
+
...result.errors.map((error) => `命令错误:${error.code} · ${error.message}`),
|
|
262
|
+
...result.recoveryActions.map((action) => `命令恢复:${action}`),
|
|
263
|
+
]),
|
|
264
|
+
...receipt.remainingTargets.map((target) => `未完成:${target.pluginId} · ${target.agent}`),
|
|
265
|
+
...receipt.recoveryActions.map((action) => `恢复:${action}`),
|
|
266
|
+
].join('\n');
|
|
267
|
+
}
|
|
42
268
|
const PLUGIN_FILTERS = [
|
|
43
269
|
'all', 'installed', 'uninstalled', 'updateAvailable', 'attention', 'project', 'user',
|
|
44
270
|
];
|
|
45
|
-
const PROJECT_STATUS_FILTERS = ['all', 'pending', 'blocked', 'unknown', 'ready'];
|
|
46
|
-
const PROJECT_STATUS_LABELS = {
|
|
47
|
-
all: '全部', pending: '待同步', blocked: '受阻', unknown: '未确认', ready: '就绪',
|
|
48
|
-
};
|
|
49
271
|
function queryMatches(query, ...values) {
|
|
50
272
|
const normalized = query.trim().toLocaleLowerCase();
|
|
51
273
|
return !normalized || values.some((value) => value?.toLocaleLowerCase().includes(normalized));
|
|
@@ -129,16 +351,25 @@ function helpTextForPage(help, busy) {
|
|
|
129
351
|
`q / Ctrl-C ${busy ? '操作完成后退出' : '退出控制台'}`,
|
|
130
352
|
...(!busy ? ['o 查看操作记录'] : []), 'Esc / ? 关闭帮助'].join('\n');
|
|
131
353
|
}
|
|
132
|
-
function projectDependencyLines(root) {
|
|
133
|
-
return root?.dependencies.flatMap((dependency) => dependency.targets.map((target) => `依赖 ${dependency.displayName} · ${dependency.pluginId} · ${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} · 目录参考 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'}${target.reasonCodes.length ? ` · ${target.reasonCodes.map((code) => safeDiagnosticCode(code, 'projectTargetUnknown')).join('、')}` : ''}`)) ?? [];
|
|
134
|
-
}
|
|
135
354
|
function configOwnerKey(route) {
|
|
136
355
|
return route.pluginId;
|
|
137
356
|
}
|
|
138
357
|
function configPageMatches(page, route) {
|
|
139
358
|
return Boolean(page && route
|
|
140
359
|
&& page.pluginId === route.pluginId
|
|
141
|
-
&& page.
|
|
360
|
+
&& page.profile === route.layer.profile
|
|
361
|
+
&& page.context === (route.layer.context ?? 'user'));
|
|
362
|
+
}
|
|
363
|
+
const CONFIG_PROFILE_KEYS = ['common', ...PLATFORM_TARGETS];
|
|
364
|
+
function configReadOptions(layer) {
|
|
365
|
+
return {
|
|
366
|
+
...(layer.profile === 'common' ? {} : { platform: layer.profile }),
|
|
367
|
+
context: layer.context ?? 'user',
|
|
368
|
+
view: 'stored',
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function configEditorOwnerKey(pluginId, layer) {
|
|
372
|
+
return `${pluginId}\0${layer.profile}\0${layer.context ?? 'user'}`;
|
|
142
373
|
}
|
|
143
374
|
function configFieldCanSet(field) {
|
|
144
375
|
return field.canSet ?? field.writable;
|
|
@@ -226,30 +457,6 @@ function pluginPlatformBadges(plugin) {
|
|
|
226
457
|
});
|
|
227
458
|
return _jsxs(Box, { children: [_jsx(StatusBadge, { items: items }), plugin.portableSkills ? _jsx(Text, { color: tuiTheme.colors.accent, children: " [Skills \u53EF\u9009]" }) : null, plugin.configOnly ? _jsx(Text, { color: tuiTheme.colors.muted, children: "[\u5DF2\u4FDD\u5B58\u8BBE\u7F6E]" }) : null] });
|
|
228
459
|
}
|
|
229
|
-
function installedScopeStates(plugin, platform) {
|
|
230
|
-
const states = new Map();
|
|
231
|
-
const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
|
|
232
|
-
if (!placement || placement.state === 'notInstalled' || placement.state === 'blocked')
|
|
233
|
-
return states;
|
|
234
|
-
const scoped = placement.state === 'scopeConflict'
|
|
235
|
-
? placement.placements
|
|
236
|
-
: placement.scopedPlacements ?? [{
|
|
237
|
-
scope: placement.scope,
|
|
238
|
-
health: placement.health,
|
|
239
|
-
revision: placement.revision,
|
|
240
|
-
actualState: placement.actualState,
|
|
241
|
-
}];
|
|
242
|
-
for (const entry of scoped) {
|
|
243
|
-
const actualState = 'actualState' in entry ? entry.actualState : undefined;
|
|
244
|
-
const healthy = entry.health === 'ready' && actualState !== 'blocked'
|
|
245
|
-
&& actualState !== 'needsSync';
|
|
246
|
-
states.set(entry.scope, healthy
|
|
247
|
-
? { symbol: '✓', status: 'success' }
|
|
248
|
-
: { symbol: '!', status: actualState === 'blocked' || entry.health === 'error'
|
|
249
|
-
? 'error' : 'warning' });
|
|
250
|
-
}
|
|
251
|
-
return states;
|
|
252
|
-
}
|
|
253
460
|
function issueMatchesTarget(issue, pluginId, platform, scope) {
|
|
254
461
|
if (!platform && !scope && issue.pluginId === pluginId)
|
|
255
462
|
return true;
|
|
@@ -556,7 +763,8 @@ function configUnavailableDiagnostic(route) {
|
|
|
556
763
|
{ key: 'pluginId', value: route.pluginId },
|
|
557
764
|
...(route.invalidStores
|
|
558
765
|
? [{ key: 'scope', value: route.invalidStores.map((store) => store.scope).join(',') }]
|
|
559
|
-
: route.layer ? [{ key:
|
|
766
|
+
: route.layer ? [{ key: route.layer.profile === 'common' ? 'storeEntry' : 'platform',
|
|
767
|
+
value: route.layer.profile }] : []),
|
|
560
768
|
],
|
|
561
769
|
checks: invalidLayer
|
|
562
770
|
? ['只删除所选 user 或 project 设置层。', '重置后重新读取继承值和默认值。']
|
|
@@ -892,7 +1100,8 @@ function dependencyRelationDescription(item) {
|
|
|
892
1100
|
}
|
|
893
1101
|
function dependencyEvidenceLines(row, subject) {
|
|
894
1102
|
const references = row.edge?.availablePlacementReferences ?? [];
|
|
895
|
-
const placementLines = subject?.placements.map((placement) => `安装位置 · ${
|
|
1103
|
+
const placementLines = subject?.placements.map((placement) => `安装位置 · ${placement.agent ? agentLabel(placement.agent)
|
|
1104
|
+
: placement.platform ? platformLabel(placement.platform) : '目标'}${placement.delivery ? ` · ${placement.delivery === 'native' ? '原生' : 'Skills'}` : ''} · ${scopeLabel(placement.scope)} · 实际安装版本 ${placement.installedVersion ?? '未确认'} · 观察 ${placement.observation === 'unknown' ? '未确认' : '已检查'}`) ?? [];
|
|
896
1105
|
const referenceLines = (reference) => [
|
|
897
1106
|
`consumer · ${reference.consumer.pluginId}`,
|
|
898
1107
|
`consumer · platform=${platformLabel(reference.consumer.platform)} · scope=${reference.consumer.scope}`,
|
|
@@ -990,7 +1199,22 @@ export function ConsoleAppV4(props) {
|
|
|
990
1199
|
const [helpPage, setHelpPage] = useState(1);
|
|
991
1200
|
const [operationConfirmation, setOperationConfirmation] = useState();
|
|
992
1201
|
const [sourceTransportOverlay, setSourceTransportOverlay] = useState();
|
|
993
|
-
const [
|
|
1202
|
+
const [scopedFlow, setScopedFlow] = useState();
|
|
1203
|
+
const [agentSettings, setAgentSettings] = useState();
|
|
1204
|
+
const [agentSettingsError, setAgentSettingsError] = useState();
|
|
1205
|
+
const [agentWelcome, setAgentWelcome] = useState(false);
|
|
1206
|
+
const [agentWelcomeFocus, setAgentWelcomeFocus] = useState(0);
|
|
1207
|
+
const [configSavedFocus, setConfigSavedFocus] = useState(0);
|
|
1208
|
+
const agentWelcomeSeen = useRef(false);
|
|
1209
|
+
const scopedFlowToken = useRef(0);
|
|
1210
|
+
const scopedFlowBusy = useRef(false);
|
|
1211
|
+
const scopedSelected = useRef({});
|
|
1212
|
+
const scopedActiveRoot = useRef({});
|
|
1213
|
+
const scopedPageContext = useRef({});
|
|
1214
|
+
const [, setScopedSelectionRevision] = useState(0);
|
|
1215
|
+
const [scopedDiscardIntent, setScopedDiscardIntent] = useState();
|
|
1216
|
+
const scopedStatusRequested = useRef({});
|
|
1217
|
+
const [splitTaskReview, setSplitTaskReview] = useState();
|
|
994
1218
|
const [discardForExit, setDiscardForExit] = useState(false);
|
|
995
1219
|
const [discardForSessionNavigation, setDiscardForSessionNavigation] = useState();
|
|
996
1220
|
const [discardDraftConfirmation, setDiscardDraftConfirmation] = useState();
|
|
@@ -1007,9 +1231,8 @@ export function ConsoleAppV4(props) {
|
|
|
1007
1231
|
?? (snapshot.router.current.route.name === 'pluginList'
|
|
1008
1232
|
&& snapshot.router.current.route.mode === 'installed' ? 'installed' : 'all'));
|
|
1009
1233
|
const [scope, setScope] = useState('user');
|
|
1010
|
-
const [
|
|
1011
|
-
const [
|
|
1012
|
-
const [installPlatformIndex, setInstallPlatformIndex] = useState(0);
|
|
1234
|
+
const [, setSelectedTargets] = useState([]);
|
|
1235
|
+
const [, setInstallPlatformScopes] = useState({});
|
|
1013
1236
|
const [uninstallTargetKeys, setUninstallTargetKeys] = useState([]);
|
|
1014
1237
|
const [technicalRouteKey, setTechnicalRouteKey] = useState();
|
|
1015
1238
|
const [exitAfterBusy, setExitAfterBusy] = useState(false);
|
|
@@ -1017,12 +1240,13 @@ export function ConsoleAppV4(props) {
|
|
|
1017
1240
|
const [, setClockTick] = useState(0);
|
|
1018
1241
|
const [dirty, setDirty] = useState(false);
|
|
1019
1242
|
const [configPage, setConfigPage] = useState();
|
|
1020
|
-
const [configLayerCounts, setConfigLayerCounts] = useState({});
|
|
1021
1243
|
const [configLayerPages, setConfigLayerPages] = useState({});
|
|
1022
1244
|
const [configDrafts, setConfigDrafts] = useState({});
|
|
1023
1245
|
const [referenceContexts, setReferenceContexts] = useState({});
|
|
1246
|
+
const [configReadRevision, setConfigReadRevision] = useState(0);
|
|
1024
1247
|
const [referenceContextError, setReferenceContextError] = useState();
|
|
1025
1248
|
const [configCandidate, setConfigCandidate] = useState();
|
|
1249
|
+
const [configSaveState, setConfigSaveState] = useState();
|
|
1026
1250
|
const [arrayDrafts, setArrayDrafts] = useState({});
|
|
1027
1251
|
const [arrayToolsOpen, setArrayToolsOpen] = useState(false);
|
|
1028
1252
|
const [arrayToolFocus, setArrayToolFocus] = useState(0);
|
|
@@ -1085,6 +1309,29 @@ export function ConsoleAppV4(props) {
|
|
|
1085
1309
|
const exitSent = useRef(false);
|
|
1086
1310
|
const operationBusy = useRef(false);
|
|
1087
1311
|
const route = snapshot.router.current.route;
|
|
1312
|
+
useEffect(() => {
|
|
1313
|
+
if (snapshot.phase !== 'ready')
|
|
1314
|
+
return;
|
|
1315
|
+
let active = true;
|
|
1316
|
+
void props.controller.readAgentSettings().then((settings) => {
|
|
1317
|
+
if (!active)
|
|
1318
|
+
return;
|
|
1319
|
+
setAgentSettings(settings);
|
|
1320
|
+
setAgentSettingsError(undefined);
|
|
1321
|
+
if (!agentWelcomeSeen.current && settings.preferences.targets.length === 0) {
|
|
1322
|
+
agentWelcomeSeen.current = true;
|
|
1323
|
+
setAgentWelcome(true);
|
|
1324
|
+
}
|
|
1325
|
+
}).catch(() => { if (active)
|
|
1326
|
+
setAgentSettingsError('无法读取 Agent 设置'); });
|
|
1327
|
+
return () => { active = false; };
|
|
1328
|
+
}, [props.controller, snapshot.phase]);
|
|
1329
|
+
useEffect(() => {
|
|
1330
|
+
if (route.name !== 'intentInventory' || scopedStatusRequested.current[route.scope])
|
|
1331
|
+
return;
|
|
1332
|
+
scopedStatusRequested.current[route.scope] = true;
|
|
1333
|
+
void props.controller.refreshScopedInventory(route.scope);
|
|
1334
|
+
}, [props.controller, route.name, route.name === 'intentInventory' ? route.scope : '']);
|
|
1088
1335
|
const platformFacts = route.name === 'platform'
|
|
1089
1336
|
? props.controller.getPluginDetailSubject(route.pluginId)?.placements
|
|
1090
1337
|
.filter((entry) => entry.platform === route.platform) ?? []
|
|
@@ -1099,15 +1346,13 @@ export function ConsoleAppV4(props) {
|
|
|
1099
1346
|
...(snapshot.router.currentFlowId ? { flowId: snapshot.router.currentFlowId } : {}),
|
|
1100
1347
|
});
|
|
1101
1348
|
}
|
|
1102
|
-
const configDraftCount =
|
|
1103
|
-
const draft = configDrafts[
|
|
1349
|
+
const configDraftCount = CONFIG_PROFILE_KEYS.reduce((count, profile) => {
|
|
1350
|
+
const draft = configDrafts[profile];
|
|
1104
1351
|
return count + Object.keys(draft?.updates ?? {}).length + (draft?.removes.length ?? 0);
|
|
1105
1352
|
}, 0);
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
...(configDrafts.project?.updates ?? {}),
|
|
1110
|
-
};
|
|
1353
|
+
const editingProfile = 'layer' in route && route.layer ? route.layer.profile : undefined;
|
|
1354
|
+
const configContext = 'layer' in route && route.layer ? route.layer.context ?? 'user' : 'user';
|
|
1355
|
+
const draftUpdates = editingProfile ? configDrafts[editingProfile]?.updates ?? {} : {};
|
|
1111
1356
|
const definition = routeDefinition(route);
|
|
1112
1357
|
const rendererFamily = definition.rendererFamily;
|
|
1113
1358
|
const routeKey = JSON.stringify(route);
|
|
@@ -1193,41 +1438,6 @@ export function ConsoleAppV4(props) {
|
|
|
1193
1438
|
props.controller.dispose();
|
|
1194
1439
|
};
|
|
1195
1440
|
}, [props.controller, props.ownsController]);
|
|
1196
|
-
useEffect(() => {
|
|
1197
|
-
const reminder = snapshot.projectIntentReminder;
|
|
1198
|
-
if (!props.onClaimProjectIntentReminder
|
|
1199
|
-
|| snapshot.phase !== 'ready'
|
|
1200
|
-
|| route.name !== 'home'
|
|
1201
|
-
|| !reminder
|
|
1202
|
-
|| reminder.actionableTargets.length + reminder.blockedTargets.length === 0
|
|
1203
|
-
|| overlay
|
|
1204
|
-
|| operationConfirmation
|
|
1205
|
-
|| sourceTransportOverlay
|
|
1206
|
-
|| dirty
|
|
1207
|
-
|| props.controller.hasSessionDraft)
|
|
1208
|
-
return;
|
|
1209
|
-
if (props.onClaimProjectIntentReminder(reminder.projectRoot, reminder.signature)) {
|
|
1210
|
-
setOverlayFocus(0);
|
|
1211
|
-
setProjectReminderOpen(true);
|
|
1212
|
-
}
|
|
1213
|
-
}, [
|
|
1214
|
-
props.controller,
|
|
1215
|
-
props.onClaimProjectIntentReminder,
|
|
1216
|
-
snapshot.phase,
|
|
1217
|
-
snapshot.projectIntentReminder?.signature,
|
|
1218
|
-
route.name,
|
|
1219
|
-
overlay,
|
|
1220
|
-
operationConfirmation,
|
|
1221
|
-
sourceTransportOverlay,
|
|
1222
|
-
dirty,
|
|
1223
|
-
]);
|
|
1224
|
-
useEffect(() => {
|
|
1225
|
-
const reminder = snapshot.projectIntentReminder;
|
|
1226
|
-
if (projectReminderOpen
|
|
1227
|
-
&& (!reminder || reminder.actionableTargets.length + reminder.blockedTargets.length === 0)) {
|
|
1228
|
-
setProjectReminderOpen(false);
|
|
1229
|
-
}
|
|
1230
|
-
}, [projectReminderOpen, snapshot.projectIntentReminder]);
|
|
1231
1441
|
useEffect(() => {
|
|
1232
1442
|
if (route.name !== 'pluginContent' || snapshot.phase !== 'ready')
|
|
1233
1443
|
return;
|
|
@@ -1258,13 +1468,12 @@ export function ConsoleAppV4(props) {
|
|
|
1258
1468
|
|| !snapshot.result.operationKind)
|
|
1259
1469
|
return;
|
|
1260
1470
|
if (snapshot.result.operationKind === 'configSaveBatch') {
|
|
1261
|
-
const
|
|
1262
|
-
?.filter((group) => group.configSaved).map((group) => group.
|
|
1471
|
+
const savedProfiles = new Set(snapshot.result.summary.configGroups
|
|
1472
|
+
?.filter((group) => group.configSaved).map((group) => group.platform ?? 'common') ?? []);
|
|
1263
1473
|
const nextDrafts = Object.fromEntries(Object.entries(configDrafts)
|
|
1264
|
-
.filter(([
|
|
1265
|
-
.map(([
|
|
1266
|
-
...evidence, storeDigests:
|
|
1267
|
-
.filter(([savedScope]) => !savedScopes.has(savedScope))),
|
|
1474
|
+
.filter(([profile]) => !savedProfiles.has(profile))
|
|
1475
|
+
.map(([profile, draft]) => [profile, draft.editEvidence ? { ...draft, editEvidence: Object.fromEntries(Object.entries(draft.editEvidence).map(([key, evidence]) => [key, {
|
|
1476
|
+
...evidence, storeDigests: {},
|
|
1268
1477
|
}])) } : draft]));
|
|
1269
1478
|
setConfigDrafts(nextDrafts);
|
|
1270
1479
|
setDirty(Object.keys(nextDrafts).length > 0);
|
|
@@ -1276,12 +1485,12 @@ export function ConsoleAppV4(props) {
|
|
|
1276
1485
|
}
|
|
1277
1486
|
else if (snapshot.result.operationKind === 'configSave'
|
|
1278
1487
|
|| snapshot.result.operationKind === 'configReset') {
|
|
1279
|
-
const
|
|
1488
|
+
const completedProfile = snapshot.result.summary.configPlatform ?? 'common';
|
|
1280
1489
|
const preserveAll = snapshot.result.operationKind === 'configSave'
|
|
1281
1490
|
&& snapshot.result.summary.configRetryApply === true;
|
|
1282
1491
|
const nextDrafts = { ...configDrafts };
|
|
1283
|
-
if (!preserveAll
|
|
1284
|
-
delete nextDrafts[
|
|
1492
|
+
if (!preserveAll)
|
|
1493
|
+
delete nextDrafts[completedProfile];
|
|
1285
1494
|
setConfigDrafts(nextDrafts);
|
|
1286
1495
|
setDirty(Object.keys(nextDrafts).length > 0);
|
|
1287
1496
|
setConfigCandidate(undefined);
|
|
@@ -1396,7 +1605,7 @@ export function ConsoleAppV4(props) {
|
|
|
1396
1605
|
if (suspended.externalDraft)
|
|
1397
1606
|
drafts.push(suspended.externalDraft);
|
|
1398
1607
|
}
|
|
1399
|
-
if ((dirty || configCandidate
|
|
1608
|
+
if ((dirty || configCandidate || nestedPropertyRevision || objectVariantPreview || Object.values(arrayDrafts).some((draft) => draft.initial === undefined ? draft.rows.length > 0 : arrayDraftChanged(draft)) || changedObjectDraftKeys.length > 0) && draftOwner) {
|
|
1400
1609
|
const flowId = externalDraftFlowIds.current.get(draftOwner);
|
|
1401
1610
|
drafts.push({
|
|
1402
1611
|
kind: 'configDraft', owner: draftOwner, revision: externalDraftRevision.current.config,
|
|
@@ -1422,38 +1631,39 @@ export function ConsoleAppV4(props) {
|
|
|
1422
1631
|
configDrafts, configCandidate, arrayDrafts, objectDrafts, nestedPropertyRevision, objectVariantPreview, route.name, snapshot.router.currentFlowId, sourceDraft,
|
|
1423
1632
|
]);
|
|
1424
1633
|
useEffect(() => () => props.controller.setExternalDraftState(), [props.controller]);
|
|
1425
|
-
const currentLayerPage = configRoute
|
|
1634
|
+
const currentLayerPage = configRoute ? configLayerPages[configRoute.layer.profile] : undefined;
|
|
1426
1635
|
const currentConfigPage = configPageMatches(configPage, configRoute) ? configPage
|
|
1427
1636
|
: configPageMatches(currentLayerPage, configRoute) ? currentLayerPage : undefined;
|
|
1428
|
-
const hasReferenceFields =
|
|
1429
|
-
.some((group) => group.fields.some((field) => configReferenceScalars(field.definition).length > 0))
|
|
1637
|
+
const hasReferenceFields = [configPage, ...Object.values(configLayerPages)]
|
|
1638
|
+
.some((page) => page?.groups.some((group) => group.fields.some((field) => configReferenceScalars(field.definition).length > 0)));
|
|
1430
1639
|
useEffect(() => {
|
|
1431
1640
|
if (!configFlowPluginId || !hasReferenceFields)
|
|
1432
1641
|
return;
|
|
1433
1642
|
const abort = new AbortController();
|
|
1434
1643
|
setReferenceContexts({});
|
|
1435
1644
|
setReferenceContextError(undefined);
|
|
1436
|
-
void Promise.allSettled(
|
|
1437
|
-
context: await props.controller.getConfigReferenceContext(configFlowPluginId,
|
|
1645
|
+
void Promise.allSettled(CONFIG_PROFILE_KEYS.map(async (profile) => ({ profile,
|
|
1646
|
+
context: await props.controller.getConfigReferenceContext(configFlowPluginId, configReadOptions({ profile, context: configContext }), configDrafts[profile] ?? { updates: {}, removes: [] }, abort.signal),
|
|
1438
1647
|
}))).then((results) => {
|
|
1439
1648
|
if (abort.signal.aborted)
|
|
1440
1649
|
return;
|
|
1441
1650
|
setReferenceContexts(Object.fromEntries(results.flatMap((result) => result.status === 'fulfilled'
|
|
1442
|
-
? [[result.value.
|
|
1651
|
+
? [[result.value.profile, result.value.context]] : [])));
|
|
1443
1652
|
if (results.some((result) => result.status === 'rejected'))
|
|
1444
1653
|
setReferenceContextError('引用来源读取失败。请返回修复来源配置后重新打开设置。');
|
|
1445
1654
|
});
|
|
1446
1655
|
return () => abort.abort();
|
|
1447
|
-
}, [props.controller, configFlowPluginId, configDrafts,
|
|
1448
|
-
const crossLayerDependencies = configCrossLayerDependencies(Object.fromEntries(
|
|
1449
|
-
|
|
1656
|
+
}, [props.controller, configFlowPluginId, configDrafts, configContext, hasReferenceFields, configReadRevision]);
|
|
1657
|
+
const crossLayerDependencies = configCrossLayerDependencies(Object.fromEntries(Object.entries(configLayerPages).map(([profile, page]) => [profile,
|
|
1658
|
+
Object.fromEntries(page.groups.flatMap((group) => group.fields.map((field) => [field.key, field.definition])))])), configDrafts, referenceContexts);
|
|
1659
|
+
const referenceContext = editingProfile ? referenceContexts[editingProfile] : undefined;
|
|
1450
1660
|
const configDraftIssues = (() => {
|
|
1451
1661
|
const issues = [];
|
|
1452
|
-
for (const
|
|
1453
|
-
const draft = configDrafts[
|
|
1662
|
+
for (const profile of CONFIG_PROFILE_KEYS) {
|
|
1663
|
+
const draft = configDrafts[profile];
|
|
1454
1664
|
if (!draft)
|
|
1455
1665
|
continue;
|
|
1456
|
-
const page = configLayerPages[
|
|
1666
|
+
const page = configLayerPages[profile];
|
|
1457
1667
|
if (!page)
|
|
1458
1668
|
continue;
|
|
1459
1669
|
const fields = new Map(page.groups.flatMap((group) => group.fields)
|
|
@@ -1462,20 +1672,21 @@ export function ConsoleAppV4(props) {
|
|
|
1462
1672
|
const field = fields.get(key);
|
|
1463
1673
|
const invalidReferences = field ? configReferenceValues(field.definition, value)
|
|
1464
1674
|
.filter(({ scalar, value: selected }) => {
|
|
1465
|
-
const
|
|
1675
|
+
const context = referenceContexts[profile];
|
|
1676
|
+
const reference = context?.selections[configReferenceKey(scalar, context.platform)];
|
|
1466
1677
|
return reference !== undefined && (reference.issues.length > 0
|
|
1467
1678
|
|| !reference.choices.some((choice) => choice.value === selected));
|
|
1468
1679
|
}).map(({ scalar }) => configReferenceSource(scalar)) : [];
|
|
1469
1680
|
if (!field || !configFieldCanSet(field)
|
|
1470
1681
|
|| validatePluginConfigValue(field.definition, value, key).length > 0
|
|
1471
1682
|
|| invalidReferences.length > 0) {
|
|
1472
|
-
issues.push({
|
|
1683
|
+
issues.push({ profile, key, source: 'update', referenceSources: invalidReferences });
|
|
1473
1684
|
}
|
|
1474
1685
|
}
|
|
1475
1686
|
for (const key of draft.removes) {
|
|
1476
1687
|
const field = fields.get(key);
|
|
1477
1688
|
if (!field || !configFieldCanUnset(field)) {
|
|
1478
|
-
issues.push({
|
|
1689
|
+
issues.push({ profile, key, source: 'remove' });
|
|
1479
1690
|
}
|
|
1480
1691
|
}
|
|
1481
1692
|
}
|
|
@@ -1486,21 +1697,16 @@ export function ConsoleAppV4(props) {
|
|
|
1486
1697
|
return;
|
|
1487
1698
|
const abort = new AbortController();
|
|
1488
1699
|
setConfigLayerPages({});
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
];
|
|
1493
|
-
void Promise.allSettled(layers.map(async ({ key, layer }) => ({
|
|
1494
|
-
key,
|
|
1495
|
-
page: await props.controller.getConfigPage(configFlowPluginId, layer, abort.signal),
|
|
1700
|
+
void Promise.allSettled(CONFIG_PROFILE_KEYS.map(async (profile) => ({
|
|
1701
|
+
key: profile,
|
|
1702
|
+
page: await props.controller.getConfigPage(configFlowPluginId, configReadOptions({ profile, context: configContext }), abort.signal),
|
|
1496
1703
|
}))).then((results) => {
|
|
1497
1704
|
if (abort.signal.aborted)
|
|
1498
1705
|
return;
|
|
1499
|
-
setConfigLayerCounts(Object.fromEntries(results.flatMap((result) => result.status === 'fulfilled' ? [[result.value.key, result.value.page.storedCount]] : [])));
|
|
1500
1706
|
setConfigLayerPages(Object.fromEntries(results.flatMap((result) => result.status === 'fulfilled' ? [[result.value.key, result.value.page]] : [])));
|
|
1501
1707
|
});
|
|
1502
1708
|
return () => abort.abort();
|
|
1503
|
-
}, [props.controller, configFlowPluginId]);
|
|
1709
|
+
}, [props.controller, configFlowPluginId, configContext, configReadRevision]);
|
|
1504
1710
|
useEffect(() => {
|
|
1505
1711
|
if (!configRoute)
|
|
1506
1712
|
return;
|
|
@@ -1526,7 +1732,7 @@ export function ConsoleAppV4(props) {
|
|
|
1526
1732
|
setConfigActionError(undefined);
|
|
1527
1733
|
}
|
|
1528
1734
|
}
|
|
1529
|
-
void props.controller.getConfigPage(expectedPluginId, configRoute.layer, abort.signal)
|
|
1735
|
+
void props.controller.getConfigPage(expectedPluginId, configReadOptions(configRoute.layer), abort.signal)
|
|
1530
1736
|
.then((page) => {
|
|
1531
1737
|
if (abort.signal.aborted
|
|
1532
1738
|
|| expectedPluginId !== configRoute.pluginId
|
|
@@ -1539,11 +1745,7 @@ export function ConsoleAppV4(props) {
|
|
|
1539
1745
|
const invalidStores = error instanceof TuiConfigInvalidStoreError
|
|
1540
1746
|
? error.evidence
|
|
1541
1747
|
: undefined;
|
|
1542
|
-
const layer =
|
|
1543
|
-
? { scope: invalidStores[0].scope }
|
|
1544
|
-
: !invalidStores && configRoute.layer.scope !== 'effective'
|
|
1545
|
-
? { scope: configRoute.layer.scope }
|
|
1546
|
-
: undefined;
|
|
1748
|
+
const layer = configRoute.layer;
|
|
1547
1749
|
const code = typeof error === 'object' && error !== null && 'code' in error
|
|
1548
1750
|
? safeDiagnosticCode(error.code, 'configUnavailable')
|
|
1549
1751
|
: error instanceof Error
|
|
@@ -1574,7 +1776,7 @@ export function ConsoleAppV4(props) {
|
|
|
1574
1776
|
}
|
|
1575
1777
|
});
|
|
1576
1778
|
return () => abort.abort();
|
|
1577
|
-
}, [configRoute?.pluginId, configRoute?.layer.
|
|
1779
|
+
}, [configRoute?.pluginId, configRoute?.layer.profile, configContext, configReadRevision, props.controller]);
|
|
1578
1780
|
useEffect(() => {
|
|
1579
1781
|
if (route.name !== 'configUnavailable'
|
|
1580
1782
|
|| route.diagnosticCode === 'configValidationMemoryExhausted'
|
|
@@ -1584,24 +1786,22 @@ export function ConsoleAppV4(props) {
|
|
|
1584
1786
|
}
|
|
1585
1787
|
const abort = new AbortController();
|
|
1586
1788
|
setConfigResetAvailability({});
|
|
1587
|
-
const
|
|
1588
|
-
|
|
1589
|
-
?? (route.layer ? [route.layer.scope] : []);
|
|
1590
|
-
void Promise.all(scopes.map(async (scope) => {
|
|
1789
|
+
const profiles = route.layer ? [route.layer.profile] : ['common'];
|
|
1790
|
+
void Promise.all(profiles.map(async (profile) => {
|
|
1591
1791
|
try {
|
|
1592
1792
|
return {
|
|
1593
|
-
|
|
1594
|
-
availability: await props.controller.getConfigResetAvailability(route.pluginId, {
|
|
1793
|
+
profile,
|
|
1794
|
+
availability: await props.controller.getConfigResetAvailability(route.pluginId, configReadOptions({ profile, context: route.layer?.context })),
|
|
1595
1795
|
};
|
|
1596
1796
|
}
|
|
1597
1797
|
catch {
|
|
1598
|
-
return {
|
|
1798
|
+
return { profile, availability: undefined };
|
|
1599
1799
|
}
|
|
1600
1800
|
})).then((results) => {
|
|
1601
1801
|
if (!abort.signal.aborted) {
|
|
1602
1802
|
setConfigResetAvailability(Object.fromEntries(results
|
|
1603
1803
|
.filter((result) => result.availability !== undefined)
|
|
1604
|
-
.map((result) => [result.
|
|
1804
|
+
.map((result) => [result.profile, result.availability])));
|
|
1605
1805
|
}
|
|
1606
1806
|
});
|
|
1607
1807
|
return () => abort.abort();
|
|
@@ -1614,15 +1814,19 @@ export function ConsoleAppV4(props) {
|
|
|
1614
1814
|
? route.invalidStores?.map((store) => `${store.scope}:${store.code}`).join('|') ?? ''
|
|
1615
1815
|
: '',
|
|
1616
1816
|
route.name === 'configUnavailable' ? route.storedScopes?.join('|') ?? '' : '',
|
|
1617
|
-
route.name === 'configUnavailable' ? route.layer?.
|
|
1817
|
+
route.name === 'configUnavailable' ? route.layer?.profile ?? '' : '',
|
|
1818
|
+
route.name === 'configUnavailable' ? route.layer?.context ?? 'user' : '',
|
|
1618
1819
|
]);
|
|
1619
1820
|
useEffect(() => {
|
|
1620
1821
|
if (route.name !== 'configFields' || !currentConfigPage)
|
|
1621
1822
|
return;
|
|
1823
|
+
const currentFrame = props.controller.snapshot.router.current;
|
|
1824
|
+
if (currentFrame.route.name !== 'configFields' || !configPageMatches(currentConfigPage, currentFrame.route))
|
|
1825
|
+
return;
|
|
1622
1826
|
const tabs = configTabs(currentConfigPage);
|
|
1623
1827
|
if (tabs.length === 0)
|
|
1624
1828
|
return;
|
|
1625
|
-
const current =
|
|
1829
|
+
const current = currentFrame.context.configGroupId;
|
|
1626
1830
|
if (tabs.some((tab) => tab.id === current))
|
|
1627
1831
|
return;
|
|
1628
1832
|
props.controller.updatePageContext({ configGroupId: tabs[0].id });
|
|
@@ -1657,6 +1861,8 @@ export function ConsoleAppV4(props) {
|
|
|
1657
1861
|
|| !frozenPluginIds.has(candidate.target.pluginId))
|
|
1658
1862
|
|| [...frozenPluginIds].some((pluginId) => !representedPluginIds.has(pluginId)))
|
|
1659
1863
|
return;
|
|
1864
|
+
if (new Set(candidates.map((candidate) => candidate.target.scope)).size > 1)
|
|
1865
|
+
return;
|
|
1660
1866
|
const currentVersionEvidence = Object.assign({}, ...[...frozenPluginIds].map((pluginId) => {
|
|
1661
1867
|
const candidatePlugin = props.controller.getPluginOverview(pluginId);
|
|
1662
1868
|
return candidatePlugin ? pluginWriteTaskVersionEvidence(candidatePlugin, candidates.filter((candidate) => candidate.target.pluginId === pluginId)) : {};
|
|
@@ -1776,7 +1982,7 @@ export function ConsoleAppV4(props) {
|
|
|
1776
1982
|
const arrayItem = route.name === 'configFieldEditor' || route.name === 'configObjectPropertyEditor'
|
|
1777
1983
|
? route.arrayItem : undefined;
|
|
1778
1984
|
const activeArrayKey = activeTopField?.definition.type === 'array' && configRoute
|
|
1779
|
-
? `${
|
|
1985
|
+
? `${configEditorOwnerKey(configRoute.pluginId, configRoute.layer)}\0${activeTopField.key}` : undefined;
|
|
1780
1986
|
const activeArrayDraft = activeArrayKey ? arrayDrafts[activeArrayKey] : undefined;
|
|
1781
1987
|
const itemValue = arrayItem && activeArrayDraft
|
|
1782
1988
|
? activeArrayDraft.rows.find((row) => row.rowId === arrayItem.rowId)?.value : undefined;
|
|
@@ -1797,11 +2003,9 @@ export function ConsoleAppV4(props) {
|
|
|
1797
2003
|
useEffect(() => {
|
|
1798
2004
|
if (!activeArrayKey || activeArrayDraft || !activeTopField || route.name !== 'configFieldEditor')
|
|
1799
2005
|
return;
|
|
1800
|
-
if (route.layer.scope === 'effective' && configReferenceScalars(activeTopField.definition).length)
|
|
1801
|
-
return;
|
|
1802
2006
|
const value = draftUpdates[activeTopField.key] ?? activeTopField.draftValue ?? activeTopField.value;
|
|
1803
2007
|
setArrayDrafts((current) => ({ ...current, [activeArrayKey]: { ...createConfigArrayDraft(Array.isArray(value) ? value : undefined), evidence: currentConfigPage?.editEvidence } }));
|
|
1804
|
-
}, [activeArrayKey, activeArrayDraft, activeTopField, route.name,
|
|
2008
|
+
}, [activeArrayKey, activeArrayDraft, activeTopField, route.name, editingProfile]);
|
|
1805
2009
|
useEffect(() => {
|
|
1806
2010
|
if (route.name !== 'configFieldEditor' || route.editor !== 'array' || !activeArrayKey || !activeArrayDraft)
|
|
1807
2011
|
return;
|
|
@@ -1829,13 +2033,14 @@ export function ConsoleAppV4(props) {
|
|
|
1829
2033
|
}
|
|
1830
2034
|
}, [props.controller, route, activeField]);
|
|
1831
2035
|
const activeObjectKey = activeField?.type === 'object' && configRoute
|
|
1832
|
-
? `${
|
|
2036
|
+
? `${configEditorOwnerKey(configRoute.pluginId, configRoute.layer)}\0${activeField.key}${arrayItem ? `\0${arrayItem.rowId ?? 'new'}` : ''}`
|
|
1833
2037
|
: undefined;
|
|
1834
2038
|
const objectValueFromUpdate = !arrayItem && activeField && Object.hasOwn(draftUpdates, activeField.key)
|
|
1835
2039
|
&& typeof draftUpdates[activeField.key] === 'object'
|
|
1836
2040
|
? draftUpdates[activeField.key]
|
|
1837
2041
|
: undefined;
|
|
1838
|
-
const activeCandidateMutation = !arrayItem && activeField
|
|
2042
|
+
const activeCandidateMutation = !arrayItem && activeField && configCandidate && configCandidate.layer.profile === editingProfile
|
|
2043
|
+
&& (configCandidate.layer.context ?? 'user') === configContext ? configCandidate.mutations[activeField.key] : undefined;
|
|
1839
2044
|
const objectValueFromCandidate = activeCandidateMutation?.kind === 'set'
|
|
1840
2045
|
&& typeof activeCandidateMutation.value === 'object'
|
|
1841
2046
|
? activeCandidateMutation.value
|
|
@@ -1867,13 +2072,13 @@ export function ConsoleAppV4(props) {
|
|
|
1867
2072
|
const activeFieldIsConst = activeField && 'const' in activeField.definition && activeField.definition.const !== undefined;
|
|
1868
2073
|
const activePropertyIsConst = activePropertyDefinition && 'const' in activePropertyDefinition && activePropertyDefinition.const !== undefined;
|
|
1869
2074
|
const referenceNotice = (scalar, value) => {
|
|
1870
|
-
const selection = referenceContext?.selections[configReferenceKey(scalar)];
|
|
2075
|
+
const selection = referenceContext?.selections[configReferenceKey(scalar, referenceContext.platform)];
|
|
1871
2076
|
const message = referenceContextError && !selection ? `来源 ${configReferenceSource(scalar)}:${referenceContextError}`
|
|
1872
2077
|
: configReferenceMessage(scalar, selection, value);
|
|
1873
2078
|
const source = 'valueRef' in scalar ? scalar.valueRef.field : '';
|
|
1874
|
-
const pendingSource = !('valueRef' in scalar && scalar.valueRef?.plugin) &&
|
|
1875
|
-
&& (Object.hasOwn(configDrafts.
|
|
1876
|
-
return [message, pendingSource ? `来源 ${source}
|
|
2079
|
+
const pendingSource = !('valueRef' in scalar && scalar.valueRef?.plugin) && editingProfile !== 'common' && selection?.source !== 'platform'
|
|
2080
|
+
&& (Object.hasOwn(configDrafts.common?.updates ?? {}, source) || configDrafts.common?.removes.includes(source));
|
|
2081
|
+
return [message, pendingSource ? `来源 ${source} 在公共设置有未保存修改。请先保存来源配置,再编辑或保存引用配置。` : undefined]
|
|
1877
2082
|
.filter(Boolean).join(' ') || undefined;
|
|
1878
2083
|
};
|
|
1879
2084
|
const openReferenceSource = (scalar, leaf) => {
|
|
@@ -1896,7 +2101,7 @@ export function ConsoleAppV4(props) {
|
|
|
1896
2101
|
useEffect(() => {
|
|
1897
2102
|
if (route.name !== 'configFieldEditor' || !activeField)
|
|
1898
2103
|
return;
|
|
1899
|
-
const candidate =
|
|
2104
|
+
const candidate = activeCandidateMutation;
|
|
1900
2105
|
const draft = arrayItem ? itemValue : candidate?.kind === 'set' ? candidate.value : draftUpdates[activeField.key];
|
|
1901
2106
|
setInputValue(activeField.sensitive
|
|
1902
2107
|
? draft === undefined ? '' : String(draft)
|
|
@@ -1907,6 +2112,8 @@ export function ConsoleAppV4(props) {
|
|
|
1907
2112
|
}, [
|
|
1908
2113
|
route.name,
|
|
1909
2114
|
route.name === 'configFieldEditor' ? `${route.fieldKey}:${route.mode ?? ''}:${route.arrayItem ? route.arrayItem.rowId ?? 'new' : 'field'}` : '',
|
|
2115
|
+
editingProfile,
|
|
2116
|
+
configContext,
|
|
1910
2117
|
activeField?.key,
|
|
1911
2118
|
configCandidate,
|
|
1912
2119
|
]);
|
|
@@ -1925,6 +2132,8 @@ export function ConsoleAppV4(props) {
|
|
|
1925
2132
|
route.name === 'configObjectPropertyEditor'
|
|
1926
2133
|
? `${route.fieldKey}:${route.propertyKey ?? ''}:${route.stage}`
|
|
1927
2134
|
: '',
|
|
2135
|
+
editingProfile,
|
|
2136
|
+
configContext,
|
|
1928
2137
|
]);
|
|
1929
2138
|
useEffect(() => {
|
|
1930
2139
|
if (route.name !== 'sourceAdd')
|
|
@@ -1979,7 +2188,12 @@ export function ConsoleAppV4(props) {
|
|
|
1979
2188
|
const supportsTechnicalDetails = !(route.name === 'pluginDependencies' && route.view !== 'removeBlocked'
|
|
1980
2189
|
|| route.name === 'pluginDependencyNode' || route.name === 'portableDependencies')
|
|
1981
2190
|
&& (snapshot.phase === 'error'
|
|
1982
|
-
|| route.name === '
|
|
2191
|
+
|| Boolean(route.name === 'intentInventory' && (snapshot.scopedInventories[route.scope]?.errors.length
|
|
2192
|
+
|| snapshot.scopedInventories[route.scope]?.declaredRoots.length
|
|
2193
|
+
|| snapshot.scopedInventories[route.scope]?.extraInstalledRoots.length
|
|
2194
|
+
|| snapshot.scopedInventoryRefresh[route.scope]?.state === 'error'
|
|
2195
|
+
|| snapshot.scopedInventories[route.scope]?.declarationState === 'invalid'
|
|
2196
|
+
|| snapshot.scopedInventories[route.scope]?.preferenceState === 'invalid'))
|
|
1983
2197
|
|| Boolean(route.name === 'reviewPreparing' && snapshot.reviewPreparation?.code)
|
|
1984
2198
|
|| Boolean(route.name === 'progress' && snapshot.progress.messages.length > 0)
|
|
1985
2199
|
|| Boolean(route.name === 'review' && snapshot.activeReview?.technicalDetails?.length)
|
|
@@ -2048,27 +2262,21 @@ export function ConsoleAppV4(props) {
|
|
|
2048
2262
|
+ Math.max(0, wrapConsoleText(actionError ?? description ?? '', size.columns - 2).length - 1)
|
|
2049
2263
|
+ Math.max(0, wrapConsoleText(title, size.columns - 2).length - 1),
|
|
2050
2264
|
});
|
|
2265
|
+
let detailNavigation;
|
|
2051
2266
|
const detailCardContent = (subject, completionLines = []) => {
|
|
2052
2267
|
const cardWidth = size.columns - 2;
|
|
2053
2268
|
const card = pluginDetailCardLayout(subject, cardWidth);
|
|
2054
2269
|
const availableRows = pageActionBudget(completionLines.length);
|
|
2055
|
-
// Compact actions use one row each; paging adds one row inside the card.
|
|
2056
2270
|
const actionRows = Math.min(3, actions.filter((action) => action.role !== 'navigation').length);
|
|
2057
|
-
const
|
|
2058
|
-
const
|
|
2059
|
-
const
|
|
2060
|
-
|
|
2061
|
-
const lineOffset = lineLimit ? (page - 1) * lineLimit : 0;
|
|
2062
|
-
if (lineLimit)
|
|
2063
|
-
readerNavigation = { page, pageCount, move: (direction) => {
|
|
2064
|
-
setBatchTargetOffset((Math.min(pageCount, Math.max(1, page + direction)) - 1) * lineLimit);
|
|
2065
|
-
} };
|
|
2271
|
+
const lineLimit = Math.max(1, availableRows - actionRows - 4);
|
|
2272
|
+
const maxOffset = Math.max(0, card.factRows - lineLimit);
|
|
2273
|
+
const lineOffset = Math.min(batchTargetOffset, maxOffset);
|
|
2274
|
+
detailNavigation = { maxOffset, lineLimit };
|
|
2066
2275
|
const cardHeight = entityCardWindowHeight(card, lineOffset, lineLimit);
|
|
2067
2276
|
currentActionViewportLineBudget = Math.max(1, pageActionBudget(completionLines.length) - cardHeight);
|
|
2068
2277
|
const viewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
2069
2278
|
help = [
|
|
2070
|
-
|
|
2071
|
-
...(needsPaging ? ['←→ 翻信息'] : []),
|
|
2279
|
+
'↑↓ 连续阅读与选择', 'PgUp/PgDn 翻动',
|
|
2072
2280
|
...(actions[focus]?.run && !actions[focus]?.disabled ? ['Enter 选择'] : []),
|
|
2073
2281
|
'Esc 返回',
|
|
2074
2282
|
].join(' · ');
|
|
@@ -2091,7 +2299,7 @@ export function ConsoleAppV4(props) {
|
|
|
2091
2299
|
setBatchTargetOffset(next);
|
|
2092
2300
|
},
|
|
2093
2301
|
};
|
|
2094
|
-
return _jsx(Reader, { text: text, columns: columns, rows: rows, page: page.page });
|
|
2302
|
+
return _jsx(Reader, { text: text, columns: columns, rows: rows, page: page.page, highlightChanges: Boolean(scopedFlow && !showTechnical) });
|
|
2095
2303
|
};
|
|
2096
2304
|
const pagedItems = (total, rows, offset = batchTargetOffset) => paginateWrappedRows({
|
|
2097
2305
|
lines: Array.from({ length: total }, (_, index) => String(index)),
|
|
@@ -2169,8 +2377,15 @@ export function ConsoleAppV4(props) {
|
|
|
2169
2377
|
return items.map((item) => item.pluginId);
|
|
2170
2378
|
};
|
|
2171
2379
|
const go = (next) => {
|
|
2172
|
-
|
|
2173
|
-
|
|
2380
|
+
scopedFlowToken.current += 1;
|
|
2381
|
+
if (scopedFlow?.stage === 'preparing')
|
|
2382
|
+
props.controller.cancelScopedPreparation();
|
|
2383
|
+
if (scopedFlow?.stage === 'review' || scopedFlow?.stage === 'risk')
|
|
2384
|
+
void props.controller.discardScopedPlan(scopedFlow.plan);
|
|
2385
|
+
scopedFlowBusy.current = false;
|
|
2386
|
+
setScopedFlow(undefined);
|
|
2387
|
+
const leavingContext = {
|
|
2388
|
+
focusIndex: route.name === 'intentInventory' || route.name === 'platform' ? effectiveFocus : focus,
|
|
2174
2389
|
scrollOffset,
|
|
2175
2390
|
entityLineOffset: entityCardNavigation || route.name === 'pluginDetail' ? batchTargetOffset : undefined,
|
|
2176
2391
|
query,
|
|
@@ -2180,21 +2395,29 @@ export function ConsoleAppV4(props) {
|
|
|
2180
2395
|
? pluginRows[focus]?.pluginId
|
|
2181
2396
|
: route.name === 'pluginContent' ? actions[focus]?.actionId?.replace(/^content:/u, '')
|
|
2182
2397
|
: route.name === 'batchSelection' ? actions[focus]?.pluginId
|
|
2183
|
-
: route.name === '
|
|
2398
|
+
: route.name === 'intentInventory' ? actions[effectiveFocus]?.pluginId
|
|
2184
2399
|
: route.name === 'sourceList' ? actions[focus]?.actionId?.replace(/^source:/u, '')
|
|
2185
2400
|
: route.name === 'portableSkillSelect' ? actions[focus]?.actionId : undefined,
|
|
2186
|
-
...(route.name === '
|
|
2401
|
+
...(route.name === 'intentInventory' || route.name === 'platform'
|
|
2187
2402
|
? { focusedActionId: actions[effectiveFocus]?.actionId } : {}),
|
|
2188
|
-
}
|
|
2189
|
-
props.controller.
|
|
2403
|
+
};
|
|
2404
|
+
props.controller.updatePageContext(leavingContext);
|
|
2405
|
+
if (route.name === 'intentInventory') {
|
|
2406
|
+
const key = route.scope === 'project' ? `project:${route.projectKey ?? ''}` : 'user';
|
|
2407
|
+
scopedPageContext.current[key] = { ...snapshot.router.current.context, ...leavingContext };
|
|
2408
|
+
}
|
|
2409
|
+
const enteringContext = next.name === 'intentInventory'
|
|
2410
|
+
? scopedPageContext.current[next.scope === 'project' ? `project:${next.projectKey ?? ''}` : 'user']
|
|
2411
|
+
: undefined;
|
|
2412
|
+
props.controller.push(next, enteringContext);
|
|
2190
2413
|
if (route.name === 'pluginContent')
|
|
2191
2414
|
setContentReader(undefined);
|
|
2192
|
-
setFocus(0);
|
|
2415
|
+
setFocus(enteringContext?.focusIndex ?? 0);
|
|
2193
2416
|
if (next.name === 'pluginDetail' || next.name === 'sourceDetail'
|
|
2194
|
-
|| next.name === '
|
|
2417
|
+
|| next.name === 'intentInventory')
|
|
2195
2418
|
setBatchTargetOffset(0);
|
|
2196
|
-
setScrollOffset(0);
|
|
2197
|
-
setQuery('');
|
|
2419
|
+
setScrollOffset(enteringContext?.scrollOffset ?? 0);
|
|
2420
|
+
setQuery(enteringContext?.query ?? '');
|
|
2198
2421
|
setSearchFocus(false);
|
|
2199
2422
|
if (next.name === 'pluginList') {
|
|
2200
2423
|
setInstallFilter(next.mode === 'installed' ? 'installed' : 'all');
|
|
@@ -2202,6 +2425,124 @@ export function ConsoleAppV4(props) {
|
|
|
2202
2425
|
setShowTechnical(false);
|
|
2203
2426
|
setActionError(undefined);
|
|
2204
2427
|
};
|
|
2428
|
+
const beginScopedTask = (request, prepare = () => props.controller.prepareScopedTask(request)) => {
|
|
2429
|
+
if (scopedFlowBusy.current)
|
|
2430
|
+
return;
|
|
2431
|
+
scopedFlowBusy.current = true;
|
|
2432
|
+
const token = ++scopedFlowToken.current;
|
|
2433
|
+
setShowTechnical(false);
|
|
2434
|
+
setBatchTargetOffset(0);
|
|
2435
|
+
setScopedFlow({ stage: 'preparing', scope: request.scope, request: request.kind, focus: 0 });
|
|
2436
|
+
void prepare().then((plan) => {
|
|
2437
|
+
if (scopedFlowToken.current !== token) {
|
|
2438
|
+
void props.controller.discardScopedPlan(plan);
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
scopedFlowBusy.current = false;
|
|
2442
|
+
setScopedFlow({ stage: 'review', scope: request.scope, request, plan, focus: 0 });
|
|
2443
|
+
}).catch((error) => {
|
|
2444
|
+
if (scopedFlowToken.current !== token)
|
|
2445
|
+
return;
|
|
2446
|
+
scopedFlowBusy.current = false;
|
|
2447
|
+
setScopedFlow({ stage: 'result', scope: request.scope,
|
|
2448
|
+
errorCode: safeDiagnosticCode(error instanceof Error ? error.message : undefined, 'recoveryPreparationFailed'), focus: 0 });
|
|
2449
|
+
});
|
|
2450
|
+
};
|
|
2451
|
+
const agentSummaries = (pluginId) => {
|
|
2452
|
+
const entries = pluginId ? [pluginId] : props.controller.listPlugins({ mode: 'browse' }).map((entry) => entry.pluginId);
|
|
2453
|
+
return Object.fromEntries((agentSettings?.preferences.targets ?? []).map((target) => {
|
|
2454
|
+
const facts = entries.flatMap((id) => props.controller.getPluginDetailSubject(id)?.placements
|
|
2455
|
+
.filter((fact) => (fact.agent ?? fact.platform) === target) ?? []);
|
|
2456
|
+
const count = new Set(entries.filter((id) => props.controller.getPluginDetailSubject(id)?.placements
|
|
2457
|
+
.some((fact) => (fact.agent ?? fact.platform) === target))).size;
|
|
2458
|
+
const symbol = facts.some((fact) => fact.projectState === 'blocked' || fact.statusState === 'blocked') ? '×'
|
|
2459
|
+
: facts.some((fact) => fact.observation === 'unknown' || fact.projectState === 'unknown') ? '?'
|
|
2460
|
+
: facts.some((fact) => fact.projectState === 'pending' || fact.statusState === 'needsSync'
|
|
2461
|
+
|| fact.statusState === 'absent' || fact.statusState === 'orphaned') ? '↻'
|
|
2462
|
+
: facts.length ? '✓' : '?';
|
|
2463
|
+
return [target, { symbol, count, label: symbol === '✓' ? '已安装' : symbol === '↻' ? '待同步'
|
|
2464
|
+
: symbol === '×' ? '安装受阻' : '未确认' }];
|
|
2465
|
+
}));
|
|
2466
|
+
};
|
|
2467
|
+
const openAgentManagement = (pluginId) => {
|
|
2468
|
+
setAgentWelcome(false);
|
|
2469
|
+
void props.controller.readAgentSettings().then((settings) => {
|
|
2470
|
+
setAgentSettings(settings);
|
|
2471
|
+
setAgentSettingsError(undefined);
|
|
2472
|
+
setScopedFlow({ stage: 'agents', scope: settings.context, settings, pluginId, focus: 0 });
|
|
2473
|
+
}).catch(() => setAgentSettingsError('无法读取 Agent 设置,请检查本机配置。'));
|
|
2474
|
+
};
|
|
2475
|
+
const closeScopedFlow = () => {
|
|
2476
|
+
scopedFlowToken.current += 1;
|
|
2477
|
+
scopedFlowBusy.current = false;
|
|
2478
|
+
if (scopedFlow?.stage === 'preparing')
|
|
2479
|
+
props.controller.cancelScopedPreparation();
|
|
2480
|
+
if (scopedFlow?.stage === 'review' || scopedFlow?.stage === 'risk')
|
|
2481
|
+
void props.controller.discardScopedPlan(scopedFlow.plan);
|
|
2482
|
+
setScopedFlow(undefined);
|
|
2483
|
+
if (scopedFlow?.stage === 'result' && scopedFlow.receipt?.ok && route.name === 'pluginWriteTask') {
|
|
2484
|
+
const parent = props.controller.pluginWriteTaskReturnRoute(route);
|
|
2485
|
+
if (parent.name === 'pluginWriteTask' && parent.goal !== route.goal) {
|
|
2486
|
+
props.controller.back();
|
|
2487
|
+
}
|
|
2488
|
+
else {
|
|
2489
|
+
props.controller.replace({ name: 'pluginDetail', pluginId: route.pluginId });
|
|
2490
|
+
}
|
|
2491
|
+
setFocus(0);
|
|
2492
|
+
setScrollOffset(0);
|
|
2493
|
+
setQuery('');
|
|
2494
|
+
}
|
|
2495
|
+
};
|
|
2496
|
+
const executeScopedReview = (plan) => {
|
|
2497
|
+
if (scopedFlowBusy.current)
|
|
2498
|
+
return;
|
|
2499
|
+
scopedFlowBusy.current = true;
|
|
2500
|
+
const token = ++scopedFlowToken.current;
|
|
2501
|
+
const request = scopedFlow?.stage === 'review' || scopedFlow?.stage === 'risk'
|
|
2502
|
+
? scopedFlow.request : undefined;
|
|
2503
|
+
const batchOwner = batchDraft && (route.name === 'batchSelection' || route.name === 'batchPlacement'
|
|
2504
|
+
|| route.name === 'batchInstallScope' || route.name === 'batchInstallPlatforms'
|
|
2505
|
+
|| route.name === 'pluginWriteTask' && route.batchOrigin?.flowId === batchDraft.flowId);
|
|
2506
|
+
const nestedTaskParent = route.name === 'pluginWriteTask' && route.goal === 'update'
|
|
2507
|
+
? props.controller.pluginWriteTaskReturnRoute(route) : undefined;
|
|
2508
|
+
setScopedFlow({ stage: 'progress', scope: plan.scope, plan, focus: 0 });
|
|
2509
|
+
void props.controller.executeScopedTask(plan).then((outcome) => {
|
|
2510
|
+
if (scopedFlowToken.current !== token)
|
|
2511
|
+
return;
|
|
2512
|
+
scopedFlowBusy.current = false;
|
|
2513
|
+
if (outcome.kind === 'recoveryReview') {
|
|
2514
|
+
const nextRequest = request?.kind === 'recovery'
|
|
2515
|
+
? { ...request, refresh: false } : request;
|
|
2516
|
+
if (!nextRequest) {
|
|
2517
|
+
setScopedFlow({ stage: 'result', scope: plan.scope,
|
|
2518
|
+
errorCode: 'recoveryReprepareFailed', focus: 0 });
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
setScopedFlow({ stage: 'review', scope: plan.scope,
|
|
2522
|
+
request: nextRequest, plan: outcome.plan, focus: 0 });
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
setScopedFlow({ stage: 'result', scope: plan.scope, plan,
|
|
2526
|
+
...(request ? { request } : {}),
|
|
2527
|
+
...(outcome.kind === 'recovery' ? { receipt: outcome.receipt } : { changed: outcome.changed }),
|
|
2528
|
+
focus: 0 });
|
|
2529
|
+
if (batchOwner && outcome.kind === 'recovery' && outcome.receipt.ok
|
|
2530
|
+
&& !(route.name === 'pluginWriteTask' && nestedTaskParent?.name === 'pluginWriteTask'
|
|
2531
|
+
&& nestedTaskParent.goal !== route.goal)) {
|
|
2532
|
+
setBatchDraft(undefined);
|
|
2533
|
+
props.controller.replace({ name: 'pluginList', mode: 'installed' });
|
|
2534
|
+
}
|
|
2535
|
+
void props.controller.load().then(() => props.controller.refreshScopedInventory(plan.scope))
|
|
2536
|
+
.catch((error) => props.controller.recordUiFailure(error, { code: 'tuiScopedStatusReloadFailed', stage: 'scoped-status-reload' }));
|
|
2537
|
+
}).catch((error) => {
|
|
2538
|
+
if (scopedFlowToken.current !== token)
|
|
2539
|
+
return;
|
|
2540
|
+
scopedFlowBusy.current = false;
|
|
2541
|
+
setScopedFlow({ stage: 'result', scope: plan.scope, plan,
|
|
2542
|
+
...(request ? { request } : {}),
|
|
2543
|
+
errorCode: safeDiagnosticCode(error instanceof Error ? error.message : undefined, 'recoveryFailed'), focus: 0 });
|
|
2544
|
+
});
|
|
2545
|
+
};
|
|
2205
2546
|
const closeBatchSelectionTools = () => {
|
|
2206
2547
|
if (!batchDraft)
|
|
2207
2548
|
return;
|
|
@@ -2220,13 +2561,6 @@ export function ConsoleAppV4(props) {
|
|
|
2220
2561
|
setFocus(portableResultDetailReturnFocus.current);
|
|
2221
2562
|
setPortableResultDetailOpen(false);
|
|
2222
2563
|
setPortableResultDetailOffset(0);
|
|
2223
|
-
if (route.name === 'projectStatus' && dynamicActionIds && dynamicActionSignature !== undefined) {
|
|
2224
|
-
const recheck = actions.findIndex((action) => action.actionId === 'project-recheck');
|
|
2225
|
-
dynamicMenuFocus.current = { routeKey, actionIds: dynamicActionSignature,
|
|
2226
|
-
focusedActionId: 'project-recheck' };
|
|
2227
|
-
setFocus(Math.max(0, recheck));
|
|
2228
|
-
setScrollOffset(0);
|
|
2229
|
-
}
|
|
2230
2564
|
setShowTechnical((value) => !value);
|
|
2231
2565
|
if (!(route.name === 'result' && snapshot.result
|
|
2232
2566
|
&& 'operationKind' in snapshot.result
|
|
@@ -2254,9 +2588,10 @@ export function ConsoleAppV4(props) {
|
|
|
2254
2588
|
? restoredEntityIndex
|
|
2255
2589
|
: Math.min(restoredFocus, Math.max(0, rowIds.length - 1));
|
|
2256
2590
|
}
|
|
2257
|
-
else if (restoredFrame.route.name === '
|
|
2591
|
+
else if (restoredFrame.route.name === 'intentInventory') {
|
|
2258
2592
|
const actionId = restored.focusedActionId
|
|
2259
|
-
?? (restored.focusedEntityId ? `
|
|
2593
|
+
?? (restored.focusedEntityId ? `inventory-root:${restored.focusedEntityId}`
|
|
2594
|
+
: 'inventory-recheck');
|
|
2260
2595
|
dynamicMenuFocus.current = {
|
|
2261
2596
|
routeKey: JSON.stringify(restoredFrame.route), actionIds: '', focusedActionId: actionId,
|
|
2262
2597
|
};
|
|
@@ -2298,7 +2633,8 @@ export function ConsoleAppV4(props) {
|
|
|
2298
2633
|
itemCount = candidates.length + 2;
|
|
2299
2634
|
restoredFocus = entityIndex >= 0 ? entityIndex : Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2300
2635
|
}
|
|
2301
|
-
const dynamicMenuRoute = restoredFrame.route.name === '
|
|
2636
|
+
const dynamicMenuRoute = restoredFrame.route.name === 'intentInventory'
|
|
2637
|
+
|| restoredFrame.route.name === 'platform';
|
|
2302
2638
|
let restoredScroll = dynamicMenuRoute
|
|
2303
2639
|
? restored.scrollOffset ?? 0
|
|
2304
2640
|
: Math.min(restored.scrollOffset ?? 0, Math.max(0, (itemCount ?? 1) - 1));
|
|
@@ -2384,18 +2720,70 @@ export function ConsoleAppV4(props) {
|
|
|
2384
2720
|
setActionError(undefined);
|
|
2385
2721
|
};
|
|
2386
2722
|
const beginReview = (intent, returnRoute) => {
|
|
2723
|
+
setSplitTaskReview(undefined);
|
|
2724
|
+
const agentForPlatform = (platform) => platform === 'claude' ? 'claude' : 'codex';
|
|
2725
|
+
if (intent.kind === 'pluginWrite') {
|
|
2726
|
+
const agents = intent.platform === 'both' ? ['codex', 'claude']
|
|
2727
|
+
: [agentForPlatform(intent.platform)];
|
|
2728
|
+
beginScopedTask({ kind: 'recovery', scope: intent.scope,
|
|
2729
|
+
command: intent.action === 'refreshAndSync' ? 'sync' : intent.action,
|
|
2730
|
+
selectors: [intent.pluginId], targets: agents,
|
|
2731
|
+
exactTargets: agents.map((agent) => ({ pluginId: intent.pluginId, agent })),
|
|
2732
|
+
...(intent.action === 'refreshAndSync' ? { refresh: true } : {}),
|
|
2733
|
+
...(intent.force ? { force: true } : {}),
|
|
2734
|
+
...(intent.dependencyScopeOverrides
|
|
2735
|
+
? { dependencyScopeOverrides: intent.dependencyScopeOverrides } : {}),
|
|
2736
|
+
...(intent.saveDependencyScopeOverrides ? { saveDependencyScopeOverrides: true } : {}),
|
|
2737
|
+
...(intent.scriptTimeoutMs !== undefined ? { scriptTimeoutMs: intent.scriptTimeoutMs } : {}),
|
|
2738
|
+
});
|
|
2739
|
+
return;
|
|
2740
|
+
}
|
|
2741
|
+
if (intent.kind === 'pluginWriteTask') {
|
|
2742
|
+
const scopes = new Set(intent.targets.map((target) => target.scope));
|
|
2743
|
+
if (scopes.size !== 1) {
|
|
2744
|
+
setActionError('个人与项目目标需要分别确认,请先选择同一范围。');
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
const exactTargets = intent.targets.map((target) => ({ pluginId: target.pluginId,
|
|
2748
|
+
agent: agentForPlatform(target.platform) }));
|
|
2749
|
+
beginScopedTask({ kind: 'recovery', scope: intent.targets[0].scope,
|
|
2750
|
+
command: intent.goal === 'update' ? 'reinstall' : 'sync',
|
|
2751
|
+
selectors: [...new Set(exactTargets.map((target) => target.pluginId))],
|
|
2752
|
+
targets: [...new Set(exactTargets.map((target) => target.agent))], exactTargets,
|
|
2753
|
+
...(intent.scriptTimeoutMs !== undefined ? { scriptTimeoutMs: intent.scriptTimeoutMs } : {}) });
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
if (intent.kind === 'pluginWriteBatch') {
|
|
2757
|
+
const targets = intent.selection.kind === 'targets' ? intent.selection.targets
|
|
2758
|
+
: intent.selection.pluginIds.flatMap((pluginId) => (intent.platform === 'both' ? ['codex', 'claude'] : [intent.platform])
|
|
2759
|
+
.map((platform) => ({ pluginId, platform, scope: intent.scope })));
|
|
2760
|
+
const scopes = new Set(targets.map((target) => target.scope));
|
|
2761
|
+
if (scopes.size !== 1) {
|
|
2762
|
+
setActionError('个人与项目目标需要分别确认,请先选择同一范围。');
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
const exactTargets = targets.map((target) => ({ pluginId: target.pluginId,
|
|
2766
|
+
agent: agentForPlatform(target.platform) }));
|
|
2767
|
+
beginScopedTask({ kind: 'recovery', scope: targets[0].scope, command: intent.action,
|
|
2768
|
+
selectors: [...new Set(exactTargets.map((target) => target.pluginId))],
|
|
2769
|
+
targets: [...new Set(exactTargets.map((target) => target.agent))], exactTargets,
|
|
2770
|
+
...(intent.dependencyScopeOverrides ? { dependencyScopeOverrides: intent.dependencyScopeOverrides } : {}),
|
|
2771
|
+
...(intent.saveDependencyScopeOverrides ? { saveDependencyScopeOverrides: true } : {}),
|
|
2772
|
+
...(intent.scriptTimeoutMs !== undefined ? { scriptTimeoutMs: intent.scriptTimeoutMs } : {}) });
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2387
2775
|
pendingReviewIntentRef.current = intent;
|
|
2388
2776
|
setPendingReviewIntent(intent);
|
|
2389
2777
|
const underlyingResultRoute = returnRoute ?? (route.name === 'result' && snapshot.result && 'returnRoute' in snapshot.result
|
|
2390
2778
|
? snapshot.result.returnRoute
|
|
2391
2779
|
: undefined);
|
|
2392
2780
|
props.controller.updatePageContext({
|
|
2393
|
-
focusIndex: route.name === '
|
|
2781
|
+
focusIndex: route.name === 'platform' ? effectiveFocus : focus,
|
|
2394
2782
|
scrollOffset,
|
|
2395
2783
|
query,
|
|
2396
2784
|
installFilter,
|
|
2397
2785
|
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
2398
|
-
...(route.name === '
|
|
2786
|
+
...(route.name === 'platform'
|
|
2399
2787
|
? { focusedActionId: actions[effectiveFocus]?.actionId } : {}),
|
|
2400
2788
|
});
|
|
2401
2789
|
props.controller.startReviewPreparation(intent, underlyingResultRoute);
|
|
@@ -2418,6 +2806,8 @@ export function ConsoleAppV4(props) {
|
|
|
2418
2806
|
: candidate.target.pluginId === route.pluginId);
|
|
2419
2807
|
if (candidates.length === 0 || candidates.some((candidate) => !candidate.eligible))
|
|
2420
2808
|
return;
|
|
2809
|
+
if (new Set(candidates.map((candidate) => candidate.target.scope)).size > 1)
|
|
2810
|
+
return;
|
|
2421
2811
|
props.controller.updatePageContext({ autoPreparedRestore: true });
|
|
2422
2812
|
beginReview({
|
|
2423
2813
|
kind: 'pluginWriteTask',
|
|
@@ -2493,6 +2883,8 @@ export function ConsoleAppV4(props) {
|
|
|
2493
2883
|
? '插件已不在当前状态中,可取消选择'
|
|
2494
2884
|
: draft.action === undefined
|
|
2495
2885
|
&& !props.controller.pluginHasInstallableTarget(candidate.plugin.pluginId)
|
|
2886
|
+
&& !candidate.plugin.portableSkills
|
|
2887
|
+
&& !props.controller.getPluginDetailSubject(candidate.plugin.pluginId)?.placements.some((placement) => placement.actual && placement.agent && placement.agent !== 'codex' && placement.agent !== 'claude')
|
|
2496
2888
|
&& props.controller.listPluginWriteBatchTargets('uninstall', [candidate.plugin.pluginId]).length === 0
|
|
2497
2889
|
&& props.controller.listPluginWriteBatchTargets('cleanup', [candidate.plugin.pluginId]).length === 0
|
|
2498
2890
|
&& !batchTaskPluginIds.has(candidate.plugin.pluginId)
|
|
@@ -2503,7 +2895,8 @@ export function ConsoleAppV4(props) {
|
|
|
2503
2895
|
? '插件来源不可用'
|
|
2504
2896
|
: draft.action === 'install' && candidate.plugin.capabilityUnsupported
|
|
2505
2897
|
? '当前 AW CLI 不支持插件所需功能'
|
|
2506
|
-
: draft.action === 'install' && !
|
|
2898
|
+
: draft.action === 'install' && !candidate.plugin.portableSkills
|
|
2899
|
+
&& !props.controller.pluginHasInstallableTarget(candidate.plugin.pluginId)
|
|
2507
2900
|
? '没有可信且兼容的缺失位置'
|
|
2508
2901
|
: undefined;
|
|
2509
2902
|
const invalidBatchSelection = (draft) => {
|
|
@@ -2590,27 +2983,6 @@ export function ConsoleAppV4(props) {
|
|
|
2590
2983
|
: [...current.selectedPluginIds, pluginId],
|
|
2591
2984
|
} : current);
|
|
2592
2985
|
};
|
|
2593
|
-
const prepareCurrentProjectTargets = (targets) => {
|
|
2594
|
-
if (targets.length === 0) {
|
|
2595
|
-
setActionError('当前项目没有已确认可同步的目标。');
|
|
2596
|
-
return;
|
|
2597
|
-
}
|
|
2598
|
-
beginReview({
|
|
2599
|
-
kind: 'pluginWriteBatch',
|
|
2600
|
-
action: 'sync',
|
|
2601
|
-
selection: {
|
|
2602
|
-
kind: 'currentProjectIntentTargets',
|
|
2603
|
-
targets: targets.map((target) => ({
|
|
2604
|
-
pluginId: target.pluginId,
|
|
2605
|
-
platform: target.platform,
|
|
2606
|
-
scope: target.scope,
|
|
2607
|
-
})),
|
|
2608
|
-
},
|
|
2609
|
-
platform: new Set(targets.map((target) => target.platform)).size > 1
|
|
2610
|
-
? 'both' : targets[0].platform,
|
|
2611
|
-
scope: 'project',
|
|
2612
|
-
});
|
|
2613
|
-
};
|
|
2614
2986
|
const openSelectedPluginTask = (goal, pluginIds, scopeConstraint) => {
|
|
2615
2987
|
if (batchDraft && invalidBatchSelection(batchDraft).length > 0) {
|
|
2616
2988
|
setActionError('已选插件状态已变化。请返回选择页检查并取消失效项。');
|
|
@@ -2809,7 +3181,9 @@ export function ConsoleAppV4(props) {
|
|
|
2809
3181
|
return;
|
|
2810
3182
|
}
|
|
2811
3183
|
try {
|
|
2812
|
-
|
|
3184
|
+
const agent = platform === 'claude' ? 'claude' : 'codex';
|
|
3185
|
+
beginScopedTask({ kind: 'recovery', command: 'cleanup', scope: selectedScope,
|
|
3186
|
+
selectors: [pluginId], targets: [agent], exactTargets: [{ pluginId, agent }], force: true }, () => props.controller.startForceCleanupReview(candidate));
|
|
2813
3187
|
setFocus(0);
|
|
2814
3188
|
setScrollOffset(0);
|
|
2815
3189
|
setActionError(undefined);
|
|
@@ -3017,9 +3391,7 @@ export function ConsoleAppV4(props) {
|
|
|
3017
3391
|
setDirty(false);
|
|
3018
3392
|
go({ name: 'sourceEdit', sourceId, owner });
|
|
3019
3393
|
};
|
|
3020
|
-
const configLayerLabel = (layer) => layer.
|
|
3021
|
-
: layer.scope === 'user' ? '个人默认'
|
|
3022
|
-
: '当前项目';
|
|
3394
|
+
const configLayerLabel = (layer) => layer.profile === 'common' ? '公共设置' : agentLabel(layer.profile);
|
|
3023
3395
|
const fieldValueText = (field, value, full = false, configured = value !== undefined) => presentConfigValue({
|
|
3024
3396
|
definition: field.definition,
|
|
3025
3397
|
value,
|
|
@@ -3035,8 +3407,8 @@ export function ConsoleAppV4(props) {
|
|
|
3035
3407
|
}
|
|
3036
3408
|
return `${field.effectiveDisplayValue}${field.unit && field.effectiveDisplayValue !== '未设置' ? ` ${field.unit}` : ''}`;
|
|
3037
3409
|
};
|
|
3038
|
-
const pendingFieldDisplay = (field,
|
|
3039
|
-
const draft = configDrafts[
|
|
3410
|
+
const pendingFieldDisplay = (field, profile) => {
|
|
3411
|
+
const draft = configDrafts[profile];
|
|
3040
3412
|
if (!draft)
|
|
3041
3413
|
return undefined;
|
|
3042
3414
|
if (draft.removes.includes(field.key))
|
|
@@ -3044,49 +3416,42 @@ export function ConsoleAppV4(props) {
|
|
|
3044
3416
|
if (!Object.hasOwn(draft.updates, field.key))
|
|
3045
3417
|
return undefined;
|
|
3046
3418
|
const value = draft.updates[field.key];
|
|
3047
|
-
|
|
3419
|
+
const targetField = configLayerPages[profile]?.groups.flatMap((group) => group.fields)
|
|
3420
|
+
.find((candidate) => candidate.key === field.key);
|
|
3421
|
+
if (!targetField || targetField.sensitive)
|
|
3048
3422
|
return '将更新';
|
|
3049
|
-
return fieldValueText(
|
|
3423
|
+
return fieldValueText(targetField, value);
|
|
3050
3424
|
};
|
|
3051
|
-
const pendingFieldTargets = (field) =>
|
|
3052
|
-
const value = pendingFieldDisplay(field,
|
|
3053
|
-
return value ? [{
|
|
3425
|
+
const pendingFieldTargets = (field) => CONFIG_PROFILE_KEYS.flatMap((profile) => {
|
|
3426
|
+
const value = pendingFieldDisplay(field, profile);
|
|
3427
|
+
return value ? [{ profile, value }] : [];
|
|
3054
3428
|
});
|
|
3055
3429
|
const fieldEditor = (field) => {
|
|
3056
3430
|
return field.type;
|
|
3057
3431
|
};
|
|
3058
3432
|
const fieldEditorMode = (field) => (field.choices?.length ?? 0) > 0 || 'valueRef' in field.definition && field.definition.valueRef ? { mode: 'choices' } : {};
|
|
3059
|
-
const openConfigTarget = (candidate
|
|
3433
|
+
const openConfigTarget = (candidate) => {
|
|
3060
3434
|
const currentFlowId = snapshot.router.currentFlowId;
|
|
3061
3435
|
const storedAnchor = configOverviewAnchors.current.get(candidate.pluginId);
|
|
3062
3436
|
const storedOverviewFrameId = storedAnchor && storedAnchor.flowId === currentFlowId
|
|
3063
3437
|
? storedAnchor.overviewFrameId : undefined;
|
|
3064
3438
|
const overviewFrameId = storedOverviewFrameId
|
|
3065
3439
|
?? props.controller.configOverviewFrameId(currentFlowId, candidate.pluginId);
|
|
3066
|
-
|
|
3440
|
+
const selected = {
|
|
3067
3441
|
...candidate,
|
|
3068
3442
|
...(currentFlowId ? { flowId: currentFlowId } : {}),
|
|
3069
3443
|
...(overviewFrameId ? { overviewFrameId } : {}),
|
|
3070
|
-
});
|
|
3071
|
-
const targetRoute = {
|
|
3072
|
-
name: 'configLayer',
|
|
3073
|
-
pluginId: candidate.pluginId,
|
|
3074
|
-
...(candidate.fieldKey ? { fieldKey: candidate.fieldKey } : {}),
|
|
3075
|
-
...(candidate.groupId ? { groupId: candidate.groupId } : {}),
|
|
3076
|
-
...(candidate.presetId ? { presetId: candidate.presetId } : {}),
|
|
3077
3444
|
};
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
else
|
|
3081
|
-
go(targetRoute);
|
|
3445
|
+
setConfigCandidate(selected);
|
|
3446
|
+
openConfigLayerTarget(candidate.layer, selected);
|
|
3082
3447
|
};
|
|
3083
3448
|
const updateDraft = (key, value) => {
|
|
3084
3449
|
if (!configRoute || !currentConfigPage)
|
|
3085
3450
|
return;
|
|
3086
3451
|
setConfigActionError(undefined);
|
|
3087
|
-
if (activeField &&
|
|
3452
|
+
if (activeField && editingProfile) {
|
|
3088
3453
|
for (const { scalar, value: referencedValue } of configReferenceValues(activeField.definition, value)) {
|
|
3089
|
-
const selected = referenceContext?.selections[configReferenceKey(scalar)];
|
|
3454
|
+
const selected = referenceContext?.selections[configReferenceKey(scalar, referenceContext.platform)];
|
|
3090
3455
|
if (!selected || selected.issues.length || !selected.choices.some((choice) => choice.value === referencedValue)) {
|
|
3091
3456
|
setObjectError(referenceNotice(scalar, referencedValue));
|
|
3092
3457
|
setInputError(referenceNotice(scalar, referencedValue));
|
|
@@ -3119,16 +3484,13 @@ export function ConsoleAppV4(props) {
|
|
|
3119
3484
|
}
|
|
3120
3485
|
const candidate = {
|
|
3121
3486
|
pluginId: configRoute.pluginId,
|
|
3487
|
+
layer: configRoute.layer,
|
|
3122
3488
|
fieldKey: key,
|
|
3123
3489
|
groupId: snapshot.router.current.context.configGroupId,
|
|
3124
3490
|
mutations: { [key]: { kind: 'set', value } },
|
|
3125
|
-
|
|
3491
|
+
editEvidence: activeArrayDraft?.evidence ?? currentConfigPage.editEvidence,
|
|
3126
3492
|
};
|
|
3127
|
-
|
|
3128
|
-
openConfigLayerTarget({ scope: editingScope }, candidate);
|
|
3129
|
-
}
|
|
3130
|
-
else
|
|
3131
|
-
openConfigTarget(candidate);
|
|
3493
|
+
openConfigTarget(candidate);
|
|
3132
3494
|
};
|
|
3133
3495
|
const editArray = (edit) => {
|
|
3134
3496
|
if (!activeArrayKey || !activeArrayDraft)
|
|
@@ -3173,14 +3535,13 @@ export function ConsoleAppV4(props) {
|
|
|
3173
3535
|
setConfigActionError(undefined);
|
|
3174
3536
|
const candidate = {
|
|
3175
3537
|
pluginId: configRoute.pluginId,
|
|
3538
|
+
layer: configRoute.layer,
|
|
3539
|
+
editEvidence: currentConfigPage?.editEvidence,
|
|
3176
3540
|
fieldKey: key,
|
|
3177
3541
|
groupId: snapshot.router.current.context.configGroupId,
|
|
3178
3542
|
mutations: { [key]: { kind: 'unset' } },
|
|
3179
3543
|
};
|
|
3180
|
-
|
|
3181
|
-
openConfigLayerTarget({ scope: editingScope }, candidate);
|
|
3182
|
-
else
|
|
3183
|
-
openConfigTarget(candidate);
|
|
3544
|
+
openConfigTarget(candidate);
|
|
3184
3545
|
};
|
|
3185
3546
|
const updateObjectDraft = (value) => {
|
|
3186
3547
|
if (!activeObjectKey)
|
|
@@ -3243,7 +3604,7 @@ export function ConsoleAppV4(props) {
|
|
|
3243
3604
|
const definition = activeObjectProperties?.[propertyKey] ?? activeField.definition.additionalProperties;
|
|
3244
3605
|
if (definition && value !== undefined) {
|
|
3245
3606
|
for (const { scalar, value: selected } of configReferenceValues(definition, value)) {
|
|
3246
|
-
const reference = referenceContext?.selections[configReferenceKey(scalar)];
|
|
3607
|
+
const reference = referenceContext?.selections[configReferenceKey(scalar, referenceContext.platform)];
|
|
3247
3608
|
if (!reference || reference.issues.length || !reference.choices.some((choice) => choice.value === selected)) {
|
|
3248
3609
|
setInputError(referenceNotice(scalar, selected) ?? '引用选项已变化,请重新选择。');
|
|
3249
3610
|
return false;
|
|
@@ -3317,6 +3678,8 @@ export function ConsoleAppV4(props) {
|
|
|
3317
3678
|
: undefined);
|
|
3318
3679
|
openConfigTarget({
|
|
3319
3680
|
pluginId: page.pluginId,
|
|
3681
|
+
layer: { profile: page.profile, context: page.context },
|
|
3682
|
+
editEvidence: page.editEvidence,
|
|
3320
3683
|
presetId,
|
|
3321
3684
|
groupId: target?.groupId,
|
|
3322
3685
|
mutations,
|
|
@@ -3324,51 +3687,31 @@ export function ConsoleAppV4(props) {
|
|
|
3324
3687
|
};
|
|
3325
3688
|
const openConfigLayerTarget = (layer, explicitCandidate) => {
|
|
3326
3689
|
const candidate = explicitCandidate ?? configCandidate;
|
|
3327
|
-
if (!candidate
|
|
3690
|
+
if (!candidate || candidate.layer.profile !== layer.profile
|
|
3691
|
+
|| (candidate.layer.context ?? 'user') !== (layer.context ?? 'user'))
|
|
3328
3692
|
return;
|
|
3329
3693
|
const selectedCandidate = { ...candidate,
|
|
3330
3694
|
flowId: candidate.flowId ?? snapshot.router.currentFlowId,
|
|
3331
3695
|
overviewFrameId: candidate.overviewFrameId ?? configOverviewAnchors.current.get(candidate.pluginId)?.overviewFrameId,
|
|
3332
3696
|
};
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
if (field.definition.type === 'array') {
|
|
3338
|
-
const owner = `${candidate.pluginId}\0${field.key}`;
|
|
3339
|
-
const working = arrayDrafts[owner];
|
|
3340
|
-
if (working) {
|
|
3341
|
-
const stored = configDrafts[layer.scope]?.updates[field.key] ?? field.draftValue ?? field.value;
|
|
3342
|
-
setArrayDrafts((current) => ({ ...current, [owner]: { ...working,
|
|
3343
|
-
initial: Array.isArray(stored) ? structuredClone(stored) : undefined,
|
|
3344
|
-
evidence: configLayerPages[layer.scope]?.editEvidence,
|
|
3345
|
-
} }));
|
|
3346
|
-
}
|
|
3347
|
-
}
|
|
3348
|
-
const available = ['user', 'project'].filter((scope) => configLayerPages[scope]?.groups
|
|
3349
|
-
.flatMap((group) => group.fields).some((entry) => entry.key === field.key && configFieldCanSet(entry)));
|
|
3350
|
-
const next = { name: 'configFieldEditor', pluginId: candidate.pluginId, layer, fieldKey: field.key,
|
|
3351
|
-
editor: fieldEditor(field), ...fieldEditorMode(field) };
|
|
3352
|
-
if (available.length === 1)
|
|
3353
|
-
props.controller.replace(next);
|
|
3354
|
-
else
|
|
3355
|
-
go(next);
|
|
3356
|
-
return;
|
|
3357
|
-
}
|
|
3358
|
-
const page = configLayerPages[layer.scope];
|
|
3359
|
-
if (!page)
|
|
3697
|
+
const page = currentConfigPage?.pluginId === candidate.pluginId
|
|
3698
|
+
&& currentConfigPage.profile === layer.profile && currentConfigPage.context === (layer.context ?? 'user')
|
|
3699
|
+
? currentConfigPage : configLayerPages[layer.profile];
|
|
3700
|
+
if (!page || page.pluginId !== candidate.pluginId || page.context !== (layer.context ?? 'user'))
|
|
3360
3701
|
return;
|
|
3361
3702
|
const fields = new Map(page.groups.flatMap((group) => group.fields)
|
|
3362
3703
|
.map((field) => [field.key, field]));
|
|
3363
3704
|
const valid = Object.entries(selectedCandidate.mutations).every(([key, mutation]) => {
|
|
3364
3705
|
const field = fields.get(key);
|
|
3365
|
-
return Boolean(field && (mutation.kind === 'set'
|
|
3706
|
+
return Boolean(field && (mutation.kind === 'set'
|
|
3707
|
+
? configFieldCanSet(field) && validatePluginConfigValue(field.definition, mutation.value, key).length === 0
|
|
3708
|
+
: configFieldCanUnset(field)));
|
|
3366
3709
|
});
|
|
3367
3710
|
if (!valid) {
|
|
3368
|
-
setConfigActionError('
|
|
3711
|
+
setConfigActionError('此值不符合当前配置。请检查修改后的值。');
|
|
3369
3712
|
return;
|
|
3370
3713
|
}
|
|
3371
|
-
const previous = configDrafts[layer.
|
|
3714
|
+
const previous = configDrafts[layer.profile] ?? { updates: {}, removes: [] };
|
|
3372
3715
|
const updates = { ...previous.updates };
|
|
3373
3716
|
const removes = new Set(previous.removes);
|
|
3374
3717
|
const editEvidence = { ...previous.editEvidence };
|
|
@@ -3395,9 +3738,9 @@ export function ConsoleAppV4(props) {
|
|
|
3395
3738
|
removes.delete(key);
|
|
3396
3739
|
}
|
|
3397
3740
|
}
|
|
3398
|
-
const next = { ...configDrafts, [layer.
|
|
3741
|
+
const next = { ...configDrafts, [layer.profile]: { updates, removes: [...removes], editEvidence } };
|
|
3399
3742
|
if (Object.keys(updates).length === 0 && removes.size === 0)
|
|
3400
|
-
delete next[layer.
|
|
3743
|
+
delete next[layer.profile];
|
|
3401
3744
|
const target = selectedCandidate.fieldKey
|
|
3402
3745
|
? configFieldTarget(configPage ?? page, selectedCandidate.fieldKey)
|
|
3403
3746
|
: selectedCandidate.groupId ? { groupId: selectedCandidate.groupId, focusIndex: 0 } : undefined;
|
|
@@ -3407,10 +3750,10 @@ export function ConsoleAppV4(props) {
|
|
|
3407
3750
|
return;
|
|
3408
3751
|
}
|
|
3409
3752
|
if (!selectedCandidate.flowId) {
|
|
3410
|
-
props.controller.replace({ name: 'configFields', pluginId: selectedCandidate.pluginId, layer
|
|
3753
|
+
props.controller.replace({ name: 'configFields', pluginId: selectedCandidate.pluginId, layer }, target ? { configGroupId: target.groupId, focusIndex: target.focusIndex, scrollOffset: 0 } : undefined);
|
|
3411
3754
|
}
|
|
3412
3755
|
if (selectedCandidate.fieldKey) {
|
|
3413
|
-
const owner = `${selectedCandidate.pluginId}\0${selectedCandidate.fieldKey}`;
|
|
3756
|
+
const owner = `${configEditorOwnerKey(selectedCandidate.pluginId, layer)}\0${selectedCandidate.fieldKey}`;
|
|
3414
3757
|
const belongsToField = (key) => key === owner || key.startsWith(`${owner}\0`);
|
|
3415
3758
|
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== owner)));
|
|
3416
3759
|
setObjectDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !belongsToField(key))));
|
|
@@ -3422,33 +3765,6 @@ export function ConsoleAppV4(props) {
|
|
|
3422
3765
|
setFocus(target?.focusIndex ?? 0);
|
|
3423
3766
|
setScrollOffset(0);
|
|
3424
3767
|
};
|
|
3425
|
-
useEffect(() => {
|
|
3426
|
-
if (route.name !== 'configLayer' || !configCandidate)
|
|
3427
|
-
return;
|
|
3428
|
-
const scopes = ['project', 'user'].filter((scope) => {
|
|
3429
|
-
const page = configLayerPages[scope];
|
|
3430
|
-
if (!page)
|
|
3431
|
-
return false;
|
|
3432
|
-
const fields = page.groups.flatMap((group) => group.fields);
|
|
3433
|
-
if (configCandidate.selectingScope)
|
|
3434
|
-
return fields.some((field) => field.key === configCandidate.fieldKey && configFieldCanSet(field));
|
|
3435
|
-
return Object.entries(configCandidate.mutations).every(([key, mutation]) => {
|
|
3436
|
-
const field = fields.find((entry) => entry.key === key);
|
|
3437
|
-
return field && (mutation.kind === 'set' ? configFieldCanSet(field) : configFieldCanUnset(field));
|
|
3438
|
-
});
|
|
3439
|
-
});
|
|
3440
|
-
if (scopes.length === 1) {
|
|
3441
|
-
openConfigLayerTarget({ scope: scopes[0] });
|
|
3442
|
-
if (!configCandidate.selectingScope)
|
|
3443
|
-
setConfigActionError(`仅可保存到${configLayerLabel({ scope: scopes[0] })};已加入待保存修改。`);
|
|
3444
|
-
}
|
|
3445
|
-
}, [route.name, configCandidate, configLayerPages]);
|
|
3446
|
-
useEffect(() => {
|
|
3447
|
-
if (route.name !== 'configFieldEditor' || route.layer.scope !== 'effective' || arrayItem || !activeTopField
|
|
3448
|
-
|| !configReferenceScalars(activeTopField.definition).length)
|
|
3449
|
-
return;
|
|
3450
|
-
openConfigTarget({ pluginId: route.pluginId, fieldKey: activeTopField.key, selectingScope: true, mutations: {} }, true);
|
|
3451
|
-
}, [route.name, route.name === 'configFieldEditor' ? route.layer.scope : '', activeTopField?.key]);
|
|
3452
3768
|
const configChanges = (page, draft) => {
|
|
3453
3769
|
const fields = page.groups.flatMap((group) => group.fields);
|
|
3454
3770
|
return [...Object.keys(draft.updates), ...draft.removes.filter((key) => !Object.hasOwn(draft.updates, key))]
|
|
@@ -3525,44 +3841,45 @@ export function ConsoleAppV4(props) {
|
|
|
3525
3841
|
const discardInvalidConfigDraftChanges = () => {
|
|
3526
3842
|
if (!configRoute || configDraftIssues.length === 0)
|
|
3527
3843
|
return;
|
|
3528
|
-
const
|
|
3529
|
-
|
|
3530
|
-
const draft = configDrafts[scope];
|
|
3844
|
+
const nextDrafts = Object.fromEntries(CONFIG_PROFILE_KEYS.flatMap((profile) => {
|
|
3845
|
+
const draft = configDrafts[profile];
|
|
3531
3846
|
if (!draft)
|
|
3532
3847
|
return [];
|
|
3533
|
-
const
|
|
3534
|
-
.filter((issue) => issue.
|
|
3848
|
+
const invalidForProfile = new Set(configDraftIssues
|
|
3849
|
+
.filter((issue) => issue.profile === profile).map((issue) => issue.key));
|
|
3535
3850
|
const updates = Object.fromEntries(Object.entries(draft.updates)
|
|
3536
|
-
.filter(([key]) => !
|
|
3537
|
-
const removes = draft.removes.filter((key) => !
|
|
3538
|
-
return Object.keys(updates).length + removes.length > 0 ? [[
|
|
3851
|
+
.filter(([key]) => !invalidForProfile.has(key)));
|
|
3852
|
+
const removes = draft.removes.filter((key) => !invalidForProfile.has(key));
|
|
3853
|
+
return Object.keys(updates).length + removes.length > 0 ? [[profile, { updates, removes,
|
|
3539
3854
|
...(draft.editEvidence ? { editEvidence: Object.fromEntries(Object.entries(draft.editEvidence)
|
|
3540
3855
|
.filter(([key]) => Object.hasOwn(updates, key))) } : {}),
|
|
3541
3856
|
}]] : [];
|
|
3542
3857
|
}));
|
|
3543
|
-
const
|
|
3544
|
-
const
|
|
3545
|
-
|
|
3858
|
+
const invalidEditorOwners = configDraftIssues.map(({ profile, key }) => `${configEditorOwnerKey(configRoute.pluginId, { profile, context: configContext })}\0${key}`);
|
|
3859
|
+
const invalidEditor = (key) => invalidEditorOwners.some((owner) => key === owner || key.startsWith(`${owner}\0`));
|
|
3860
|
+
const nextChangedObjectKeys = changedObjectDraftKeys.filter((key) => !invalidEditor(key));
|
|
3546
3861
|
setConfigDrafts(nextDrafts);
|
|
3547
3862
|
setObjectDrafts((current) => Object.fromEntries(Object.entries(current)
|
|
3548
|
-
.filter(([key]) => !key
|
|
3549
|
-
|
|
3863
|
+
.filter(([key]) => !invalidEditor(key))));
|
|
3864
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current)
|
|
3865
|
+
.filter(([key]) => !invalidEditor(key))));
|
|
3550
3866
|
setChangedObjectDraftKeys(nextChangedObjectKeys);
|
|
3551
3867
|
setDirty(Object.keys(nextDrafts).length + nextChangedObjectKeys.length > 0);
|
|
3552
3868
|
setObjectError(undefined);
|
|
3553
3869
|
setConfigActionError(undefined);
|
|
3554
3870
|
};
|
|
3555
|
-
const configSaveIntent = (apply) => {
|
|
3871
|
+
const configSaveIntent = (apply = false) => {
|
|
3556
3872
|
if (!configRoute || configDraftCount === 0)
|
|
3557
3873
|
return;
|
|
3558
|
-
const groups =
|
|
3559
|
-
const draft = configDrafts[
|
|
3560
|
-
const page = configLayerPages[
|
|
3561
|
-
if (!draft || !page)
|
|
3874
|
+
const groups = CONFIG_PROFILE_KEYS.flatMap((profile) => {
|
|
3875
|
+
const draft = configDrafts[profile];
|
|
3876
|
+
const page = configLayerPages[profile];
|
|
3877
|
+
if (!draft || !page || page.pluginId !== configRoute.pluginId || page.context !== configContext)
|
|
3562
3878
|
return [];
|
|
3563
3879
|
const fields = page.groups.flatMap((group) => group.fields);
|
|
3564
3880
|
return [{
|
|
3565
|
-
scope,
|
|
3881
|
+
scope: 'user',
|
|
3882
|
+
...(profile === 'common' ? {} : { platform: profile }),
|
|
3566
3883
|
updates: draft.updates,
|
|
3567
3884
|
...(draft.editEvidence ? { editEvidence: draft.editEvidence } : {}),
|
|
3568
3885
|
removeKeys: draft.removes,
|
|
@@ -3577,17 +3894,49 @@ export function ConsoleAppV4(props) {
|
|
|
3577
3894
|
return {
|
|
3578
3895
|
kind: 'configSaveBatch',
|
|
3579
3896
|
pluginId: configRoute.pluginId,
|
|
3897
|
+
context: configContext,
|
|
3580
3898
|
apply,
|
|
3581
3899
|
groups,
|
|
3582
3900
|
};
|
|
3583
3901
|
};
|
|
3584
|
-
const
|
|
3902
|
+
const discardDependentConfigDrafts = () => {
|
|
3903
|
+
if (!configRoute || crossLayerDependencies.length === 0)
|
|
3904
|
+
return;
|
|
3905
|
+
const next = { ...configDrafts };
|
|
3906
|
+
for (const { profile, key } of crossLayerDependencies) {
|
|
3907
|
+
const previous = next[profile];
|
|
3908
|
+
if (!previous)
|
|
3909
|
+
continue;
|
|
3910
|
+
const updates = { ...previous.updates };
|
|
3911
|
+
delete updates[key];
|
|
3912
|
+
const removes = previous.removes.filter((removed) => removed !== key);
|
|
3913
|
+
const editEvidence = { ...previous.editEvidence };
|
|
3914
|
+
delete editEvidence[key];
|
|
3915
|
+
if (Object.keys(updates).length + removes.length === 0)
|
|
3916
|
+
delete next[profile];
|
|
3917
|
+
else
|
|
3918
|
+
next[profile] = { updates, removes, editEvidence };
|
|
3919
|
+
}
|
|
3920
|
+
const removedOwners = crossLayerDependencies.map(({ profile, key }) => `${configEditorOwnerKey(configRoute.pluginId, { profile, context: configContext })}\0${key}`);
|
|
3921
|
+
const removed = (key) => removedOwners.some((owner) => key === owner || key.startsWith(`${owner}\0`));
|
|
3922
|
+
const changedObjects = changedObjectDraftKeys.filter((key) => !removed(key));
|
|
3923
|
+
setConfigDrafts(next);
|
|
3924
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !removed(key))));
|
|
3925
|
+
setObjectDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !removed(key))));
|
|
3926
|
+
setChangedObjectDraftKeys(changedObjects);
|
|
3927
|
+
setDirty(Object.keys(next).length + changedObjects.length > 0);
|
|
3928
|
+
setObjectError(undefined);
|
|
3929
|
+
setConfigActionError(undefined);
|
|
3930
|
+
};
|
|
3931
|
+
const onSaveConfig = async () => {
|
|
3932
|
+
if (configSaveState?.status === 'saving')
|
|
3933
|
+
return;
|
|
3585
3934
|
if (crossLayerDependencies.length) {
|
|
3586
|
-
setConfigActionError(`请先保存来源配置,再编辑或保存引用配置:${[...new Set(crossLayerDependencies.map((entry) => entry.source))].join('、')}
|
|
3935
|
+
setConfigActionError(`请先保存来源配置,再编辑或保存引用配置:${[...new Set(crossLayerDependencies.map((entry) => entry.source))].join('、')}(公共设置)。请取消相关引用草稿后保存来源。`);
|
|
3587
3936
|
return;
|
|
3588
3937
|
}
|
|
3589
|
-
if (
|
|
3590
|
-
&& Object.keys(configDrafts[
|
|
3938
|
+
if (CONFIG_PROFILE_KEYS.some((profile) => !referenceContexts[profile]
|
|
3939
|
+
&& Object.keys(configDrafts[profile]?.updates ?? {}).some((key) => configLayerPages[profile]?.groups.flatMap((group) => group.fields)
|
|
3591
3940
|
.some((field) => field.key === key && configReferenceScalars(field.definition).length)))) {
|
|
3592
3941
|
setConfigActionError(referenceContextError ?? '正在读取引用来源,请稍后保存。');
|
|
3593
3942
|
return;
|
|
@@ -3596,22 +3945,73 @@ export function ConsoleAppV4(props) {
|
|
|
3596
3945
|
setConfigActionError('字段已变化。请先放弃失效草稿,或返回继续检查来源。');
|
|
3597
3946
|
return;
|
|
3598
3947
|
}
|
|
3599
|
-
const intent = configSaveIntent(
|
|
3948
|
+
const intent = configSaveIntent();
|
|
3600
3949
|
if (!intent)
|
|
3601
3950
|
return;
|
|
3951
|
+
const profiles = intent.groups.map((group) => group.platform ?? 'common');
|
|
3952
|
+
const context = intent.context ?? 'user';
|
|
3953
|
+
const previouslySaved = configSaveState?.status === 'error' && configSaveState.pluginId === intent.pluginId
|
|
3954
|
+
&& configSaveState.context === context ? configSaveState.profiles : [];
|
|
3955
|
+
setConfigSaveState({ status: 'saving', pluginId: intent.pluginId, profiles, context });
|
|
3956
|
+
setConfigActionError(undefined);
|
|
3602
3957
|
try {
|
|
3603
|
-
await
|
|
3604
|
-
|
|
3958
|
+
const result = await props.controller.saveConfigDraft(intent);
|
|
3959
|
+
const saved = new Set(result.savedProfiles);
|
|
3960
|
+
const pendingApply = CONFIG_PROFILE_KEYS.filter((profile) => saved.has(profile) || previouslySaved.includes(profile));
|
|
3961
|
+
const nextDrafts = Object.fromEntries(Object.entries(configDrafts)
|
|
3962
|
+
.filter(([profile]) => !saved.has(profile))
|
|
3963
|
+
.map(([profile, draft]) => [profile, draft.editEvidence ? { ...draft, editEvidence: Object.fromEntries(Object.entries(draft.editEvidence).map(([key, evidence]) => [key, { ...evidence, storeDigests: {} }])) } : draft]));
|
|
3964
|
+
const savedEditorOwners = result.savedProfiles.map((profile) => `${configEditorOwnerKey(intent.pluginId, { profile, context })}\0`);
|
|
3965
|
+
const belongsToSavedProfile = (key) => savedEditorOwners.some((owner) => key.startsWith(owner));
|
|
3966
|
+
const nextChangedObjects = changedObjectDraftKeys.filter((key) => !belongsToSavedProfile(key));
|
|
3967
|
+
setConfigDrafts(nextDrafts);
|
|
3968
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !belongsToSavedProfile(key))));
|
|
3969
|
+
setObjectDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !belongsToSavedProfile(key))));
|
|
3970
|
+
setChangedObjectDraftKeys(nextChangedObjects);
|
|
3971
|
+
setConfigCandidate(undefined);
|
|
3972
|
+
setDirty(Object.keys(nextDrafts).length + nextChangedObjects.length > 0);
|
|
3973
|
+
if (saved.size > 0) {
|
|
3974
|
+
setConfigPage(undefined);
|
|
3975
|
+
setConfigLayerPages({});
|
|
3976
|
+
setReferenceContexts({});
|
|
3977
|
+
setConfigReadRevision((revision) => revision + 1);
|
|
3978
|
+
}
|
|
3979
|
+
if (result.status === 'succeeded') {
|
|
3980
|
+
setConfigSaveState({ status: 'saved', pluginId: intent.pluginId, profiles: pendingApply, context });
|
|
3981
|
+
}
|
|
3982
|
+
else {
|
|
3983
|
+
const error = result.status === 'partial' ? '部分设置未保存。已保留未保存的草稿,请检查后重试。'
|
|
3984
|
+
: '设置未保存。已保留草稿,请检查后重试。';
|
|
3985
|
+
setConfigSaveState({ status: 'error', pluginId: intent.pluginId, profiles: pendingApply, context, error });
|
|
3986
|
+
setConfigActionError(error);
|
|
3987
|
+
}
|
|
3605
3988
|
}
|
|
3606
3989
|
catch (error) {
|
|
3607
|
-
props.controller.recordUiFailure(error, { code: '
|
|
3608
|
-
|
|
3990
|
+
props.controller.recordUiFailure(error, { code: 'tuiConfigSaveFailed', stage: 'config-save' });
|
|
3991
|
+
const message = '设置未保存。请重新检查修改后重试。';
|
|
3992
|
+
setConfigSaveState({ status: 'error', pluginId: intent.pluginId, profiles: previouslySaved, context, error: message });
|
|
3993
|
+
setConfigActionError(message);
|
|
3609
3994
|
}
|
|
3610
3995
|
};
|
|
3996
|
+
const beginConfigSave = onSaveConfig;
|
|
3997
|
+
const dismissConfigSaved = () => { setConfigSaveState(undefined); };
|
|
3998
|
+
const applySavedConfig = () => {
|
|
3999
|
+
if (configSaveState?.status !== 'saved' || configSaveState.profiles.length === 0)
|
|
4000
|
+
return;
|
|
4001
|
+
const saved = configSaveState;
|
|
4002
|
+
const platforms = saved.profiles.filter((profile) => profile !== 'common');
|
|
4003
|
+
setConfigSaveState(undefined);
|
|
4004
|
+
beginReview({
|
|
4005
|
+
kind: 'configSave', pluginId: saved.pluginId, context: saved.context,
|
|
4006
|
+
apply: true, retryApply: true, updates: {},
|
|
4007
|
+
...(saved.profiles.includes('common') ? {}
|
|
4008
|
+
: platforms.length === 1 ? { platform: platforms[0] } : { targets: platforms }),
|
|
4009
|
+
});
|
|
4010
|
+
};
|
|
3611
4011
|
useEffect(() => {
|
|
3612
4012
|
if (route.name === 'configSave' && route.view !== 'changes' && configDraftCount > 0 && currentConfigPage) {
|
|
3613
4013
|
props.controller.replace({ name: 'configFields', pluginId: route.pluginId, layer: route.layer });
|
|
3614
|
-
beginConfigSave(
|
|
4014
|
+
void beginConfigSave();
|
|
3615
4015
|
}
|
|
3616
4016
|
}, [route.name, route.name === 'configSave' ? route.view : undefined, currentConfigPage, configDraftCount]);
|
|
3617
4017
|
useEffect(() => {
|
|
@@ -3620,15 +4020,6 @@ export function ConsoleAppV4(props) {
|
|
|
3620
4020
|
props.controller.replace({ name: 'sourceEdit', sourceId: sourceDraft.sourceId, owner: sourceDraft.owner });
|
|
3621
4021
|
beginSourceReview(sourceDraft, false);
|
|
3622
4022
|
}, [route.name, route.name === 'sourceSave' ? route.view : undefined, sourceDraft]);
|
|
3623
|
-
useEffect(() => {
|
|
3624
|
-
const intent = pendingReviewIntentRef.current;
|
|
3625
|
-
if (route.name !== 'review' || intent?.kind !== 'configSaveBatch' || !intent.apply
|
|
3626
|
-
|| snapshot.activeReview?.configImpact?.state !== 'known'
|
|
3627
|
-
|| snapshot.activeReview.configImpact.affectedTargetCount !== 0)
|
|
3628
|
-
return;
|
|
3629
|
-
back();
|
|
3630
|
-
void beginReview({ ...intent, apply: false });
|
|
3631
|
-
}, [route.name, snapshot.activeReview?.reviewToken]);
|
|
3632
4023
|
useEffect(() => {
|
|
3633
4024
|
if (!operationConfirmation)
|
|
3634
4025
|
return;
|
|
@@ -3638,7 +4029,7 @@ export function ConsoleAppV4(props) {
|
|
|
3638
4029
|
setOverlayFocus(0);
|
|
3639
4030
|
}
|
|
3640
4031
|
}, [operationConfirmation, route.name, snapshot.activeReview?.reviewToken]);
|
|
3641
|
-
if (snapshot.phase === 'error' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
4032
|
+
if (!scopedFlow && snapshot.phase === 'error' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
3642
4033
|
&& !isOperationHistoryRoute(route)) {
|
|
3643
4034
|
const sourcesConfigInvalid = snapshot.loadError?.code === 'catalogSourcesConfigInvalid';
|
|
3644
4035
|
breadcrumb = sourcesConfigInvalid ? 'Catalog 来源恢复' : '启动失败';
|
|
@@ -3656,7 +4047,7 @@ export function ConsoleAppV4(props) {
|
|
|
3656
4047
|
}] : [];
|
|
3657
4048
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "error", title: "\u63D2\u4EF6\u72B6\u6001\u65E0\u6CD5\u8BFB\u53D6", description: "\u6CA1\u6709\u6267\u884C\u4EFB\u4F55\u5199\u64CD\u4F5C\u3002" }), actions.length > 0 ? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' }) : null, showTechnical ? _jsx(TechnicalDetails, { children: _jsx(StaticRow, { title: "\u5B89\u5168\u8BCA\u65AD", meta: snapshot.loadError?.code ?? 'statusLoadFailed', compact: true }) }) : null] });
|
|
3658
4049
|
}
|
|
3659
|
-
else if (snapshot.phase !== 'ready' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
4050
|
+
else if (!scopedFlow && snapshot.phase !== 'ready' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
3660
4051
|
&& !isOperationHistoryRoute(route)) {
|
|
3661
4052
|
breadcrumb = '启动';
|
|
3662
4053
|
title = '正在检查插件状态';
|
|
@@ -3664,9 +4055,20 @@ export function ConsoleAppV4(props) {
|
|
|
3664
4055
|
help = 'Ctrl-C 退出';
|
|
3665
4056
|
content = _jsxs(StatusCard, { status: "info", title: "\u51C6\u5907\u63A7\u5236\u53F0", children: [_jsx(ProgressRow, { title: "\u8BFB\u53D6\u63D2\u4EF6\u6765\u6E90", state: "active" }), _jsx(ProgressRow, { title: "\u68C0\u67E5\u5F53\u524D\u9879\u76EE", state: "pending" }), _jsx(ProgressRow, { title: "\u6C47\u603B\u53EF\u7528\u64CD\u4F5C", state: "pending" })] });
|
|
3666
4057
|
}
|
|
3667
|
-
else if (
|
|
4058
|
+
else if (configSaveState?.status === 'saved') {
|
|
4059
|
+
title = '插件设置';
|
|
4060
|
+
description = undefined;
|
|
4061
|
+
content = _jsx(Box, { height: Math.max(5, size.rows - 7), flexDirection: "column", justifyContent: "center", children: _jsxs(Box, { borderStyle: "round", borderColor: tuiTheme.roles.focus, paddingX: 2, flexDirection: "column", children: [_jsx(Text, { bold: true, color: tuiTheme.roles.focus, children: "\u5DF2\u4FDD\u5B58\uFF0C\u662F\u5426\u66F4\u65B0\u63D2\u4EF6\uFF1F" }), _jsx(Text, { children: "\u66F4\u65B0\u5DF2\u7ECF\u5B89\u88C5\u7684\u63D2\u4EF6\uFF0C\u4F7F\u8BBE\u7F6E\u751F\u6548\u3002" }), ['更新插件', '暂不更新'].map((label, index) => _jsxs(Text, { color: index === configSavedFocus ? tuiTheme.roles.focus : undefined, children: [index === configSavedFocus ? '› ' : ' ', label] }, label))] }) });
|
|
4062
|
+
help = '↑↓ 选择 · Enter 确认 · Esc 暂不更新';
|
|
4063
|
+
}
|
|
4064
|
+
else if (agentWelcome) {
|
|
4065
|
+
title = '欢迎使用 AW';
|
|
4066
|
+
description = undefined;
|
|
4067
|
+
content = _jsx(Box, { height: Math.max(5, size.rows - 7), flexDirection: "column", justifyContent: "center", children: _jsxs(Box, { borderStyle: "round", borderColor: tuiTheme.roles.focus, paddingX: 2, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "\u5148\u9009\u62E9\u4F60\u5728\u8FD9\u53F0\u7535\u8111\u4E0A\u4F7F\u7528\u7684 Agent" }), ['选择 Agent', '稍后再说'].map((label, index) => _jsxs(Text, { color: index === agentWelcomeFocus ? tuiTheme.roles.focus : undefined, children: [index === agentWelcomeFocus ? '› ' : ' ', label] }, label))] }) });
|
|
4068
|
+
help = '↑↓ 选择 · Enter 确认 · Esc 稍后再说';
|
|
4069
|
+
}
|
|
4070
|
+
else if (rendererFamily === 'home' && route.name === 'home' && !scopedFlow) {
|
|
3668
4071
|
const home = snapshot.viewModel.home;
|
|
3669
|
-
const inventory = snapshot.projectInventory;
|
|
3670
4072
|
const plugins = props.controller.listPlugins({ mode: 'browse' });
|
|
3671
4073
|
const attention = new Set(plugins.filter((entry) => entry.primaryAttention).map((entry) => entry.pluginId)).size;
|
|
3672
4074
|
const unknown = new Set(plugins.filter((entry) => entry.supportedPlatforms.some((platform) => pluginPlatformUnknown(entry, platform))).map((entry) => entry.pluginId)).size;
|
|
@@ -3675,145 +4077,332 @@ export function ConsoleAppV4(props) {
|
|
|
3675
4077
|
context = undefined;
|
|
3676
4078
|
const pluginState = [home.pluginUpdateCount ? `↑ ${home.pluginUpdateCount} 可更新` : '',
|
|
3677
4079
|
attention ? `! ${attention} 需处理` : '', unknown ? `? ${unknown} 未确认` : ''].filter(Boolean).join(' · ') || '✓ 当前可用';
|
|
3678
|
-
const projectState = !inventory ? '检查中' : inventory.declarationState === 'notProject' ? '当前不是项目'
|
|
3679
|
-
: inventory.declarationState === 'absent' ? '未配置项目插件'
|
|
3680
|
-
: inventory.declarationState !== 'ready' ? '! 项目声明需要检查'
|
|
3681
|
-
: [inventory.counts.blocked ? `! ${inventory.counts.blocked} 受阻` : '',
|
|
3682
|
-
inventory.counts.pending ? `↻ ${inventory.counts.pending} 待同步` : '',
|
|
3683
|
-
inventory.counts.unknown ? `? ${inventory.counts.unknown} 未确认` : ''].filter(Boolean).join(' · ') || '✓ 全部就绪';
|
|
3684
|
-
const visibleProjectState = snapshot.projectInventoryRefresh.state === 'error'
|
|
3685
|
-
? `! 项目检查失败,显示上次检查结果 · ${projectState}` : projectState;
|
|
3686
4080
|
const sourceState = [home.catalogUpdateCount ? `↑ ${home.catalogUpdateCount} 有更新` : '',
|
|
3687
4081
|
home.catalogRecoveryCount ? `! ${home.catalogRecoveryCount} 检查失败` : '',
|
|
3688
4082
|
home.unavailableSourceCount ? `! ${home.unavailableSourceCount} 不可用` : '',
|
|
3689
4083
|
home.catalogCheckingCount ? `${home.catalogCheckingCount} 检查中` : ''].filter(Boolean).join(' · ') || '✓ 当前可用';
|
|
3690
4084
|
actions = [
|
|
3691
4085
|
{ label: '插件', description: pluginState, run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
3692
|
-
{ label: '项目插件', description:
|
|
4086
|
+
{ label: '项目插件', description: (() => {
|
|
4087
|
+
const counts = snapshot.scopedInventories.project?.counts;
|
|
4088
|
+
return counts ? `✓ ${counts.ready} 个就绪 · ! ${counts.pending + counts.blocked + counts.unknown} 个需处理` : '查看当前项目的插件';
|
|
4089
|
+
})(), run: () => snapshot.projectRoot
|
|
4090
|
+
? go({ name: 'intentInventory', scope: 'project', projectKey: snapshot.projectRoot })
|
|
4091
|
+
: go({ name: 'intentInventory', scope: 'user' }) },
|
|
3693
4092
|
{ label: '插件来源', description: sourceState, run: () => go({ name: 'sourceList' }) },
|
|
4093
|
+
{ label: 'Agent 管理', description: agentSettingsError ?? (agentSettings?.preferences.targets
|
|
4094
|
+
.map((target) => `${agentSummaries()[target]?.symbol ?? '?'} ${agentLabel(target)}`).join(' · ') || '请选择本机使用的 Agent'),
|
|
4095
|
+
run: () => openAgentManagement() },
|
|
3694
4096
|
];
|
|
3695
|
-
|
|
3696
|
-
|
|
4097
|
+
if (size.rows <= 18 && wrapConsoleText(actions[3].description ?? '', size.columns - 4).length > 1)
|
|
4098
|
+
title = '';
|
|
4099
|
+
content = _jsxs(Box, { flexDirection: "column", children: [actions.map((action, index) => _jsx(Box, { flexShrink: 0, marginBottom: size.rows >= 24 ? 1 : 0, children: _jsx(HomeNavTile, { width: size.columns - 2, title: action.label, focused: focus === index, children: index === 3 ? _jsx(AgentStatusText, { text: action.description ?? '' }) : _jsx(Text, { wrap: "wrap", children: action.description?.split(' · ').map((state, stateIndex) => _jsxs(Text, { color: state.startsWith('↑') ? tuiTheme.roles.change : state.startsWith('!') || state.startsWith('↻') ? tuiTheme.roles.warning
|
|
4100
|
+
: state.startsWith('?') ? tuiTheme.roles.unknown : state.startsWith('✓') ? tuiTheme.roles.success : tuiTheme.roles.secondary, children: [stateIndex > 0 ? ' ' : '', "[", state, "]"] }, stateIndex)) }) }) }, action.label)), snapshot.projectRoot ? _jsx(Box, { flexShrink: 0, children: _jsxs(Text, { color: tuiTheme.roles.secondary, children: ["\u5F53\u524D\u9879\u76EE ", path.basename(snapshot.projectRoot)] }) }) : null] });
|
|
4101
|
+
}
|
|
4102
|
+
else if (route.name === 'intentScopes') {
|
|
4103
|
+
breadcrumb = '插件清单';
|
|
4104
|
+
title = '选择清单范围';
|
|
4105
|
+
context = undefined;
|
|
4106
|
+
description = undefined;
|
|
4107
|
+
const scopes = snapshot.projectRoot ? ['user', 'project'] : ['user'];
|
|
4108
|
+
actions = scopes.map((scope) => {
|
|
4109
|
+
const inventory = snapshot.scopedInventories[scope];
|
|
4110
|
+
const count = inventory?.counts;
|
|
4111
|
+
const status = !inventory ? '未检查' : inventory.declarationState !== 'ready' && inventory.declarationState !== 'absent'
|
|
4112
|
+
? '需求清单需检查' : count.blocked ? `${count.blocked} 受阻`
|
|
4113
|
+
: count.unknown ? `${count.unknown} 未确认` : count.pending ? `${count.pending} 待同步`
|
|
4114
|
+
: inventory.allSatisfied === true ? '已就绪' : '尚未添加插件';
|
|
4115
|
+
return { actionId: `scope:${scope}`, label: scope === 'user' ? '个人' : `当前项目 · ${inventory?.projectName ?? path.basename(snapshot.projectRoot ?? '')}`,
|
|
4116
|
+
description: `${scope === 'user' ? '跨项目使用的插件需求' : '随仓库共享的插件需求'} · ${status}`,
|
|
4117
|
+
run: () => go({ name: 'intentInventory', scope,
|
|
4118
|
+
...(scope === 'project' && snapshot.projectRoot
|
|
4119
|
+
? { projectKey: snapshot.projectRoot } : {}) }),
|
|
4120
|
+
};
|
|
4121
|
+
});
|
|
4122
|
+
content = _jsxs(Box, { flexDirection: "column", children: [actions.map((action, index) => _jsx(Box, { marginBottom: 1, children: _jsx(HomeNavTile, { width: size.columns - 2, title: action.label, focused: focus === index, children: _jsx(Text, { children: action.description }) }) }, action.actionId)), !snapshot.projectRoot
|
|
4123
|
+
? _jsx(StaticRow, { title: "\u5F53\u524D\u6CA1\u6709\u9879\u76EE\uFF1B\u4E2A\u4EBA\u6E05\u5355\u4ECD\u53EF\u4F7F\u7528\u3002", status: "muted", compact: true }) : null] });
|
|
4124
|
+
}
|
|
4125
|
+
else if (scopedFlow) {
|
|
4126
|
+
const flow = scopedFlow;
|
|
4127
|
+
const ReviewSurface = showTechnical ? TechnicalDetails : ReviewCard;
|
|
4128
|
+
const ResultSurface = showTechnical ? TechnicalDetails : ResultCard;
|
|
4129
|
+
const detailsAction = { label: showTechnical ? '返回摘要 [d]' : '查看详情 [d]',
|
|
4130
|
+
run: () => { setShowTechnical((value) => !value); setBatchTargetOffset(0); } };
|
|
4131
|
+
breadcrumb = `插件清单 / ${flow.scope === 'user' ? '个人' : '当前项目'}`;
|
|
4132
|
+
context = undefined;
|
|
4133
|
+
if (flow.stage === 'agents') {
|
|
4134
|
+
title = 'Agent 管理';
|
|
4135
|
+
description = flow.pluginId ? `选择 ${flow.pluginId.split('/').pop()} 在哪些 Agent 中使用`
|
|
4136
|
+
: '选择这台电脑上使用的 Agent';
|
|
4137
|
+
breadcrumb = flow.pluginId ? '插件 / Agent 管理' : '首页 / Agent 管理';
|
|
4138
|
+
content = _jsx(AgentManagement, { controller: props.controller, settings: flow.settings, pluginId: flow.pluginId, columns: size.columns, rows: size.rows, summaries: agentSummaries(flow.pluginId), active: !overlay, onBack: closeScopedFlow, onExit: requestConfirmedExit, onSaved: setAgentSettings, onHome: () => { closeScopedFlow(); requestSessionNavigation('home'); }, onReload: () => { closeScopedFlow(); requestSessionNavigation('reload'); }, onHelp: () => { setOverlayFocus(0); setHelpPage(1); setOverlay('help'); }, onSync: () => beginScopedTask({ kind: 'recovery', command: 'sync', scope: flow.scope,
|
|
4139
|
+
selectors: flow.pluginId ? [flow.pluginId] : [], retireUnselectedTargets: true }) });
|
|
4140
|
+
help = '↑↓ 移动 · Enter/Space 勾选 · s 保存 · Esc 返回';
|
|
4141
|
+
}
|
|
4142
|
+
else if (flow.stage === 'recoveryScope') {
|
|
4143
|
+
title = flow.command === 'uninstall' ? '选择卸载范围' : '选择清理范围';
|
|
4144
|
+
description = '确认变化后处理此范围,保留其他范围仍需要的内容。';
|
|
4145
|
+
actions = flow.scopes.map((selectedScope) => ({ label: scopeLabel(selectedScope),
|
|
4146
|
+
status: 'error', run: () => beginScopedTask({ kind: 'recovery', command: flow.command,
|
|
4147
|
+
scope: selectedScope, selectors: flow.selectors ?? [flow.selector] }) }));
|
|
4148
|
+
content = _jsx(SemanticRows, { actions: actions, focus: flow.focus, kind: "action", compact: true });
|
|
4149
|
+
}
|
|
4150
|
+
else if (flow.stage === 'intentScope') {
|
|
4151
|
+
title = flow.action === 'add' ? '选择插件需求范围' : '选择要移出的清单';
|
|
4152
|
+
description = flow.action === 'add' ? '保存需求不会立即安装。'
|
|
4153
|
+
: '移出需求后,现有安装仍会保留。';
|
|
4154
|
+
actions = flow.scopes
|
|
4155
|
+
.map((selectedScope) => ({ label: selectedScope === 'user' ? '个人(所有项目)' : '当前项目',
|
|
4156
|
+
run: () => beginScopedTask({ kind: 'intent', action: flow.action, scope: selectedScope,
|
|
4157
|
+
selector: flow.selector }) }));
|
|
4158
|
+
content = _jsx(SemanticRows, { actions: actions, focus: flow.focus, kind: "action", compact: true });
|
|
4159
|
+
}
|
|
4160
|
+
else if (flow.stage === 'targets') {
|
|
4161
|
+
title = flow.scope === 'user' ? '个人插件使用的工具' : '此项目在本机使用的工具';
|
|
4162
|
+
description = '保存后可单独准备同步任务。';
|
|
4163
|
+
actions = ['codex', 'claude', 'cursor'].map((agent) => ({
|
|
4164
|
+
actionId: `agent:${agent}`, label: `${flow.agents.includes(agent) ? '✓' : '○'} ${agent}`,
|
|
4165
|
+
run: () => setScopedFlow({ ...flow,
|
|
4166
|
+
agents: flow.agents.includes(agent) ? flow.agents.filter((value) => value !== agent)
|
|
4167
|
+
: [...flow.agents, agent].sort(),
|
|
4168
|
+
}),
|
|
4169
|
+
}));
|
|
4170
|
+
actions.push({ actionId: 'targets-save', label: '保存选择', run: () => beginScopedTask({ kind: 'targets', action: 'set', scope: flow.scope, agents: flow.agents }) });
|
|
4171
|
+
actions.push({ actionId: 'targets-reset', label: '重置本机部署偏好', run: () => beginScopedTask({ kind: 'targets', action: 'reset', scope: flow.scope, agents: [] }) });
|
|
4172
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsx(SemanticRows, { actions: actions, focus: flow.focus, kind: "action", compact: true, limit: Math.max(1, pageActionBudget(3)) }) });
|
|
4173
|
+
help = '↑↓ 移动 · Enter/Space 勾选 · s 保存 · Esc 返回';
|
|
4174
|
+
}
|
|
4175
|
+
else if (flow.stage === 'preparing' || flow.stage === 'progress') {
|
|
4176
|
+
title = flow.stage === 'preparing' ? '正在准备操作汇总' : '正在执行恢复';
|
|
4177
|
+
description = flow.stage === 'preparing' ? '正在检查当前证据。Esc 可取消准备。'
|
|
4178
|
+
: '操作已开始。完成后显示每个目标结果。';
|
|
4179
|
+
readingText = flow.stage === 'progress' ? [
|
|
4180
|
+
...(snapshot.progress.current
|
|
4181
|
+
? [`${snapshot.progress.current.target} · ${snapshot.progress.current.phase}`] : []),
|
|
4182
|
+
...snapshot.progress.messages,
|
|
4183
|
+
...(flow.plan.kind === 'recovery' ? scopedCommandPlanText(flow.plan) : []),
|
|
4184
|
+
].join('\n') : undefined;
|
|
4185
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ProgressRow, { title: title, state: "active" }), readingText ? readingPage(readingText, size.columns - 6, Math.max(1, pageActionBudget(4))) : null] });
|
|
4186
|
+
}
|
|
4187
|
+
else if (flow.stage === 'timeout') {
|
|
4188
|
+
title = '设置脚本超时';
|
|
4189
|
+
description = `输入 ${SCRIPT_TIMEOUT_MIN_MS}–${SCRIPT_TIMEOUT_MAX_MS} 毫秒。默认 ${SCRIPT_TIMEOUT_DEFAULT_MS} 毫秒。`;
|
|
4190
|
+
help = '输入 · Enter 应用并重新准备 · Ctrl-U 清空 · Esc 保留原值';
|
|
4191
|
+
content = _jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: scriptTimeoutInput, columns: Math.max(8, size.columns - 8) }), description: scriptTimeoutError ?? '此值只用于本次任务。', status: scriptTimeoutError ? 'error' : undefined });
|
|
4192
|
+
}
|
|
4193
|
+
else if (flow.stage === 'review') {
|
|
4194
|
+
title = '操作汇总';
|
|
4195
|
+
description = '确认需求、偏好、配置及输出影响。';
|
|
4196
|
+
readingText = scopedPlanText(flow.plan, showTechnical);
|
|
4197
|
+
help = '←→ 阅读 · ↑↓ 操作 · Enter 选择 · d 详情 · Esc 返回';
|
|
4198
|
+
actions = [
|
|
4199
|
+
{ label: flow.plan.kind === 'targets' ? '确认保存本机工具'
|
|
4200
|
+
: flow.plan.kind === 'intent' ? '确认修改需求' : '确认恢复可执行目标',
|
|
4201
|
+
run: () => scopedRiskReasons(flow.plan).length
|
|
4202
|
+
? setScopedFlow({ stage: 'risk', scope: flow.scope, request: flow.request,
|
|
4203
|
+
plan: flow.plan, focus: 0 }) : executeScopedReview(flow.plan) },
|
|
4204
|
+
{ label: '返回修改', run: closeScopedFlow },
|
|
4205
|
+
detailsAction,
|
|
4206
|
+
...(flow.request.kind === 'recovery' && batchDraft?.maintenanceTargets?.length
|
|
4207
|
+
&& batchDraft.maintenanceTargets.every((target) => target.scope === flow.scope)
|
|
4208
|
+
? [{ label: '修改处理范围', run: () => {
|
|
4209
|
+
void props.controller.discardScopedPlan(flow.plan);
|
|
4210
|
+
setScopedFlow(undefined);
|
|
4211
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
4212
|
+
setFocus(0);
|
|
4213
|
+
setScrollOffset(0);
|
|
4214
|
+
} }] : []),
|
|
4215
|
+
...(flow.request.kind === 'recovery' ? [{ label: '修改脚本超时', run: () => {
|
|
4216
|
+
const request = flow.request;
|
|
4217
|
+
if (request.kind !== 'recovery')
|
|
4218
|
+
return;
|
|
4219
|
+
void props.controller.discardScopedPlan(flow.plan);
|
|
4220
|
+
setScriptTimeoutInput(String(request.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS));
|
|
4221
|
+
setScriptTimeoutError(undefined);
|
|
4222
|
+
setScopedFlow({ stage: 'timeout', scope: flow.scope, request, focus: 0 });
|
|
4223
|
+
} }] : []),
|
|
4224
|
+
];
|
|
4225
|
+
content = _jsxs(ReviewSurface, { title: showTechnical ? "技术详情" : "本次变化", children: [readingPage(readingText, size.columns - 6, Math.max(1, pageActionBudget(actions.length + (showTechnical ? 5 : 4)))), _jsx(SemanticRows, { actions: actions, focus: flow.focus, kind: "action", compact: true })] });
|
|
4226
|
+
}
|
|
4227
|
+
else if (flow.stage === 'risk') {
|
|
4228
|
+
title = '操作汇总';
|
|
4229
|
+
description = '确认需求、偏好、配置及输出影响。';
|
|
4230
|
+
readingText = scopedPlanText(flow.plan, showTechnical);
|
|
4231
|
+
actions = [
|
|
4232
|
+
{ label: '返回检查', run: () => setScopedFlow({ ...flow, stage: 'review', focus: 0 }) },
|
|
4233
|
+
{ label: '确认并执行', run: () => executeScopedReview(flow.plan) },
|
|
4234
|
+
detailsAction,
|
|
4235
|
+
];
|
|
4236
|
+
content = _jsxs(ReviewSurface, { title: showTechnical ? "技术详情" : "本次变化", children: [readingPage(readingText, size.columns - 6, Math.max(1, pageActionBudget(actions.length + (showTechnical ? 5 : 4)))), _jsx(SemanticRows, { actions: actions, focus: flow.focus, kind: "action", compact: true })] });
|
|
4237
|
+
}
|
|
4238
|
+
else {
|
|
4239
|
+
title = '恢复结果';
|
|
4240
|
+
help = '←→ 阅读 · ↑↓ 操作 · Enter 选择 · d 详情 · Esc 返回';
|
|
4241
|
+
description = flow.errorCode ? recoveryReason(flow.errorCode)
|
|
4242
|
+
: flow.receipt ? flow.receipt.ok ? '请求已完成。' : '请求仍有未完成目标。'
|
|
4243
|
+
: '本机工具已保存。';
|
|
4244
|
+
readingText = flow.receipt ? scopedResultText(flow.receipt, showTechnical)
|
|
4245
|
+
: (flow.errorCode ? showTechnical ? flow.errorCode : recoveryReason(flow.errorCode) : undefined) ?? (flow.changed ? '本机工具已保存。' : '本机工具没有变化。');
|
|
4246
|
+
const retryTargets = flow.receipt?.targetResults.filter((target) => (target.execution === 'failed' || target.execution === 'partial')
|
|
4247
|
+
&& (target.observation === 'missing' || target.content === 'absent')) ?? [];
|
|
4248
|
+
const retryKeys = new Set(retryTargets.map((target) => `${target.pluginId}\0${target.agent}`));
|
|
4249
|
+
const unknownTargets = flow.receipt?.targetResults.filter((target) => target.observation === 'unknown'
|
|
4250
|
+
&& !retryKeys.has(`${target.pluginId}\0${target.agent}`)) ?? [];
|
|
4251
|
+
const reviewStale = flow.receipt
|
|
4252
|
+
? [...flow.receipt.blockers, ...flow.receipt.errors]
|
|
4253
|
+
.some((entry) => entry.code === 'operationReviewStale')
|
|
4254
|
+
: false;
|
|
4255
|
+
actions = [
|
|
4256
|
+
{ label: route.name === 'issueDetail' ? '返回问题详情'
|
|
4257
|
+
: route.name === 'pluginWriteTask' ? '返回插件任务'
|
|
4258
|
+
: route.name === 'pluginDetail' ? '返回插件详情'
|
|
4259
|
+
: route.name === 'intentInventory' ? '返回当前清单'
|
|
4260
|
+
: '返回插件列表', run: closeScopedFlow },
|
|
4261
|
+
detailsAction,
|
|
4262
|
+
...(reviewStale && flow.request ? [{ label: '重新准备本次操作', run: () => beginScopedTask(flow.request) }] : []),
|
|
4263
|
+
...(flow.plan?.kind === 'targets' ? [{ label: '立即同步', run: () => beginScopedTask({
|
|
4264
|
+
kind: 'recovery', scope: flow.scope, selectors: [],
|
|
4265
|
+
}) }] : []),
|
|
4266
|
+
...(unknownTargets.length ? [{ label: `检查未确认项(${unknownTargets.length})`, run: () => {
|
|
4267
|
+
closeScopedFlow();
|
|
4268
|
+
void props.controller.refreshScopedInventory(flow.scope);
|
|
4269
|
+
} }] : []),
|
|
4270
|
+
...(retryTargets.length ? [{ label: `重试明确失败项(${retryTargets.length})`, run: () => beginScopedTask({
|
|
4271
|
+
kind: 'recovery', scope: flow.scope, command: flow.receipt.command,
|
|
4272
|
+
selectors: [...new Set(retryTargets.map((target) => target.pluginId))],
|
|
4273
|
+
targets: [...new Set(retryTargets.map((target) => target.agent))],
|
|
4274
|
+
exactTargets: retryTargets.map((target) => ({ pluginId: target.pluginId, agent: target.agent })),
|
|
4275
|
+
}) }] : []),
|
|
4276
|
+
];
|
|
4277
|
+
content = _jsxs(ResultSurface, { status: flow.errorCode || flow.receipt && !flow.receipt.ok ? 'warning' : 'success', title: description, children: [readingPage(readingText, size.columns - 6, Math.max(1, pageActionBudget(actions.length + (showTechnical ? 5 : 4)))), _jsx(SemanticRows, { actions: actions, focus: flow.focus, kind: "action", compact: true })] });
|
|
4278
|
+
}
|
|
3697
4279
|
}
|
|
3698
|
-
else if (route.name === '
|
|
3699
|
-
const inventory = snapshot.
|
|
3700
|
-
const section = snapshot.router.current.context.
|
|
3701
|
-
const statusFilter = snapshot.router.current.context.
|
|
3702
|
-
const
|
|
3703
|
-
const searchedRoots =
|
|
4280
|
+
else if (route.name === 'intentInventory') {
|
|
4281
|
+
const inventory = snapshot.scopedInventories[route.scope];
|
|
4282
|
+
const section = snapshot.router.current.context.inventorySection ?? 'declared';
|
|
4283
|
+
const statusFilter = snapshot.router.current.context.inventoryStatusFilter ?? 'all';
|
|
4284
|
+
const allRoots = props.controller.listScopedInventoryRoots(route.scope, section);
|
|
4285
|
+
const searchedRoots = allRoots.filter((root) => queryMatches(query, root.displayName, root.pluginId, root.sourceId));
|
|
3704
4286
|
const roots = searchedRoots.filter((root) => statusFilter === 'all' || root.state === statusFilter);
|
|
3705
|
-
const
|
|
4287
|
+
const filters = ['all', 'blocked', 'unknown', 'pending', 'ready'];
|
|
4288
|
+
const filterLabels = { all: '全部', blocked: '受阻', unknown: '未确认', pending: '待同步', ready: '就绪' };
|
|
4289
|
+
const counts = Object.fromEntries(filters.map((filter) => [filter,
|
|
3706
4290
|
filter === 'all' ? searchedRoots.length : searchedRoots.filter((root) => root.state === filter).length,
|
|
3707
4291
|
]));
|
|
3708
4292
|
toggleProjectStatusFilter = () => {
|
|
3709
|
-
const available =
|
|
4293
|
+
const available = filters.filter((filter) => filter === 'all' || filter === statusFilter || counts[filter] > 0);
|
|
3710
4294
|
const next = available[(available.indexOf(statusFilter) + 1) % available.length];
|
|
3711
|
-
props.controller.updatePageContext({
|
|
3712
|
-
dynamicMenuFocus.current = undefined;
|
|
3713
|
-
actionJumpAnchor.current = undefined;
|
|
4295
|
+
props.controller.updatePageContext({ inventoryStatusFilter: next });
|
|
3714
4296
|
setFocus(0);
|
|
3715
4297
|
setScrollOffset(0);
|
|
3716
4298
|
};
|
|
3717
4299
|
moveProjectSection = () => {
|
|
3718
|
-
|
|
3719
|
-
props.controller.updatePageContext({ projectSection: next });
|
|
3720
|
-
dynamicMenuFocus.current = undefined;
|
|
3721
|
-
actionJumpAnchor.current = undefined;
|
|
4300
|
+
props.controller.updatePageContext({ inventorySection: section === 'declared' ? 'extraInstalled' : 'declared' });
|
|
3722
4301
|
setFocus(0);
|
|
3723
4302
|
setScrollOffset(0);
|
|
3724
4303
|
};
|
|
3725
|
-
breadcrumb = '
|
|
3726
|
-
title = showTechnical ? '
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
: inventory?.declarationState === '
|
|
3732
|
-
|
|
4304
|
+
breadcrumb = `插件清单 / ${route.scope === 'user' ? '个人' : '当前项目'}`;
|
|
4305
|
+
title = showTechnical ? '清单安全详情' : route.scope === 'user' ? '个人清单' : '当前项目清单';
|
|
4306
|
+
description = snapshot.scopedInventoryRefresh[route.scope]?.errorCode === 'catalogSessionReloadRequired'
|
|
4307
|
+
? 'Catalog 已变化;以下保留会话结果。请按 H 重新加载,再检查清单。'
|
|
4308
|
+
: snapshot.scopedInventoryRefresh[route.scope]?.state === 'error'
|
|
4309
|
+
? '检查失败;以下显示上次结果,当前状态未确认。' : inventory?.declarationState === 'absent'
|
|
4310
|
+
? '清单尚未添加插件。' : inventory?.declarationState === 'invalid'
|
|
4311
|
+
? '无法确认需求清单;请查看详情并修正。' : !inventory ? '正在检查清单。'
|
|
4312
|
+
: inventory.selectedAgents.length === 0 && inventory.declaredRoots.length > 0
|
|
4313
|
+
? `已有 ${inventory.declaredRoots.length} 项需求;本机工具未设置。`
|
|
4314
|
+
: inventory.coverage?.portable === false
|
|
4315
|
+
? '通用 Skills 额外安装未完整覆盖;不会猜测无法关联的归属。' : undefined;
|
|
3733
4316
|
actions = roots.map((root) => ({
|
|
3734
|
-
actionId: `
|
|
3735
|
-
label: root.displayName,
|
|
4317
|
+
actionId: `inventory-root:${root.pluginId}`, pluginId: root.pluginId, label: root.displayName,
|
|
3736
4318
|
run: () => go({ name: 'pluginDetail', pluginId: root.pluginId }),
|
|
3737
4319
|
}));
|
|
3738
|
-
const
|
|
3739
|
-
const
|
|
3740
|
-
const
|
|
3741
|
-
|
|
3742
|
-
|
|
4320
|
+
const selectedKey = route.scope === 'project' ? `project:${route.projectKey ?? ''}` : 'user';
|
|
4321
|
+
const selectedIds = scopedSelected.current[selectedKey] ?? [];
|
|
4322
|
+
const focusedRoot = roots.find((root) => root.pluginId === actions[effectiveFocus]?.pluginId);
|
|
4323
|
+
if (focusedRoot)
|
|
4324
|
+
scopedActiveRoot.current[selectedKey] = focusedRoot.pluginId;
|
|
4325
|
+
const activeRoot = focusedRoot ?? roots.find((root) => root.pluginId === scopedActiveRoot.current[selectedKey]);
|
|
3743
4326
|
bottomActions = [
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
4327
|
+
{ actionId: 'inventory-sync-all', label: `同步可执行项(${new Set(inventory?.syncTargets.map((target) => target.pluginId)).size})`,
|
|
4328
|
+
disabled: !inventory?.syncTargets.length ? '当前没有可执行目标' : undefined,
|
|
4329
|
+
run: () => {
|
|
4330
|
+
const syncTargets = inventory?.syncTargets ?? [];
|
|
4331
|
+
beginScopedTask({ kind: 'recovery', scope: route.scope,
|
|
4332
|
+
selectors: [...new Set(syncTargets.map((target) => target.pluginId))],
|
|
4333
|
+
targets: [...new Set(syncTargets.map((target) => target.agent))],
|
|
4334
|
+
exactTargets: syncTargets.map(({ pluginId, agent }) => ({ pluginId, agent })) });
|
|
4335
|
+
} },
|
|
4336
|
+
...(selectedIds.length ? [{ actionId: 'inventory-sync-selected', label: `同步所选插件(${selectedIds.length})`,
|
|
4337
|
+
run: () => beginScopedTask({ kind: 'recovery', scope: route.scope, selectors: selectedIds }) }] : []),
|
|
4338
|
+
...(section === 'declared' && activeRoot
|
|
4339
|
+
? [{ actionId: 'inventory-remove-demand', label: '移除此插件需求(保留安装)',
|
|
4340
|
+
run: () => beginScopedTask({ kind: 'intent', action: 'remove', scope: route.scope,
|
|
4341
|
+
selector: activeRoot.pluginId }) }] : []),
|
|
4342
|
+
...(activeRoot && activeRoot.sourceState === 'ready'
|
|
4343
|
+
&& activeRoot.blockers?.some((blocker) => blocker.code === 'pluginUnavailable')
|
|
4344
|
+
? [{ actionId: 'inventory-retire', label: '停用已移除插件(保留设置)',
|
|
4345
|
+
run: () => beginScopedTask({ kind: 'retire', scope: route.scope,
|
|
4346
|
+
selector: activeRoot.pluginId, deleteConfig: false }) },
|
|
4347
|
+
{ actionId: 'inventory-retire-config', label: '停用已移除插件并删除设置',
|
|
4348
|
+
run: () => beginScopedTask({ kind: 'retire', scope: route.scope,
|
|
4349
|
+
selector: activeRoot.pluginId, deleteConfig: true }) }] : []),
|
|
4350
|
+
{ actionId: 'inventory-targets', label: 'Agent 管理', run: () => openAgentManagement() },
|
|
4351
|
+
{ actionId: 'inventory-add', label: '添加插件需求', run: () => {
|
|
4352
|
+
setScope(route.scope);
|
|
4353
|
+
go({ name: 'pluginList', mode: 'browse' });
|
|
4354
|
+
} },
|
|
4355
|
+
{ actionId: 'inventory-switch', label: '切换清单范围', run: () => go({ name: 'intentScopes' }) },
|
|
4356
|
+
{ actionId: 'inventory-recheck', label: '重新检查', run: () => { void props.controller.refreshScopedInventory(route.scope); } },
|
|
3767
4357
|
];
|
|
3768
|
-
|
|
3769
|
-
...props.controller.listProjectInventoryRoots(section).flatMap((root) => [...projectDependencyLines(root), ...root.targets.flatMap((target) => [
|
|
3770
|
-
`${root.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
3771
|
-
`目录参考 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'} · ${stateLabel[target.state]}`,
|
|
3772
|
-
...target.reasonCodes.map((code) => `原因 · ${safeDiagnosticCode(code, 'projectTargetUnknown')}`)
|
|
3773
|
-
])])].filter(Boolean).join('\n');
|
|
3774
|
-
const itemCount = showTechnical ? 0 : actions.length;
|
|
3775
|
-
actions = [...(showTechnical ? [] : actions), ...bottomActions];
|
|
4358
|
+
actions = [...actions, ...bottomActions];
|
|
3776
4359
|
dynamicActionIds = actions.map((action) => action.actionId);
|
|
3777
4360
|
dynamicActionSignature = dynamicActionIds.join('\0');
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
4361
|
+
effectiveFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
4362
|
+
help = '↑↓ 移动 · Enter 详情 · Space 勾选 · Esc 返回 · [a] 操作';
|
|
4363
|
+
const cardWidth = size.columns - 2;
|
|
4364
|
+
const cardSubjects = roots.map((root) => props.controller.getPluginDetailSubject(root.pluginId));
|
|
4365
|
+
const cardLayouts = roots.map((root, index) => projectRootCardLayout(root, cardSubjects[index], cardWidth));
|
|
4366
|
+
const lineLimit = Math.max(1, Math.min(3, pageActionBudget(3) - 4));
|
|
4367
|
+
entityCardNavigation = { itemCount: roots.length, factRows: cardLayouts.map((card) => card.factRows), lineLimit };
|
|
4368
|
+
const viewport = rowViewport({
|
|
4369
|
+
rowHeights: [...cardLayouts.map((card, index) => entityCardWindowHeight(card, index === effectiveFocus ? batchTargetOffset : 0, lineLimit)), ...bottomActions.map(() => 1)],
|
|
4370
|
+
focus: effectiveFocus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(4),
|
|
4371
|
+
});
|
|
4372
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, tabs: { items: [
|
|
4373
|
+
{ id: 'declared', label: '声明插件', count: props.controller.listScopedInventoryRoots(route.scope, 'declared').length },
|
|
4374
|
+
{ id: 'extraInstalled', label: '额外安装', count: props.controller.listScopedInventoryRoots(route.scope, 'extraInstalled').length },
|
|
4375
|
+
], currentId: section }, search: { value: query, placeholder: '名称或来源', input: searchFocus
|
|
4376
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: querySearchWidth(size.columns - 2) }) : undefined }, filter: { label: filterLabels[statusFilter], count: counts[statusFilter],
|
|
4377
|
+
currentZero: counts[statusFilter] === 0 && statusFilter !== 'all' }, selection: selectedIds.length ? { total: selectedIds.length,
|
|
4378
|
+
hidden: selectedIds.filter((id) => !roots.some((root) => root.pluginId === id)).length } : undefined }), _jsx(StaticRow, { title: `本机工具 ${inventory?.selectedAgents.map(agentLabel).join('、') || '未设置'}`, compact: true }), roots.length === 0 ? _jsx(StaticRow, { title: query || statusFilter !== 'all' ? '当前条件无匹配;筛选保持原样。'
|
|
4379
|
+
: inventory?.declarationState === 'absent' ? '清单尚未添加插件。' : '当前分区没有插件。', compact: true }) : null, _jsx(ContentViewport, { children: actions.slice(viewport.offset, viewport.offset + viewport.limit).map((action, visibleIndex) => {
|
|
4380
|
+
const index = viewport.offset + visibleIndex;
|
|
4381
|
+
if (index >= roots.length)
|
|
4382
|
+
return _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : effectiveFocus, kind: "action", compact: true, offset: index, limit: 1 }, action.actionId);
|
|
4383
|
+
return _jsx(ProjectRootCard, { root: roots[index], subject: { ...cardSubjects[index], displayName: `${selectedIds.includes(roots[index].pluginId) ? '✓ ' : ''}${cardSubjects[index].displayName}` }, width: cardWidth, focused: !searchFocus && effectiveFocus === index, lineOffset: effectiveFocus === index ? batchTargetOffset : 0, lineLimit: lineLimit }, action.actionId);
|
|
4384
|
+
}) })] });
|
|
3787
4385
|
if (showTechnical) {
|
|
3788
|
-
readingText =
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: querySearchWidth(size.columns - 2) }) : undefined }, filter: { label: PROJECT_STATUS_LABELS[statusFilter], count: statusCounts[statusFilter],
|
|
3809
|
-
currentZero: statusCounts[statusFilter] === 0 && statusFilter !== 'all' } }), emptyHint
|
|
3810
|
-
? _jsx(Text, { color: tuiTheme.roles.secondary, children: "\u5F53\u524D\u6761\u4EF6\u65E0\u5339\u914D\uFF1B\u641C\u7D22\u548C\u7B5B\u9009\u4FDD\u6301\u539F\u6837\u3002" }) : null, _jsx(ContentViewport, { children: actions.slice(projectViewport.offset, projectViewport.offset + projectViewport.limit)
|
|
3811
|
-
.map((action, visibleIndex) => {
|
|
3812
|
-
const index = projectViewport.offset + visibleIndex;
|
|
3813
|
-
if (index >= itemCount)
|
|
3814
|
-
return _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : effectiveFocus, kind: "action", compact: true, offset: index, limit: 1 }, action.actionId);
|
|
3815
|
-
return _jsx(ProjectRootCard, { root: roots[index], subject: cardSubjects[index], width: cardWidth, focused: !searchFocus && effectiveFocus === index, lineOffset: effectiveFocus === index ? batchTargetOffset : 0, lineLimit: lineLimit }, action.actionId);
|
|
3816
|
-
}) })] });
|
|
4386
|
+
readingText = [
|
|
4387
|
+
`范围:${route.scope === 'user' ? '个人' : '当前项目'}`,
|
|
4388
|
+
`需求清单:${inventory?.declarationState ?? '未检查'}`,
|
|
4389
|
+
`本机偏好:${inventory?.preferenceState ?? '未检查'}`,
|
|
4390
|
+
...(inventory?.coverage?.portable === false ? ['通用 Skills 覆盖:未完整覆盖'] : []),
|
|
4391
|
+
...(inventory?.errors.map((error) => `原因:${safeDiagnosticCode(error.code, 'inventoryUnavailable')}`) ?? []),
|
|
4392
|
+
...roots.flatMap((root) => [root.pluginId,
|
|
4393
|
+
...(root.blockers ?? []).map((reason) => safeDiagnosticCode(reason.code, 'inventoryUnavailable')),
|
|
4394
|
+
...(root.agentResults ?? []).flatMap((target) => [
|
|
4395
|
+
`${target.agent} · ${target.observation} · ${target.content}`,
|
|
4396
|
+
...target.blockers.map((reason) => safeDiagnosticCode(reason.code, 'targetUnavailable')),
|
|
4397
|
+
...(target.reasons ?? []).map((reason) => `${reason.code} · ${reason.message}`),
|
|
4398
|
+
]),
|
|
4399
|
+
]),
|
|
4400
|
+
...(snapshot.scopedInventoryRefresh[route.scope]?.state === 'error'
|
|
4401
|
+
? [`上次快照已过期:${safeDiagnosticCode(snapshot.scopedInventoryRefresh[route.scope]?.errorCode, 'inventoryRefreshFailed')}`] : []),
|
|
4402
|
+
'重新检查只读取当前范围;不会安装或清理。',
|
|
4403
|
+
].join('\n');
|
|
4404
|
+
actions = [{ label: '返回清单', run: toggleTechnical }];
|
|
4405
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, pageActionBudget(3)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
3817
4406
|
}
|
|
3818
4407
|
}
|
|
3819
4408
|
else if (route.name === 'pluginList') {
|
|
@@ -3936,9 +4525,33 @@ export function ConsoleAppV4(props) {
|
|
|
3936
4525
|
description: item.configOnly
|
|
3937
4526
|
? '来源当前没有这个插件;可以查看已保存设置或定点重置设置层。'
|
|
3938
4527
|
: item.description,
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
4528
|
+
placementSummary: (() => {
|
|
4529
|
+
const subject = props.controller.getPluginDetailSubject(item.pluginId);
|
|
4530
|
+
const targets = agentSettings?.preferences.targets ?? [];
|
|
4531
|
+
return targets.filter((target) => !subject?.supportedPlatforms
|
|
4532
|
+
|| subject.supportedPlatforms.includes(target)
|
|
4533
|
+
|| subject.placements.some((entry) => (entry.agent ?? entry.platform) === target)).map((target) => {
|
|
4534
|
+
const excluded = agentSettings?.preferences.exclusions.some((entry) => entry.plugin === item.pluginId && entry.target === target);
|
|
4535
|
+
const facts = subject?.placements.filter((entry) => (entry.agent ?? entry.platform) === target) ?? [];
|
|
4536
|
+
const symbol = excluded ? '⊘' : facts.some((entry) => entry.projectState === 'blocked' || entry.statusState === 'blocked') ? '×'
|
|
4537
|
+
: facts.some((entry) => entry.projectState === 'pending' || entry.statusState === 'needsSync') ? '↻'
|
|
4538
|
+
: facts.length && facts.every((entry) => entry.observation === 'known' && (entry.projectState === 'ready' || entry.statusState === 'ready')) ? '✓' : '?';
|
|
4539
|
+
return `${symbol} ${agentLabel(target)}`;
|
|
4540
|
+
}).join(' ');
|
|
4541
|
+
})(),
|
|
4542
|
+
tags: (() => {
|
|
4543
|
+
const subject = props.controller.getPluginDetailSubject(item.pluginId);
|
|
4544
|
+
const badges = ['user', 'project'].flatMap((placementScope) => {
|
|
4545
|
+
const direct = subject?.declarations[placementScope].state === 'declared';
|
|
4546
|
+
const facts = subject?.placements.filter((entry) => entry.scope === placementScope) ?? [];
|
|
4547
|
+
if (!direct && !facts.length)
|
|
4548
|
+
return [];
|
|
4549
|
+
const ready = facts.length > 0 && facts.every((entry) => entry.observation === 'known'
|
|
4550
|
+
&& (entry.projectState === 'ready' || entry.statusState === 'ready'));
|
|
4551
|
+
return [_jsxs(Text, { backgroundColor: placementScope === 'user' ? '#543e78' : '#234b70', children: [direct ? ready ? '■' : '□' : facts.some((entry) => entry.statusState === 'orphaned') ? '!' : '▣', " ", scopeLabel(placementScope), direct ? ready ? '' : ' 待安装' : facts.some((entry) => entry.statusState === 'orphaned') ? ' 可清理' : ' 依赖'] }, placementScope)];
|
|
4552
|
+
});
|
|
4553
|
+
return badges.length ? _jsx(Text, { children: badges }) : _jsx(Text, { color: tuiTheme.roles.secondary, children: "\u25CB \u5C1A\u672A\u6DFB\u52A0" });
|
|
4554
|
+
})(),
|
|
3942
4555
|
statusLabel: statusText(item),
|
|
3943
4556
|
status: pluginRowStatus(item),
|
|
3944
4557
|
}]),
|
|
@@ -4012,7 +4625,7 @@ export function ConsoleAppV4(props) {
|
|
|
4012
4625
|
...pluginRows[index], width: size.columns - 2,
|
|
4013
4626
|
hasTags: Boolean(pluginRows[index].tags),
|
|
4014
4627
|
}).height : 1),
|
|
4015
|
-
focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(
|
|
4628
|
+
focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(4),
|
|
4016
4629
|
});
|
|
4017
4630
|
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, tabs: { label: '来源', items: sourceTabs, currentId: sourceId }, search: { value: query, placeholder: '名称或用途', input: searchFocus
|
|
4018
4631
|
? _jsx(InputDisplay, { value: query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: querySearchWidth(size.columns - 2) }) : undefined }, filter: { label: pluginFilterLabel(installFilter), count: filterCounts[installFilter],
|
|
@@ -4021,8 +4634,8 @@ export function ConsoleAppV4(props) {
|
|
|
4021
4634
|
: route.mode === 'installed' && items.length === 0 && !query && sourceId === 'all'
|
|
4022
4635
|
? installFilter === 'updateAvailable'
|
|
4023
4636
|
? _jsx(Callout, { title: "\u6CA1\u6709\u53EF\u66F4\u65B0\u63D2\u4EF6", description: "\u5F53\u524D\u7B5B\u9009\u8303\u56F4\u5185\u6CA1\u6709\u7ECF\u8FC7\u7248\u672C\u8BC1\u636E\u786E\u8BA4\u7684\u66F4\u65B0\u3002" })
|
|
4024
|
-
: _jsx(Callout, { title: "\u5F00\u59CB\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u63D2\u4EF6", description: "\
|
|
4025
|
-
: null, _jsxs(ContentViewport, { children: [_jsx(PluginRows, { columns: size.columns - 2, rows: pluginRows, focus: focus, viewport: viewport, active: !searchFocus }), _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : focus, kind: "action", compact: true, offset: Math.max(pluginRows.length, viewport.offset), limit: Math.max(0, viewport.offset + viewport.limit - Math.max(pluginRows.length, viewport.offset)) })] })] }));
|
|
4637
|
+
: _jsx(Callout, { title: "\u5F00\u59CB\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u63D2\u4EF6", description: "\u5148\u9009\u62E9\u672C\u673A\u4F7F\u7528\u7684 Agent\uFF0C\u518D\u6DFB\u52A0\u63D2\u4EF6\u3002" })
|
|
4638
|
+
: null, _jsxs(ContentViewport, { children: [_jsx(PluginRows, { columns: size.columns - 2, rows: pluginRows, focus: focus, viewport: viewport, active: !searchFocus }), _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : focus, kind: "action", compact: true, offset: Math.max(pluginRows.length, viewport.offset), limit: Math.max(0, viewport.offset + viewport.limit - Math.max(pluginRows.length, viewport.offset)) })] }), _jsx(Text, { color: tuiTheme.roles.secondary, children: size.columns < 80 ? '■ 已安装 □ 待安装 ▣ 依赖' : '■ 已安装 □ 待安装 ▣ 依赖 │ ✓ 正常 ↻ 待同步 × 失败 ? 未知 ⊘ 已关闭' })] }));
|
|
4026
4639
|
}
|
|
4027
4640
|
else if (route.name === 'batchSelection') {
|
|
4028
4641
|
breadcrumb = '插件 / 选择多个';
|
|
@@ -4187,6 +4800,21 @@ export function ConsoleAppV4(props) {
|
|
|
4187
4800
|
setActionError('已选插件状态已变化。请返回选择页检查并取消失效项。');
|
|
4188
4801
|
return;
|
|
4189
4802
|
}
|
|
4803
|
+
const subjects = selectedIds.flatMap((id) => props.controller.getPluginDetailSubject(id) ?? []);
|
|
4804
|
+
if (subjects.some((subject) => subject.placements.some((placement) => placement.agent
|
|
4805
|
+
&& placement.agent !== 'codex' && placement.agent !== 'claude' && placement.actual))) {
|
|
4806
|
+
const scopes = ['user', 'project'].filter((candidate) => subjects.some((subject) => subject.placements.some((placement) => placement.scope === candidate && placement.actual)
|
|
4807
|
+
|| subject.declarations[candidate].state === 'declared'));
|
|
4808
|
+
if ((action === 'uninstall' || action === 'cleanup') && scopes.length > 1) {
|
|
4809
|
+
setScopedFlow({ stage: 'recoveryScope', scope, selector: selectedIds[0],
|
|
4810
|
+
selectors: selectedIds, command: action, scopes: [...scopes], focus: 0 });
|
|
4811
|
+
}
|
|
4812
|
+
else
|
|
4813
|
+
beginScopedTask({ kind: 'recovery', command: action,
|
|
4814
|
+
scope: action === 'sync' || action === 'reinstall'
|
|
4815
|
+
? scopes.includes('project') ? 'project' : 'user' : scopes[0], selectors: selectedIds });
|
|
4816
|
+
return;
|
|
4817
|
+
}
|
|
4190
4818
|
const exactTargets = props.controller.listPluginWriteBatchTargets(action, selectedIds);
|
|
4191
4819
|
if (exactTargets.length === 0) {
|
|
4192
4820
|
setActionError('所选插件没有可用的已有安装位置。');
|
|
@@ -4208,11 +4836,14 @@ export function ConsoleAppV4(props) {
|
|
|
4208
4836
|
};
|
|
4209
4837
|
const syncTargets = props.controller.listPluginWriteBatchTargets('sync', selectedIds);
|
|
4210
4838
|
const uninstallTargets = props.controller.listPluginWriteBatchTargets('uninstall', selectedIds);
|
|
4839
|
+
const portableTargetCount = selectedIds.flatMap((id) => props.controller.getPluginDetailSubject(id)?.placements ?? [])
|
|
4840
|
+
.filter((placement) => placement.actual && placement.agent && placement.agent !== 'codex'
|
|
4841
|
+
&& placement.agent !== 'claude').length;
|
|
4211
4842
|
if (batchDraft.stage === 'maintenance') {
|
|
4212
4843
|
actions = [
|
|
4213
|
-
{ label: '同步已保存使用设置', status: 'info', description: `重新应用 ${syncTargets.length} 个已保存或现场目标`, run: () => startExistingBatch('sync') },
|
|
4214
|
-
{ label: '重新安装', status: 'info', description: `重新生成 ${props.controller.listPluginWriteBatchTargets('reinstall', selectedIds).length} 个已保存或现场目标`, run: () => startExistingBatch('reinstall') },
|
|
4215
|
-
{ label: '清理遗留内容', status: 'error', description: `${props.controller.listPluginWriteBatchTargets('cleanup', selectedIds).length} 个已核验位置;保留不属于插件或被共用的内容`, run: () => startExistingBatch('cleanup') },
|
|
4844
|
+
{ label: '同步已保存使用设置', status: 'info', description: `重新应用 ${syncTargets.length + portableTargetCount} 个已保存或现场目标`, run: () => startExistingBatch('sync') },
|
|
4845
|
+
{ label: '重新安装', status: 'info', description: `重新生成 ${props.controller.listPluginWriteBatchTargets('reinstall', selectedIds).length + portableTargetCount} 个已保存或现场目标`, run: () => startExistingBatch('reinstall') },
|
|
4846
|
+
{ label: '清理遗留内容', status: 'error', description: `${props.controller.listPluginWriteBatchTargets('cleanup', selectedIds).length + portableTargetCount} 个已核验位置;保留不属于插件或被共用的内容`, run: () => startExistingBatch('cleanup') },
|
|
4216
4847
|
{
|
|
4217
4848
|
role: 'navigation', label: '返回任务选择',
|
|
4218
4849
|
run: () => moveBatchStage('task'),
|
|
@@ -4242,7 +4873,7 @@ export function ConsoleAppV4(props) {
|
|
|
4242
4873
|
{
|
|
4243
4874
|
label: '安装到缺失位置',
|
|
4244
4875
|
description: `${addTargetCount} 个当前工具和范围内的可安装位置`,
|
|
4245
|
-
disabled:
|
|
4876
|
+
disabled: !agentSettings?.preferences.targets.length ? '请先选择 Agent' : undefined,
|
|
4246
4877
|
run: () => {
|
|
4247
4878
|
setBatchDraft((current) => current ? { ...current, action: 'install' } : current);
|
|
4248
4879
|
props.controller.push({ name: 'batchInstallPlatforms' });
|
|
@@ -4250,7 +4881,9 @@ export function ConsoleAppV4(props) {
|
|
|
4250
4881
|
setScrollOffset(0);
|
|
4251
4882
|
},
|
|
4252
4883
|
},
|
|
4253
|
-
|
|
4884
|
+
...(!agentSettings?.preferences.targets.length
|
|
4885
|
+
? [{ label: '打开 Agent 管理', run: () => openAgentManagement() }] : []),
|
|
4886
|
+
{ label: '卸载所选插件', description: `${uninstallTargets.length + portableTargetCount} 个现有位置;选择范围后确认;保留设置`, status: 'error', run: () => startExistingBatch('uninstall') },
|
|
4254
4887
|
{
|
|
4255
4888
|
label: '其他维护方式',
|
|
4256
4889
|
description: '同步、重新安装和普通清理',
|
|
@@ -4266,63 +4899,21 @@ export function ConsoleAppV4(props) {
|
|
|
4266
4899
|
}
|
|
4267
4900
|
}
|
|
4268
4901
|
else if (route.name === 'batchInstallPlatforms' && batchDraft?.action === 'install') {
|
|
4269
|
-
breadcrumb = '插件 /
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
?
|
|
4275
|
-
:
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
if (next.size === 1) {
|
|
4285
|
-
setActionError('至少选择一个目标平台。');
|
|
4286
|
-
return current;
|
|
4287
|
-
}
|
|
4288
|
-
next.delete(platform);
|
|
4289
|
-
}
|
|
4290
|
-
else
|
|
4291
|
-
next.add(platform);
|
|
4292
|
-
setActionError(undefined);
|
|
4293
|
-
return { ...current, platform: next.size === 2 ? 'both' : [...next][0] };
|
|
4294
|
-
});
|
|
4295
|
-
};
|
|
4296
|
-
const confirmPlatforms = {
|
|
4297
|
-
actionId: 'batch-install-platforms-confirm',
|
|
4298
|
-
label: '确定所选平台 [c]',
|
|
4299
|
-
description: `${[...selectedPlatforms].map(platformLabel).join('、')} · 继续选择目标`,
|
|
4300
|
-
run: () => {
|
|
4301
|
-
props.controller.push({ name: 'batchInstallScope' });
|
|
4302
|
-
setFocus(0);
|
|
4303
|
-
setScrollOffset(0);
|
|
4304
|
-
},
|
|
4305
|
-
};
|
|
4306
|
-
actions = [
|
|
4307
|
-
{
|
|
4308
|
-
actionId: 'batch-install-platform-codex',
|
|
4309
|
-
label: 'Codex',
|
|
4310
|
-
description: '添加到 Codex 的合法缺失位置',
|
|
4311
|
-
selected: selectedPlatforms.has('codex'),
|
|
4312
|
-
run: () => togglePlatform('codex'),
|
|
4313
|
-
},
|
|
4314
|
-
{
|
|
4315
|
-
actionId: 'batch-install-platform-claude',
|
|
4316
|
-
label: 'Claude Code',
|
|
4317
|
-
description: '添加到 Claude Code 的合法缺失位置',
|
|
4318
|
-
selected: selectedPlatforms.has('claude'),
|
|
4319
|
-
run: () => togglePlatform('claude'),
|
|
4320
|
-
},
|
|
4321
|
-
confirmPlatforms,
|
|
4322
|
-
];
|
|
4323
|
-
bottomActions = [confirmPlatforms];
|
|
4324
|
-
help = '↑↓ 移动 · Space/Enter 切换 · c 确定所选平台 · Esc 返回任务选择';
|
|
4325
|
-
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ContentViewport, { children: [_jsx(SemanticRows, { actions: actions.slice(0, 2), focus: focus, kind: "choice", compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: 2 })] }) });
|
|
4902
|
+
breadcrumb = '插件 / 批量添加';
|
|
4903
|
+
title = '选择使用范围';
|
|
4904
|
+
description = `${batchDraft.selectedPluginIds.length} 个插件使用本机已选择的 Agent。`;
|
|
4905
|
+
const configured = Boolean(agentSettings?.preferences.targets.length);
|
|
4906
|
+
actions = (snapshot.projectRoot ? ['user', 'project'] : ['user']).map((selectedScope) => ({
|
|
4907
|
+
label: selectedScope === 'user' ? '■ 所有项目' : '■ 当前项目',
|
|
4908
|
+
disabled: configured ? undefined : '请先选择 Agent',
|
|
4909
|
+
run: () => beginScopedTask({ kind: 'recovery', command: 'install', scope: selectedScope,
|
|
4910
|
+
selectors: batchDraft.selectedPluginIds }),
|
|
4911
|
+
}));
|
|
4912
|
+
if (!configured)
|
|
4913
|
+
actions.push({ label: '打开 Agent 管理', run: () => openAgentManagement() });
|
|
4914
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: true }), _jsxs(Text, { color: tuiTheme.roles.focus, wrap: "wrap", children: ["\u5C06\u5B89\u88C5\u5230 ", agentSettings?.preferences.targets
|
|
4915
|
+
.map((target) => `◆ ${agentLabel(target)}`).join(' ') || '请先选择 Agent'] })] });
|
|
4916
|
+
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4326
4917
|
}
|
|
4327
4918
|
else if (route.name === 'batchInstallScope' || route.name === 'batchPlacement') {
|
|
4328
4919
|
breadcrumb = route.name === 'batchInstallScope'
|
|
@@ -4337,9 +4928,7 @@ export function ConsoleAppV4(props) {
|
|
|
4337
4928
|
: batchDraft?.action ? batchActionLabel(batchDraft.action) : '处理';
|
|
4338
4929
|
title = route.name === 'batchInstallScope' ? '选择安装目标'
|
|
4339
4930
|
: maintenanceMode ? `确认批量${maintenanceLabel}范围`
|
|
4340
|
-
: batchDraft?.
|
|
4341
|
-
? '同步当前项目全部保存目标'
|
|
4342
|
-
: `设置批量${batchDraft?.action ? batchActionLabel(batchDraft.action) : ''}位置`;
|
|
4931
|
+
: `设置批量${batchDraft?.action ? batchActionLabel(batchDraft.action) : ''}位置`;
|
|
4343
4932
|
description = route.name === 'batchInstallScope'
|
|
4344
4933
|
? '选择范围后检查可添加、已有和受阻的精确位置。操作汇总之后才执行。'
|
|
4345
4934
|
: maintenanceMode
|
|
@@ -4409,7 +4998,6 @@ export function ConsoleAppV4(props) {
|
|
|
4409
4998
|
bottomActions = [confirmMaintenance];
|
|
4410
4999
|
}
|
|
4411
5000
|
else {
|
|
4412
|
-
const projectIntent = batchDraft.selection === 'currentProjectIntentRoots';
|
|
4413
5001
|
installPlacementTargets = batchDraft.action === 'install'
|
|
4414
5002
|
&& batchDraft.selection === 'selected'
|
|
4415
5003
|
? props.controller.listPluginInstallBatchTargets(batchDraft.selectedPluginIds, batchDraft.platform, batchDraft.scope)
|
|
@@ -4430,7 +5018,6 @@ export function ConsoleAppV4(props) {
|
|
|
4430
5018
|
{
|
|
4431
5019
|
actionId: 'batch-placement-scope-user',
|
|
4432
5020
|
label: '所有项目', selected: batchDraft.scope === 'user',
|
|
4433
|
-
disabled: projectIntent ? '项目全部同步固定使用当前项目' : undefined,
|
|
4434
5021
|
run: () => setBatchDraft((current) => current ? { ...current, scope: 'user' } : current),
|
|
4435
5022
|
},
|
|
4436
5023
|
],
|
|
@@ -4455,7 +5042,7 @@ export function ConsoleAppV4(props) {
|
|
|
4455
5042
|
: `查看批量${batchActionLabel(batchDraft.action)}确认`,
|
|
4456
5043
|
description: batchDraft.action === 'install'
|
|
4457
5044
|
? `${addTargets.length} 个可添加 · ${existingTargets.length} 个已有 · ${blockedTargets.length} 个受阻`
|
|
4458
|
-
: `${batchDraft.
|
|
5045
|
+
: `${batchDraft.selectedPluginIds.length} 个所选插件 · ${scopeLabel(batchDraft.scope)}`,
|
|
4459
5046
|
disabled: batchDraft.selection === 'selected' && batchDraft.selectedPluginIds.length === 0
|
|
4460
5047
|
? '请至少选择一个插件'
|
|
4461
5048
|
: batchDraft.action === 'install' && addTargets.length === 0
|
|
@@ -4473,13 +5060,11 @@ export function ConsoleAppV4(props) {
|
|
|
4473
5060
|
? { kind: 'targets', targets: addTargets.map(({ pluginId, platform, scope }) => ({
|
|
4474
5061
|
pluginId, platform, scope,
|
|
4475
5062
|
})) }
|
|
4476
|
-
:
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
}
|
|
4482
|
-
: { kind: 'currentProjectIntentRoots' },
|
|
5063
|
+
: {
|
|
5064
|
+
kind: 'selected',
|
|
5065
|
+
pluginIds: [...new Set(batchDraft.selectedPluginIds)]
|
|
5066
|
+
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
5067
|
+
},
|
|
4483
5068
|
platform: addTargets[0]?.platform ?? batchDraft.platform,
|
|
4484
5069
|
scope: addTargets[0]?.scope ?? batchDraft.scope,
|
|
4485
5070
|
};
|
|
@@ -4667,7 +5252,7 @@ export function ConsoleAppV4(props) {
|
|
|
4667
5252
|
title = '输入 Agent ID';
|
|
4668
5253
|
description = '多个 Agent 可使用逗号或空格分隔。';
|
|
4669
5254
|
help = '输入 Agent ID · Enter 保存 · Ctrl-U 清空输入 · Esc 返回';
|
|
4670
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: portableAgentInput, columns: Math.max(8, size.columns - 8) }), description: inputError ?? 'Enter 保存并继续 · Esc 返回;q 和 b 会作为输入。', status: inputError ? 'error' : undefined }), _jsx(StaticRow, { title: "\u793A\u4F8B", meta: "cursor,
|
|
5255
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: portableAgentInput, columns: Math.max(8, size.columns - 8) }), description: inputError ?? 'Enter 保存并继续 · Esc 返回;q 和 b 会作为输入。', status: inputError ? 'error' : undefined }), _jsx(StaticRow, { title: "\u793A\u4F8B", meta: "cursor, opencode, generic", status: "muted", compact: true })] });
|
|
4671
5256
|
}
|
|
4672
5257
|
else if (route.name === 'portableScope' && plugin && snapshot.portableDraft) {
|
|
4673
5258
|
breadcrumb = `${plugin.displayName} / 通用 Skills / 范围`;
|
|
@@ -4700,7 +5285,7 @@ export function ConsoleAppV4(props) {
|
|
|
4700
5285
|
{ label: '返回问题摘要', run: () => toggleTechnical() },
|
|
4701
5286
|
] : [
|
|
4702
5287
|
...(route.code === 'requiredConfigMissing' ? [{ label: '完成插件设置', run: () => {
|
|
4703
|
-
props.controller.replace({ name: 'configFields', pluginId: plugin.pluginId, layer: {
|
|
5288
|
+
props.controller.replace({ name: 'configFields', pluginId: plugin.pluginId, layer: { profile: 'common' } });
|
|
4704
5289
|
setFocus(0);
|
|
4705
5290
|
setScrollOffset(0);
|
|
4706
5291
|
} }] : []),
|
|
@@ -4822,207 +5407,24 @@ export function ConsoleAppV4(props) {
|
|
|
4822
5407
|
content = _jsx(Box, { flexDirection: "column", children: readingPage(readingText, size.columns - 2, pageActionBudget(2)) });
|
|
4823
5408
|
help = '←→/PgUp/PgDn 翻页 · y 复制全文 · Esc 返回';
|
|
4824
5409
|
}
|
|
4825
|
-
else if (route.name === 'installTargets' && plugin) {
|
|
4826
|
-
breadcrumb = `${plugin.displayName} /
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
description = '
|
|
4830
|
-
const
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
const selectedNativeTargets = snapshot.installPlacementDraft?.pluginId === plugin.pluginId
|
|
4844
|
-
? snapshot.installPlacementDraft.platforms
|
|
4845
|
-
: selectedTargets;
|
|
4846
|
-
const validSelectedTargets = selectedNativeTargets.filter((platform) => choices.some((choice) => choice.platform === platform && choice.compatible));
|
|
4847
|
-
const allUnavailable = choices.every((choice) => !choice.compatible) && !plugin.portableSkills;
|
|
4848
|
-
if (route.view === 'blocked' || allUnavailable) {
|
|
4849
|
-
context = '依赖限制';
|
|
4850
|
-
title = '无法添加到所选目标平台';
|
|
4851
|
-
description = '根插件或必需依赖不支持当前目标平台组合,因此不会进入确认页。';
|
|
4852
|
-
actions = [
|
|
4853
|
-
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
|
|
4854
|
-
{ role: 'navigation', label: '返回插件详情', run: back },
|
|
4855
|
-
];
|
|
4856
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "warning", title: "\u6240\u9009\u76EE\u6807\u5E73\u53F0\u4E0D\u53EF\u7528", children: choices.map((choice) => _jsx(StaticRow, { title: `${platformLabel(choice.platform)} · ${choice.disabled ? `不可选 · ${choice.disabled}` : '不可选'}`, status: "muted", compact: policy.mode === 'compact' }, choice.platform)) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4857
|
-
}
|
|
4858
|
-
else {
|
|
4859
|
-
const confirmNativePlatforms = () => {
|
|
4860
|
-
if (validSelectedTargets.length === 0) {
|
|
4861
|
-
setActionError('请至少选择一个目标平台。');
|
|
4862
|
-
return;
|
|
4863
|
-
}
|
|
4864
|
-
setDirty(false);
|
|
4865
|
-
props.controller.setInstallPlatforms(validSelectedTargets);
|
|
4866
|
-
setInstallPlatformIndex(0);
|
|
4867
|
-
go({ name: 'installPlacement', pluginId: plugin.pluginId });
|
|
4868
|
-
};
|
|
4869
|
-
const nativeActions = choices.map((choice) => {
|
|
4870
|
-
const installed = installedScopeStates(plugin, choice.platform);
|
|
4871
|
-
const configuredScopes = plugin.supportedScopes?.[choice.platform];
|
|
4872
|
-
const supportedScopes = ['project', 'user'].filter((candidateScope) => configuredScopes?.includes(candidateScope)
|
|
4873
|
-
|| installed.has(candidateScope) || choice.scopes.includes(candidateScope));
|
|
4874
|
-
const locationItems = supportedScopes.map((candidateScope) => {
|
|
4875
|
-
const state = installed.get(candidateScope);
|
|
4876
|
-
if (state)
|
|
4877
|
-
return {
|
|
4878
|
-
key: `${choice.platform}:${candidateScope}`,
|
|
4879
|
-
label: scopeLabel(candidateScope),
|
|
4880
|
-
symbol: state.symbol,
|
|
4881
|
-
status: state.status,
|
|
4882
|
-
};
|
|
4883
|
-
return choice.scopes.includes(candidateScope)
|
|
4884
|
-
? { key: `${choice.platform}:${candidateScope}`, label: scopeLabel(candidateScope),
|
|
4885
|
-
symbol: '○', status: 'muted' }
|
|
4886
|
-
: { key: `${choice.platform}:${candidateScope}`, label: scopeLabel(candidateScope),
|
|
4887
|
-
symbol: '?', status: 'muted' };
|
|
4888
|
-
});
|
|
4889
|
-
const missingLabels = choice.scopes.filter((candidateScope) => !installed.has(candidateScope))
|
|
4890
|
-
.map(scopeLabel);
|
|
4891
|
-
return {
|
|
4892
|
-
actionId: `install-platform-${choice.platform}`,
|
|
4893
|
-
label: platformLabel(choice.platform),
|
|
4894
|
-
selected: validSelectedTargets.includes(choice.platform),
|
|
4895
|
-
description: choice.disabled ? `不可选择:${choice.disabled}` : (missingLabels.length > 0
|
|
4896
|
-
? `可添加到:${missingLabels.join('、')}` : '所有支持位置已经安装'),
|
|
4897
|
-
status: choice.disabled ? 'muted' : undefined,
|
|
4898
|
-
tags: locationItems.length > 0 ? _jsx(StatusBadge, { items: locationItems }) : undefined,
|
|
4899
|
-
run: () => {
|
|
4900
|
-
if (!choice.compatible)
|
|
4901
|
-
return;
|
|
4902
|
-
setActionError(undefined);
|
|
4903
|
-
setSelectedTargets((current) => {
|
|
4904
|
-
const next = current.includes(choice.platform)
|
|
4905
|
-
? current.filter((platform) => platform !== choice.platform)
|
|
4906
|
-
: [...current, choice.platform];
|
|
4907
|
-
props.controller.setInstallPlatforms(next);
|
|
4908
|
-
return next;
|
|
4909
|
-
});
|
|
4910
|
-
},
|
|
4911
|
-
};
|
|
4912
|
-
});
|
|
4913
|
-
const portableAction = {
|
|
4914
|
-
actionId: 'portable-agent-skills',
|
|
4915
|
-
label: '通用 Agent Skills →',
|
|
4916
|
-
description: plugin.portableSkills
|
|
4917
|
-
? '通过外部 Skills CLI 安装到支持的 Agent;只安装选定 Skills'
|
|
4918
|
-
: '这个插件不提供通用 Agent Skills',
|
|
4919
|
-
status: plugin.portableSkills ? undefined : 'muted',
|
|
4920
|
-
tags: plugin.portableSkills
|
|
4921
|
-
? _jsx(Text, { color: tuiTheme.colors.accent, children: "[Skills \u53EF\u9009]" })
|
|
4922
|
-
: undefined,
|
|
4923
|
-
run: () => {
|
|
4924
|
-
if (!plugin.portableSkills)
|
|
4925
|
-
return;
|
|
4926
|
-
beginPortable(plugin.pluginId);
|
|
4927
|
-
},
|
|
4928
|
-
};
|
|
4929
|
-
const confirmAction = {
|
|
4930
|
-
actionId: 'confirm-install-platforms',
|
|
4931
|
-
label: '确定所选平台 [c]',
|
|
4932
|
-
disabled: validSelectedTargets.length === 0 ? '至少选择一个目标平台' : undefined,
|
|
4933
|
-
run: confirmNativePlatforms,
|
|
4934
|
-
};
|
|
4935
|
-
actions = [...nativeActions, portableAction, confirmAction];
|
|
4936
|
-
bottomActions = [confirmAction];
|
|
4937
|
-
help = '↑↓ 移动 · Enter/Space 选择 · Esc 返回';
|
|
4938
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions.slice(0, nativeActions.length).map((action) => ({
|
|
4939
|
-
...action, label: `${action.selected ? '[x]' : '[ ]'} ${action.label}`,
|
|
4940
|
-
})), focus: focus, kind: "action", compact: false }), _jsx(SemanticRows, { actions: actions, focus: focus, offset: nativeActions.length, kind: "action", compact: false })] });
|
|
4941
|
-
}
|
|
4942
|
-
}
|
|
4943
|
-
else if (route.name === 'installPlacement' && plugin) {
|
|
4944
|
-
breadcrumb = `${plugin.displayName} / 添加 / 使用范围`;
|
|
4945
|
-
context = '第 2 步,共 2 步';
|
|
4946
|
-
title = `你希望在哪里使用 ${plugin.displayName}?`;
|
|
4947
|
-
const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
|
|
4948
|
-
const selectedPlatforms = snapshot.installPlacementDraft?.platforms ?? selectedTargets;
|
|
4949
|
-
const legalScopes = (platform) => ['project', 'user'].filter((candidateScope) => props.controller.listInstallTargets(plugin.pluginId, candidateScope)
|
|
4950
|
-
.some((entry) => entry.platform === platform && entry.compatible));
|
|
4951
|
-
const commonScopes = ['project', 'user'].filter((candidateScope) => selectedPlatforms.every((platform) => legalScopes(platform).includes(candidateScope)));
|
|
4952
|
-
const continueCommonScope = async (candidateScope) => {
|
|
4953
|
-
setScope(candidateScope);
|
|
4954
|
-
props.controller.setInstallRootScope(candidateScope);
|
|
4955
|
-
props.controller.setInstallPlatforms(selectedPlatforms);
|
|
4956
|
-
if (hasDependencies) {
|
|
4957
|
-
const configurable = await props.controller.prepareInstallDependencyPlacement();
|
|
4958
|
-
if (configurable) {
|
|
4959
|
-
go({ name: 'dependencyPlacement', pluginId: plugin.pluginId });
|
|
4960
|
-
return;
|
|
4961
|
-
}
|
|
4962
|
-
}
|
|
4963
|
-
beginReview(props.controller.installPlacementIntent());
|
|
4964
|
-
};
|
|
4965
|
-
if (commonScopes.length > 0) {
|
|
4966
|
-
description = '所选平台使用同一个范围。操作汇总会显示实际目标和依赖计划。';
|
|
4967
|
-
actions = commonScopes.map((candidateScope) => ({
|
|
4968
|
-
label: scopeLabel(candidateScope),
|
|
4969
|
-
description: candidateScope === 'user'
|
|
4970
|
-
? '在任意项目中可用,会改变你的用户环境'
|
|
4971
|
-
: '仅供当前项目使用,适合随项目共享',
|
|
4972
|
-
meta: candidateScope === 'project' ? '推荐' : undefined,
|
|
4973
|
-
run: () => continueCommonScope(candidateScope),
|
|
4974
|
-
}));
|
|
4975
|
-
}
|
|
4976
|
-
else {
|
|
4977
|
-
description = '所选平台没有共同范围。请分别确定每个平台的合法位置。';
|
|
4978
|
-
const platformIndex = Math.min(installPlatformIndex, Math.max(0, selectedPlatforms.length - 1));
|
|
4979
|
-
const platform = selectedPlatforms[platformIndex];
|
|
4980
|
-
title = platform ? `${platformLabel(platform)} 的使用范围` : '请返回选择目标平台';
|
|
4981
|
-
context = `平台 ${platformIndex + 1} / ${selectedPlatforms.length}`;
|
|
4982
|
-
if (platformIndex > 0)
|
|
4983
|
-
returnToPreviousInstallPlatform = () => {
|
|
4984
|
-
setInstallPlatformIndex(platformIndex - 1);
|
|
4985
|
-
setFocus(0);
|
|
4986
|
-
setScrollOffset(0);
|
|
4987
|
-
};
|
|
4988
|
-
actions = platform ? legalScopes(platform).map((candidateScope) => ({
|
|
4989
|
-
label: scopeLabel(candidateScope),
|
|
4990
|
-
selected: installPlatformScopes[platform] === candidateScope,
|
|
4991
|
-
description: platformIndex + 1 < selectedPlatforms.length
|
|
4992
|
-
? `下一步:${platformLabel(selectedPlatforms[platformIndex + 1])} 的使用范围`
|
|
4993
|
-
: '下一步:查看这些精确目标的操作汇总',
|
|
4994
|
-
run: async () => {
|
|
4995
|
-
const nextScopes = { ...installPlatformScopes, [platform]: candidateScope };
|
|
4996
|
-
setInstallPlatformScopes(nextScopes);
|
|
4997
|
-
if (platformIndex + 1 < selectedPlatforms.length) {
|
|
4998
|
-
setInstallPlatformIndex(platformIndex + 1);
|
|
4999
|
-
setFocus(0);
|
|
5000
|
-
setScrollOffset(0);
|
|
5001
|
-
return;
|
|
5002
|
-
}
|
|
5003
|
-
const exactTargets = selectedPlatforms.flatMap((selectedPlatform) => {
|
|
5004
|
-
const selectedScope = nextScopes[selectedPlatform];
|
|
5005
|
-
return selectedScope && legalScopes(selectedPlatform).includes(selectedScope)
|
|
5006
|
-
? [{ pluginId: plugin.pluginId, platform: selectedPlatform, scope: selectedScope }] : [];
|
|
5007
|
-
});
|
|
5008
|
-
if (exactTargets.length !== selectedPlatforms.length) {
|
|
5009
|
-
setInstallPlatformIndex(0);
|
|
5010
|
-
setFocus(0);
|
|
5011
|
-
setScrollOffset(0);
|
|
5012
|
-
setActionError('合法位置已变化。请重新确定每个平台的范围。');
|
|
5013
|
-
return;
|
|
5014
|
-
}
|
|
5015
|
-
props.controller.setInstallRootTargets(exactTargets);
|
|
5016
|
-
if (hasDependencies && await props.controller.prepareInstallDependencyPlacement()) {
|
|
5017
|
-
go({ name: 'dependencyPlacement', pluginId: plugin.pluginId });
|
|
5018
|
-
return;
|
|
5019
|
-
}
|
|
5020
|
-
beginReview(props.controller.installPlacementIntent());
|
|
5021
|
-
},
|
|
5022
|
-
})) : [];
|
|
5023
|
-
}
|
|
5024
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' }), hasDependencies ? _jsx(Callout, { title: "\u4F9D\u8D56\u8BA1\u5212", description: "\u6240\u9700\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u6392\uFF1B\u786E\u8BA4\u524D\u53EF\u67E5\u770B\u5F71\u54CD\u3002" }) : null] });
|
|
5025
|
-
help = '↑↓ 移动 · Enter 选择/继续 · Esc 返回';
|
|
5410
|
+
else if ((route.name === 'installTargets' || route.name === 'installPlacement') && plugin) {
|
|
5411
|
+
breadcrumb = `${plugin.displayName} / 添加插件`;
|
|
5412
|
+
title = '选择使用范围';
|
|
5413
|
+
context = undefined;
|
|
5414
|
+
description = '插件会使用本机已选择的 Agent。';
|
|
5415
|
+
const configured = Boolean(agentSettings?.preferences.targets.length);
|
|
5416
|
+
actions = (snapshot.projectRoot ? ['user', 'project'] : ['user']).map((selectedScope) => ({
|
|
5417
|
+
label: selectedScope === 'user' ? '■ 所有项目' : `■ 当前项目 · ${path.basename(snapshot.projectRoot ?? '')}`,
|
|
5418
|
+
description: selectedScope === 'user' ? '在你的每个项目中都能使用' : '记录到当前项目,与团队共享',
|
|
5419
|
+
disabled: configured ? undefined : '请先选择 Agent',
|
|
5420
|
+
run: () => beginScopedTask({ kind: 'recovery', command: 'install', scope: selectedScope,
|
|
5421
|
+
selectors: [plugin.pluginId] }),
|
|
5422
|
+
}));
|
|
5423
|
+
if (!configured)
|
|
5424
|
+
actions.push({ label: '打开 Agent 管理', run: () => openAgentManagement() });
|
|
5425
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: true }), _jsxs(Text, { color: tuiTheme.roles.focus, wrap: "wrap", children: ["\u5C06\u5B89\u88C5\u5230 ", agentSettings?.preferences.targets
|
|
5426
|
+
.map((target) => `◆ ${agentLabel(target)}`).join(' ') || '请先选择 Agent'] })] });
|
|
5427
|
+
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
5026
5428
|
}
|
|
5027
5429
|
else if (route.name === 'uninstallTargets' && plugin) {
|
|
5028
5430
|
breadcrumb = `${plugin.displayName} / 卸载`;
|
|
@@ -5216,7 +5618,7 @@ export function ConsoleAppV4(props) {
|
|
|
5216
5618
|
props.controller.replace({
|
|
5217
5619
|
name: 'configFields',
|
|
5218
5620
|
pluginId: target.pluginId,
|
|
5219
|
-
layer: {
|
|
5621
|
+
layer: { profile: 'common' },
|
|
5220
5622
|
});
|
|
5221
5623
|
},
|
|
5222
5624
|
}]];
|
|
@@ -5306,19 +5708,25 @@ export function ConsoleAppV4(props) {
|
|
|
5306
5708
|
}]];
|
|
5307
5709
|
})).values()];
|
|
5308
5710
|
const taskLabel = pluginWriteTaskLabel(route.goal);
|
|
5309
|
-
const
|
|
5711
|
+
const mixedScopes = new Set(candidates.map((candidate) => candidate.target.scope)).size > 1;
|
|
5712
|
+
const startTaskReview = (executionPolicy, selectedScope) => {
|
|
5310
5713
|
if (targetSetChanged) {
|
|
5311
5714
|
setActionError('本次安装位置已变化。请返回调整后重新开始。');
|
|
5312
5715
|
return;
|
|
5313
5716
|
}
|
|
5314
|
-
|
|
5717
|
+
if (selectedScope) {
|
|
5718
|
+
beginReview({ kind: 'pluginWriteTask', goal: route.goal,
|
|
5719
|
+
targets: candidates.filter((candidate) => candidate.target.scope === selectedScope)
|
|
5720
|
+
.map((candidate) => candidate.target), executionPolicy }, props.controller.pluginWriteTaskReturnRoute(route));
|
|
5721
|
+
return;
|
|
5722
|
+
}
|
|
5723
|
+
const intent = {
|
|
5315
5724
|
kind: 'pluginWriteTask',
|
|
5316
5725
|
goal: route.goal,
|
|
5317
5726
|
targets: candidates.map((candidate) => candidate.target),
|
|
5318
5727
|
executionPolicy,
|
|
5319
|
-
}
|
|
5320
|
-
|
|
5321
|
-
setScrollOffset(0);
|
|
5728
|
+
};
|
|
5729
|
+
beginReview(intent, props.controller.pluginWriteTaskReturnRoute(route));
|
|
5322
5730
|
};
|
|
5323
5731
|
breadcrumb = taskPluginIds.length > 1
|
|
5324
5732
|
? `我的插件 / ${taskLabel} ${taskPluginIds.length} 个插件`
|
|
@@ -5334,14 +5742,21 @@ export function ConsoleAppV4(props) {
|
|
|
5334
5742
|
? 'PgUp/PgDn 查看全部位置 · ↑↓ 移动 · Enter 选择 · Esc 返回'
|
|
5335
5743
|
: '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
5336
5744
|
actions = [
|
|
5337
|
-
...(!targetSetChanged && !versionEvidenceChanged
|
|
5745
|
+
...(!targetSetChanged && !versionEvidenceChanged && mixedScopes
|
|
5746
|
+
? ['user', 'project'].filter((selectedScope) => executable.some((candidate) => candidate.target.scope === selectedScope)).map((selectedScope) => ({
|
|
5747
|
+
label: `先处理${scopeLabel(selectedScope)} ${executable.filter((candidate) => candidate.target.scope === selectedScope).length} 个位置`,
|
|
5748
|
+
description: `完整请求 ${candidates.length} 个位置;另一范围留待重新准备和确认`,
|
|
5749
|
+
status: 'warning',
|
|
5750
|
+
run: () => startTaskReview(blockedCandidates.length > 0 ? 'executableSubset' : 'all', selectedScope),
|
|
5751
|
+
})) : []),
|
|
5752
|
+
...(!mixedScopes && !targetSetChanged && !versionEvidenceChanged
|
|
5338
5753
|
&& blockedCandidates.length === 0 && executable.length > 0 ? [{
|
|
5339
5754
|
label: `确认${taskLabel} ${executable.length} 个位置`,
|
|
5340
5755
|
description: '进入统一影响确认;执行前再次检查当前证据',
|
|
5341
5756
|
status: 'success',
|
|
5342
5757
|
run: () => startTaskReview('all'),
|
|
5343
5758
|
}] : []),
|
|
5344
|
-
...(!targetSetChanged && !versionEvidenceChanged
|
|
5759
|
+
...(!mixedScopes && !targetSetChanged && !versionEvidenceChanged
|
|
5345
5760
|
&& blockedCandidates.length > 0 && executable.length > 0 ? [{
|
|
5346
5761
|
label: `先处理可执行的 ${executable.length} 个位置`,
|
|
5347
5762
|
description: `${blockedCandidates.length} 个受阻位置保留,不会无声跳过`,
|
|
@@ -5405,7 +5820,6 @@ export function ConsoleAppV4(props) {
|
|
|
5405
5820
|
description = undefined;
|
|
5406
5821
|
context = '插件详情';
|
|
5407
5822
|
const issues = props.controller.listUserIssues().filter((entry) => issueMatchesTarget(entry, detailSubject.pluginId));
|
|
5408
|
-
const root = props.controller.getProjectInventoryRoot(detailSubject.pluginId);
|
|
5409
5823
|
actions = [
|
|
5410
5824
|
...(issues.length > 0 ? [{ actionId: 'exceptional', label: '查看处理建议',
|
|
5411
5825
|
status: 'warning',
|
|
@@ -5417,8 +5831,6 @@ export function ConsoleAppV4(props) {
|
|
|
5417
5831
|
label: '依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: detailSubject.pluginId }) }] : []),
|
|
5418
5832
|
...(props.controller.getSourceOverview(detailSubject.sourceId).source ? [{ actionId: 'source',
|
|
5419
5833
|
label: '查看插件来源', run: () => go({ name: 'sourceDetail', sourceId: detailSubject.sourceId }) }] : []),
|
|
5420
|
-
...(root && root.state !== 'ready' && issues.length === 0 ? [{ actionId: 'project-recheck',
|
|
5421
|
-
label: '重新检查项目', run: async () => { await props.controller.refreshProjectInventory(); } }] : []),
|
|
5422
5834
|
];
|
|
5423
5835
|
content = detailCardContent(detailSubject);
|
|
5424
5836
|
}
|
|
@@ -5503,7 +5915,9 @@ export function ConsoleAppV4(props) {
|
|
|
5503
5915
|
? go({ name: 'issueList' })
|
|
5504
5916
|
: go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
5505
5917
|
} : undefined;
|
|
5506
|
-
const
|
|
5918
|
+
const hasPortablePlacement = detailSubject.placements.some((placement) => placement.agent
|
|
5919
|
+
&& placement.agent !== 'codex' && placement.agent !== 'claude' && placement.actual);
|
|
5920
|
+
const installed = hasInstalledPlacement(plugin) || hasPortablePlacement;
|
|
5507
5921
|
const hasInstallableTarget = props.controller.pluginHasInstallableTarget(plugin.pluginId);
|
|
5508
5922
|
const hasInstallationOption = hasInstallableTarget || plugin.portableSkills;
|
|
5509
5923
|
const beginInstall = () => {
|
|
@@ -5521,7 +5935,7 @@ export function ConsoleAppV4(props) {
|
|
|
5521
5935
|
};
|
|
5522
5936
|
const installAction = !plugin.catalogEntryMissing ? {
|
|
5523
5937
|
actionId: 'install',
|
|
5524
|
-
label: installed ? '
|
|
5938
|
+
label: installed ? '添加到其他位置…' : '添加插件…',
|
|
5525
5939
|
description: plugin.sourceUnavailable
|
|
5526
5940
|
? '插件来源不可用'
|
|
5527
5941
|
: plugin.capabilityUnsupported
|
|
@@ -5529,8 +5943,8 @@ export function ConsoleAppV4(props) {
|
|
|
5529
5943
|
: !hasInstallationOption
|
|
5530
5944
|
? '当前工具和插件没有可用的安装方式'
|
|
5531
5945
|
: '选择工具和使用范围',
|
|
5532
|
-
...(plugin.sourceUnavailable || plugin.capabilityUnsupported || !hasInstallationOption
|
|
5533
|
-
? { disabled: plugin.sourceUnavailable ? '插件来源不可用'
|
|
5946
|
+
...(!agentSettings?.preferences.targets.length || plugin.sourceUnavailable || plugin.capabilityUnsupported || !hasInstallationOption
|
|
5947
|
+
? { disabled: !agentSettings?.preferences.targets.length ? '请先选择 Agent' : plugin.sourceUnavailable ? '插件来源不可用'
|
|
5534
5948
|
: plugin.capabilityUnsupported ? '需要升级 AW CLI' : '没有可用的安装方式' }
|
|
5535
5949
|
: { run: beginInstall }),
|
|
5536
5950
|
} : undefined;
|
|
@@ -5565,6 +5979,17 @@ export function ConsoleAppV4(props) {
|
|
|
5565
5979
|
: '选择一个现有平台和实际安装位置',
|
|
5566
5980
|
status: 'error',
|
|
5567
5981
|
run: () => {
|
|
5982
|
+
if (hasPortablePlacement) {
|
|
5983
|
+
const scopes = ['user', 'project'].filter((candidate) => detailSubject.placements.some((placement) => placement.scope === candidate && placement.actual)
|
|
5984
|
+
|| detailSubject.declarations[candidate].state === 'declared');
|
|
5985
|
+
if (scopes.length === 1)
|
|
5986
|
+
beginScopedTask({ kind: 'recovery', command: 'uninstall',
|
|
5987
|
+
scope: scopes[0], selectors: [plugin.pluginId] });
|
|
5988
|
+
else
|
|
5989
|
+
setScopedFlow({ stage: 'recoveryScope', scope, selector: plugin.pluginId,
|
|
5990
|
+
command: 'uninstall', scopes: [...scopes], focus: 0 });
|
|
5991
|
+
return;
|
|
5992
|
+
}
|
|
5568
5993
|
const targets = props.controller.listPluginWriteBatchTargets('uninstall', [plugin.pluginId]);
|
|
5569
5994
|
if (targets.length === 1) {
|
|
5570
5995
|
const target = targets[0];
|
|
@@ -5595,9 +6020,36 @@ export function ConsoleAppV4(props) {
|
|
|
5595
6020
|
actions = dedupeActions([
|
|
5596
6021
|
...(exceptionalAction ? [{ ...exceptionalAction, actionId: 'exceptional' }] : []),
|
|
5597
6022
|
...(restoreAction ? [{ ...restoreAction, actionId: 'restore' }] : []),
|
|
6023
|
+
...(hasPortablePlacement && !restoreAction ? [{ actionId: 'sync', label: '同步并修复…',
|
|
6024
|
+
run: () => beginScopedTask({ kind: 'recovery', command: 'sync',
|
|
6025
|
+
scope: detailSubject.placements.some((placement) => placement.scope === 'project')
|
|
6026
|
+
|| detailSubject.declarations.project.state === 'declared' ? 'project' : 'user',
|
|
6027
|
+
selectors: [plugin.pluginId] }) }] : []),
|
|
5598
6028
|
...(updateAction ? [{ ...updateAction, actionId: 'update' }] : []),
|
|
5599
6029
|
...(installAction ? [installAction] : []),
|
|
6030
|
+
...(!agentSettings?.preferences.targets.length ? [{ actionId: 'choose-agents', label: '打开 Agent 管理', run: () => openAgentManagement() }] : []),
|
|
6031
|
+
...(!plugin.catalogEntryMissing && !plugin.sourceUnavailable ? [{ actionId: 'add-demand',
|
|
6032
|
+
label: '加入插件清单(暂不安装)',
|
|
6033
|
+
run: () => snapshot.projectRoot
|
|
6034
|
+
? setScopedFlow({ stage: 'intentScope', scope, selector: plugin.pluginId, action: 'add',
|
|
6035
|
+
scopes: ['user', 'project'],
|
|
6036
|
+
focus: scope === 'project' ? 1 : 0 })
|
|
6037
|
+
: beginScopedTask({ kind: 'intent', action: 'add', scope: 'user',
|
|
6038
|
+
selector: plugin.pluginId }) }] : []),
|
|
6039
|
+
...(['user', 'project'].some((declaredScope) => detailSubject?.declarations[declaredScope].state === 'declared')
|
|
6040
|
+
? [{ actionId: 'remove-demand', label: '移出插件清单(保留安装)', run: () => {
|
|
6041
|
+
const scopes = ['user', 'project'].filter((candidate) => detailSubject.declarations[candidate].state === 'declared');
|
|
6042
|
+
if (scopes.length === 1)
|
|
6043
|
+
beginScopedTask({ kind: 'intent', action: 'remove',
|
|
6044
|
+
scope: scopes[0], selector: plugin.pluginId });
|
|
6045
|
+
else
|
|
6046
|
+
setScopedFlow({ stage: 'intentScope', scope, selector: plugin.pluginId,
|
|
6047
|
+
action: 'remove', scopes: [...scopes], focus: 0 });
|
|
6048
|
+
} }] : []),
|
|
5600
6049
|
...(settingsAction ? [{ ...settingsAction, actionId: 'settings' }] : []),
|
|
6050
|
+
...(agentSettings?.plugins.some((entry) => entry.pluginId === plugin.pluginId)
|
|
6051
|
+
? [{ actionId: 'plugin-agents', label: 'Agent 管理',
|
|
6052
|
+
run: () => openAgentManagement(plugin.pluginId) }] : []),
|
|
5601
6053
|
...(contentAction ? [contentAction] : []),
|
|
5602
6054
|
...(dependencyAction ? [dependencyAction] : []),
|
|
5603
6055
|
...(uninstallAction ? [uninstallAction] : []),
|
|
@@ -5829,8 +6281,8 @@ export function ConsoleAppV4(props) {
|
|
|
5829
6281
|
const selectedIssue = platformSelectedScope ? props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId && candidate.platform === route.platform
|
|
5830
6282
|
&& candidate.scope === platformSelectedScope) : undefined;
|
|
5831
6283
|
const factActions = platformFacts.map((entry) => ({
|
|
5832
|
-
actionId: `placement:${
|
|
5833
|
-
label: `${platformLabel(
|
|
6284
|
+
actionId: `placement:${route.platform}:${entry.scope}`,
|
|
6285
|
+
label: `${platformLabel(route.platform)} · ${scopeLabel(entry.scope)}`,
|
|
5834
6286
|
selected: entry.scope === platformSelectedScope,
|
|
5835
6287
|
run: () => {
|
|
5836
6288
|
props.controller.updatePageContext({
|
|
@@ -5887,67 +6339,9 @@ export function ConsoleAppV4(props) {
|
|
|
5887
6339
|
}) })] });
|
|
5888
6340
|
}
|
|
5889
6341
|
else if (route.name === 'configLayer' && plugin) {
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
? configLayerDefinitionPage.groups.flatMap((group) => group.fields).find((field) => field.key === route.fieldKey)
|
|
5894
|
-
: undefined;
|
|
5895
|
-
const selectedPreset = route.presetId && configLayerDefinitionPage
|
|
5896
|
-
? configLayerDefinitionPage.presets.find((preset) => preset.id === route.presetId)
|
|
5897
|
-
: undefined;
|
|
5898
|
-
const selectedMutation = selectedField && configCandidate
|
|
5899
|
-
? configCandidate.mutations[selectedField.key]
|
|
5900
|
-
: undefined;
|
|
5901
|
-
const candidateSummary = selectedField && selectedMutation
|
|
5902
|
-
? selectedMutation.kind === 'unset' ? '恢复继承'
|
|
5903
|
-
: selectedField.sensitive ? '已更新'
|
|
5904
|
-
: fieldValueText(selectedField, selectedMutation.value)
|
|
5905
|
-
: selectedPreset && configCandidate
|
|
5906
|
-
? `方案“${selectedPreset.title}” · ${Object.keys(configCandidate.mutations).length} 项修改`
|
|
5907
|
-
: undefined;
|
|
5908
|
-
const candidateSummaryLines = candidateSummary
|
|
5909
|
-
? wrapConsoleText(candidateSummary, Math.max(12, size.columns - 14))
|
|
5910
|
-
: [];
|
|
5911
|
-
const candidateSummaryDisplay = candidateSummaryLines.length > 1
|
|
5912
|
-
? `${candidateSummaryLines[0]}…`
|
|
5913
|
-
: candidateSummaryLines[0];
|
|
5914
|
-
title = selectedField ? `“${selectedField.title}”保存到哪里?`
|
|
5915
|
-
: selectedPreset ? `“${selectedPreset.title}”保存到哪里?` : '这个值保存到哪里?';
|
|
5916
|
-
description = configCandidate?.selectingScope ? '先选择保存位置,再按该层配置选择引用值。' : selectedField
|
|
5917
|
-
? `${selectedField.sensitive ? '敏感值只能保存到个人设置。' : '选择后回到分组总览;磁盘尚未改变。'}`
|
|
5918
|
-
: selectedPreset ? '选择后回到分组总览;磁盘尚未改变。' : '请返回重新选择值。';
|
|
5919
|
-
if (configActionError)
|
|
5920
|
-
description = configActionError;
|
|
5921
|
-
help = '↑↓ 移动 · Enter 选择 · Esc 返回修改值';
|
|
5922
|
-
const canUseLayer = (scope) => {
|
|
5923
|
-
const page = configLayerPages[scope];
|
|
5924
|
-
if (!page || !configCandidate)
|
|
5925
|
-
return false;
|
|
5926
|
-
const fields = page.groups.flatMap((group) => group.fields);
|
|
5927
|
-
if (configCandidate.selectingScope)
|
|
5928
|
-
return fields.some((field) => field.key === configCandidate.fieldKey && configFieldCanSet(field));
|
|
5929
|
-
return Object.entries(configCandidate.mutations).every(([key, mutation]) => {
|
|
5930
|
-
const field = fields.find((candidate) => candidate.key === key);
|
|
5931
|
-
return Boolean(field && (mutation.kind === 'set' ? configFieldCanSet(field) : configFieldCanUnset(field)));
|
|
5932
|
-
});
|
|
5933
|
-
};
|
|
5934
|
-
actions = [
|
|
5935
|
-
...(canUseLayer('project') ? [{
|
|
5936
|
-
label: '当前项目',
|
|
5937
|
-
description: '修改仅对当前项目生效;适合随仓库共享的约定',
|
|
5938
|
-
meta: configLayerCounts.project === undefined ? undefined : `${configLayerCounts.project} 个值`,
|
|
5939
|
-
run: () => openConfigLayerTarget({ scope: 'project' }),
|
|
5940
|
-
}] : []),
|
|
5941
|
-
...(canUseLayer('user') ? [{
|
|
5942
|
-
label: '个人默认',
|
|
5943
|
-
description: '影响个人的所有项目;项目设置仍可覆盖',
|
|
5944
|
-
meta: configLayerCounts.user === undefined ? undefined : `${configLayerCounts.user} 个值`,
|
|
5945
|
-
run: () => openConfigLayerTarget({ scope: 'user' }),
|
|
5946
|
-
}] : []),
|
|
5947
|
-
];
|
|
5948
|
-
content = actions.length > 0
|
|
5949
|
-
? _jsxs(Box, { flexDirection: "column", children: [candidateSummaryDisplay ? _jsx(StatusCard, { status: "info", title: "\u5DF2\u786E\u5B9A\u65B0\u503C", description: `新值:${candidateSummaryDisplay}`, compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "section", compact: policy.mode === 'compact' })] })
|
|
5950
|
-
: _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u68C0\u67E5\u53EF\u7528\u4FDD\u5B58\u4F4D\u7F6E" });
|
|
6342
|
+
title = '插件设置';
|
|
6343
|
+
description = '正在准备当前 Agent 的设置…';
|
|
6344
|
+
content = _jsx(ProgressRow, { title: "\u8BFB\u53D6\u8BBE\u7F6E\u5B57\u6BB5", state: "active" });
|
|
5951
6345
|
}
|
|
5952
6346
|
else if (route.name === 'configFields' && plugin) {
|
|
5953
6347
|
const readonly = false;
|
|
@@ -5972,7 +6366,7 @@ export function ConsoleAppV4(props) {
|
|
|
5972
6366
|
const sources = [...new Set(configDraftIssues.flatMap((issue) => issue.referenceSources ?? []))];
|
|
5973
6367
|
description = `${description ?? ''} 有 ${configDraftIssues.length} 项未保存修改已不适用于当前设置。${sources.length ? ` 请检查来源 ${sources.join('、')}。` : ''}`;
|
|
5974
6368
|
}
|
|
5975
|
-
const mutableLayer =
|
|
6369
|
+
const mutableLayer = route.layer;
|
|
5976
6370
|
const tabActions = activeTab?.kind === 'presets'
|
|
5977
6371
|
? currentConfigPage.presets.map((preset) => ({
|
|
5978
6372
|
label: preset.title,
|
|
@@ -5985,10 +6379,7 @@ export function ConsoleAppV4(props) {
|
|
|
5985
6379
|
label: field.title,
|
|
5986
6380
|
description: field.invalidOverrideIssue ?? field.description,
|
|
5987
6381
|
configTile: (() => {
|
|
5988
|
-
const
|
|
5989
|
-
const user = pendingFieldDisplay(field, 'user');
|
|
5990
|
-
const covered = user !== undefined && field.effectiveSourceLabel === '当前项目' && project === undefined;
|
|
5991
|
-
const pending = project ?? (covered ? undefined : user);
|
|
6382
|
+
const pending = pendingFieldDisplay(field, route.layer.profile);
|
|
5992
6383
|
const targets = pendingFieldTargets(field);
|
|
5993
6384
|
return {
|
|
5994
6385
|
value: pending ?? effectiveFieldDisplay(field),
|
|
@@ -5996,86 +6387,48 @@ export function ConsoleAppV4(props) {
|
|
|
5996
6387
|
description: field.description?.split('\n').map(safeConfigText).join('\n'),
|
|
5997
6388
|
issue: field.invalidOverrideIssue?.split('\n').map(safeConfigText).join('\n'),
|
|
5998
6389
|
invalid: Boolean(field.invalidOverrideIssue),
|
|
5999
|
-
...(targets.length ? { pending:
|
|
6000
|
-
? '待保存到个人;个人修改不会影响本项目'
|
|
6001
|
-
: `待保存到${targets.map(({ scope }) => configLayerLabel({ scope })).join('、')}` } : {}),
|
|
6390
|
+
...(targets.length ? { pending: '● 未保存' } : {}),
|
|
6002
6391
|
};
|
|
6003
6392
|
})(),
|
|
6004
6393
|
configKey: field.key,
|
|
6005
|
-
run: () =>
|
|
6006
|
-
|
|
6007
|
-
: go({ name: 'configFieldEditor', pluginId: plugin.pluginId, layer: mutableLayer,
|
|
6008
|
-
fieldKey: field.key, editor: fieldEditor(field), ...fieldEditorMode(field) }),
|
|
6394
|
+
run: () => go({ name: 'configFieldEditor', pluginId: plugin.pluginId, layer: mutableLayer,
|
|
6395
|
+
fieldKey: field.key, editor: fieldEditor(field), ...fieldEditorMode(field) }),
|
|
6009
6396
|
}));
|
|
6010
6397
|
const operationActions = [
|
|
6011
|
-
...(crossLayerDependencies.length ? [{
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
run:
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
setObjectDrafts({});
|
|
6025
|
-
setArrayDrafts({});
|
|
6026
|
-
setChangedObjectDraftKeys([]);
|
|
6027
|
-
setConfigActionError('已取消相关引用草稿。请先保存来源,再重新编辑引用。');
|
|
6028
|
-
},
|
|
6029
|
-
}] : []),
|
|
6030
|
-
...(configDraftIssues.length > 0 ? [{
|
|
6031
|
-
label: '放弃无法保存的修改',
|
|
6032
|
-
description: `${configDraftIssues.length} 项未保存修改已不再适用于当前插件设置;放弃后不会修改已保存设置。`,
|
|
6033
|
-
status: 'warning',
|
|
6034
|
-
run: discardInvalidConfigDraftChanges,
|
|
6035
|
-
}] : []),
|
|
6036
|
-
...['project', 'user'].flatMap((resetScope) => {
|
|
6037
|
-
const page = configLayerPages[resetScope];
|
|
6038
|
-
if (!page || page.storedCount === 0)
|
|
6039
|
-
return [];
|
|
6040
|
-
return [{
|
|
6041
|
-
label: resetScope === 'user' ? '清空个人默认' : '清空当前项目设置',
|
|
6042
|
-
description: `删除${resetScope === 'user' ? '个人默认' : '当前项目'}中的全部值并恢复继承;其他设置层保持不变`,
|
|
6043
|
-
status: 'error',
|
|
6044
|
-
run: () => beginReview({
|
|
6045
|
-
kind: 'configReset', pluginId: plugin.pluginId, scope: resetScope,
|
|
6046
|
-
changes: page.groups.flatMap((group) => group.fields).filter((field) => field.configured).map((field) => ({
|
|
6047
|
-
key: field.key,
|
|
6048
|
-
title: field.title,
|
|
6049
|
-
before: field.displayValue,
|
|
6050
|
-
after: `恢复继承(${field.fallbackDisplayValue ?? '未设置'} · ${field.fallbackSourceLabel ?? '未设置'})`,
|
|
6051
|
-
...(field.fallbackObjectChanges?.length
|
|
6052
|
-
? { children: field.fallbackObjectChanges }
|
|
6053
|
-
: {}),
|
|
6054
|
-
})),
|
|
6055
|
-
}),
|
|
6056
|
-
}];
|
|
6057
|
-
}),
|
|
6398
|
+
...(crossLayerDependencies.length ? [{ label: '取消相关引用草稿', status: 'warning',
|
|
6399
|
+
description: '保留来源设置修改,只放弃依赖这些未保存值的引用草稿。', run: discardDependentConfigDrafts }] : []),
|
|
6400
|
+
...(configDraftIssues.length > 0 ? [{ label: '放弃无法保存的修改',
|
|
6401
|
+
status: 'warning', run: discardInvalidConfigDraftChanges }] : []),
|
|
6402
|
+
{ actionId: 'config-reset-profile', label: '恢复默认值 [u]', status: 'error',
|
|
6403
|
+
description: '删除当前 Agent 设置中的覆盖值,恢复继承。',
|
|
6404
|
+
run: () => beginReview({ kind: 'configReset', pluginId: plugin.pluginId, scope: 'user',
|
|
6405
|
+
...configReadOptions(route.layer),
|
|
6406
|
+
changes: currentConfigPage.groups.flatMap((group) => group.fields).filter((field) => field.configured).map((field) => ({
|
|
6407
|
+
key: field.key, title: field.title, before: field.displayValue,
|
|
6408
|
+
after: `恢复继承(${field.fallbackDisplayValue ?? '未设置'})`,
|
|
6409
|
+
})),
|
|
6410
|
+
}) },
|
|
6058
6411
|
];
|
|
6059
6412
|
const configSaveAction = {
|
|
6060
6413
|
actionId: 'config-save-summary',
|
|
6061
|
-
label:
|
|
6062
|
-
description: '
|
|
6414
|
+
label: '保存 [s]',
|
|
6415
|
+
description: '保存设置后,可选择更新已安装的插件。',
|
|
6063
6416
|
status: configDraftCount > 0 ? 'info' : 'muted',
|
|
6064
6417
|
disabled: configDraftCount === 0 ? '没有待保存设置'
|
|
6065
6418
|
: configDraftIssues.length > 0 ? '请先放弃失效草稿' : undefined,
|
|
6066
6419
|
run: () => {
|
|
6067
6420
|
setFocus(0);
|
|
6068
6421
|
setScrollOffset(0);
|
|
6069
|
-
|
|
6422
|
+
onSaveConfig();
|
|
6070
6423
|
},
|
|
6071
6424
|
};
|
|
6072
|
-
bottomActions = [configSaveAction, ...
|
|
6425
|
+
bottomActions = [configSaveAction, ...operationActions, {
|
|
6073
6426
|
label: '设置选项', run: () => go({ ...route, view: 'options' }),
|
|
6074
6427
|
}];
|
|
6075
6428
|
actions = [...tabActions, ...bottomActions];
|
|
6076
6429
|
if (route.view === 'options') {
|
|
6077
6430
|
title = '设置选项';
|
|
6078
|
-
description = '
|
|
6431
|
+
description = '查看用户设置,或恢复当前 Agent 的默认值。';
|
|
6079
6432
|
actions = [{ label: '查看当前值的来源', run: () => {
|
|
6080
6433
|
setBatchTargetOffset(0);
|
|
6081
6434
|
go({ ...route, view: 'values' });
|
|
@@ -6083,7 +6436,7 @@ export function ConsoleAppV4(props) {
|
|
|
6083
6436
|
}
|
|
6084
6437
|
const effectiveConfigFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
6085
6438
|
const focusedConfigField = activeGroup?.fields.find((field) => field.key === actions[effectiveConfigFocus]?.configKey);
|
|
6086
|
-
const configRowBudget = pageActionBudget(Number(visibleConfigTabs.length > 1)
|
|
6439
|
+
const configRowBudget = pageActionBudget(1 + Number(visibleConfigTabs.length > 1)
|
|
6087
6440
|
+ (configDraftIssues.length > 0 ? 3 : 0));
|
|
6088
6441
|
const focusedTile = actions[effectiveConfigFocus]?.configTile;
|
|
6089
6442
|
if (focusedTile && route.view !== 'options' && route.view !== 'values') {
|
|
@@ -6095,40 +6448,42 @@ export function ConsoleAppV4(props) {
|
|
|
6095
6448
|
}
|
|
6096
6449
|
if (!readonly) {
|
|
6097
6450
|
const canUnsetFocused = focusedConfigField
|
|
6098
|
-
?
|
|
6451
|
+
? CONFIG_PROFILE_KEYS.some((profile) => configLayerPages[profile]?.groups.flatMap((group) => group.fields)
|
|
6099
6452
|
.some((field) => field.key === focusedConfigField.key && configFieldCanUnset(field)))
|
|
6100
6453
|
: false;
|
|
6101
6454
|
help = `↑↓ 选择 · Enter ${focusedConfigField ? '修改' : '选择'}${!route.view && visibleConfigTabs.length > 1
|
|
6102
6455
|
? ' · ←→ 分组' : ''}${(configCardNavigation?.pageCount ?? 1) > 1 ? ' · PgUp/PgDn 卡片全文' : ''}`
|
|
6103
|
-
+ `${configDraftCount > 0 ? ' · s
|
|
6456
|
+
+ `${configDraftCount > 0 ? ' · s 保存' : ''}${canUnsetFocused ? ' · u 恢复默认值' : ''} · Esc 返回`;
|
|
6104
6457
|
}
|
|
6105
6458
|
const totalFields = currentConfigPage.groups.reduce((count, group) => count + group.fields.length, 0);
|
|
6106
6459
|
const countLabel = `${totalFields} 项${currentConfigPage.presets.length > 0 ? ` · ${currentConfigPage.presets.length} 个方案` : ''}`;
|
|
6460
|
+
description = configActionError ?? (configSaveState?.status === 'error' ? configSaveState.error
|
|
6461
|
+
: configSaveState?.status === 'saving' ? '正在保存设置…'
|
|
6462
|
+
: `${configLayerLabel(route.layer)} · 用户设置${route.layer.context === 'project' ? ' · 当前项目' : ''}`);
|
|
6463
|
+
help += ' · Tab 切换 Agent';
|
|
6107
6464
|
context = configDraftCount > 0 ? `${configDraftCount} 项待保存` : countLabel;
|
|
6108
6465
|
content = route.view === 'options' ? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })
|
|
6109
6466
|
: route.view === 'values' ? (() => {
|
|
6110
6467
|
title = '完整设置值与来源';
|
|
6111
6468
|
description = '显示各保存位置的完整安全值;敏感值不回显。';
|
|
6112
|
-
readingText =
|
|
6113
|
-
? field.invalidOverrideIssue ? '需修正' : fieldValueText(field, field.draftValue ?? field.effectiveValue, true, field.effectiveSourceLabel !== '未设置')
|
|
6114
|
-
: fieldValueText(field, field.draftValue ?? field.value, true, field.configured)}`)).join('\n');
|
|
6469
|
+
readingText = CONFIG_PROFILE_KEYS.flatMap((profile) => (configLayerPages[profile]?.groups.flatMap((group) => group.fields) ?? []).map((field) => `${field.title} · ${configLayerLabel({ profile })}:${fieldValueText(field, field.draftValue ?? field.value, true, field.configured)}`)).join('\n');
|
|
6115
6470
|
actions = [{ label: '返回设置', run: back }];
|
|
6116
6471
|
help = '←→ 或 PgUp/PgDn 阅读 · Esc 返回';
|
|
6117
6472
|
return _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, Math.max(1, pageActionBudget(4) - 1)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
6118
|
-
})() : _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab && visibleConfigTabs.length > 1 ? _jsx(QueryHeader, { width: size.columns - 2, tabs: { label: '分组', items: visibleConfigTabs.map((tab) => ({
|
|
6473
|
+
})() : _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.roles.focus, bold: true, children: [route.layer.profile === 'common' ? '◎ 全部 Agent' : `${configLayerLabel(route.layer)} 专用`, " [Tab]"] }), !readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab && visibleConfigTabs.length > 1 ? _jsx(QueryHeader, { width: size.columns - 2, tabs: { label: '分组', items: visibleConfigTabs.map((tab) => ({
|
|
6119
6474
|
id: tab.id, label: tab.label, count: tab.count,
|
|
6120
6475
|
})), currentId: activeTab.id } }) : null, _jsx(SequenceRows, { actions: actions, itemCount: tabActions.length, focus: effectiveConfigFocus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: configRowBudget, configPage: configCardNavigation?.page, compact: policy.mode === 'compact' })] });
|
|
6121
6476
|
}
|
|
6122
6477
|
}
|
|
6123
6478
|
else if (route.name === 'configSave' && plugin) {
|
|
6124
|
-
const draftGroups =
|
|
6479
|
+
const draftGroups = CONFIG_PROFILE_KEYS.flatMap((scope) => {
|
|
6125
6480
|
const draft = configDrafts[scope];
|
|
6126
6481
|
const page = configLayerPages[scope];
|
|
6127
|
-
return draft && page ? [{ scope, draft, changes: configChanges(page, draft) }] : [];
|
|
6482
|
+
return draft && page ? [{ profile: scope, draft, changes: configChanges(page, draft) }] : [];
|
|
6128
6483
|
});
|
|
6129
6484
|
const changes = draftGroups.flatMap((group) => group.changes.map((change) => ({
|
|
6130
6485
|
...change,
|
|
6131
|
-
title: `${
|
|
6486
|
+
title: `${configLayerLabel({ profile: group.profile })} · ${change.title}`,
|
|
6132
6487
|
})));
|
|
6133
6488
|
if (route.view === 'changes') {
|
|
6134
6489
|
readingText = changeDetailText(changes);
|
|
@@ -6153,12 +6508,12 @@ export function ConsoleAppV4(props) {
|
|
|
6153
6508
|
label: '保存并生效',
|
|
6154
6509
|
description: '保存全部草稿,并重新应用实际使用这些设置的目标',
|
|
6155
6510
|
status: 'success',
|
|
6156
|
-
run: () =>
|
|
6511
|
+
run: () => onSaveConfig(),
|
|
6157
6512
|
},
|
|
6158
6513
|
{
|
|
6159
6514
|
label: '只保存,稍后应用',
|
|
6160
6515
|
description: '保存全部设置层,不重新应用安装目标',
|
|
6161
|
-
run: () =>
|
|
6516
|
+
run: () => onSaveConfig(),
|
|
6162
6517
|
},
|
|
6163
6518
|
{ label: '返回修改', run: back },
|
|
6164
6519
|
];
|
|
@@ -6182,7 +6537,7 @@ export function ConsoleAppV4(props) {
|
|
|
6182
6537
|
breadcrumb = `${plugin.displayName} / 设置 / ${activeField.title}`;
|
|
6183
6538
|
const scalarReference = 'valueRef' in activeField.definition && activeField.definition.valueRef ? activeField.definition : undefined;
|
|
6184
6539
|
const showChoices = Boolean(scalarReference) || (activeField.choices?.length ?? 0) > 0 && route.mode !== 'custom';
|
|
6185
|
-
const targetFields = [
|
|
6540
|
+
const targetFields = [route.layer.profile].flatMap((profile) => configLayerPages[profile]?.groups.flatMap((group) => group.fields).filter((field) => field.key === activeField.key) ?? []);
|
|
6186
6541
|
const canSet = targetFields.some(configFieldCanSet);
|
|
6187
6542
|
const canUnset = !arrayItem && targetFields.some(configFieldCanUnset);
|
|
6188
6543
|
if (!canSet) {
|
|
@@ -6195,7 +6550,7 @@ export function ConsoleAppV4(props) {
|
|
|
6195
6550
|
const selection = configReferenceSelection(referenceContext, scalarReference);
|
|
6196
6551
|
const current = arrayItem ? itemValue : draftUpdates[activeField.key] ?? activeField.draftValue ?? activeField.value;
|
|
6197
6552
|
title = `选择${activeField.title}`;
|
|
6198
|
-
description = referenceNotice(scalarReference, current) ?? `选项来自 ${configReferenceSource(scalarReference)};保存到${
|
|
6553
|
+
description = referenceNotice(scalarReference, current) ?? `选项来自 ${configReferenceSource(scalarReference)};保存到${configLayerLabel(route.layer)}。`;
|
|
6199
6554
|
help = '↑↓ 移动 · Enter 使用 · Esc 返回修复来源';
|
|
6200
6555
|
actions = selection.choices.map((choice) => ({ label: safeConfigText(choice.title), selected: choice.value === current,
|
|
6201
6556
|
run: () => updateDraft(activeField.key, choice.value) }));
|
|
@@ -6383,10 +6738,6 @@ export function ConsoleAppV4(props) {
|
|
|
6383
6738
|
: activeField.sensitive ? '当前值不会回显;留空不会自动删除旧值。' : activeField.description ?? '';
|
|
6384
6739
|
content = _jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: inputValue, sensitive: activeField.sensitive, placeholder: activeField.placeholder, columns: Math.max(8, size.columns - 8) }), description: inputError ?? (activeField.sensitive ? '输入已隐藏。' : activeField.placeholder ?? '按 Enter 使用输入值。'), status: inputError ? 'error' : undefined });
|
|
6385
6740
|
}
|
|
6386
|
-
if (editingScope === 'user' && activeTopField && configReferenceScalars(activeTopField.definition).length
|
|
6387
|
-
&& configLayerPages.project?.groups.flatMap((group) => group.fields).some((field) => field.key === activeTopField.key && field.configured)) {
|
|
6388
|
-
description = `${description ?? ''} 个人默认在当前项目被覆盖。`;
|
|
6389
|
-
}
|
|
6390
6741
|
if (canUnset) {
|
|
6391
6742
|
const unset = { label: '恢复继承', description: '选择要删除覆盖值的位置', run: () => removeDraft(activeField.key) };
|
|
6392
6743
|
const inputEditor = canSet && !showChoices && (route.editor === 'number' || route.editor === 'string');
|
|
@@ -6565,39 +6916,33 @@ export function ConsoleAppV4(props) {
|
|
|
6565
6916
|
const canRecoverSource = configUnavailableCanRecoverSource(route);
|
|
6566
6917
|
title = diagnostic.title;
|
|
6567
6918
|
description = '';
|
|
6568
|
-
const
|
|
6569
|
-
: route.
|
|
6570
|
-
?? route.storedScopes
|
|
6571
|
-
?? (route.layer ? [route.layer.scope] : []);
|
|
6919
|
+
const resetProfiles = route.diagnosticCode === 'configValidationMemoryExhausted' ? []
|
|
6920
|
+
: [route.layer?.profile ?? 'common'];
|
|
6572
6921
|
actions = [
|
|
6573
|
-
...
|
|
6574
|
-
const availability = configResetAvailability[
|
|
6922
|
+
...resetProfiles.flatMap((profile) => {
|
|
6923
|
+
const availability = configResetAvailability[profile];
|
|
6575
6924
|
return availability?.exists && availability.resettable !== false
|
|
6576
6925
|
? [{
|
|
6577
|
-
label: `重置${
|
|
6578
|
-
description: '
|
|
6926
|
+
label: `重置${configLayerLabel({ profile })}`,
|
|
6927
|
+
description: '清除当前设置中的覆盖值;损坏的文档按恢复规则处理。',
|
|
6579
6928
|
status: 'error',
|
|
6580
6929
|
run: () => beginReview({
|
|
6581
|
-
kind: 'configReset',
|
|
6582
|
-
|
|
6583
|
-
scope,
|
|
6930
|
+
kind: 'configReset', pluginId: route.pluginId, scope: 'user',
|
|
6931
|
+
...configReadOptions({ profile, context: route.layer?.context }),
|
|
6584
6932
|
...(availability.state === 'invalid' ? { expectedStoreState: 'invalid' } : {}),
|
|
6585
6933
|
}),
|
|
6586
6934
|
}]
|
|
6587
6935
|
: [];
|
|
6588
6936
|
}),
|
|
6589
6937
|
...(!invalidLayer && canRecoverSource ? [
|
|
6590
|
-
{
|
|
6591
|
-
|
|
6592
|
-
description: `检查并恢复 ${unavailableSourceId};完成后返回当前设置位置。`,
|
|
6593
|
-
run: () => props.controller.beginConfigSourceRecovery(unavailableSourceId),
|
|
6594
|
-
},
|
|
6938
|
+
{ label: '修复插件来源', description: `检查并恢复 ${unavailableSourceId};完成后返回当前设置位置。`,
|
|
6939
|
+
run: () => props.controller.beginConfigSourceRecovery(unavailableSourceId) },
|
|
6595
6940
|
] : []),
|
|
6596
6941
|
{ label: showTechnical ? '返回问题摘要' : '技术详情', description: '查看脱敏错误代码和阶段', run: () => toggleTechnical() },
|
|
6597
6942
|
{ role: 'navigation', label: '返回', run: back },
|
|
6598
6943
|
];
|
|
6599
|
-
const
|
|
6600
|
-
|
|
6944
|
+
const unsupportedRows = resetProfiles.filter((profile) => configResetAvailability[profile]?.resettable === false)
|
|
6945
|
+
.map((profile) => _jsx(StaticRow, { title: `${configLayerLabel({ profile })}不能自动重置:特殊节点,请手动移动`, status: "warning", compact: true }, `unsupported-reset:${profile}`));
|
|
6601
6946
|
const technicalPageRows = responsiveBudget(2, 4);
|
|
6602
6947
|
if (showTechnical && !invalidLayer) {
|
|
6603
6948
|
referenceDetailPagination = {
|
|
@@ -7332,18 +7677,8 @@ export function ConsoleAppV4(props) {
|
|
|
7332
7677
|
: go({ name: 'issueDetail', issueId: entry.issueId }),
|
|
7333
7678
|
}));
|
|
7334
7679
|
}
|
|
7335
|
-
const reminder = snapshot.projectIntentReminder;
|
|
7336
|
-
const pendingProjectTargets = (reminder?.actionableTargets.length ?? 0)
|
|
7337
|
-
+ (reminder?.blockedTargets.length ?? 0) + (reminder?.unconfirmedTargets.length ?? 0);
|
|
7338
|
-
if (snapshot.projectIntentIssue || pendingProjectTargets > 0) {
|
|
7339
|
-
title = '需要处理';
|
|
7340
|
-
description = '查看项目插件、安装、来源或设置问题。';
|
|
7341
|
-
actions.unshift({ actionId: 'project-status', label: '项目插件',
|
|
7342
|
-
description: snapshot.projectIntentIssue ? '项目声明需要检查' : `${pendingProjectTargets} 个位置待处理或未确认`,
|
|
7343
|
-
status: 'warning', run: () => go({ name: 'projectStatus' }) });
|
|
7344
|
-
}
|
|
7345
7680
|
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(8, 12));
|
|
7346
|
-
if (issues.length === 0
|
|
7681
|
+
if (issues.length === 0) {
|
|
7347
7682
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit });
|
|
7348
7683
|
}
|
|
7349
7684
|
else {
|
|
@@ -7389,7 +7724,7 @@ export function ConsoleAppV4(props) {
|
|
|
7389
7724
|
else if (entry.id === 'viewSourceChange' && issue.sourceId)
|
|
7390
7725
|
go({ name: 'sourceTechnical', sourceId: issue.sourceId });
|
|
7391
7726
|
else if (entry.id === 'openConfig' && issue.pluginId) {
|
|
7392
|
-
go({ name: 'configFields', pluginId: issue.pluginId, layer: {
|
|
7727
|
+
go({ name: 'configFields', pluginId: issue.pluginId, layer: { profile: 'common' } });
|
|
7393
7728
|
}
|
|
7394
7729
|
else if (entry.id === 'viewDependencies' && issue.pluginId) {
|
|
7395
7730
|
go({ name: 'pluginDependencies', pluginId: issue.pluginId });
|
|
@@ -7651,6 +7986,8 @@ export function ConsoleAppV4(props) {
|
|
|
7651
7986
|
else if (route.name === 'review' && snapshot.activeReview) {
|
|
7652
7987
|
const review = snapshot.activeReview;
|
|
7653
7988
|
readingText = [
|
|
7989
|
+
...(review.operationKind === 'pluginWriteTask' && splitTaskReview
|
|
7990
|
+
? splitTaskLines(splitTaskReview) : []),
|
|
7654
7991
|
`${review.title} · ${review.subject.name}`,
|
|
7655
7992
|
review.description,
|
|
7656
7993
|
...(review.equivalentCommand && !review.requiresConfirmation
|
|
@@ -8027,9 +8364,9 @@ export function ConsoleAppV4(props) {
|
|
|
8027
8364
|
};
|
|
8028
8365
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
8029
8366
|
}
|
|
8030
|
-
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: reviewRootSummary ? `本次插件 · ${reviewRootSummary}` : review.subject.name, meta: reviewRootSummary ? `版本 ${reviewVersionSummary} · 范围 ${reviewRangeSummary || '未知'}` : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, showTechnical && visibleTechnicalDetails.length > 0 ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: catalogReferenceDetailOffset >= 0 && technicalDetailPage.offset + index >= catalogReferenceDetailOffset ? undefined : index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, `${technicalDetailPage.offset + index}:${detail}`)) }) : null, reviewPageCount > 1 ? _jsx(Pager, { page: reviewCurrentPage, pageCount: reviewPageCount, columns: size.columns - 6 }) : null, portableReview ? null : _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" })] }) });
|
|
8367
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: reviewRootSummary ? `本次插件 · ${reviewRootSummary}` : review.subject.name, meta: reviewRootSummary ? `版本 ${reviewVersionSummary} · 范围 ${reviewRangeSummary || '未知'}` : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [review.operationKind === 'pluginWriteTask' && splitTaskReview ? _jsx(StaticRow, { title: `完整请求 ${splitTaskReview.targets.length} 个位置 · 本次${scopeLabel(splitTaskReview.currentScope)} ${splitTaskReview.targets.filter((target) => target.scope === splitTaskReview.currentScope).length} 个`, description: `其余${scopeLabel(splitTaskReview.currentScope === 'user' ? 'project' : 'user')}位置需另行确认;y 复制完整请求`, status: "warning", compact: true }) : null, showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, showTechnical && visibleTechnicalDetails.length > 0 ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: catalogReferenceDetailOffset >= 0 && technicalDetailPage.offset + index >= catalogReferenceDetailOffset ? undefined : index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, `${technicalDetailPage.offset + index}:${detail}`)) }) : null, reviewPageCount > 1 ? _jsx(Pager, { page: reviewCurrentPage, pageCount: reviewPageCount, columns: size.columns - 6 }) : null, portableReview ? null : _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" })] }) });
|
|
8031
8368
|
if (pendingReviewIntent?.kind === 'configSaveBatch' || sourceDraft && (pendingReviewIntent?.kind === 'sourceMutation' || pendingReviewIntent?.kind === 'catalogGeneration') && pendingReviewIntent.sourceId === sourceDraft.sourceId) {
|
|
8032
|
-
const changesText = pendingReviewIntent.kind === 'configSaveBatch' ? pendingReviewIntent.groups.map((group) => `${configLayerLabel({
|
|
8369
|
+
const changesText = pendingReviewIntent.kind === 'configSaveBatch' ? pendingReviewIntent.groups.map((group) => `${configLayerLabel({ profile: group.platform ?? 'common' })}\n${(group.changes ?? []).flatMap((change) => [
|
|
8033
8370
|
`${change.title} · ${change.before ?? '未设置'} → ${change.after}${change.children?.length ? ` · ${change.children.length} 个子属性变化` : ''}`,
|
|
8034
8371
|
...(change.children ?? []).map((child) => `${child.property} · ${child.before} → ${child.after}`),
|
|
8035
8372
|
]).join('\n')}`).join('\n') : changeDetailText(sourceDraftChanges(sourceDraft));
|
|
@@ -8371,6 +8708,11 @@ export function ConsoleAppV4(props) {
|
|
|
8371
8708
|
? resultSummary?.message ?? '设置层已经保存;平台应用失败不会回滚设置。'
|
|
8372
8709
|
: '设置层已经更新。'
|
|
8373
8710
|
: '状态已刷新。';
|
|
8711
|
+
if (taskOperation && splitTaskReview?.currentScope === 'user') {
|
|
8712
|
+
if (!blocked && !partial)
|
|
8713
|
+
title = '✓ 所有项目范围已处理';
|
|
8714
|
+
description += ` 当前项目的 ${splitTaskReview.targets.filter((target) => target.scope === 'project').length} 个位置尚未处理。`;
|
|
8715
|
+
}
|
|
8374
8716
|
const pluginId = resultSummary?.pluginId
|
|
8375
8717
|
?? ('pluginId' in result ? result.pluginId : undefined);
|
|
8376
8718
|
const configResultPluginId = pluginId ?? ('returnRoute' in result
|
|
@@ -8378,12 +8720,11 @@ export function ConsoleAppV4(props) {
|
|
|
8378
8720
|
&& 'pluginId' in result.returnRoute
|
|
8379
8721
|
? result.returnRoute.pluginId
|
|
8380
8722
|
: undefined);
|
|
8381
|
-
const
|
|
8723
|
+
const configRetryProfiles = configResultPluginId === undefined ? []
|
|
8382
8724
|
: operationKind === 'configSaveBatch'
|
|
8383
8725
|
? resultSummary?.configGroups?.filter((group) => group.configSaved && group.applyStatus === 'failed'
|
|
8384
|
-
&& group.errorCode !== 'invalidConfig').map((group) => group.
|
|
8385
|
-
: resultSummary?.configApplyRetryAvailable === true
|
|
8386
|
-
? [resultSummary.configScope] : [];
|
|
8726
|
+
&& group.errorCode !== 'invalidConfig').map((group) => group.platform ?? 'common') ?? []
|
|
8727
|
+
: resultSummary?.configApplyRetryAvailable === true ? [resultSummary.configPlatform ?? 'common'] : [];
|
|
8387
8728
|
const configOrigin = successfulConfigOperation && pluginId
|
|
8388
8729
|
? props.controller.configOrigin(pluginId)
|
|
8389
8730
|
: undefined;
|
|
@@ -8613,13 +8954,14 @@ export function ConsoleAppV4(props) {
|
|
|
8613
8954
|
label: '返回首页', run: () => requestSessionNavigation('home') }]
|
|
8614
8955
|
: configOperation && partial
|
|
8615
8956
|
? [
|
|
8616
|
-
...
|
|
8617
|
-
label:
|
|
8957
|
+
...configRetryProfiles.map((profile) => ({
|
|
8958
|
+
label: configRetryProfiles.length > 1 ? `重试应用 · ${configLayerLabel({ profile })}` : '重试应用',
|
|
8618
8959
|
description: '先重新显示操作汇总,并重新读取当前状态',
|
|
8619
8960
|
run: () => beginReview({
|
|
8620
8961
|
kind: 'configSave',
|
|
8621
8962
|
pluginId: configResultPluginId,
|
|
8622
|
-
scope,
|
|
8963
|
+
scope: 'user',
|
|
8964
|
+
...configReadOptions({ profile, context: resultSummary?.configContext }),
|
|
8623
8965
|
apply: true,
|
|
8624
8966
|
retryApply: true,
|
|
8625
8967
|
updates: {},
|
|
@@ -8711,9 +9053,10 @@ export function ConsoleAppV4(props) {
|
|
|
8711
9053
|
const resultTargetName = (target) => target.pluginId
|
|
8712
9054
|
? props.controller.getPluginOverview(target.pluginId)?.displayName
|
|
8713
9055
|
?? target.pluginId.split('/').at(-1)
|
|
8714
|
-
: `${
|
|
9056
|
+
: `${agentLabel(target.platform)} · ${scopeLabel(target.scope)}`;
|
|
8715
9057
|
const resultStatus = blocked ? 'error' : partial ? 'warning' : 'success';
|
|
8716
|
-
const resultTargets = resultSummary?.targets ?? []
|
|
9058
|
+
const resultTargets = configOperation ? resultSummary?.configTargets ?? resultSummary?.targets ?? []
|
|
9059
|
+
: resultSummary?.targets ?? [];
|
|
8717
9060
|
const commandResults = resultSummary?.commandResults ?? [];
|
|
8718
9061
|
const retainedCommandPackage = (result) => result.producer.kind === 'package' && (resultSummary?.action === 'cleanup' || resultSummary?.action === 'uninstall');
|
|
8719
9062
|
const commandResultRows = commandResults.map((result, index) => _jsx(ResultRow, { title: `${retainedCommandPackage(result) ? '保留' : ''}命令${result.producer.kind === 'package' ? '包' : '生产者'} ${result.producer.key}`, description: `${result.producer.sourceId}/${result.producer.pluginName} · ${scopeLabel(result.producer.scope)} · ${retainedCommandPackage(result) ? result.producer.scope === 'user' ? '所有项目共用 · ' : '当前项目内共用 · ' : ''}${result.commandIds.length} 个命令${result.removedCommandIds.length ? ` · 撤销 ${result.removedCommandIds.length} 个` : ''}${result.errors[0] ? ` · ${result.errors[0].code}` : ''}`, meta: retainedCommandPackage(result) ? '保留' : result.state === 'completed' ? '完成' : result.state === 'partial' ? '部分完成' : '失败', status: result.state === 'completed' ? 'success' : result.state === 'partial' ? 'warning' : 'error', compact: policy.mode === 'compact' }, `command:${index}`));
|
|
@@ -8794,12 +9137,13 @@ export function ConsoleAppV4(props) {
|
|
|
8794
9137
|
`materialization digest · ${portableReceipt.materializationDigest}`] : []),
|
|
8795
9138
|
...(resultSummary?.elapsedMs !== undefined ? [`elapsed · ${resultSummary.elapsedMs} ms`] : []),
|
|
8796
9139
|
].join('\n') : '当前结果已变化。请返回结果摘要。' : [
|
|
9140
|
+
...(taskOperation && splitTaskReview ? splitTaskLines(splitTaskReview) : []),
|
|
8797
9141
|
resultSummary?.message ?? (blocked ? result.message : '操作结果'),
|
|
8798
9142
|
...(resultSummary?.messages ?? []).map(productResultMessage),
|
|
8799
9143
|
...(resultSummary?.changes ?? []),
|
|
8800
9144
|
...(resultSummary?.warnings ?? []),
|
|
8801
9145
|
...(resultSummary?.recoveryActions ?? []),
|
|
8802
|
-
...prioritizedTargets.map((target) => `${resultTargetName(target)} · ${
|
|
9146
|
+
...prioritizedTargets.map((target) => `${resultTargetName(target)} · ${agentLabel(target.platform)} · ${scopeLabel(target.scope)} · ${operationTargetStateLabel(target.state).label}`),
|
|
8803
9147
|
...commandResults.flatMap((result) => [
|
|
8804
9148
|
`命令生产者 ${result.producer.key} · ${scopeLabel(result.producer.scope)} · ${result.state}`,
|
|
8805
9149
|
...(retainedCommandPackage(result) ? [`保留命令包 ${getCommandPackageId(result.producer)} · ${result.producer.scope === 'user' ? '所有项目共用' : '当前项目内共用'}`] : []),
|
|
@@ -8910,7 +9254,7 @@ export function ConsoleAppV4(props) {
|
|
|
8910
9254
|
: group.applyStatus === 'failed' ? '已保存 · 应用失败'
|
|
8911
9255
|
: group.applyStatus === 'succeeded' ? '已保存 · 已应用'
|
|
8912
9256
|
: '已保存';
|
|
8913
|
-
return _jsx(ResultRow, { title:
|
|
9257
|
+
return _jsx(ResultRow, { title: configLayerLabel({ profile: group.platform ?? 'common' }), description: group.errorCode, meta: outcome, status: status, compact: true }, `config:${group.platform ?? 'common'}`);
|
|
8914
9258
|
}) ?? [];
|
|
8915
9259
|
const taskRootResults = taskSummary?.rootResults ?? [];
|
|
8916
9260
|
const blockedTaskTargets = new Map((taskSummary?.blockedTargets ?? []).map((entry) => [
|
|
@@ -8950,7 +9294,7 @@ export function ConsoleAppV4(props) {
|
|
|
8950
9294
|
: target.relation === 'transitive' ? '间接依赖' : '所选插件位置';
|
|
8951
9295
|
return {
|
|
8952
9296
|
key: `target:${root.pluginId}:${key}`,
|
|
8953
|
-
title: `${root.pluginId}${targetPluginId === root.pluginId ? '' : ` → ${targetPluginId}`} · ${
|
|
9297
|
+
title: `${root.pluginId}${targetPluginId === root.pluginId ? '' : ` → ${targetPluginId}`} · ${agentLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
8954
9298
|
description: blockedTarget
|
|
8955
9299
|
? pluginWriteTaskBlockLabel(blockedTarget.reason)
|
|
8956
9300
|
: `${relation}${target.errorCode ? ` · ${target.errorCode}` : ''}`,
|
|
@@ -8982,7 +9326,7 @@ export function ConsoleAppV4(props) {
|
|
|
8982
9326
|
const relation = target.relation === 'requested' ? '你选择的插件'
|
|
8983
9327
|
: target.relation === 'direct' ? '直接依赖'
|
|
8984
9328
|
: target.relation === 'transitive' ? '间接依赖' : undefined;
|
|
8985
|
-
return _jsx(ResultRow, { title: `${resultTargetName(target)} · ${
|
|
9329
|
+
return _jsx(ResultRow, { title: `${resultTargetName(target)} · ${agentLabel(target.platform)} · ${scopeLabel(target.scope)}`, description: relation, meta: state.label, status: state.status ?? 'muted', compact: true }, `batch-target:${target.pluginId ?? ''}:${target.platform}:${target.scope}`);
|
|
8986
9330
|
});
|
|
8987
9331
|
const taskResultLimit = Math.max(1, pageActionBudget(5) - resultActionViewport.limit);
|
|
8988
9332
|
const pluginResultRows = taskOperation ? taskSelectedPluginRows : batchPluginResultRows;
|
|
@@ -9253,7 +9597,7 @@ export function ConsoleAppV4(props) {
|
|
|
9253
9597
|
const hints = [];
|
|
9254
9598
|
if (route.name === 'pluginList')
|
|
9255
9599
|
hints.push('/ 搜索', 'f 筛选', '←→ 来源', 'm 多选');
|
|
9256
|
-
if (route.name === '
|
|
9600
|
+
if (route.name === 'intentInventory' && !showTechnical)
|
|
9257
9601
|
hints.push('/ 搜索', '←→ 分区', 'f 筛选');
|
|
9258
9602
|
if (route.name === 'sourceList') {
|
|
9259
9603
|
hints.push('/ 搜索');
|
|
@@ -9267,9 +9611,9 @@ export function ConsoleAppV4(props) {
|
|
|
9267
9611
|
}
|
|
9268
9612
|
if (route.name === 'portableSkillSelect' && !portableToolsOpen)
|
|
9269
9613
|
hints.push('/ 搜索', 'c 继续');
|
|
9270
|
-
if (bottomActions.some((action) => action.actionId === '
|
|
9271
|
-
hints.push('r
|
|
9272
|
-
if (bottomActions.some((action) => action.actionId === '
|
|
9614
|
+
if (bottomActions.some((action) => action.actionId === 'inventory-recheck' && !action.disabled))
|
|
9615
|
+
hints.push('r 检查清单');
|
|
9616
|
+
if (bottomActions.some((action) => action.actionId === 'inventory-sync-all' && !action.disabled))
|
|
9273
9617
|
hints.push('c 同步声明');
|
|
9274
9618
|
if (bottomActions.some((action) => action.actionId === 'source-check-all' && !action.disabled))
|
|
9275
9619
|
hints.push('r 检查全部');
|
|
@@ -9405,7 +9749,7 @@ export function ConsoleAppV4(props) {
|
|
|
9405
9749
|
setScrollOffset(0);
|
|
9406
9750
|
});
|
|
9407
9751
|
useEffect(() => {
|
|
9408
|
-
if (!targetActionSignature || !actions[focus]?.disabled)
|
|
9752
|
+
if (!targetActionSignature || actions[focus] && !actions[focus]?.disabled)
|
|
9409
9753
|
return;
|
|
9410
9754
|
const firstEnabled = actions.findIndex((action) => !action.disabled);
|
|
9411
9755
|
if (firstEnabled < 0)
|
|
@@ -9525,6 +9869,173 @@ export function ConsoleAppV4(props) {
|
|
|
9525
9869
|
? Math.min(effectiveFocus, Math.max(0, actions.length - 1))
|
|
9526
9870
|
: effectiveFocus;
|
|
9527
9871
|
useInput((input, key) => {
|
|
9872
|
+
if (scopedFlow && tooSmall && !overlay) {
|
|
9873
|
+
if (scopedFlow.stage !== 'progress' && (input.toLowerCase() === 'q'
|
|
9874
|
+
|| key.ctrl && input.toLowerCase() === 'c'))
|
|
9875
|
+
requestConfirmedExit();
|
|
9876
|
+
return;
|
|
9877
|
+
}
|
|
9878
|
+
if (configSaveState?.status === 'saving')
|
|
9879
|
+
return;
|
|
9880
|
+
if (configSaveState?.status === 'saved') {
|
|
9881
|
+
if (tooSmall)
|
|
9882
|
+
return;
|
|
9883
|
+
if (key.ctrl && input.toLowerCase() === 'c') {
|
|
9884
|
+
requestConfirmedExit();
|
|
9885
|
+
return;
|
|
9886
|
+
}
|
|
9887
|
+
if (key.escape) {
|
|
9888
|
+
dismissConfigSaved();
|
|
9889
|
+
setConfigSavedFocus(0);
|
|
9890
|
+
return;
|
|
9891
|
+
}
|
|
9892
|
+
if (key.upArrow || key.downArrow) {
|
|
9893
|
+
setConfigSavedFocus((value) => 1 - value);
|
|
9894
|
+
return;
|
|
9895
|
+
}
|
|
9896
|
+
if (key.return) {
|
|
9897
|
+
if (configSavedFocus === 0)
|
|
9898
|
+
applySavedConfig();
|
|
9899
|
+
else
|
|
9900
|
+
dismissConfigSaved();
|
|
9901
|
+
setConfigSavedFocus(0);
|
|
9902
|
+
}
|
|
9903
|
+
return;
|
|
9904
|
+
}
|
|
9905
|
+
if (agentWelcome) {
|
|
9906
|
+
if (tooSmall)
|
|
9907
|
+
return;
|
|
9908
|
+
if (key.ctrl && input.toLowerCase() === 'c') {
|
|
9909
|
+
requestConfirmedExit();
|
|
9910
|
+
return;
|
|
9911
|
+
}
|
|
9912
|
+
if (key.escape) {
|
|
9913
|
+
setAgentWelcome(false);
|
|
9914
|
+
return;
|
|
9915
|
+
}
|
|
9916
|
+
if (key.upArrow || key.downArrow) {
|
|
9917
|
+
setAgentWelcomeFocus((value) => 1 - value);
|
|
9918
|
+
return;
|
|
9919
|
+
}
|
|
9920
|
+
if (key.return) {
|
|
9921
|
+
if (agentWelcomeFocus === 0)
|
|
9922
|
+
openAgentManagement();
|
|
9923
|
+
else
|
|
9924
|
+
setAgentWelcome(false);
|
|
9925
|
+
}
|
|
9926
|
+
return;
|
|
9927
|
+
}
|
|
9928
|
+
if (scopedFlow?.stage === 'agents' && !overlay)
|
|
9929
|
+
return;
|
|
9930
|
+
if (scopedDiscardIntent) {
|
|
9931
|
+
if (key.escape || input.toLowerCase() === 'b' || input.toLowerCase() === 'n') {
|
|
9932
|
+
setScopedDiscardIntent(undefined);
|
|
9933
|
+
return;
|
|
9934
|
+
}
|
|
9935
|
+
if (input.toLowerCase() === 'y' || key.return) {
|
|
9936
|
+
const intent = scopedDiscardIntent;
|
|
9937
|
+
setScopedDiscardIntent(undefined);
|
|
9938
|
+
closeScopedFlow();
|
|
9939
|
+
if (intent === 'home' || intent === 'reload')
|
|
9940
|
+
requestSessionNavigation(intent);
|
|
9941
|
+
if (intent === 'exit')
|
|
9942
|
+
requestConfirmedExit();
|
|
9943
|
+
}
|
|
9944
|
+
return;
|
|
9945
|
+
}
|
|
9946
|
+
if (scopedFlow && !overlay) {
|
|
9947
|
+
const flow = scopedFlow;
|
|
9948
|
+
if (flow.stage === 'timeout') {
|
|
9949
|
+
if (key.escape) {
|
|
9950
|
+
beginScopedTask(flow.request);
|
|
9951
|
+
return;
|
|
9952
|
+
}
|
|
9953
|
+
if (key.return) {
|
|
9954
|
+
try {
|
|
9955
|
+
const scriptTimeoutMs = parseScriptTimeoutMs(scriptTimeoutInput);
|
|
9956
|
+
setScriptTimeoutError(undefined);
|
|
9957
|
+
beginScopedTask({ ...flow.request, scriptTimeoutMs });
|
|
9958
|
+
}
|
|
9959
|
+
catch (error) {
|
|
9960
|
+
setScriptTimeoutError(error instanceof Error ? error.message : '脚本超时无效。');
|
|
9961
|
+
}
|
|
9962
|
+
return;
|
|
9963
|
+
}
|
|
9964
|
+
if (input === '\u0015' || input.toLowerCase() === 'u' && key.ctrl)
|
|
9965
|
+
setScriptTimeoutInput('');
|
|
9966
|
+
else if (key.backspace || key.delete)
|
|
9967
|
+
setScriptTimeoutInput(removeLastGrapheme);
|
|
9968
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
9969
|
+
setScriptTimeoutInput((value) => value + input);
|
|
9970
|
+
setScriptTimeoutError(undefined);
|
|
9971
|
+
return;
|
|
9972
|
+
}
|
|
9973
|
+
if (input.toLowerCase() === 'd' && (flow.stage === 'review' || flow.stage === 'risk' || flow.stage === 'result')) {
|
|
9974
|
+
setShowTechnical((value) => !value);
|
|
9975
|
+
setBatchTargetOffset(0);
|
|
9976
|
+
return;
|
|
9977
|
+
}
|
|
9978
|
+
const dirtyTargets = flow.stage === 'targets'
|
|
9979
|
+
&& flow.agents.join('\0') !== flow.savedAgents.join('\0');
|
|
9980
|
+
const navigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: false });
|
|
9981
|
+
if (navigation) {
|
|
9982
|
+
if (flow.stage === 'progress')
|
|
9983
|
+
return;
|
|
9984
|
+
if (dirtyTargets) {
|
|
9985
|
+
setScopedDiscardIntent(navigation);
|
|
9986
|
+
return;
|
|
9987
|
+
}
|
|
9988
|
+
closeScopedFlow();
|
|
9989
|
+
requestSessionNavigation(navigation);
|
|
9990
|
+
return;
|
|
9991
|
+
}
|
|
9992
|
+
if (input.toLowerCase() === 'q' || key.ctrl && input.toLowerCase() === 'c') {
|
|
9993
|
+
if (flow.stage !== 'progress') {
|
|
9994
|
+
if (dirtyTargets)
|
|
9995
|
+
setScopedDiscardIntent('exit');
|
|
9996
|
+
else
|
|
9997
|
+
requestConfirmedExit();
|
|
9998
|
+
}
|
|
9999
|
+
return;
|
|
10000
|
+
}
|
|
10001
|
+
if (flow.stage === 'review' || flow.stage === 'risk' || flow.stage === 'result') {
|
|
10002
|
+
if (input.toLowerCase() === 'y' && readingText !== undefined) {
|
|
10003
|
+
void copyToClipboard(readingText).then(() => setCopyFeedback({ routeKey, message: '全文已复制' })).catch(() => setCopyFeedback({ routeKey, message: '复制失败,请重试' }));
|
|
10004
|
+
return;
|
|
10005
|
+
}
|
|
10006
|
+
if (key.pageUp || key.pageDown || key.leftArrow || key.rightArrow) {
|
|
10007
|
+
readerNavigation?.move(key.pageUp || key.leftArrow ? -1 : 1);
|
|
10008
|
+
return;
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
10011
|
+
if (flow.stage === 'progress')
|
|
10012
|
+
return;
|
|
10013
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
10014
|
+
if (flow.stage === 'risk') {
|
|
10015
|
+
setScopedFlow({ ...flow, stage: 'review', focus: 0 });
|
|
10016
|
+
return;
|
|
10017
|
+
}
|
|
10018
|
+
if (dirtyTargets) {
|
|
10019
|
+
setScopedDiscardIntent('close');
|
|
10020
|
+
return;
|
|
10021
|
+
}
|
|
10022
|
+
closeScopedFlow();
|
|
10023
|
+
return;
|
|
10024
|
+
}
|
|
10025
|
+
if (key.upArrow || key.downArrow || input.toLowerCase() === 'j' || input.toLowerCase() === 'k') {
|
|
10026
|
+
setScopedFlow({ ...flow, focus: moveIndex(actions.length, flow.focus, key.upArrow || input.toLowerCase() === 'k' ? -1 : 1) });
|
|
10027
|
+
return;
|
|
10028
|
+
}
|
|
10029
|
+
if (flow.stage === 'targets' && input.toLowerCase() === 's') {
|
|
10030
|
+
runAction(actions.find((action) => action.actionId === 'targets-save'));
|
|
10031
|
+
return;
|
|
10032
|
+
}
|
|
10033
|
+
if (key.return || input === ' ' && flow.stage === 'targets')
|
|
10034
|
+
runAction(actions[flow.focus]);
|
|
10035
|
+
else if (key.return)
|
|
10036
|
+
runAction(actions[flow.focus]);
|
|
10037
|
+
return;
|
|
10038
|
+
}
|
|
9528
10039
|
if (!overlay && route.name === 'reviewPreparing' && snapshot.reviewPreparation?.state === 'failed'
|
|
9529
10040
|
&& (key.escape || input.toLowerCase() === 'b')) {
|
|
9530
10041
|
back();
|
|
@@ -9644,43 +10155,6 @@ export function ConsoleAppV4(props) {
|
|
|
9644
10155
|
}
|
|
9645
10156
|
return;
|
|
9646
10157
|
}
|
|
9647
|
-
if (projectReminderOpen) {
|
|
9648
|
-
const closeProjectReminder = () => {
|
|
9649
|
-
setProjectReminderOpen(false);
|
|
9650
|
-
setOverlayFocus(0);
|
|
9651
|
-
};
|
|
9652
|
-
if (key.escape || input.toLowerCase() === 'b') {
|
|
9653
|
-
closeProjectReminder();
|
|
9654
|
-
}
|
|
9655
|
-
else if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
9656
|
-
requestConfirmedExit(closeProjectReminder);
|
|
9657
|
-
}
|
|
9658
|
-
else if (props.onSessionNavigation && (input === 'h' || input === 'H')) {
|
|
9659
|
-
closeProjectReminder();
|
|
9660
|
-
requestSessionNavigation(input === 'H' ? 'reload' : 'home');
|
|
9661
|
-
}
|
|
9662
|
-
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
9663
|
-
setOverlayFocus((value) => {
|
|
9664
|
-
if ((snapshot.projectIntentReminder?.actionableTargets.length ?? 0) > 0) {
|
|
9665
|
-
return moveIndex(3, value, key.upArrow || key.leftArrow ? -1 : 1);
|
|
9666
|
-
}
|
|
9667
|
-
return value === 0 ? 2 : 0;
|
|
9668
|
-
});
|
|
9669
|
-
}
|
|
9670
|
-
else if (key.return) {
|
|
9671
|
-
const reminder = snapshot.projectIntentReminder;
|
|
9672
|
-
closeProjectReminder();
|
|
9673
|
-
if (!reminder || overlayFocus === 0)
|
|
9674
|
-
return;
|
|
9675
|
-
if (overlayFocus === 1 && reminder.actionableTargets.length > 0) {
|
|
9676
|
-
prepareCurrentProjectTargets(reminder.actionableTargets);
|
|
9677
|
-
}
|
|
9678
|
-
else {
|
|
9679
|
-
go({ name: 'projectStatus' });
|
|
9680
|
-
}
|
|
9681
|
-
}
|
|
9682
|
-
return;
|
|
9683
|
-
}
|
|
9684
10158
|
if (operationConfirmation) {
|
|
9685
10159
|
const activeReview = snapshot.activeReview;
|
|
9686
10160
|
const closeConfirmation = () => {
|
|
@@ -9903,6 +10377,15 @@ export function ConsoleAppV4(props) {
|
|
|
9903
10377
|
});
|
|
9904
10378
|
return;
|
|
9905
10379
|
}
|
|
10380
|
+
if (route.name === 'configFields' && !route.view && visibleConfigTabs.length > 1
|
|
10381
|
+
&& (key.leftArrow || key.rightArrow)) {
|
|
10382
|
+
const current = Math.max(0, visibleConfigTabs.findIndex((tab) => tab.id === activeConfigTabId));
|
|
10383
|
+
const next = visibleConfigTabs[(current + (key.leftArrow ? -1 : 1) + visibleConfigTabs.length) % visibleConfigTabs.length];
|
|
10384
|
+
props.controller.updatePageContext({ configGroupId: next.id, focusIndex: 0, scrollOffset: 0 });
|
|
10385
|
+
setFocus(0);
|
|
10386
|
+
setScrollOffset(0);
|
|
10387
|
+
return;
|
|
10388
|
+
}
|
|
9906
10389
|
if (readerNavigation && (key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)) {
|
|
9907
10390
|
readerNavigation.move(key.pageUp || key.leftArrow ? -1 : 1);
|
|
9908
10391
|
return;
|
|
@@ -10015,7 +10498,7 @@ export function ConsoleAppV4(props) {
|
|
|
10015
10498
|
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
10016
10499
|
&& (route.name === 'portableDependencies' || route.name === 'pluginContent'
|
|
10017
10500
|
|| route.name === 'pluginTechnical'
|
|
10018
|
-
|
|
10501
|
+
&& showTechnical
|
|
10019
10502
|
|| route.name === 'issueDetail' && showTechnical
|
|
10020
10503
|
|| route.name === 'portableBlocker' && showTechnical
|
|
10021
10504
|
|| route.name === 'configUnavailable' && showTechnical
|
|
@@ -10208,7 +10691,7 @@ export function ConsoleAppV4(props) {
|
|
|
10208
10691
|
return;
|
|
10209
10692
|
}
|
|
10210
10693
|
if (route.name === 'configFieldEditor' && !arrayItem && activeField && key.ctrl && input.toLowerCase() === 'r') {
|
|
10211
|
-
const canUnset = [
|
|
10694
|
+
const canUnset = [route.layer.profile].some((profile) => configLayerPages[profile]?.groups.flatMap((group) => group.fields)
|
|
10212
10695
|
.some((field) => field.key === activeField.key && configFieldCanUnset(field)));
|
|
10213
10696
|
if (canUnset)
|
|
10214
10697
|
removeDraft(activeField.key);
|
|
@@ -10219,7 +10702,7 @@ export function ConsoleAppV4(props) {
|
|
|
10219
10702
|
&& !(activeField && 'valueRef' in activeField.definition && activeField.definition.valueRef)
|
|
10220
10703
|
&& (route.editor === 'string' || route.editor === 'number')
|
|
10221
10704
|
&& ((activeField?.choices?.length ?? 0) === 0 || route.mode === 'custom')
|
|
10222
|
-
&& [
|
|
10705
|
+
&& [route.layer.profile].some((profile) => configLayerPages[profile]?.groups.flatMap((group) => group.fields)
|
|
10223
10706
|
.some((field) => field.key === activeField?.key && configFieldCanSet(field)))) {
|
|
10224
10707
|
if (key.escape) {
|
|
10225
10708
|
setConfigCandidate(undefined);
|
|
@@ -10335,7 +10818,8 @@ export function ConsoleAppV4(props) {
|
|
|
10335
10818
|
setInputError(undefined);
|
|
10336
10819
|
return;
|
|
10337
10820
|
}
|
|
10338
|
-
if (searchFocus && (route.name === 'pluginList'
|
|
10821
|
+
if (searchFocus && (route.name === 'pluginList'
|
|
10822
|
+
|| route.name === 'intentInventory'
|
|
10339
10823
|
|| route.name === 'pluginContent'
|
|
10340
10824
|
|| route.name === 'batchSelection' || route.name === 'sourceList'
|
|
10341
10825
|
|| route.name === 'portableSkillSelect')) {
|
|
@@ -10351,7 +10835,7 @@ export function ConsoleAppV4(props) {
|
|
|
10351
10835
|
setSearchFocus(false);
|
|
10352
10836
|
const candidateCount = route.name === 'batchSelection' && batchDraft
|
|
10353
10837
|
? batchCandidates(batchDraft).length
|
|
10354
|
-
: route.name === '
|
|
10838
|
+
: route.name === 'intentInventory' || route.name === 'pluginContent'
|
|
10355
10839
|
|| route.name === 'portableSkillSelect'
|
|
10356
10840
|
|| route.name === 'sourceList' ? actions.length - bottomActions.length : pluginRows.length;
|
|
10357
10841
|
setFocus((value) => moveIndex(candidateCount, Math.min(value, Math.max(0, candidateCount - 1)), key.upArrow ? -1 : 1));
|
|
@@ -10387,12 +10871,12 @@ export function ConsoleAppV4(props) {
|
|
|
10387
10871
|
...(nextQuery ? { query: nextQuery } : {}), installFilter }).map((item) => `plugin:${item.pluginId}`);
|
|
10388
10872
|
retained = ids.indexOf(focusedId);
|
|
10389
10873
|
}
|
|
10390
|
-
else if (route.name === '
|
|
10391
|
-
const section = snapshot.router.current.context.
|
|
10392
|
-
const filter = snapshot.router.current.context.
|
|
10393
|
-
retained = props.controller.
|
|
10874
|
+
else if (route.name === 'intentInventory') {
|
|
10875
|
+
const section = snapshot.router.current.context.inventorySection ?? 'declared';
|
|
10876
|
+
const filter = snapshot.router.current.context.inventoryStatusFilter ?? 'all';
|
|
10877
|
+
retained = props.controller.listScopedInventoryRoots(route.scope, section).filter((root) => queryMatches(nextQuery, root.displayName, root.pluginId, root.sourceId)
|
|
10394
10878
|
&& (filter === 'all' || root.state === filter))
|
|
10395
|
-
.findIndex((root) => `
|
|
10879
|
+
.findIndex((root) => `inventory-root:${root.pluginId}` === focusedId);
|
|
10396
10880
|
}
|
|
10397
10881
|
else if (route.name === 'pluginContent') {
|
|
10398
10882
|
const type = snapshot.router.current.context.contentTypeTab ?? 'all';
|
|
@@ -10418,7 +10902,7 @@ export function ConsoleAppV4(props) {
|
|
|
10418
10902
|
if (route.name !== 'batchSelection') {
|
|
10419
10903
|
setQuery(nextQuery);
|
|
10420
10904
|
props.controller.updatePageContext({ query: nextQuery });
|
|
10421
|
-
if (route.name === '
|
|
10905
|
+
if (route.name === 'intentInventory') {
|
|
10422
10906
|
dynamicMenuFocus.current = undefined;
|
|
10423
10907
|
actionJumpAnchor.current = undefined;
|
|
10424
10908
|
}
|
|
@@ -10433,9 +10917,18 @@ export function ConsoleAppV4(props) {
|
|
|
10433
10917
|
// Paste and composed text are data, never multiple background commands.
|
|
10434
10918
|
if (input.length > 1 && !key.ctrl && !key.meta)
|
|
10435
10919
|
return;
|
|
10920
|
+
if (key.tab && route.name === 'configFields') {
|
|
10921
|
+
const index = CONFIG_PROFILE_KEYS.indexOf(route.layer.profile);
|
|
10922
|
+
const profile = CONFIG_PROFILE_KEYS[(index + (key.shift ? -1 : 1) + CONFIG_PROFILE_KEYS.length) % CONFIG_PROFILE_KEYS.length];
|
|
10923
|
+
props.controller.replace({ ...route, layer: { ...route.layer, profile } });
|
|
10924
|
+
setFocus(0);
|
|
10925
|
+
setScrollOffset(0);
|
|
10926
|
+
return;
|
|
10927
|
+
}
|
|
10436
10928
|
if (key.tab)
|
|
10437
10929
|
return;
|
|
10438
|
-
if ((route.name === '
|
|
10930
|
+
if ((route.name === 'intentInventory' && !showTechnical
|
|
10931
|
+
|| route.name === 'intentInventory' && !showTechnical
|
|
10439
10932
|
|| route.name === 'pluginContent' && !contentReader
|
|
10440
10933
|
|| route.name === 'sourceList' || route.name === 'portableSkillSelect' && !portableToolsOpen) && input === '/') {
|
|
10441
10934
|
setSearchCursor([...new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
@@ -10465,7 +10958,7 @@ export function ConsoleAppV4(props) {
|
|
|
10465
10958
|
const entityIndex = anchor.entityId
|
|
10466
10959
|
? actions.findIndex((action) => action.pluginId === anchor.entityId) : -1;
|
|
10467
10960
|
const next = entityIndex >= 0 ? entityIndex : Math.min(anchor.index, Math.max(0, firstAction - 1));
|
|
10468
|
-
if (route.name === '
|
|
10961
|
+
if ((route.name === 'intentInventory') && dynamicActionIds && dynamicActionSignature) {
|
|
10469
10962
|
dynamicMenuFocus.current = { routeKey, actionIds: dynamicActionSignature,
|
|
10470
10963
|
focusedActionId: dynamicActionIds[next] };
|
|
10471
10964
|
}
|
|
@@ -10474,7 +10967,7 @@ export function ConsoleAppV4(props) {
|
|
|
10474
10967
|
}
|
|
10475
10968
|
else {
|
|
10476
10969
|
actionJumpAnchor.current = { routeKey, index: focus, entityId: actions[focus]?.pluginId };
|
|
10477
|
-
if (route.name === '
|
|
10970
|
+
if ((route.name === 'intentInventory') && dynamicActionIds && dynamicActionSignature) {
|
|
10478
10971
|
dynamicMenuFocus.current = { routeKey, actionIds: dynamicActionSignature,
|
|
10479
10972
|
focusedActionId: dynamicActionIds[firstAction] };
|
|
10480
10973
|
}
|
|
@@ -10544,7 +11037,7 @@ export function ConsoleAppV4(props) {
|
|
|
10544
11037
|
back();
|
|
10545
11038
|
}
|
|
10546
11039
|
else if (globalAction === 'back' && route.name === 'configLayer') {
|
|
10547
|
-
if (configCandidate
|
|
11040
|
+
if (configCandidate) {
|
|
10548
11041
|
setConfigCandidate(undefined);
|
|
10549
11042
|
const owner = `${route.pluginId}\0${route.fieldKey}`;
|
|
10550
11043
|
setObjectDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(owner))));
|
|
@@ -10559,10 +11052,7 @@ export function ConsoleAppV4(props) {
|
|
|
10559
11052
|
}
|
|
10560
11053
|
else if (globalAction === 'back'
|
|
10561
11054
|
&& (route.name === 'configFieldEditor' || route.name === 'configFieldAction')) {
|
|
10562
|
-
const returnToReferenceScope =
|
|
10563
|
-
&& configReferenceScalars(activeTopField.definition).length > 0
|
|
10564
|
-
&& ['user', 'project'].filter((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
10565
|
-
.some((field) => field.key === activeTopField.key && configFieldCanSet(field))).length > 1;
|
|
11055
|
+
const returnToReferenceScope = false;
|
|
10566
11056
|
if (!returnToReferenceScope)
|
|
10567
11057
|
setConfigCandidate(undefined);
|
|
10568
11058
|
if (activeArrayKey && !arrayItem && !returnToReferenceScope) {
|
|
@@ -10631,21 +11121,24 @@ export function ConsoleAppV4(props) {
|
|
|
10631
11121
|
setFocus(0);
|
|
10632
11122
|
setScrollOffset(0);
|
|
10633
11123
|
}
|
|
10634
|
-
else if (route.name === '
|
|
11124
|
+
else if (route.name === 'intentInventory' && input.toLowerCase() === 'f' && !showTechnical) {
|
|
10635
11125
|
toggleProjectStatusFilter?.();
|
|
10636
11126
|
}
|
|
10637
|
-
else if (route.name === '
|
|
10638
|
-
runAction(bottomActions.find((action) => action.actionId === '
|
|
11127
|
+
else if (route.name === 'intentInventory' && input.toLowerCase() === 'r') {
|
|
11128
|
+
runAction(bottomActions.find((action) => action.actionId === 'inventory-recheck'));
|
|
10639
11129
|
}
|
|
10640
|
-
else if (route.name === '
|
|
10641
|
-
runAction(bottomActions.find((action) => action.actionId === '
|
|
11130
|
+
else if (route.name === 'intentInventory' && input.toLowerCase() === 'c') {
|
|
11131
|
+
runAction(bottomActions.find((action) => action.actionId === 'inventory-sync-all'));
|
|
10642
11132
|
}
|
|
10643
|
-
else if (route.name === '
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
11133
|
+
else if (route.name === 'intentInventory' && input === ' ' && !showTechnical) {
|
|
11134
|
+
const pluginId = actions[effectiveFocus]?.pluginId;
|
|
11135
|
+
if (pluginId) {
|
|
11136
|
+
const selectedKey = route.scope === 'project' ? `project:${route.projectKey ?? ''}` : 'user';
|
|
11137
|
+
const selected = scopedSelected.current[selectedKey] ?? [];
|
|
11138
|
+
scopedSelected.current[selectedKey] = selected.includes(pluginId)
|
|
11139
|
+
? selected.filter((item) => item !== pluginId) : [...selected, pluginId];
|
|
11140
|
+
setScopedSelectionRevision((value) => value + 1);
|
|
11141
|
+
}
|
|
10649
11142
|
}
|
|
10650
11143
|
else if (route.name === 'batchInstallScope' && input.toLowerCase() === 'c') {
|
|
10651
11144
|
runAction(actions.find((action) => action.actionId === 'batch-placement-confirm'));
|
|
@@ -10700,14 +11193,7 @@ export function ConsoleAppV4(props) {
|
|
|
10700
11193
|
runAction(bottomActions[0]);
|
|
10701
11194
|
}
|
|
10702
11195
|
else if (route.name === 'configFields' && input.toLowerCase() === 'u') {
|
|
10703
|
-
|
|
10704
|
-
const field = currentConfigPage?.groups.flatMap((group) => group.fields)
|
|
10705
|
-
.find((candidate) => candidate.key === key);
|
|
10706
|
-
const canUnset = ['project', 'user'].some((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
10707
|
-
.some((candidate) => candidate.key === key && configFieldCanUnset(candidate)));
|
|
10708
|
-
if (field && canUnset) {
|
|
10709
|
-
removeDraft(field.key);
|
|
10710
|
-
}
|
|
11196
|
+
runAction(actions.find((action) => action.actionId === 'config-reset-profile'));
|
|
10711
11197
|
}
|
|
10712
11198
|
else if (route.name === 'configFields' && !route.view && visibleConfigTabs.length > 1
|
|
10713
11199
|
&& (key.leftArrow || key.rightArrow)) {
|
|
@@ -10718,7 +11204,7 @@ export function ConsoleAppV4(props) {
|
|
|
10718
11204
|
setFocus(0);
|
|
10719
11205
|
setScrollOffset(0);
|
|
10720
11206
|
}
|
|
10721
|
-
else if (route.name === '
|
|
11207
|
+
else if ((route.name === 'intentInventory') && !showTechnical
|
|
10722
11208
|
&& (key.leftArrow || key.rightArrow)) {
|
|
10723
11209
|
moveProjectSection?.(key.leftArrow ? -1 : 1);
|
|
10724
11210
|
}
|
|
@@ -10750,6 +11236,20 @@ export function ConsoleAppV4(props) {
|
|
|
10750
11236
|
if (candidate)
|
|
10751
11237
|
props.controller.togglePortableSkill(candidate.name);
|
|
10752
11238
|
}
|
|
11239
|
+
else if (route.name === 'pluginDetail' && detailNavigation && (key.leftArrow || key.rightArrow)) {
|
|
11240
|
+
return;
|
|
11241
|
+
}
|
|
11242
|
+
else if (route.name === 'pluginDetail' && detailNavigation && (key.pageUp || key.pageDown)) {
|
|
11243
|
+
setBatchTargetOffset((value) => Math.max(0, Math.min(detailNavigation.maxOffset, value + (key.pageUp ? -detailNavigation.lineLimit : detailNavigation.lineLimit))));
|
|
11244
|
+
}
|
|
11245
|
+
else if (route.name === 'pluginDetail' && detailNavigation && focus === 0
|
|
11246
|
+
&& (key.upArrow || input === 'k') && batchTargetOffset > 0) {
|
|
11247
|
+
setBatchTargetOffset((value) => value - 1);
|
|
11248
|
+
}
|
|
11249
|
+
else if (route.name === 'pluginDetail' && detailNavigation
|
|
11250
|
+
&& (key.downArrow || input === 'j') && batchTargetOffset < detailNavigation.maxOffset) {
|
|
11251
|
+
setBatchTargetOffset((value) => value + 1);
|
|
11252
|
+
}
|
|
10753
11253
|
else if (entityCardNavigation && focus < entityCardNavigation.itemCount
|
|
10754
11254
|
&& (key.upArrow || input.toLowerCase() === 'k') && batchTargetOffset > 0) {
|
|
10755
11255
|
setBatchTargetOffset((value) => value - 1);
|
|
@@ -10834,7 +11334,14 @@ export function ConsoleAppV4(props) {
|
|
|
10834
11334
|
}
|
|
10835
11335
|
if (actionError)
|
|
10836
11336
|
description = actionError;
|
|
10837
|
-
const base = (_jsx(ScreenFrame, { title:
|
|
11337
|
+
const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, headerHint: route.name === 'pluginList' ? { key: 'm', label: '多选' } : undefined, help: portableEmptySelectionOpen ? 'Enter 返回选择 · Esc 返回' : help, size: size, children: sourceTransportOverlay ? null : content }));
|
|
11338
|
+
if (scopedFlow?.stage === 'risk') {
|
|
11339
|
+
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
11340
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor((size.columns - modalWidth) / 2)), width: modalWidth, flexDirection: "column", children: _jsxs(ReviewCard, { title: "\u8BF7\u786E\u8BA4\u672C\u6B21\u98CE\u9669", description: "\u786E\u8BA4\u53EA\u5BF9\u5F53\u524D\u64CD\u4F5C\u6C47\u603B\u6709\u6548\uFF1B\u9ED8\u8BA4\u8FD4\u56DE\u68C0\u67E5\u3002", children: [readingPage(readingText ?? '', modalWidth - 6, Math.max(1, pageActionBudget(7))), _jsx(SemanticRows, { actions: actions, focus: scopedFlow.focus, kind: "action", compact: true })] }) })] });
|
|
11341
|
+
}
|
|
11342
|
+
if (scopedDiscardIntent) {
|
|
11343
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 6)), width: Math.max(40, Math.floor(size.columns * 2 / 3)), flexDirection: "column", children: _jsxs(ReviewCard, { title: "\u653E\u5F03\u672A\u4FDD\u5B58\u7684\u5DE5\u5177\u9009\u62E9\uFF1F", description: "\u6B64\u6E05\u5355\u7684\u672C\u673A\u5DE5\u5177\u8349\u7A3F\u5C1A\u672A\u4FDD\u5B58\u3002", children: [_jsx(SemanticRows, { actions: [{ label: '继续编辑' }, { label: '放弃修改', status: 'warning' }], focus: 1, kind: "action", compact: true }), _jsx(Text, { color: tuiTheme.colors.muted, children: "y / Enter \u653E\u5F03 \u00B7 Esc \u8FD4\u56DE" })] }) })] });
|
|
11344
|
+
}
|
|
10838
11345
|
if (portableEmptySelectionOpen) {
|
|
10839
11346
|
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
10840
11347
|
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 7, marginLeft: Math.max(2, Math.floor((size.columns - modalWidth) / 2)), width: modalWidth, flexDirection: "column", backgroundColor: tuiTheme.colors.panelBackground, children: _jsx(ReviewCard, { title: "\u8FD8\u6CA1\u6709\u9009\u62E9 Skill", description: "\u7528 Space \u52FE\u9009\uFF1B\u4E5F\u53EF\u4EE5\u6253\u5F00\u9009\u62E9\u5DE5\u5177\u3002", children: _jsx(SemanticRows, { actions: [{ label: '返回选择' }], focus: 0, kind: "action", compact: true }) }) })] });
|
|
@@ -10846,27 +11353,6 @@ export function ConsoleAppV4(props) {
|
|
|
10846
11353
|
definition: activeField.definition.items, value: row.value, summary: activeField.itemSummary,
|
|
10847
11354
|
}).text : '', children: [_jsx(SemanticRows, { actions: [{ label: '上移' }, { label: '下移' }, { label: '删除这一项', status: 'error' }], focus: arrayToolFocus, kind: "action", compact: true }), _jsx(Text, { children: "\u2191\u2193 \u9009\u62E9 \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u8FD4\u56DE" })] }) })] });
|
|
10848
11355
|
}
|
|
10849
|
-
if (projectReminderOpen && snapshot.projectIntentReminder) {
|
|
10850
|
-
const reminder = snapshot.projectIntentReminder;
|
|
10851
|
-
const pluginCount = new Set([
|
|
10852
|
-
...reminder.actionableTargets,
|
|
10853
|
-
...reminder.blockedTargets,
|
|
10854
|
-
...reminder.unconfirmedTargets,
|
|
10855
|
-
].map((target) => target.pluginId)).size;
|
|
10856
|
-
const reminderActions = [
|
|
10857
|
-
{ label: '稍后处理', description: '首页会保留项目插件入口' },
|
|
10858
|
-
{
|
|
10859
|
-
label: `查看并同步 ${reminder.actionableTargets.length} 个位置`,
|
|
10860
|
-
description: `先显示完整操作汇总和脚本影响,再单独确认;保留 ${reminder.blockedTargets.length} 个受阻位置和 ${reminder.unconfirmedTargets.length} 个未确认位置`,
|
|
10861
|
-
disabled: reminder.actionableTargets.length === 0 ? '当前没有已确认可同步的目标' : undefined,
|
|
10862
|
-
},
|
|
10863
|
-
{ label: '查看全部项目插件', description: '包含需要先处理和无法确认的项目目标' },
|
|
10864
|
-
];
|
|
10865
|
-
const reminderWidth = Math.max(40, size.columns - (policy.mode === 'compact' ? 8 : 16));
|
|
10866
|
-
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: policy.mode === 'compact' ? 2 : 5, marginLeft: Math.max(2, Math.floor((size.columns - reminderWidth) / 2)), width: reminderWidth, flexDirection: "column", children: _jsxs(ReviewCard, { title: `本项目有 ${pluginCount} 个插件尚未就绪`, description: `${reminder.actionableTargets.length} 个位置可同步;`
|
|
10867
|
-
+ `${reminder.blockedTargets.length} 个位置需要先处理;`
|
|
10868
|
-
+ `${reminder.unconfirmedTargets.length} 个位置未确认。`, children: [_jsx(StaticRow, { title: "\u6309\u9879\u76EE\u5171\u4EAB\u8BBE\u7F6E\u6062\u590D\uFF0C\u4E2A\u4EBA\u8BBE\u7F6E\u4E0D\u4F1A\u88AB\u6E05\u7A7A\u3002", compact: true }), _jsx(StaticRow, { title: "\u786E\u8BA4\u540C\u6B65\u524D\u53EF\u67E5\u770B\u811A\u672C\u548C\u5B8C\u6574\u5F71\u54CD\u3002", status: "warning", compact: true }), _jsx(SemanticRows, { actions: reminderActions, focus: overlayFocus, kind: "action", compact: true }), _jsx(StaticRow, { title: "\u2191\u2193 \u9009\u62E9 \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u7A0D\u540E\u5904\u7406", status: "muted", compact: true })] }) })] });
|
|
10869
|
-
}
|
|
10870
11356
|
if (operationConfirmation && snapshot.activeReview) {
|
|
10871
11357
|
const review = snapshot.activeReview;
|
|
10872
11358
|
const portableUserConfirmation = review.operationKind === 'portableInstall'
|