@lesliechan721/aw 0.1.0-beta.10 → 0.1.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring/assets/aw-config.d.mts +1 -50
- package/dist/authoring/assets/aw-config.mjs +58 -928
- package/dist/authoring/assets/aw-config.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +28 -33
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +165 -45
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/bundle-plugins.js +18 -4
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.d.ts +2 -0
- package/dist/scripts/test-plugin-scripts.js +21 -5
- 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 +1 -1
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/commands/catalog-dev-source.d.ts +6 -3
- package/dist/src/commands/catalog-dev-source.js +96 -35
- package/dist/src/commands/catalog-dev-source.js.map +1 -1
- package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
- package/dist/src/commands/catalog-publication-inspection.js +54 -8
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.js +1 -1
- package/dist/src/commands/catalog-publication-journal.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +19 -4
- package/dist/src/commands/catalog-source.js +121 -12
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-store.d.ts +26 -7
- package/dist/src/commands/catalog-store.js +361 -42
- package/dist/src/commands/catalog-store.js.map +1 -1
- package/dist/src/commands/console.js +28 -17
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/operation-plan.d.ts +2 -1
- package/dist/src/commands/operation-plan.js +24 -0
- package/dist/src/commands/operation-plan.js.map +1 -1
- package/dist/src/commands/operation-planner.d.ts +2 -2
- package/dist/src/commands/operation-planner.js +11 -19
- package/dist/src/commands/operation-planner.js.map +1 -1
- package/dist/src/commands/options.d.ts +2 -2
- package/dist/src/commands/options.js +18 -4
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +2 -2
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +7 -0
- package/dist/src/commands/rebuild-command.js +769 -279
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +24 -47
- package/dist/src/commands/rebuild-integration.js +1074 -1011
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +50 -12
- package/dist/src/commands/rebuild-operations.js +207 -101
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/status-v6.d.ts +21 -2
- package/dist/src/commands/status-v6.js +163 -31
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/sync-command.d.ts +17 -0
- package/dist/src/commands/sync-command.js +45 -37
- package/dist/src/commands/sync-command.js.map +1 -1
- package/dist/src/commands/write-commands.d.ts +5 -0
- package/dist/src/commands/write-commands.js +50 -3
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/generators/local-plugin.js +14 -4
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.js +216 -85
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.js +1 -3
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +1 -3
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/materializer.js +6 -6
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/tui/catalog-generation.js +41 -1
- package/dist/src/tui/catalog-generation.js.map +1 -1
- package/dist/src/tui/catalog-sources/session.d.ts +1 -0
- package/dist/src/tui/catalog-sources/session.js +16 -3
- package/dist/src/tui/catalog-sources/session.js.map +1 -1
- package/dist/src/tui/catalog-sources/types.d.ts +3 -2
- package/dist/src/tui/components.d.ts +4 -0
- package/dist/src/tui/components.js +36 -24
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +13 -4
- package/dist/src/tui/console-app-v4.js +2067 -839
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +585 -33
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +152 -6
- package/dist/src/tui/controller.js +1168 -156
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostic-projection.d.ts +22 -0
- package/dist/src/tui/diagnostic-projection.js +437 -0
- package/dist/src/tui/diagnostic-projection.js.map +1 -0
- package/dist/src/tui/diagnostics.d.ts +1 -1
- package/dist/src/tui/diagnostics.js +6 -1
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/glossary.d.ts +23 -0
- package/dist/src/tui/glossary.js +42 -0
- package/dist/src/tui/glossary.js.map +1 -0
- package/dist/src/tui/index.d.ts +4 -1
- package/dist/src/tui/index.js +4 -1
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/keys.d.ts +44 -0
- package/dist/src/tui/keys.js +43 -15
- package/dist/src/tui/keys.js.map +1 -1
- package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
- package/dist/src/tui/lifecycle-action-view.js +34 -0
- package/dist/src/tui/lifecycle-action-view.js.map +1 -0
- package/dist/src/tui/model.d.ts +12 -2
- package/dist/src/tui/model.js +128 -52
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
- package/dist/src/tui/operation-diagnostic-screens.js +101 -0
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
- package/dist/src/tui/operation-history.d.ts +80 -0
- package/dist/src/tui/operation-history.js +130 -0
- package/dist/src/tui/operation-history.js.map +1 -0
- package/dist/src/tui/operation-review-planner.js +306 -86
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/router.d.ts +82 -17
- package/dist/src/tui/router.js +186 -38
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +546 -0
- package/dist/src/tui/screen-registry.js +179 -0
- package/dist/src/tui/screen-registry.js.map +1 -0
- package/dist/src/tui/session-console-app.d.ts +6 -0
- package/dist/src/tui/session-console-app.js +13 -0
- package/dist/src/tui/session-console-app.js.map +1 -0
- package/dist/src/tui/session-host.d.ts +44 -0
- package/dist/src/tui/session-host.js +197 -0
- package/dist/src/tui/session-host.js.map +1 -0
- package/dist/src/types.d.ts +64 -3
- package/dist/src/types.js +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.js +1 -1
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +2 -1
- package/dist/src/utils/artifact-renderer.js +52 -52
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +4 -4
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
- package/dist/src/utils/catalog-capability-projection.js +14 -0
- package/dist/src/utils/catalog-capability-projection.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
- package/dist/src/utils/catalog-generation-resolver.js +49 -27
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
- package/dist/src/utils/catalog-generation-service.d.ts +8 -1
- package/dist/src/utils/catalog-generation-service.js +81 -47
- package/dist/src/utils/catalog-generation-service.js.map +1 -1
- package/dist/src/utils/catalog-generation-store.d.ts +1 -0
- package/dist/src/utils/catalog-generation-store.js +60 -5
- package/dist/src/utils/catalog-generation-store.js.map +1 -1
- package/dist/src/utils/catalog-operation-context.js +1 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -1
- package/dist/src/utils/catalog-public-error.js +4 -0
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-source-impact.d.ts +3 -2
- package/dist/src/utils/catalog-source-impact.js +142 -20
- package/dist/src/utils/catalog-source-impact.js.map +1 -1
- package/dist/src/utils/catalog-source-references.js +95 -9
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +1 -1
- package/dist/src/utils/catalog-sources.js +19 -3
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/codex-manager.js +8 -0
- package/dist/src/utils/codex-manager.js.map +1 -1
- package/dist/src/utils/codex-native-observation.d.ts +34 -1
- package/dist/src/utils/codex-native-observation.js +132 -0
- package/dist/src/utils/codex-native-observation.js.map +1 -1
- package/dist/src/utils/codex-reconciler.d.ts +0 -2
- package/dist/src/utils/codex-reconciler.js.map +1 -1
- package/dist/src/utils/codex-shared-native.d.ts +2 -26
- package/dist/src/utils/codex-shared-native.js +8 -82
- package/dist/src/utils/codex-shared-native.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.d.ts +3 -0
- package/dist/src/utils/git-catalog-provider.js +40 -1
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +1 -0
- package/dist/src/utils/local-marketplace.js +69 -73
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.js +1 -0
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +9 -4
- package/dist/src/utils/native-cli.js +280 -34
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.js +2 -0
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/plugin-config-write.d.ts +15 -13
- package/dist/src/utils/plugin-config-write.js +418 -198
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +48 -4
- package/dist/src/utils/plugin-config.js +289 -38
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.d.ts +20 -12
- package/dist/src/utils/plugin-scripts.js +376 -109
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/process-tree.d.ts +2 -2
- package/dist/src/utils/process-tree.js +1 -1
- package/dist/src/utils/process-tree.js.map +1 -1
- package/dist/src/utils/project-boundary.d.ts +11 -1
- package/dist/src/utils/project-boundary.js +17 -8
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
- package/dist/src/utils/safe-operation-diagnostic.js +33 -0
- package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
- package/dist/src/utils/sensitive-content.d.ts +1 -0
- package/dist/src/utils/sensitive-content.js +15 -0
- package/dist/src/utils/sensitive-content.js.map +1 -0
- package/dist/src/utils/windows-claude-shim.d.ts +9 -0
- package/dist/src/utils/windows-claude-shim.js +141 -0
- package/dist/src/utils/windows-claude-shim.js.map +1 -0
- package/dist/vitest.config.js +91 -10
- package/dist/vitest.config.js.map +1 -1
- package/package.json +9 -9
- package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
- package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
- package/dist/authoring/templates/aw-config-runtime.mjs.map +0 -1
|
@@ -7,33 +7,54 @@ import { CATALOG_BETA_REF, CATALOG_LATEST_REF, OFFICIAL_CATALOG_SOURCE_SSH_URL,
|
|
|
7
7
|
import { CATALOG_SOURCE_ID_PATTERN } from '../utils/catalog-sources.js';
|
|
8
8
|
import { canonicalizeCatalogGitUrl } from '../utils/catalog-source-identity.js';
|
|
9
9
|
import { normalizePortableAgents } from '../portable/commands.js';
|
|
10
|
-
import {
|
|
10
|
+
import { selectLifecycleRetryAction, } from '../commands/operation-planner.js';
|
|
11
|
+
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, SCRIPT_TIMEOUT_MAX_MS, SCRIPT_TIMEOUT_MIN_MS, } from '../commands/options.js';
|
|
11
12
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
12
|
-
import { ActionRow, Callout, ChoiceRow, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
13
|
+
import { ActionRow, Callout, ChoiceRow, diagnosticDetailRowCount, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
13
14
|
import { safeDiagnosticCode, sanitizeDiagnosticText, } from './diagnostics.js';
|
|
14
15
|
import { extractPortableError, TuiConfigInvalidStoreError, userIssueKey, } from './controller.js';
|
|
15
16
|
import { catalogSourceFreshnessLabel, catalogSourceListPresentation, } from './catalog-sources/screens.js';
|
|
17
|
+
import { operationDiagnosticLineCount, operationHistoryMeta, operationHistoryState, operationReportLineCount, OperationDiagnosticScreen, OperationReportPreviewScreen, } from './operation-diagnostic-screens.js';
|
|
16
18
|
import { resolveTuiSizePolicy } from './layout.js';
|
|
19
|
+
import { platformLabel, scopeLabel } from './glossary.js';
|
|
17
20
|
import { InputDisplay, removeLastGrapheme } from './input-display.js';
|
|
18
21
|
import { catalogRefPolicyForPreset } from './source-ref-presets.js';
|
|
19
22
|
import { buildPlatformActions, buildPluginDetailActions, buildPluginPreviewActions, configObjectHasSensitiveChild, hasInstalledPlacement, configValuesEqual, } from './model.js';
|
|
23
|
+
import { resolveTuiGlobalKeyAction, resolveTuiSessionNavigationKey } from './keys.js';
|
|
20
24
|
import { tuiTheme } from './theme.js';
|
|
21
25
|
import { contentRowBudget, rowViewport } from './viewport.js';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
import { resolveRouteScreenId, routeDefinition, TUI_SCREEN_IDS, unreachableTuiRoute, } from './screen-registry.js';
|
|
27
|
+
export const CONSOLE_V4_SCREEN_IDS = TUI_SCREEN_IDS;
|
|
28
|
+
function sourceOperationBlockLabel(reason) {
|
|
29
|
+
switch (reason) {
|
|
30
|
+
case 'configDraft': return '未保存的插件设置';
|
|
31
|
+
case 'sourceDraft': return '未保存的来源修改';
|
|
32
|
+
case 'batchDraft': return '未完成的批量操作选择';
|
|
33
|
+
case 'portableDraft': return '未完成的通用 Skills 选择';
|
|
34
|
+
case 'activeReview': return '等待确认的操作';
|
|
35
|
+
case 'reviewPreparation': return '正在准备确认信息的操作';
|
|
36
|
+
case 'writeProgress': return '正在执行的写入操作';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function catalogGenerationStateLabel(state) {
|
|
40
|
+
switch (state) {
|
|
41
|
+
case 'ready': return '可用';
|
|
42
|
+
case 'activationRequired': return '需要启用';
|
|
43
|
+
case 'repairRequired': return '需要修复';
|
|
44
|
+
case 'configMismatch': return '来源设置已变化';
|
|
45
|
+
case 'stateInvalid': return '版本状态需要恢复';
|
|
46
|
+
case 'incompatible': return '当前 AW 不兼容';
|
|
47
|
+
case 'unavailable': return '不可用';
|
|
48
|
+
case undefined: return '尚未取得状态';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function productResultMessage(message) {
|
|
52
|
+
if (message === '后续:当前项目 sync')
|
|
53
|
+
return '后续:同步当前项目位置';
|
|
54
|
+
if (message === '后续:所有项目 sync')
|
|
55
|
+
return '后续:同步所有项目位置';
|
|
56
|
+
return message;
|
|
57
|
+
}
|
|
37
58
|
function pluginMenuActionId(action) {
|
|
38
59
|
return action.id === 'platform' || action.id === 'scopeConflict'
|
|
39
60
|
? `${action.id}:${action.platform}`
|
|
@@ -44,6 +65,8 @@ function pluginMenuActionId(action) {
|
|
|
44
65
|
: action.id;
|
|
45
66
|
}
|
|
46
67
|
function isCharacterInputMode(route, searchMode) {
|
|
68
|
+
if (routeDefinition(route).keymap !== 'menuOrTextInput')
|
|
69
|
+
return false;
|
|
47
70
|
return searchMode
|
|
48
71
|
|| route.name === 'portableAgentInput'
|
|
49
72
|
|| route.name === 'configFieldEditor'
|
|
@@ -51,9 +74,10 @@ function isCharacterInputMode(route, searchMode) {
|
|
|
51
74
|
&& route.mode !== 'choices'
|
|
52
75
|
|| route.name === 'configObjectPropertyEditor'
|
|
53
76
|
&& (route.stage === 'key' || route.stage === 'custom')
|
|
54
|
-
|| route.name === 'sourceAdd' && route.step !== 'type' && route.step !== 'refPolicy'
|
|
77
|
+
|| route.name === 'sourceAdd' && route.step !== 'type' && route.step !== 'refPolicy'
|
|
78
|
+
|| route.name === 'scriptTimeout';
|
|
55
79
|
}
|
|
56
|
-
function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy) {
|
|
80
|
+
function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy, sessionNavigation) {
|
|
57
81
|
const rows = [];
|
|
58
82
|
if (!busy && actionCount > 1)
|
|
59
83
|
rows.push(['↑/↓ · j/k', '移动当前唯一焦点']);
|
|
@@ -71,8 +95,13 @@ function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy) {
|
|
|
71
95
|
rows.push(['Space', '切换当前 Skill']);
|
|
72
96
|
rows.push(['a', '全选 Skills']);
|
|
73
97
|
}
|
|
98
|
+
if (route.name === 'batchSelection') {
|
|
99
|
+
rows.push(['Space', '切换当前插件']);
|
|
100
|
+
rows.push(['a', '全选或清空当前列表']);
|
|
101
|
+
rows.push(['c', '继续选择位置和平台']);
|
|
102
|
+
}
|
|
74
103
|
if (route.name === 'configFields' && route.layer.scope !== 'effective') {
|
|
75
|
-
rows.push(['s', '
|
|
104
|
+
rows.push(['s', '查看保存汇总']);
|
|
76
105
|
rows.push(['u', '恢复当前字段继承']);
|
|
77
106
|
}
|
|
78
107
|
if (route.name === 'sourceList' || route.name === 'sourceDetail') {
|
|
@@ -81,8 +110,16 @@ function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy) {
|
|
|
81
110
|
if (supportsTechnicalDetails) {
|
|
82
111
|
rows.push(['d', route.name === 'progress' ? '打开技术进度' : '打开技术详情']);
|
|
83
112
|
}
|
|
113
|
+
if (sessionNavigation && !busy && !isCharacterInputMode(route, false)) {
|
|
114
|
+
rows.push(['h', '返回首页']);
|
|
115
|
+
rows.push(['H', '重新读取本地状态']);
|
|
116
|
+
}
|
|
117
|
+
if (sessionNavigation && busy) {
|
|
118
|
+
rows.push(['h / H', '操作成功后返回首页或重新读取']);
|
|
119
|
+
rows.push(['Esc', '撤销等待中的页面跳转']);
|
|
120
|
+
}
|
|
84
121
|
rows.push(['q / Ctrl-C', busy ? '操作完成后退出' : '退出控制台']);
|
|
85
|
-
rows.push(['?', '关闭帮助']);
|
|
122
|
+
rows.push(['? / o', '关闭帮助 / 查看操作记录']);
|
|
86
123
|
return rows;
|
|
87
124
|
}
|
|
88
125
|
function configOwnerKey(route) {
|
|
@@ -93,13 +130,24 @@ function configPageMatches(page, route) {
|
|
|
93
130
|
&& page.pluginId === route.pluginId
|
|
94
131
|
&& page.layer.scope === route.layer.scope);
|
|
95
132
|
}
|
|
96
|
-
function
|
|
97
|
-
return
|
|
133
|
+
function configFieldCanSet(field) {
|
|
134
|
+
return field.canSet ?? field.writable;
|
|
98
135
|
}
|
|
99
|
-
function
|
|
100
|
-
return
|
|
136
|
+
function configFieldCanUnset(field) {
|
|
137
|
+
return field.canUnset ?? (field.writable && field.configured);
|
|
138
|
+
}
|
|
139
|
+
function batchActionLabel(action) {
|
|
140
|
+
return {
|
|
141
|
+
install: '添加',
|
|
142
|
+
sync: '同步',
|
|
143
|
+
reinstall: '重新安装',
|
|
144
|
+
uninstall: '卸载',
|
|
145
|
+
cleanup: '清理',
|
|
146
|
+
}[action];
|
|
101
147
|
}
|
|
102
148
|
function statusText(plugin) {
|
|
149
|
+
if (plugin.configOnly)
|
|
150
|
+
return '已保存设置';
|
|
103
151
|
return {
|
|
104
152
|
available: '未添加',
|
|
105
153
|
installed: '可用',
|
|
@@ -110,6 +158,11 @@ function statusText(plugin) {
|
|
|
110
158
|
}[plugin.aggregateStatus];
|
|
111
159
|
}
|
|
112
160
|
function pluginPlacementSummary(plugin) {
|
|
161
|
+
if (plugin.configOnly) {
|
|
162
|
+
return plugin.savedConfigScopes.length > 0
|
|
163
|
+
? `已保存${plugin.savedConfigScopes.map(scopeLabel).join('、')}设置`
|
|
164
|
+
: '已保存设置';
|
|
165
|
+
}
|
|
113
166
|
return plugin.supportedPlatforms.map((platform) => {
|
|
114
167
|
const label = platformLabel(platform);
|
|
115
168
|
const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
|
|
@@ -145,6 +198,8 @@ function issueGroupLabel(group) {
|
|
|
145
198
|
}[group];
|
|
146
199
|
}
|
|
147
200
|
function pluginRowStatus(plugin) {
|
|
201
|
+
if (plugin.configOnly)
|
|
202
|
+
return 'warning';
|
|
148
203
|
if (plugin.aggregateStatus === 'needsAttention')
|
|
149
204
|
return 'warning';
|
|
150
205
|
if (plugin.aggregateStatus === 'installed')
|
|
@@ -303,12 +358,27 @@ function portableBlockerDiagnostic(route) {
|
|
|
303
358
|
},
|
|
304
359
|
};
|
|
305
360
|
}
|
|
361
|
+
function configUnavailableCanRecoverSource(route) {
|
|
362
|
+
if (route.storedScopes?.length)
|
|
363
|
+
return false;
|
|
364
|
+
return new Set([
|
|
365
|
+
'catalogSourceUnavailable',
|
|
366
|
+
'bundleUnavailable',
|
|
367
|
+
'sourceMissing',
|
|
368
|
+
'catalogGenerationUnavailable',
|
|
369
|
+
'catalogGenerationStateInvalid',
|
|
370
|
+
'catalogGenerationStoreInvalid',
|
|
371
|
+
'catalogStateInvalid',
|
|
372
|
+
'catalogStoreInvalid',
|
|
373
|
+
'configReadFailed',
|
|
374
|
+
]).has(route.diagnosticCode ?? '');
|
|
375
|
+
}
|
|
306
376
|
function configUnavailableDiagnostic(route) {
|
|
307
377
|
const invalidLayer = (route.invalidStores?.length ?? 0) > 0;
|
|
308
378
|
const invalidLayerLabel = route.invalidStores?.map((store) => store.scope === 'user' ? '个人默认' : '当前项目').join('、');
|
|
309
379
|
return {
|
|
310
380
|
severity: 'error',
|
|
311
|
-
title: invalidLayer ? '设置层无效,已阻止编辑' : '
|
|
381
|
+
title: invalidLayer ? '设置层无效,已阻止编辑' : '插件定义或来源不可用',
|
|
312
382
|
userMessage: invalidLayer
|
|
313
383
|
? '高优先级设置层无效,不能静默使用较低层或默认值。'
|
|
314
384
|
: '当前无法取得可信的字段定义,因此不能验证新值。',
|
|
@@ -322,7 +392,9 @@ function configUnavailableDiagnostic(route) {
|
|
|
322
392
|
],
|
|
323
393
|
nextStep: invalidLayer
|
|
324
394
|
? '确认后重置整个目标设置层;不会创建备份。'
|
|
325
|
-
:
|
|
395
|
+
: configUnavailableCanRecoverSource(route)
|
|
396
|
+
? '重新检查来源,然后再次打开设置。'
|
|
397
|
+
: '查看技术详情,确认字段定义不可用的原因。',
|
|
326
398
|
technical: {
|
|
327
399
|
code: safeDiagnosticCode(route.diagnosticCode, 'configUnavailable'),
|
|
328
400
|
stage: safeDiagnosticCode(route.diagnosticStage, 'read-config'),
|
|
@@ -334,20 +406,43 @@ function configUnavailableDiagnostic(route) {
|
|
|
334
406
|
],
|
|
335
407
|
checks: invalidLayer
|
|
336
408
|
? ['只删除所选 user 或 project 设置层。', '重置后重新读取继承值和默认值。']
|
|
337
|
-
: ['
|
|
409
|
+
: ['恢复插件来源和可信插件定义。', '重新检查来源后再次打开设置。'],
|
|
338
410
|
},
|
|
339
411
|
};
|
|
340
412
|
}
|
|
341
413
|
function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
342
414
|
const unavailable = state === 'unavailable' || state === 'error';
|
|
415
|
+
if (generation?.compatibility?.state === 'unsupported') {
|
|
416
|
+
const { requiredCapabilities, missingCapabilities } = generation.compatibility;
|
|
417
|
+
return {
|
|
418
|
+
severity: 'error',
|
|
419
|
+
title: '当前目录版本不兼容',
|
|
420
|
+
userMessage: `当前 AW 缺少 ${missingCapabilities.length} 项所需功能支持。`,
|
|
421
|
+
impact: [
|
|
422
|
+
{ state: 'blocked', message: '这个目录版本不能用于浏览、安装或同步。' },
|
|
423
|
+
{ state: 'preserved', message: '当前启用版本和已下载内容保持不变。' },
|
|
424
|
+
],
|
|
425
|
+
nextStep: '升级 AW 到支持所需能力的版本,或修改来源后重新启用。',
|
|
426
|
+
technical: {
|
|
427
|
+
code: 'awCapabilityUnsupported',
|
|
428
|
+
stage: 'resolve-source',
|
|
429
|
+
sourceId,
|
|
430
|
+
context: [{
|
|
431
|
+
key: 'compatibility',
|
|
432
|
+
value: `required: ${requiredCapabilities.join('、')}; missing: ${missingCapabilities.join('、')}`,
|
|
433
|
+
}],
|
|
434
|
+
checks: ['检查所需和缺少的 Catalog capability。', '升级 AW 后重新加载来源。'],
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
}
|
|
343
438
|
if (!unavailable && freshness?.state === 'checkFailed') {
|
|
344
439
|
return {
|
|
345
440
|
severity: 'warning',
|
|
346
441
|
title: '更新检查失败',
|
|
347
|
-
userMessage: '
|
|
442
|
+
userMessage: '待检查版本检查失败,但当前目录版本仍可用。',
|
|
348
443
|
impact: [
|
|
349
444
|
{ state: 'degraded', message: '当前无法判断是否有更新。' },
|
|
350
|
-
{ state: 'preserved', message: '
|
|
445
|
+
{ state: 'preserved', message: '浏览、安装和当前启用版本不受影响。' },
|
|
351
446
|
],
|
|
352
447
|
nextStep: '返回来源详情并选择“重新检查”。',
|
|
353
448
|
technical: {
|
|
@@ -362,15 +457,15 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
362
457
|
const hasActive = generation?.active !== undefined;
|
|
363
458
|
return {
|
|
364
459
|
severity: 'error',
|
|
365
|
-
title: '
|
|
460
|
+
title: '待检查版本不兼容',
|
|
366
461
|
userMessage: hasActive
|
|
367
|
-
? `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0}
|
|
368
|
-
: `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0}
|
|
462
|
+
? `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能。普通更新已阻止,当前目录版本保持不变。`
|
|
463
|
+
: `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能。这个来源没有可用目录版本。`,
|
|
369
464
|
impact: [
|
|
370
|
-
{ state: 'blocked', message: '
|
|
371
|
-
...(hasActive ? [{ state: 'preserved', message: '
|
|
465
|
+
{ state: 'blocked', message: '不能直接切换到这个待检查版本。' },
|
|
466
|
+
...(hasActive ? [{ state: 'preserved', message: '当前目录版本和插件浏览仍可用。' }] : []),
|
|
372
467
|
],
|
|
373
|
-
nextStep: '升级 AW
|
|
468
|
+
nextStep: '升级 AW 到支持待检查版本所需功能的版本,或编辑来源后重新验证。',
|
|
374
469
|
technical: {
|
|
375
470
|
code: safeDiagnosticCode(freshness.errorCode, 'catalogincompatible'),
|
|
376
471
|
stage: 'compare-candidate',
|
|
@@ -386,19 +481,19 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
386
481
|
|| freshness?.state === 'rollbackDetected')) {
|
|
387
482
|
return {
|
|
388
483
|
severity: 'error',
|
|
389
|
-
title: freshness.state === 'versionConflict' ? '
|
|
390
|
-
: '
|
|
391
|
-
userMessage: '
|
|
484
|
+
title: freshness.state === 'versionConflict' ? '插件目录版本冲突'
|
|
485
|
+
: '检测到插件目录版本回退',
|
|
486
|
+
userMessage: '普通更新已阻止。当前目录版本保持不变。',
|
|
392
487
|
impact: [
|
|
393
|
-
{ state: 'blocked', message: '
|
|
394
|
-
{ state: 'preserved', message: '
|
|
488
|
+
{ state: 'blocked', message: '不能直接切换到这个待检查版本。' },
|
|
489
|
+
{ state: 'preserved', message: '当前目录版本和插件浏览仍可用。' },
|
|
395
490
|
],
|
|
396
|
-
nextStep: '
|
|
491
|
+
nextStep: '检查待检查版本、来源引用和发布身份后,再决定是否修改来源。',
|
|
397
492
|
technical: {
|
|
398
493
|
code: safeDiagnosticCode(freshness.errorCode, `catalog${freshness.state}`),
|
|
399
494
|
stage: 'compare-candidate',
|
|
400
495
|
sourceId,
|
|
401
|
-
checks: ['
|
|
496
|
+
checks: ['检查当前版本和待检查版本。', '检查来源引用和发布身份。'],
|
|
402
497
|
},
|
|
403
498
|
};
|
|
404
499
|
}
|
|
@@ -407,7 +502,7 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
407
502
|
const metadataInvalid = generation?.errorCode === 'catalogGenerationMetadataInvalid';
|
|
408
503
|
return {
|
|
409
504
|
severity: 'error',
|
|
410
|
-
title: '
|
|
505
|
+
title: '插件来源不可用',
|
|
411
506
|
userMessage: '当前无法读取这个来源。',
|
|
412
507
|
impact: [
|
|
413
508
|
{ state: 'blocked', message: '当前不能浏览或安装这个来源中的插件。' },
|
|
@@ -415,7 +510,7 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
415
510
|
],
|
|
416
511
|
nextStep: metadataInvalid && generation.recoveryEntryPath
|
|
417
512
|
? '在来源详情中选择“重新下载并修复”。'
|
|
418
|
-
: '
|
|
513
|
+
: '在来源详情中重新取得或修复目录版本;如仍失败,请编辑来源地址、路径或引用。',
|
|
419
514
|
technical: {
|
|
420
515
|
code: safeDiagnosticCode(generation?.errorCode ?? detail?.errorCode, 'catalogSourceUnavailable'),
|
|
421
516
|
stage: 'resolve-source',
|
|
@@ -438,11 +533,10 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
438
533
|
function capabilityDiagnostic(plugin) {
|
|
439
534
|
const missingCapabilities = plugin.missingCapabilities?.join('、')
|
|
440
535
|
?? 'Status 未提供具体 capability';
|
|
441
|
-
const recovery =
|
|
442
|
-
?? '升级 AW CLI 到支持插件所需 capability 的版本后重试。';
|
|
536
|
+
const recovery = '升级 AW CLI 到支持此插件的版本后重试。';
|
|
443
537
|
return {
|
|
444
538
|
severity: 'error',
|
|
445
|
-
title: 'AW
|
|
539
|
+
title: '当前 AW 版本不支持此插件',
|
|
446
540
|
userMessage: '当前 AW CLI 不能安装或重新安装这个插件。来源保持可用。',
|
|
447
541
|
impact: [
|
|
448
542
|
{ state: 'blocked', message: '安装和重新安装会保持阻断,直到 AW CLI 升级。' },
|
|
@@ -494,7 +588,7 @@ function SemanticRows(props) {
|
|
|
494
588
|
}
|
|
495
589
|
function PluginRows(props) {
|
|
496
590
|
return (_jsx(Box, { flexDirection: "column", children: props.rows.slice(props.viewport.offset, props.viewport.offset + props.viewport.limit)
|
|
497
|
-
.map((row, visibleIndex) => (_jsx(PluginListRow, { name: row.name, sourceLabel: row.sourceLabel, description: row.description, placementSummary: row.placementSummary, statusLabel: row.statusLabel, status: row.status, focused: props.viewport.offset + visibleIndex === props.focus }, row.pluginId))) }));
|
|
591
|
+
.map((row, visibleIndex) => row.menuAction ? (_jsx(TaskRow, { title: row.name, description: row.description, meta: row.statusLabel, status: row.status, focused: props.viewport.offset + visibleIndex === props.focus, compact: true }, row.pluginId)) : (_jsx(PluginListRow, { name: row.name, sourceLabel: row.sourceLabel, description: row.description, attention: row.attention, placementSummary: row.placementSummary, statusLabel: row.statusLabel, status: row.status, focused: props.viewport.offset + visibleIndex === props.focus }, row.pluginId))) }));
|
|
498
592
|
}
|
|
499
593
|
function IssueTechnicalDetails(props) {
|
|
500
594
|
const visible = props.details.slice(0, props.compact ? 1 : 2);
|
|
@@ -576,7 +670,7 @@ function placementStatus(placement) {
|
|
|
576
670
|
return {
|
|
577
671
|
value: `${placement.desiredScopes?.map(scopeLabel).join('+') || scopeLabel(placement.scope)} · ${placement.actualState === 'needsSync' ? '需要同步' : placement.actualState === 'blocked' ? '已阻断' : placement.health === 'ready' ? '可用' : placement.health === 'incomplete' ? '需要处理' : '不可用'}`,
|
|
578
672
|
status: placement.actualState === 'blocked' ? 'error' : placement.health === 'ready' ? 'success' : placement.health === 'incomplete' ? 'warning' : 'error',
|
|
579
|
-
description: placement.actualState === 'needsSync' ? '
|
|
673
|
+
description: placement.actualState === 'needsSync' ? '已保存的使用设置与当前安装不同;可安全同步'
|
|
580
674
|
: placement.actualState === 'blocked' ? '当前有安全阻断;查看恢复路径'
|
|
581
675
|
: placement.health === 'ready' ? '当前检查正常' : '查看影响和建议操作',
|
|
582
676
|
};
|
|
@@ -608,7 +702,7 @@ function dependencyPlacementLabel(item) {
|
|
|
608
702
|
const state = entry.actualState === 'needsSync' ? '需同步'
|
|
609
703
|
: entry.actualState === 'blocked' ? '已阻断'
|
|
610
704
|
: entry.actualState === 'ready' ? '可用' : aggregate.value;
|
|
611
|
-
return `${platformLabel(entry.platform)} ·
|
|
705
|
+
return `${platformLabel(entry.platform)} · 已保存 ${desired} · 当前 ${actual} · ${state}`;
|
|
612
706
|
}).join(' | ');
|
|
613
707
|
}
|
|
614
708
|
if (item.aggregateStatus !== 'ready')
|
|
@@ -637,7 +731,7 @@ function dependencySummaryText(summary) {
|
|
|
637
731
|
function isCatalogSourcesBootstrapFlow(snapshot) {
|
|
638
732
|
const route = snapshot.router.current.route;
|
|
639
733
|
if (route.name === 'reviewPreparing') {
|
|
640
|
-
return snapshot.reviewPreparation?.
|
|
734
|
+
return snapshot.reviewPreparation?.operationKind === 'catalogSourcesRecovery';
|
|
641
735
|
}
|
|
642
736
|
if (route.name === 'review' || route.name === 'progress') {
|
|
643
737
|
return snapshot.activeReview?.operationKind === 'catalogSourcesRecovery';
|
|
@@ -647,6 +741,11 @@ function isCatalogSourcesBootstrapFlow(snapshot) {
|
|
|
647
741
|
&& 'operationKind' in snapshot.result
|
|
648
742
|
&& snapshot.result.operationKind === 'catalogSourcesRecovery';
|
|
649
743
|
}
|
|
744
|
+
function isOperationHistoryRoute(route) {
|
|
745
|
+
return route.name === 'operationHistory'
|
|
746
|
+
|| route.name === 'operationDiagnostic'
|
|
747
|
+
|| route.name === 'operationReportPreview';
|
|
748
|
+
}
|
|
650
749
|
function operationTargetStateLabel(state) {
|
|
651
750
|
const labels = {
|
|
652
751
|
completed: { label: '已完成', status: 'success' },
|
|
@@ -669,191 +768,14 @@ export function resolveConsoleV4ScreenId(snapshot, controller, overlay, tooSmall
|
|
|
669
768
|
return 'G03';
|
|
670
769
|
if (overlay === 'discard')
|
|
671
770
|
return 'G04';
|
|
672
|
-
if (snapshot.phase !== 'ready' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
771
|
+
if (snapshot.phase !== 'ready' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
772
|
+
&& !isOperationHistoryRoute(snapshot.router.current.route))
|
|
673
773
|
return 'G01';
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if (route.mode === 'installed' && items.length === 0)
|
|
680
|
-
return 'P12';
|
|
681
|
-
if (route.sourceId
|
|
682
|
-
&& controller.getSourceOverview(route.sourceId).health?.state === 'unavailable')
|
|
683
|
-
return 'P03';
|
|
684
|
-
return route.mode === 'installed' ? 'P11' : searchMode ? 'P02' : 'P01';
|
|
685
|
-
}
|
|
686
|
-
if (route.name === 'pluginPreview') {
|
|
687
|
-
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D01' : 'P04';
|
|
688
|
-
}
|
|
689
|
-
if (route.name === 'portableSkillSelect')
|
|
690
|
-
return 'U01';
|
|
691
|
-
if (route.name === 'portableDependencies')
|
|
692
|
-
return 'U02';
|
|
693
|
-
if (route.name === 'portableAgentMode')
|
|
694
|
-
return 'U03';
|
|
695
|
-
if (route.name === 'portableAgentInput')
|
|
696
|
-
return 'U04';
|
|
697
|
-
if (route.name === 'portableScope')
|
|
698
|
-
return 'U05';
|
|
699
|
-
if (route.name === 'portableBlocker') {
|
|
700
|
-
if (route.code === 'requiredConfigMissing')
|
|
701
|
-
return 'U09';
|
|
702
|
-
if (route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible')
|
|
703
|
-
return 'U10';
|
|
704
|
-
return 'U11';
|
|
705
|
-
}
|
|
706
|
-
if (route.name === 'pluginDependencies') {
|
|
707
|
-
if (route.view === 'removeBlocked')
|
|
708
|
-
return 'D12';
|
|
709
|
-
const plugin = controller.getPluginOverview(route.pluginId);
|
|
710
|
-
return plugin && hasInstalledPlacement(plugin) ? 'D10' : 'D02';
|
|
711
|
-
}
|
|
712
|
-
if (route.name === 'installPlacement') {
|
|
713
|
-
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D03' : 'P05';
|
|
714
|
-
}
|
|
715
|
-
if (route.name === 'installTargets') {
|
|
716
|
-
if (route.view === 'blocked')
|
|
717
|
-
return 'D16';
|
|
718
|
-
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D04' : 'P06';
|
|
719
|
-
}
|
|
720
|
-
if (route.name === 'dependencyPlacement')
|
|
721
|
-
return 'D03';
|
|
722
|
-
if (route.name === 'dependencyPlacementEditor')
|
|
723
|
-
return 'D04';
|
|
724
|
-
if (route.name === 'pluginDetail') {
|
|
725
|
-
return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D09' : 'P13';
|
|
726
|
-
}
|
|
727
|
-
if (route.name === 'platform') {
|
|
728
|
-
const plugin = controller.getPluginOverview(route.pluginId);
|
|
729
|
-
if (plugin?.aggregateStatus === 'unavailable')
|
|
730
|
-
return 'P17';
|
|
731
|
-
const placement = controller.getPlatformPlacement(route.pluginId, route.platform);
|
|
732
|
-
if (!placement || placement.state === 'notInstalled')
|
|
733
|
-
return 'P15';
|
|
734
|
-
if (placement.state === 'installed' && placement.health === 'ready')
|
|
735
|
-
return 'P14';
|
|
736
|
-
return 'P16';
|
|
737
|
-
}
|
|
738
|
-
if (route.name === 'reviewPreparing')
|
|
739
|
-
return 'P07';
|
|
740
|
-
if (route.name === 'review' && snapshot.activeReview?.originIssue)
|
|
741
|
-
return 'I10';
|
|
742
|
-
const dependencyOperation = snapshot.activeReview?.operationKind === 'pluginWrite'
|
|
743
|
-
&& Boolean(snapshot.activeReview.changes.some((change) => change.relation === 'direct' || change.relation === 'transitive'
|
|
744
|
-
|| change.group === 'dependencies'));
|
|
745
|
-
if (route.name === 'review' && dependencyOperation) {
|
|
746
|
-
return snapshot.activeReview?.pluginAction === 'uninstall' || snapshot.activeReview?.pluginAction === 'cleanup'
|
|
747
|
-
? 'D11' : 'D05';
|
|
748
|
-
}
|
|
749
|
-
if (route.name === 'progress' && snapshot.progressTargets.length > 0)
|
|
750
|
-
return 'D06';
|
|
751
|
-
if (route.name === 'result' && snapshot.result) {
|
|
752
|
-
const dependencyTargets = snapshot.result.state === 'succeeded' || snapshot.result.state === 'partial'
|
|
753
|
-
? snapshot.result.summary.targets
|
|
754
|
-
: snapshot.result.state === 'blocked' && snapshot.result.code === 'operationFailed'
|
|
755
|
-
? snapshot.result.summary?.targets : undefined;
|
|
756
|
-
if (dependencyTargets?.some((target) => target.relation !== undefined && target.relation !== 'requested')) {
|
|
757
|
-
return snapshot.result.state === 'succeeded' ? 'D07' : 'D08';
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
if (route.name === 'progress' && snapshot.activeReview?.originIssue)
|
|
761
|
-
return 'I11';
|
|
762
|
-
if (route.name === 'result' && snapshot.result?.originIssue) {
|
|
763
|
-
return snapshot.result.state === 'succeeded' ? 'I12' : 'I13';
|
|
764
|
-
}
|
|
765
|
-
if (route.name === 'review' && (snapshot.activeReview?.operationKind === 'sourceMutation'
|
|
766
|
-
|| snapshot.activeReview?.operationKind === 'catalogGeneration') && snapshot.activeReview.sourceAction) {
|
|
767
|
-
return { add: 'S10', update: 'S15', setPrimary: 'S16', remove: 'S18' }[snapshot.activeReview.sourceAction];
|
|
768
|
-
}
|
|
769
|
-
if (route.name === 'progress' && (snapshot.activeReview?.operationKind === 'sourceMutation'
|
|
770
|
-
|| snapshot.activeReview?.operationKind === 'catalogGeneration'
|
|
771
|
-
|| snapshot.activeReview?.operationKind === 'catalogSourcesRecovery'))
|
|
772
|
-
return 'S11';
|
|
773
|
-
if (route.name === 'result' && snapshot.result && 'operationKind' in snapshot.result
|
|
774
|
-
&& (snapshot.result.operationKind === 'sourceMutation'
|
|
775
|
-
|| snapshot.result.operationKind === 'catalogGeneration'
|
|
776
|
-
|| snapshot.result.operationKind === 'catalogSourcesRecovery')) {
|
|
777
|
-
return snapshot.result.state === 'succeeded' ? 'S12' : 'S13';
|
|
778
|
-
}
|
|
779
|
-
if (route.name === 'review')
|
|
780
|
-
return snapshot.activeReview?.operationKind === 'portableInstall' ? 'U06'
|
|
781
|
-
: snapshot.activeReview?.operationKind === 'configSave'
|
|
782
|
-
? 'C12'
|
|
783
|
-
: snapshot.activeReview?.operationKind === 'configReset'
|
|
784
|
-
? 'C16'
|
|
785
|
-
: snapshot.activeReview?.operationKind === 'pluginWrite'
|
|
786
|
-
&& (snapshot.activeReview.pluginAction === 'refreshAndSync' || snapshot.activeReview.pluginAction === 'reinstall')
|
|
787
|
-
? 'P18'
|
|
788
|
-
: snapshot.activeReview?.operationKind === 'pluginWrite'
|
|
789
|
-
&& (snapshot.activeReview.pluginAction === 'uninstall' || snapshot.activeReview.pluginAction === 'cleanup')
|
|
790
|
-
? 'P19' : 'P07';
|
|
791
|
-
if (route.name === 'progress')
|
|
792
|
-
return snapshot.activeReview?.operationKind === 'portableInstall' ? 'U07'
|
|
793
|
-
: snapshot.activeReview?.operationKind === 'configSave'
|
|
794
|
-
|| snapshot.activeReview?.operationKind === 'configReset' ? 'C13' : 'P08';
|
|
795
|
-
if (route.name === 'result')
|
|
796
|
-
return snapshot.result?.operationKind === 'portableInstall' ? 'U08'
|
|
797
|
-
: snapshot.result?.operationKind === 'configSave'
|
|
798
|
-
|| snapshot.result?.operationKind === 'configReset'
|
|
799
|
-
? snapshot.result.state === 'succeeded' ? 'C14' : 'C15'
|
|
800
|
-
: snapshot.result?.state === 'partial' ? 'P10' : 'P09';
|
|
801
|
-
if (route.name === 'pluginAbout')
|
|
802
|
-
return 'P20';
|
|
803
|
-
if (route.name === 'pluginTechnical')
|
|
804
|
-
return 'P21';
|
|
805
|
-
if (route.name === 'configLayer')
|
|
806
|
-
return 'C01';
|
|
807
|
-
if (route.name === 'configFields')
|
|
808
|
-
return route.layer.scope === 'effective' ? 'C02' : 'C03';
|
|
809
|
-
if (route.name === 'configFieldAction')
|
|
810
|
-
return 'C04';
|
|
811
|
-
if (route.name === 'configFieldEditor')
|
|
812
|
-
return route.mode === 'choices' ? 'C05' : {
|
|
813
|
-
boolean: 'C06', number: 'C07', string: 'C08', object: 'C09',
|
|
814
|
-
}[route.editor];
|
|
815
|
-
if (route.name === 'configObjectPropertyEditor')
|
|
816
|
-
return 'C10';
|
|
817
|
-
if (route.name === 'configUnavailable')
|
|
818
|
-
return 'C17';
|
|
819
|
-
if (route.name === 'sourceList')
|
|
820
|
-
return 'S01';
|
|
821
|
-
if (route.name === 'sourceDetail')
|
|
822
|
-
return 'K02';
|
|
823
|
-
if (route.name === 'sourceTechnical')
|
|
824
|
-
return 'S03';
|
|
825
|
-
if (route.name === 'sourceAdd')
|
|
826
|
-
return {
|
|
827
|
-
type: 'S04', id: 'S05', location: 'S06', refPolicy: 'S07', ref: 'S08', subdir: 'S09',
|
|
828
|
-
}[route.step];
|
|
829
|
-
if (route.name === 'sourceEdit')
|
|
830
|
-
return 'S14';
|
|
831
|
-
if (route.name === 'sourceRemoveBlocked')
|
|
832
|
-
return 'S17';
|
|
833
|
-
if (route.name === 'issueList') {
|
|
834
|
-
const issues = controller.listUserIssues();
|
|
835
|
-
return issues.length === 0 ? 'I02' : issues.some((issue) => issue.category.startsWith('dependency')) ? 'D13' : 'I01';
|
|
836
|
-
}
|
|
837
|
-
if (route.name === 'issueDetail') {
|
|
838
|
-
const category = controller.listUserIssues().find((issue) => issue.issueId === route.issueId)?.category;
|
|
839
|
-
return {
|
|
840
|
-
incompleteInstallation: 'I03',
|
|
841
|
-
artifactCollision: 'I09',
|
|
842
|
-
sourceUnavailable: 'I04',
|
|
843
|
-
sourceMissing: 'I05',
|
|
844
|
-
sourceIdentityChanged: 'I06',
|
|
845
|
-
requiredConfigMissing: 'I07',
|
|
846
|
-
scopeConflict: 'I08',
|
|
847
|
-
dependencyMissing: 'D14',
|
|
848
|
-
dependencyNotReady: 'D15',
|
|
849
|
-
dependencyTargetUnsupported: 'D16',
|
|
850
|
-
dependencyStillRequired: 'D12',
|
|
851
|
-
dependencyGraphInvalid: 'D17',
|
|
852
|
-
legacyState: 'I09',
|
|
853
|
-
diagnostic: 'I09',
|
|
854
|
-
}[category ?? 'diagnostic'];
|
|
855
|
-
}
|
|
856
|
-
return 'G02';
|
|
774
|
+
return resolveRouteScreenId(snapshot.router.current.route, {
|
|
775
|
+
snapshot,
|
|
776
|
+
controller,
|
|
777
|
+
searchMode,
|
|
778
|
+
});
|
|
857
779
|
}
|
|
858
780
|
export function ConsoleAppV4(props) {
|
|
859
781
|
const windowSize = useWindowSize();
|
|
@@ -863,6 +785,8 @@ export function ConsoleAppV4(props) {
|
|
|
863
785
|
const [overlay, setOverlay] = useState();
|
|
864
786
|
const [overlayFocus, setOverlayFocus] = useState(0);
|
|
865
787
|
const [discardForExit, setDiscardForExit] = useState(false);
|
|
788
|
+
const [discardForSessionNavigation, setDiscardForSessionNavigation] = useState();
|
|
789
|
+
const [discardDraftConfirmation, setDiscardDraftConfirmation] = useState();
|
|
866
790
|
const [focus, setFocus] = useState(0);
|
|
867
791
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
868
792
|
const [searchFocus, setSearchFocus] = useState(false);
|
|
@@ -882,6 +806,7 @@ export function ConsoleAppV4(props) {
|
|
|
882
806
|
const [draftUpdates, setDraftUpdates] = useState({});
|
|
883
807
|
const [draftRemoves, setDraftRemoves] = useState([]);
|
|
884
808
|
const [objectDrafts, setObjectDrafts] = useState({});
|
|
809
|
+
const [changedObjectDraftKeys, setChangedObjectDraftKeys] = useState([]);
|
|
885
810
|
const [objectError, setObjectError] = useState();
|
|
886
811
|
const [inputValue, setInputValue] = useState('');
|
|
887
812
|
const [inputError, setInputError] = useState();
|
|
@@ -889,25 +814,51 @@ export function ConsoleAppV4(props) {
|
|
|
889
814
|
const [actionError, setActionError] = useState();
|
|
890
815
|
const [configResetAvailability, setConfigResetAvailability] = useState({});
|
|
891
816
|
const [sourceDraft, setSourceDraft] = useState();
|
|
817
|
+
const [batchDraft, setBatchDraft] = useState();
|
|
818
|
+
const [batchTargetOffset, setBatchTargetOffset] = useState(0);
|
|
892
819
|
const [sourceInput, setSourceInput] = useState('');
|
|
893
820
|
const [sourceInputError, setSourceInputError] = useState();
|
|
821
|
+
const [pendingReviewIntent, setPendingReviewIntent] = useState();
|
|
822
|
+
const pendingReviewIntentRef = useRef(undefined);
|
|
823
|
+
const [scriptTimeoutInput, setScriptTimeoutInput] = useState(String(SCRIPT_TIMEOUT_DEFAULT_MS));
|
|
824
|
+
const [scriptTimeoutError, setScriptTimeoutError] = useState();
|
|
894
825
|
const [sourceReferenceSummary, setSourceReferenceSummary] = useState();
|
|
895
826
|
const [sourceDependenciesExpanded, setSourceDependenciesExpanded] = useState(false);
|
|
896
827
|
const [portableAgentInput, setPortableAgentInput] = useState('');
|
|
828
|
+
const [reportSaveMode, setReportSaveMode] = useState(false);
|
|
829
|
+
const [reportSavePath, setReportSavePath] = useState('');
|
|
830
|
+
const [reportSaveResult, setReportSaveResult] = useState();
|
|
831
|
+
const [reportSaveError, setReportSaveError] = useState();
|
|
897
832
|
const [draftOwner, setDraftOwner] = useState();
|
|
898
833
|
const [pendingPresetId, setPendingPresetId] = useState();
|
|
899
834
|
const [pendingLayerFieldKey, setPendingLayerFieldKey] = useState();
|
|
900
835
|
const [confirmedSharedReviewToken, setConfirmedSharedReviewToken] = useState();
|
|
901
836
|
const dynamicMenuFocus = useRef(undefined);
|
|
837
|
+
const externalDraftRevision = useRef({ config: 0, source: 0, batch: 0 });
|
|
838
|
+
const previousExternalDraftValues = useRef({
|
|
839
|
+
draftUpdates, draftRemoves, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
840
|
+
});
|
|
841
|
+
const externalDraftFlowIds = useRef(new Map());
|
|
902
842
|
const exitSent = useRef(false);
|
|
903
843
|
const operationBusy = useRef(false);
|
|
904
844
|
const route = snapshot.router.current.route;
|
|
845
|
+
const definition = routeDefinition(route);
|
|
846
|
+
const rendererFamily = definition.rendererFamily;
|
|
905
847
|
const routeKey = JSON.stringify(route);
|
|
906
848
|
const showTechnical = technicalRouteKey === routeKey;
|
|
907
849
|
const setShowTechnical = (next) => {
|
|
908
850
|
const visible = typeof next === 'function' ? next(showTechnical) : next;
|
|
909
851
|
setTechnicalRouteKey(visible ? routeKey : undefined);
|
|
910
852
|
};
|
|
853
|
+
const resetRootUiState = () => {
|
|
854
|
+
setFocus(0);
|
|
855
|
+
setScrollOffset(0);
|
|
856
|
+
setSearchFocus(false);
|
|
857
|
+
setQuery('');
|
|
858
|
+
setShowTechnical(false);
|
|
859
|
+
setSourceDependenciesExpanded(false);
|
|
860
|
+
setBatchTargetOffset(0);
|
|
861
|
+
};
|
|
911
862
|
const plugin = currentPlugin(props.controller, route);
|
|
912
863
|
const issue = route.name === 'issueDetail'
|
|
913
864
|
? props.controller.listUserIssues().find((entry) => entry.issueId === route.issueId)
|
|
@@ -931,8 +882,10 @@ export function ConsoleAppV4(props) {
|
|
|
931
882
|
useEffect(() => {
|
|
932
883
|
const unsubscribe = props.controller.subscribe(setSnapshot);
|
|
933
884
|
if (props.controller.snapshot.phase === 'idle') {
|
|
934
|
-
void props.controller.load().catch(() => {
|
|
935
|
-
|
|
885
|
+
void props.controller.load().catch((error) => {
|
|
886
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
887
|
+
return;
|
|
888
|
+
props.controller.recordUiFailure(error, { code: 'tuiLoadFailed', stage: 'ui-load' });
|
|
936
889
|
});
|
|
937
890
|
}
|
|
938
891
|
return () => {
|
|
@@ -941,10 +894,6 @@ export function ConsoleAppV4(props) {
|
|
|
941
894
|
props.controller.dispose();
|
|
942
895
|
};
|
|
943
896
|
}, [props.controller, props.ownsController]);
|
|
944
|
-
useEffect(() => {
|
|
945
|
-
props.controller.setExternalDraftState(sourceDraft ? 'sourceDraft' : dirty ? 'configDraft' : undefined);
|
|
946
|
-
return () => props.controller.setExternalDraftState(undefined);
|
|
947
|
-
}, [props.controller, dirty, sourceDraft]);
|
|
948
897
|
useEffect(() => {
|
|
949
898
|
if (!exitAfterBusy || !snapshot.result)
|
|
950
899
|
return;
|
|
@@ -969,18 +918,49 @@ export function ConsoleAppV4(props) {
|
|
|
969
918
|
|| (snapshot.result.state !== 'succeeded' && snapshot.result.state !== 'partial')
|
|
970
919
|
|| !snapshot.result.operationKind)
|
|
971
920
|
return;
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
921
|
+
if (snapshot.result.operationKind === 'configSave'
|
|
922
|
+
|| snapshot.result.operationKind === 'configReset') {
|
|
923
|
+
setDirty(false);
|
|
924
|
+
setDraftUpdates({});
|
|
925
|
+
setDraftRemoves([]);
|
|
926
|
+
setObjectDrafts({});
|
|
927
|
+
setChangedObjectDraftKeys([]);
|
|
928
|
+
setObjectError(undefined);
|
|
929
|
+
}
|
|
930
|
+
if ((snapshot.result.operationKind === 'sourceMutation'
|
|
931
|
+
|| snapshot.result.operationKind === 'catalogGeneration') && sourceDraft) {
|
|
978
932
|
setSourceDraft(undefined);
|
|
933
|
+
setDirty(Object.keys(draftUpdates).length + draftRemoves.length + changedObjectDraftKeys.length > 0);
|
|
934
|
+
}
|
|
979
935
|
}, [route.name, snapshot.result]);
|
|
936
|
+
useEffect(() => {
|
|
937
|
+
if (route.name !== 'result' || snapshot.result?.operationKind !== 'pluginWriteBatch')
|
|
938
|
+
return;
|
|
939
|
+
setBatchDraft(undefined);
|
|
940
|
+
setDirty(false);
|
|
941
|
+
}, [route.name, snapshot.result?.operationKind]);
|
|
942
|
+
useEffect(() => {
|
|
943
|
+
if (!batchDraft || (route.name !== 'batchSelection' && route.name !== 'batchPlacement'))
|
|
944
|
+
return;
|
|
945
|
+
setDirty(true);
|
|
946
|
+
}, [batchDraft, route.name]);
|
|
980
947
|
useEffect(() => {
|
|
981
948
|
if (route.name !== 'sourceAdd')
|
|
982
949
|
setSourceInput('');
|
|
983
950
|
}, [route.name]);
|
|
951
|
+
useEffect(() => {
|
|
952
|
+
if (route.name === 'operationReportPreview')
|
|
953
|
+
return;
|
|
954
|
+
setReportSaveMode(false);
|
|
955
|
+
setReportSavePath('');
|
|
956
|
+
setReportSaveResult(undefined);
|
|
957
|
+
setReportSaveError(undefined);
|
|
958
|
+
}, [route.name]);
|
|
959
|
+
useEffect(() => {
|
|
960
|
+
if (route.name !== 'home')
|
|
961
|
+
return;
|
|
962
|
+
resetRootUiState();
|
|
963
|
+
}, [route.name]);
|
|
984
964
|
useEffect(() => {
|
|
985
965
|
if (route.name === 'portableAgentInput') {
|
|
986
966
|
setPortableAgentInput(snapshot.portableDraft?.requestedAgents.join(' ') ?? '');
|
|
@@ -997,9 +977,96 @@ export function ConsoleAppV4(props) {
|
|
|
997
977
|
|| route.name === 'configFieldEditor'
|
|
998
978
|
|| route.name === 'configObjectPropertyEditor'
|
|
999
979
|
|| route.name === 'configFieldAction'
|
|
980
|
+
|| route.name === 'configSave'
|
|
1000
981
|
? route
|
|
1001
982
|
: undefined;
|
|
983
|
+
const configRouteOwner = configRoute ? configOwnerKey(configRoute) : undefined;
|
|
984
|
+
useEffect(() => {
|
|
985
|
+
const previous = previousExternalDraftValues.current;
|
|
986
|
+
if (previous.draftUpdates !== draftUpdates
|
|
987
|
+
|| previous.draftRemoves !== draftRemoves
|
|
988
|
+
|| previous.objectDrafts !== objectDrafts
|
|
989
|
+
|| previous.changedObjectDraftKeys !== changedObjectDraftKeys) {
|
|
990
|
+
externalDraftRevision.current.config += 1;
|
|
991
|
+
}
|
|
992
|
+
if (previous.sourceDraft !== sourceDraft)
|
|
993
|
+
externalDraftRevision.current.source += 1;
|
|
994
|
+
if (previous.batchDraft !== batchDraft)
|
|
995
|
+
externalDraftRevision.current.batch += 1;
|
|
996
|
+
previousExternalDraftValues.current = {
|
|
997
|
+
draftUpdates, draftRemoves, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
998
|
+
};
|
|
999
|
+
const currentFlowId = snapshot.router.currentFlowId;
|
|
1000
|
+
if (currentFlowId && configRouteOwner) {
|
|
1001
|
+
externalDraftFlowIds.current.set(configRouteOwner, currentFlowId);
|
|
1002
|
+
}
|
|
1003
|
+
if (currentFlowId && sourceDraft)
|
|
1004
|
+
externalDraftFlowIds.current.set(sourceDraft.owner, currentFlowId);
|
|
1005
|
+
if (currentFlowId && batchDraft)
|
|
1006
|
+
externalDraftFlowIds.current.set('batch-write', currentFlowId);
|
|
1007
|
+
const drafts = [];
|
|
1008
|
+
if ((dirty || changedObjectDraftKeys.length > 0) && draftOwner) {
|
|
1009
|
+
const flowId = externalDraftFlowIds.current.get(draftOwner);
|
|
1010
|
+
drafts.push({
|
|
1011
|
+
kind: 'configDraft', owner: draftOwner, revision: externalDraftRevision.current.config,
|
|
1012
|
+
...(flowId ? { flowId } : {}),
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
if (sourceDraft) {
|
|
1016
|
+
const flowId = externalDraftFlowIds.current.get(sourceDraft.owner);
|
|
1017
|
+
drafts.push({
|
|
1018
|
+
kind: 'sourceDraft', owner: sourceDraft.owner, revision: externalDraftRevision.current.source,
|
|
1019
|
+
...(flowId ? { flowId } : {}),
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
if (batchDraft) {
|
|
1023
|
+
const flowId = externalDraftFlowIds.current.get('batch-write');
|
|
1024
|
+
drafts.push({
|
|
1025
|
+
kind: 'batchDraft', owner: 'batch-write', revision: externalDraftRevision.current.batch,
|
|
1026
|
+
...(flowId ? { flowId } : {}),
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
props.controller.setExternalDraftState(drafts);
|
|
1030
|
+
}, [
|
|
1031
|
+
props.controller, batchDraft, changedObjectDraftKeys, configRouteOwner, dirty, draftOwner,
|
|
1032
|
+
draftRemoves, draftUpdates, objectDrafts, snapshot.router.currentFlowId, sourceDraft,
|
|
1033
|
+
]);
|
|
1034
|
+
useEffect(() => () => props.controller.setExternalDraftState(), [props.controller]);
|
|
1002
1035
|
const currentConfigPage = configPageMatches(configPage, configRoute) ? configPage : undefined;
|
|
1036
|
+
const configDraftIssues = (() => {
|
|
1037
|
+
if (!configRoute || !currentConfigPage)
|
|
1038
|
+
return [];
|
|
1039
|
+
const fields = new Map(currentConfigPage.groups.flatMap((group) => group.fields)
|
|
1040
|
+
.map((field) => [field.key, field]));
|
|
1041
|
+
const issues = [];
|
|
1042
|
+
for (const [key, value] of Object.entries(draftUpdates)) {
|
|
1043
|
+
const field = fields.get(key);
|
|
1044
|
+
if (!field || !configFieldCanSet(field)
|
|
1045
|
+
|| validatePluginConfigValue(field.definition, value, key).length > 0) {
|
|
1046
|
+
issues.push({ key, source: 'update' });
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
for (const key of draftRemoves) {
|
|
1050
|
+
const field = fields.get(key);
|
|
1051
|
+
if (!field || !configFieldCanUnset(field))
|
|
1052
|
+
issues.push({ key, source: 'remove' });
|
|
1053
|
+
}
|
|
1054
|
+
const owner = `${configOwnerKey(configRoute)}\0`;
|
|
1055
|
+
for (const objectKey of changedObjectDraftKeys) {
|
|
1056
|
+
if (!objectKey.startsWith(owner))
|
|
1057
|
+
continue;
|
|
1058
|
+
const key = objectKey.slice(owner.length);
|
|
1059
|
+
if (issues.some((issue) => issue.key === key))
|
|
1060
|
+
continue;
|
|
1061
|
+
const field = fields.get(key);
|
|
1062
|
+
const value = objectDrafts[objectKey];
|
|
1063
|
+
if (!field || !configFieldCanSet(field) || field.definition.type !== 'object' || !value
|
|
1064
|
+
|| validatePluginConfigValue(field.definition, value, key).length > 0) {
|
|
1065
|
+
issues.push({ key, source: 'object' });
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return issues;
|
|
1069
|
+
})();
|
|
1003
1070
|
useEffect(() => {
|
|
1004
1071
|
if (route.name !== 'configLayer')
|
|
1005
1072
|
return;
|
|
@@ -1031,6 +1098,7 @@ export function ConsoleAppV4(props) {
|
|
|
1031
1098
|
setDraftUpdates({});
|
|
1032
1099
|
setDraftRemoves([]);
|
|
1033
1100
|
setObjectDrafts({});
|
|
1101
|
+
setChangedObjectDraftKeys([]);
|
|
1034
1102
|
setObjectError(undefined);
|
|
1035
1103
|
setDirty(false);
|
|
1036
1104
|
setConfigActionError(undefined);
|
|
@@ -1065,6 +1133,17 @@ export function ConsoleAppV4(props) {
|
|
|
1065
1133
|
diagnosticStage: 'read-config',
|
|
1066
1134
|
...(invalidStores ? { invalidStores } : {}),
|
|
1067
1135
|
...(layer ? { layer } : {}),
|
|
1136
|
+
...(configRoute ? { resumeRoute: structuredClone(configRoute.name === 'configSave'
|
|
1137
|
+
? { name: 'configFields', pluginId: configRoute.pluginId, layer: configRoute.layer }
|
|
1138
|
+
: configRoute) } : {}),
|
|
1139
|
+
...(configRoute ? { resumeContext: {
|
|
1140
|
+
...(snapshot.router.current.context.configGroupId !== undefined
|
|
1141
|
+
? { configGroupId: snapshot.router.current.context.configGroupId } : {}),
|
|
1142
|
+
...(snapshot.router.current.context.focusIndex !== undefined
|
|
1143
|
+
? { focusIndex: snapshot.router.current.context.focusIndex } : {}),
|
|
1144
|
+
...(snapshot.router.current.context.scrollOffset !== undefined
|
|
1145
|
+
? { scrollOffset: snapshot.router.current.context.scrollOffset } : {}),
|
|
1146
|
+
} } : {}),
|
|
1068
1147
|
});
|
|
1069
1148
|
}
|
|
1070
1149
|
});
|
|
@@ -1072,13 +1151,14 @@ export function ConsoleAppV4(props) {
|
|
|
1072
1151
|
}, [configRoute?.pluginId, configRoute?.layer.scope, props.controller]);
|
|
1073
1152
|
useEffect(() => {
|
|
1074
1153
|
if (route.name !== 'configUnavailable'
|
|
1075
|
-
|| (!route.invalidStores?.length && !route.layer)) {
|
|
1154
|
+
|| (!route.invalidStores?.length && !route.storedScopes?.length && !route.layer)) {
|
|
1076
1155
|
setConfigResetAvailability({});
|
|
1077
1156
|
return;
|
|
1078
1157
|
}
|
|
1079
1158
|
const abort = new AbortController();
|
|
1080
1159
|
setConfigResetAvailability({});
|
|
1081
1160
|
const scopes = route.invalidStores?.map(({ scope }) => scope)
|
|
1161
|
+
?? route.storedScopes
|
|
1082
1162
|
?? (route.layer ? [route.layer.scope] : []);
|
|
1083
1163
|
void Promise.all(scopes.map(async (scope) => {
|
|
1084
1164
|
try {
|
|
@@ -1105,6 +1185,7 @@ export function ConsoleAppV4(props) {
|
|
|
1105
1185
|
route.name === 'configUnavailable'
|
|
1106
1186
|
? route.invalidStores?.map((store) => `${store.scope}:${store.code}`).join('|') ?? ''
|
|
1107
1187
|
: '',
|
|
1188
|
+
route.name === 'configUnavailable' ? route.storedScopes?.join('|') ?? '' : '',
|
|
1108
1189
|
route.name === 'configUnavailable' ? route.layer?.scope ?? '' : '',
|
|
1109
1190
|
]);
|
|
1110
1191
|
useEffect(() => {
|
|
@@ -1121,11 +1202,14 @@ export function ConsoleAppV4(props) {
|
|
|
1121
1202
|
useEffect(() => {
|
|
1122
1203
|
let count;
|
|
1123
1204
|
if (route.name === 'sourceList')
|
|
1124
|
-
count = props.controller.listSources().length +
|
|
1205
|
+
count = props.controller.listSources().length + 2;
|
|
1125
1206
|
else if (route.name === 'issueList') {
|
|
1126
1207
|
const issueCount = props.controller.listUserIssues().length;
|
|
1127
1208
|
count = issueCount || 2;
|
|
1128
1209
|
}
|
|
1210
|
+
else if (route.name === 'operationHistory') {
|
|
1211
|
+
count = snapshot.operationHistorySummaries.length + 1;
|
|
1212
|
+
}
|
|
1129
1213
|
else if (route.name === 'pluginList') {
|
|
1130
1214
|
const itemCount = props.controller.listPlugins({
|
|
1131
1215
|
mode: route.mode,
|
|
@@ -1133,7 +1217,8 @@ export function ConsoleAppV4(props) {
|
|
|
1133
1217
|
...(query ? { query } : {}),
|
|
1134
1218
|
installFilter,
|
|
1135
1219
|
}).length;
|
|
1136
|
-
|
|
1220
|
+
const menuCount = itemCount > 0 ? route.mode === 'installed' ? 2 : 1 : 0;
|
|
1221
|
+
count = itemCount + menuCount || 1;
|
|
1137
1222
|
}
|
|
1138
1223
|
if (count === undefined)
|
|
1139
1224
|
return;
|
|
@@ -1149,11 +1234,32 @@ export function ConsoleAppV4(props) {
|
|
|
1149
1234
|
route.name === 'pluginList' ? route.sourceId : '',
|
|
1150
1235
|
snapshot.viewModel,
|
|
1151
1236
|
]);
|
|
1237
|
+
useEffect(() => {
|
|
1238
|
+
if (route.name !== 'operationReportPreview')
|
|
1239
|
+
return;
|
|
1240
|
+
setScrollOffset(0);
|
|
1241
|
+
}, [route.name, route.name === 'operationReportPreview' ? route.recordId : '']);
|
|
1242
|
+
useEffect(() => {
|
|
1243
|
+
setBatchTargetOffset(0);
|
|
1244
|
+
}, [route.name, route.name === 'review' || route.name === 'result' ? route.operationId : '']);
|
|
1152
1245
|
const activeField = route.name === 'configFieldEditor'
|
|
1153
1246
|
|| route.name === 'configObjectPropertyEditor'
|
|
1154
1247
|
|| route.name === 'configFieldAction'
|
|
1155
1248
|
? currentConfigPage?.groups.flatMap((group) => group.fields).find((field) => field.key === route.fieldKey)
|
|
1156
1249
|
: undefined;
|
|
1250
|
+
useEffect(() => {
|
|
1251
|
+
if (!configRoute
|
|
1252
|
+
|| configRoute.name === 'configFields'
|
|
1253
|
+
|| configRoute.name === 'configSave'
|
|
1254
|
+
|| !currentConfigPage
|
|
1255
|
+
|| activeField)
|
|
1256
|
+
return;
|
|
1257
|
+
props.controller.replace({
|
|
1258
|
+
name: 'configFields',
|
|
1259
|
+
pluginId: configRoute.pluginId,
|
|
1260
|
+
layer: configRoute.layer,
|
|
1261
|
+
}, snapshot.router.current.context);
|
|
1262
|
+
}, [props.controller, configRoute, currentConfigPage, activeField, snapshot.router.current.context]);
|
|
1157
1263
|
const activeObjectKey = activeField?.type === 'object' && configRoute
|
|
1158
1264
|
? `${configOwnerKey(configRoute)}\0${activeField.key}`
|
|
1159
1265
|
: undefined;
|
|
@@ -1219,6 +1325,14 @@ export function ConsoleAppV4(props) {
|
|
|
1219
1325
|
: route.step === 'subdir' ? draft?.subdir ?? '' : '');
|
|
1220
1326
|
setSourceInputError(undefined);
|
|
1221
1327
|
}, [route.name, route.name === 'sourceAdd' ? `${route.owner}:${route.step}` : '', sourceDraft?.owner]);
|
|
1328
|
+
useEffect(() => {
|
|
1329
|
+
if (route.name !== 'scriptTimeout')
|
|
1330
|
+
return;
|
|
1331
|
+
setScriptTimeoutInput(String(pendingReviewIntent && 'scriptTimeoutMs' in pendingReviewIntent
|
|
1332
|
+
? pendingReviewIntent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS
|
|
1333
|
+
: SCRIPT_TIMEOUT_DEFAULT_MS));
|
|
1334
|
+
setScriptTimeoutError(undefined);
|
|
1335
|
+
}, [route.name, route.name === 'scriptTimeout' ? route.owner : '']);
|
|
1222
1336
|
useEffect(() => {
|
|
1223
1337
|
if (route.name !== 'sourceRemoveBlocked')
|
|
1224
1338
|
return;
|
|
@@ -1237,6 +1351,7 @@ export function ConsoleAppV4(props) {
|
|
|
1237
1351
|
? snapshot.portableDraft.candidates.filter(portableScopeVariantsDiffer)
|
|
1238
1352
|
: [];
|
|
1239
1353
|
const supportsTechnicalDetails = snapshot.phase === 'error'
|
|
1354
|
+
|| Boolean(route.name === 'reviewPreparing' && snapshot.reviewPreparation?.code)
|
|
1240
1355
|
|| Boolean(route.name === 'progress' && snapshot.progress.messages.length > 0)
|
|
1241
1356
|
|| Boolean(route.name === 'review' && snapshot.activeReview?.technicalDetails?.length)
|
|
1242
1357
|
|| Boolean(route.name === 'issueDetail' && issue
|
|
@@ -1294,13 +1409,26 @@ export function ConsoleAppV4(props) {
|
|
|
1294
1409
|
staticRows,
|
|
1295
1410
|
});
|
|
1296
1411
|
const dependencyPlacementStaticRows = policy.mode === 'compact' ? 3 : 10;
|
|
1412
|
+
const actionViewportStaticRows = route.name === 'dependencyPlacement'
|
|
1413
|
+
? dependencyPlacementStaticRows
|
|
1414
|
+
: route.name === 'operationHistory' ? 4
|
|
1415
|
+
: route.name === 'operationReportPreview' ? 5 : 0;
|
|
1297
1416
|
const responsiveBudget = (compactRows, standardRows) => {
|
|
1298
1417
|
const baseline = policy.mode === 'compact' ? 18 : 24;
|
|
1299
1418
|
const base = policy.mode === 'compact' ? compactRows : standardRows;
|
|
1300
1419
|
return base + Math.max(0, size.rows - baseline);
|
|
1301
1420
|
};
|
|
1302
1421
|
const go = (next) => {
|
|
1303
|
-
props.controller.updatePageContext({
|
|
1422
|
+
props.controller.updatePageContext({
|
|
1423
|
+
focusIndex: focus,
|
|
1424
|
+
scrollOffset,
|
|
1425
|
+
query,
|
|
1426
|
+
installFilter,
|
|
1427
|
+
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
1428
|
+
focusedEntityId: route.name === 'pluginList'
|
|
1429
|
+
? pluginRows[focus]?.pluginId
|
|
1430
|
+
: route.name === 'sourceList' ? props.controller.listSources()[focus]?.id : undefined,
|
|
1431
|
+
});
|
|
1304
1432
|
props.controller.push(next);
|
|
1305
1433
|
setFocus(0);
|
|
1306
1434
|
setScrollOffset(0);
|
|
@@ -1308,6 +1436,51 @@ export function ConsoleAppV4(props) {
|
|
|
1308
1436
|
setShowTechnical(false);
|
|
1309
1437
|
setActionError(undefined);
|
|
1310
1438
|
};
|
|
1439
|
+
const toggleTechnical = () => {
|
|
1440
|
+
setShowTechnical((value) => !value);
|
|
1441
|
+
setBatchTargetOffset(0);
|
|
1442
|
+
};
|
|
1443
|
+
const restorePageContext = () => {
|
|
1444
|
+
const restoredFrame = props.controller.snapshot.router.current;
|
|
1445
|
+
const restored = restoredFrame.context;
|
|
1446
|
+
let restoredFocus = restored.focusIndex ?? 0;
|
|
1447
|
+
let itemCount;
|
|
1448
|
+
if (restoredFrame.route.name === 'pluginList') {
|
|
1449
|
+
const items = props.controller.listPlugins({
|
|
1450
|
+
mode: restoredFrame.route.mode,
|
|
1451
|
+
...(restoredFrame.route.sourceId ? { sourceId: restoredFrame.route.sourceId } : {}),
|
|
1452
|
+
...(restored.query ? { query: restored.query } : {}),
|
|
1453
|
+
installFilter: restored.installFilter ?? 'all',
|
|
1454
|
+
});
|
|
1455
|
+
itemCount = items.length;
|
|
1456
|
+
const entityIndex = restored.focusedEntityId
|
|
1457
|
+
? items.findIndex((item) => item.pluginId === restored.focusedEntityId)
|
|
1458
|
+
: -1;
|
|
1459
|
+
restoredFocus = entityIndex >= 0
|
|
1460
|
+
? entityIndex
|
|
1461
|
+
: Math.min(restoredFocus, Math.max(0, items.length - 1));
|
|
1462
|
+
}
|
|
1463
|
+
else if (restoredFrame.route.name === 'sourceList') {
|
|
1464
|
+
const sources = props.controller.listSources();
|
|
1465
|
+
itemCount = sources.length + 2;
|
|
1466
|
+
const entityIndex = restored.focusedEntityId
|
|
1467
|
+
? sources.findIndex((source) => source.id === restored.focusedEntityId)
|
|
1468
|
+
: -1;
|
|
1469
|
+
restoredFocus = entityIndex >= 0
|
|
1470
|
+
? entityIndex
|
|
1471
|
+
: Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
1472
|
+
}
|
|
1473
|
+
let restoredScroll = Math.min(restored.scrollOffset ?? 0, Math.max(0, (itemCount ?? 1) - 1));
|
|
1474
|
+
if (restoredFocus < restoredScroll)
|
|
1475
|
+
restoredScroll = restoredFocus;
|
|
1476
|
+
else if (restoredFocus >= restoredScroll + 4)
|
|
1477
|
+
restoredScroll = Math.max(0, restoredFocus - 3);
|
|
1478
|
+
setFocus(restoredFocus);
|
|
1479
|
+
setScrollOffset(restoredScroll);
|
|
1480
|
+
setQuery(restored.query ?? '');
|
|
1481
|
+
setInstallFilter(restored.installFilter ?? 'all');
|
|
1482
|
+
setSearchFocus(false);
|
|
1483
|
+
};
|
|
1311
1484
|
const beginConfigFlow = (pluginId) => {
|
|
1312
1485
|
props.controller.updatePageContext({
|
|
1313
1486
|
focusIndex: focus,
|
|
@@ -1324,30 +1497,51 @@ export function ConsoleAppV4(props) {
|
|
|
1324
1497
|
setActionError(undefined);
|
|
1325
1498
|
};
|
|
1326
1499
|
const back = () => {
|
|
1500
|
+
if (route.name === 'sourceEdit'
|
|
1501
|
+
&& sourceDraft
|
|
1502
|
+
&& sourceDraft.owner === route.owner
|
|
1503
|
+
&& sourceDraftChanges(sourceDraft).length === 0)
|
|
1504
|
+
setSourceDraft(undefined);
|
|
1505
|
+
if (route.name === 'sourceAdd'
|
|
1506
|
+
&& route.returnToDraft
|
|
1507
|
+
&& sourceDraft
|
|
1508
|
+
&& sourceDraft.owner === route.owner
|
|
1509
|
+
&& sourceDraftChanges(sourceDraft).length === 0)
|
|
1510
|
+
setSourceDraft(undefined);
|
|
1327
1511
|
if (route.name === 'result')
|
|
1328
1512
|
props.controller.leaveResult();
|
|
1329
1513
|
else
|
|
1330
1514
|
props.controller.back();
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1515
|
+
restorePageContext();
|
|
1516
|
+
setActionError(undefined);
|
|
1517
|
+
};
|
|
1518
|
+
const acceptSourceFieldAndBack = () => {
|
|
1519
|
+
props.controller.back();
|
|
1520
|
+
restorePageContext();
|
|
1337
1521
|
setActionError(undefined);
|
|
1338
1522
|
};
|
|
1523
|
+
const showSafeUiFailure = (error, code, stage) => {
|
|
1524
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
1525
|
+
return;
|
|
1526
|
+
const recordId = props.controller.recordUiFailure(error, { code, stage });
|
|
1527
|
+
go({ name: 'operationDiagnostic', recordId });
|
|
1528
|
+
};
|
|
1339
1529
|
const leaveResult = (target) => {
|
|
1340
1530
|
props.controller.leaveResult(target);
|
|
1341
|
-
|
|
1342
|
-
setFocus(restored.focusIndex ?? 0);
|
|
1343
|
-
setScrollOffset(restored.scrollOffset ?? 0);
|
|
1344
|
-
setQuery(restored.query ?? '');
|
|
1345
|
-
setInstallFilter(restored.installFilter ?? 'all');
|
|
1346
|
-
setSearchFocus(false);
|
|
1531
|
+
restorePageContext();
|
|
1347
1532
|
setShowTechnical(false);
|
|
1348
1533
|
setActionError(undefined);
|
|
1349
1534
|
};
|
|
1350
1535
|
const beginReview = (intent) => {
|
|
1536
|
+
const supportsScriptTimeout = intent.kind === 'pluginWrite'
|
|
1537
|
+
&& (intent.action === 'install' || intent.action === 'sync'
|
|
1538
|
+
|| intent.action === 'refreshAndSync' || intent.action === 'reinstall')
|
|
1539
|
+
|| intent.kind === 'pluginWriteBatch'
|
|
1540
|
+
&& (intent.action === 'install' || intent.action === 'sync' || intent.action === 'reinstall')
|
|
1541
|
+
|| intent.kind === 'configSave' && intent.apply;
|
|
1542
|
+
const pendingIntent = supportsScriptTimeout ? intent : undefined;
|
|
1543
|
+
pendingReviewIntentRef.current = pendingIntent;
|
|
1544
|
+
setPendingReviewIntent(pendingIntent);
|
|
1351
1545
|
const underlyingResultRoute = route.name === 'result' && snapshot.result && 'returnRoute' in snapshot.result
|
|
1352
1546
|
? snapshot.result.returnRoute
|
|
1353
1547
|
: undefined;
|
|
@@ -1362,6 +1556,28 @@ export function ConsoleAppV4(props) {
|
|
|
1362
1556
|
setFocus(0);
|
|
1363
1557
|
setScrollOffset(0);
|
|
1364
1558
|
};
|
|
1559
|
+
const openBatchSelection = (action) => {
|
|
1560
|
+
props.controller.updatePageContext({
|
|
1561
|
+
focusIndex: focus,
|
|
1562
|
+
scrollOffset,
|
|
1563
|
+
query,
|
|
1564
|
+
installFilter,
|
|
1565
|
+
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
1566
|
+
focusedEntityId: route.name === 'pluginList' ? pluginRows[focus]?.pluginId : undefined,
|
|
1567
|
+
});
|
|
1568
|
+
props.controller.beginPluginWriteBatch();
|
|
1569
|
+
setBatchDraft({
|
|
1570
|
+
...(action ? { action } : {}),
|
|
1571
|
+
selection: 'selected',
|
|
1572
|
+
selectedPluginIds: [],
|
|
1573
|
+
platform: 'both',
|
|
1574
|
+
scope: 'project',
|
|
1575
|
+
});
|
|
1576
|
+
setDirty(true);
|
|
1577
|
+
setFocus(0);
|
|
1578
|
+
setScrollOffset(0);
|
|
1579
|
+
setSearchFocus(false);
|
|
1580
|
+
};
|
|
1365
1581
|
const continueInstallAfterTargets = async (candidate, targets) => {
|
|
1366
1582
|
if (props.controller.snapshot.installPlacementDraft?.pluginId !== candidate.pluginId) {
|
|
1367
1583
|
props.controller.beginInstallFlow(candidate.pluginId, scope);
|
|
@@ -1460,6 +1676,22 @@ export function ConsoleAppV4(props) {
|
|
|
1460
1676
|
recoveryDescription: action.description,
|
|
1461
1677
|
});
|
|
1462
1678
|
};
|
|
1679
|
+
const beginRestrictedForceCleanup = async (pluginId, platform, selectedScope) => {
|
|
1680
|
+
const candidate = await props.controller.getForceCleanupCandidate(pluginId, platform, selectedScope);
|
|
1681
|
+
if (!candidate) {
|
|
1682
|
+
setActionError('当前没有可安全确认的受限清理目标。共享、外来、符号链接和原生内容会保持不变。');
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
try {
|
|
1686
|
+
props.controller.startForceCleanupReview(candidate);
|
|
1687
|
+
setFocus(0);
|
|
1688
|
+
setScrollOffset(0);
|
|
1689
|
+
setActionError(undefined);
|
|
1690
|
+
}
|
|
1691
|
+
catch (error) {
|
|
1692
|
+
showSafeUiFailure(error, 'tuiForceCleanupReviewFailed', 'force-cleanup-review');
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1463
1695
|
const beginAddSource = () => {
|
|
1464
1696
|
const owner = `source-add:${Date.now()}`;
|
|
1465
1697
|
setSourceDraft({ owner, mode: 'add', sourceId: '', type: 'git', location: '', refPolicy: 'cliChannel', ref: '', subdir: '', autoCheck: true });
|
|
@@ -1541,35 +1773,103 @@ export function ConsoleAppV4(props) {
|
|
|
1541
1773
|
path: draft.location,
|
|
1542
1774
|
...(draft.autoCheck ? {} : { autoCheck: false }),
|
|
1543
1775
|
};
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
kind: 'catalogGeneration',
|
|
1549
|
-
action: 'activate',
|
|
1776
|
+
const sourceDraftFromSource = (source, owner) => source.type === 'git'
|
|
1777
|
+
? {
|
|
1778
|
+
owner,
|
|
1779
|
+
mode: 'update',
|
|
1550
1780
|
sourceId: source.id,
|
|
1551
|
-
|
|
1552
|
-
|
|
1781
|
+
type: 'git',
|
|
1782
|
+
location: source.url,
|
|
1783
|
+
refPolicy: source.refPolicy.type,
|
|
1784
|
+
ref: source.refPolicy.type === 'explicit' ? source.refPolicy.ref : '',
|
|
1785
|
+
subdir: source.subdir ?? '',
|
|
1786
|
+
autoCheck: source.autoCheck !== false,
|
|
1787
|
+
}
|
|
1788
|
+
: {
|
|
1789
|
+
owner,
|
|
1790
|
+
mode: 'update',
|
|
1791
|
+
sourceId: source.id,
|
|
1792
|
+
type: 'path',
|
|
1793
|
+
location: source.path,
|
|
1794
|
+
refPolicy: 'cliChannel',
|
|
1795
|
+
ref: '',
|
|
1796
|
+
subdir: '',
|
|
1797
|
+
autoCheck: source.autoCheck !== false,
|
|
1798
|
+
};
|
|
1799
|
+
const sourceUpdateFromDraft = (draft) => draft.type === 'git'
|
|
1800
|
+
? {
|
|
1801
|
+
type: 'git',
|
|
1802
|
+
url: draft.location,
|
|
1803
|
+
refPolicy: draft.refPolicy === 'cliChannel'
|
|
1804
|
+
? { type: 'cliChannel' }
|
|
1805
|
+
: { type: 'explicit', ref: draft.ref },
|
|
1806
|
+
subdir: draft.subdir || null,
|
|
1807
|
+
autoCheck: draft.autoCheck,
|
|
1808
|
+
}
|
|
1809
|
+
: { type: 'path', path: draft.location, autoCheck: draft.autoCheck };
|
|
1810
|
+
const sourceDraftChanges = (draft) => {
|
|
1811
|
+
const current = draft.mode === 'update' ? props.controller.getConfiguredSource(draft.sourceId) : undefined;
|
|
1812
|
+
const location = (source) => source
|
|
1813
|
+
? projectCatalogSourceLocation(source.type === 'git' ? source.url : source.path) : undefined;
|
|
1814
|
+
const rawLocation = (source) => source
|
|
1815
|
+
? source.type === 'git' ? source.url : source.path : undefined;
|
|
1816
|
+
const ref = (source) => source?.type === 'git'
|
|
1817
|
+
? source.refPolicy.type === 'cliChannel' ? '跟随 AW CLI' : source.refPolicy.ref
|
|
1818
|
+
: undefined;
|
|
1819
|
+
const candidates = [
|
|
1820
|
+
{
|
|
1821
|
+
title: draft.type === 'git' ? 'Git 仓库地址' : '本地目录',
|
|
1822
|
+
before: location(current),
|
|
1823
|
+
after: location(sourceFromDraft(draft)),
|
|
1824
|
+
changed: rawLocation(current) !== draft.location,
|
|
1825
|
+
},
|
|
1826
|
+
...(draft.type === 'git' ? [
|
|
1827
|
+
{ title: '版本引用', before: ref(current), after: draft.refPolicy === 'cliChannel' ? '跟随 AW CLI' : draft.ref, changed: ref(current) !== (draft.refPolicy === 'cliChannel' ? '跟随 AW CLI' : draft.ref) },
|
|
1828
|
+
{ title: 'Catalog 子目录', before: current?.type === 'git' ? current.subdir ?? '未设置' : undefined, after: draft.subdir || '未设置', changed: current?.type !== 'git' || (current.subdir ?? '') !== draft.subdir },
|
|
1829
|
+
] : []),
|
|
1830
|
+
{ title: '自动检查更新', before: current ? current.autoCheck !== false ? '开启' : '关闭' : undefined, after: draft.autoCheck ? '开启' : '关闭', changed: !current || (current.autoCheck !== false) !== draft.autoCheck },
|
|
1831
|
+
];
|
|
1832
|
+
return candidates.filter((change) => change.changed)
|
|
1833
|
+
.map(({ changed: _changed, ...change }) => change);
|
|
1553
1834
|
};
|
|
1554
|
-
const
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1835
|
+
const beginSourceReview = (draft, activate) => {
|
|
1836
|
+
const mutation = draft.mode === 'add'
|
|
1837
|
+
? { action: 'add', source: sourceFromDraft(draft) }
|
|
1838
|
+
: { action: 'update', update: sourceUpdateFromDraft(draft) };
|
|
1839
|
+
if (activate) {
|
|
1840
|
+
beginReview({
|
|
1841
|
+
kind: 'catalogGeneration',
|
|
1842
|
+
action: 'activate',
|
|
1843
|
+
sourceId: draft.sourceId,
|
|
1844
|
+
sourceMutation: mutation,
|
|
1845
|
+
});
|
|
1557
1846
|
return;
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
:
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
kind: 'catalogGeneration',
|
|
1847
|
+
}
|
|
1848
|
+
beginReview(draft.mode === 'add' ? {
|
|
1849
|
+
kind: 'sourceMutation',
|
|
1850
|
+
action: 'add',
|
|
1851
|
+
sourceId: draft.sourceId,
|
|
1852
|
+
source: mutation.action === 'add' ? mutation.source : undefined,
|
|
1853
|
+
changes: sourceDraftChanges(draft),
|
|
1854
|
+
} : {
|
|
1855
|
+
kind: 'sourceMutation',
|
|
1568
1856
|
action: 'update',
|
|
1569
1857
|
sourceId: draft.sourceId,
|
|
1570
|
-
|
|
1858
|
+
update: mutation.action === 'update' ? mutation.update : undefined,
|
|
1859
|
+
changes: sourceDraftChanges(draft),
|
|
1571
1860
|
});
|
|
1572
1861
|
};
|
|
1862
|
+
const openSourceEdit = (sourceId) => {
|
|
1863
|
+
const source = props.controller.getConfiguredSource(sourceId);
|
|
1864
|
+
if (!source) {
|
|
1865
|
+
setActionError('无法读取完整来源配置。请重新加载后重试。');
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
const owner = `source-update:${sourceId}:${Date.now()}`;
|
|
1869
|
+
setSourceDraft(sourceDraftFromSource(source, owner));
|
|
1870
|
+
setDirty(false);
|
|
1871
|
+
go({ name: 'sourceEdit', sourceId, owner });
|
|
1872
|
+
};
|
|
1573
1873
|
const configLayerLabel = (layer) => layer.scope === 'effective' ? '有效设置'
|
|
1574
1874
|
: layer.scope === 'user' ? '个人默认'
|
|
1575
1875
|
: '当前项目';
|
|
@@ -1663,10 +1963,14 @@ export function ConsoleAppV4(props) {
|
|
|
1663
1963
|
if (!activeObjectKey)
|
|
1664
1964
|
return;
|
|
1665
1965
|
setObjectDrafts((current) => ({ ...current, [activeObjectKey]: value }));
|
|
1966
|
+
const initial = objectValueFromUpdate ?? objectValueFromField ?? {};
|
|
1967
|
+
setChangedObjectDraftKeys((current) => configValuesEqual(initial, value)
|
|
1968
|
+
? current.filter((key) => key !== activeObjectKey)
|
|
1969
|
+
: [...new Set([...current, activeObjectKey])]);
|
|
1666
1970
|
setObjectError(undefined);
|
|
1667
1971
|
setConfigActionError(undefined);
|
|
1668
1972
|
};
|
|
1669
|
-
const objectIssueText = (issue) => `${issue.path.property ?? issue.path.field}
|
|
1973
|
+
const objectIssueText = (issue) => `${issue.path.property ?? issue.path.field}:${issue.message}`;
|
|
1670
1974
|
const commitObjectDraft = () => {
|
|
1671
1975
|
if (!activeField || activeField.definition.type !== 'object' || !activeObjectDraft)
|
|
1672
1976
|
return;
|
|
@@ -1676,6 +1980,9 @@ export function ConsoleAppV4(props) {
|
|
|
1676
1980
|
return;
|
|
1677
1981
|
}
|
|
1678
1982
|
updateDraft(activeField.key, activeObjectDraft);
|
|
1983
|
+
if (activeObjectKey) {
|
|
1984
|
+
setChangedObjectDraftKeys((current) => current.filter((key) => key !== activeObjectKey));
|
|
1985
|
+
}
|
|
1679
1986
|
back();
|
|
1680
1987
|
};
|
|
1681
1988
|
const objectPropertyStage = (definition) => definition.type === 'boolean' || definition.choices ? 'choices' : 'custom';
|
|
@@ -1860,9 +2167,33 @@ export function ConsoleAppV4(props) {
|
|
|
1860
2167
|
};
|
|
1861
2168
|
});
|
|
1862
2169
|
};
|
|
2170
|
+
const discardInvalidConfigDraftChanges = () => {
|
|
2171
|
+
if (!configRoute || configDraftIssues.length === 0)
|
|
2172
|
+
return;
|
|
2173
|
+
const invalidKeys = new Set(configDraftIssues.map((issue) => issue.key));
|
|
2174
|
+
const nextUpdates = Object.fromEntries(Object.entries(draftUpdates)
|
|
2175
|
+
.filter(([key]) => !invalidKeys.has(key)));
|
|
2176
|
+
const nextRemoves = draftRemoves.filter((key) => !invalidKeys.has(key));
|
|
2177
|
+
const owner = `${configOwnerKey(configRoute)}\0`;
|
|
2178
|
+
const nextChangedObjectKeys = changedObjectDraftKeys.filter((key) => !key.startsWith(owner)
|
|
2179
|
+
|| !invalidKeys.has(key.slice(owner.length)));
|
|
2180
|
+
setDraftUpdates(nextUpdates);
|
|
2181
|
+
setDraftRemoves(nextRemoves);
|
|
2182
|
+
setObjectDrafts((current) => Object.fromEntries(Object.entries(current)
|
|
2183
|
+
.filter(([key]) => !key.startsWith(owner)
|
|
2184
|
+
|| !invalidKeys.has(key.slice(owner.length)))));
|
|
2185
|
+
setChangedObjectDraftKeys(nextChangedObjectKeys);
|
|
2186
|
+
setDirty(Object.keys(nextUpdates).length + nextRemoves.length + nextChangedObjectKeys.length > 0);
|
|
2187
|
+
setObjectError(undefined);
|
|
2188
|
+
setConfigActionError(undefined);
|
|
2189
|
+
};
|
|
1863
2190
|
const beginConfigSave = async (apply) => {
|
|
1864
2191
|
if (!configRoute || configRoute.layer.scope === 'effective' || !currentConfigPage || !dirty)
|
|
1865
2192
|
return;
|
|
2193
|
+
if (configDraftIssues.length > 0) {
|
|
2194
|
+
setConfigActionError('字段已变化。请先放弃失效草稿,或返回继续检查来源。');
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
1866
2197
|
const fields = currentConfigPage.groups.flatMap((group) => group.fields);
|
|
1867
2198
|
try {
|
|
1868
2199
|
await beginReview({
|
|
@@ -1879,7 +2210,8 @@ export function ConsoleAppV4(props) {
|
|
|
1879
2210
|
});
|
|
1880
2211
|
setConfigActionError(undefined);
|
|
1881
2212
|
}
|
|
1882
|
-
catch {
|
|
2213
|
+
catch (error) {
|
|
2214
|
+
props.controller.recordUiFailure(error, { code: 'tuiConfigReviewFailed', stage: 'config-review' });
|
|
1883
2215
|
setConfigActionError('设置值未通过验证。请重新检查修改。');
|
|
1884
2216
|
}
|
|
1885
2217
|
};
|
|
@@ -1890,10 +2222,10 @@ export function ConsoleAppV4(props) {
|
|
|
1890
2222
|
const field = currentConfigPage.groups.flatMap((group) => group.fields)
|
|
1891
2223
|
.find((candidate) => candidate.key === pendingLayerFieldKey);
|
|
1892
2224
|
setPendingLayerFieldKey(undefined);
|
|
1893
|
-
if (!field
|
|
2225
|
+
if (!field || (!configFieldCanSet(field) && !configFieldCanUnset(field)))
|
|
1894
2226
|
return;
|
|
1895
2227
|
const layer = { scope: route.layer.scope };
|
|
1896
|
-
props.controller.push(field.configured ? {
|
|
2228
|
+
props.controller.push(field.configured || !configFieldCanSet(field) ? {
|
|
1897
2229
|
name: 'configFieldAction',
|
|
1898
2230
|
pluginId: route.pluginId,
|
|
1899
2231
|
layer,
|
|
@@ -1931,7 +2263,8 @@ export function ConsoleAppV4(props) {
|
|
|
1931
2263
|
return;
|
|
1932
2264
|
setPendingPresetId(undefined);
|
|
1933
2265
|
}, [pendingPresetId, route.name, route.name === 'configFields' ? route.layer.scope : '']);
|
|
1934
|
-
if (snapshot.phase === 'error' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
2266
|
+
if (snapshot.phase === 'error' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
2267
|
+
&& !isOperationHistoryRoute(route)) {
|
|
1935
2268
|
const sourcesConfigInvalid = snapshot.loadError?.code === 'catalogSourcesConfigInvalid';
|
|
1936
2269
|
breadcrumb = sourcesConfigInvalid ? 'Catalog 来源恢复' : '启动失败';
|
|
1937
2270
|
title = sourcesConfigInvalid ? 'Catalog 来源配置损坏' : '无法读取插件状态';
|
|
@@ -1943,19 +2276,20 @@ export function ConsoleAppV4(props) {
|
|
|
1943
2276
|
: 'd 技术详情 · q 退出 · Ctrl-C 退出';
|
|
1944
2277
|
actions = sourcesConfigInvalid ? [{
|
|
1945
2278
|
label: '备份并恢复官方默认来源',
|
|
1946
|
-
description: '
|
|
2279
|
+
description: '先进入确认页;不会删除目录版本状态或本地缓存',
|
|
1947
2280
|
run: () => beginReview({ kind: 'catalogSourcesRecovery' }),
|
|
1948
2281
|
}] : [];
|
|
1949
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "error", title:
|
|
2282
|
+
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] });
|
|
1950
2283
|
}
|
|
1951
|
-
else if (snapshot.phase !== 'ready' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
2284
|
+
else if (snapshot.phase !== 'ready' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
2285
|
+
&& !isOperationHistoryRoute(route)) {
|
|
1952
2286
|
breadcrumb = '启动';
|
|
1953
2287
|
title = '正在检查插件状态';
|
|
1954
2288
|
description = '单个来源失败不会阻止其他来源继续加载。';
|
|
1955
|
-
help = 'Ctrl-C
|
|
2289
|
+
help = 'Ctrl-C 退出';
|
|
1956
2290
|
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" })] });
|
|
1957
2291
|
}
|
|
1958
|
-
else if (route.name === 'home') {
|
|
2292
|
+
else if (rendererFamily === 'home' && route.name === 'home') {
|
|
1959
2293
|
const home = snapshot.viewModel.home;
|
|
1960
2294
|
context = '当前项目';
|
|
1961
2295
|
actions = [
|
|
@@ -1972,7 +2306,9 @@ export function ConsoleAppV4(props) {
|
|
|
1972
2306
|
run: () => go({ name: 'sourceList' }),
|
|
1973
2307
|
},
|
|
1974
2308
|
];
|
|
1975
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusCard, { status: home.issueCount > 0 || home.unavailableSourceCount > 0 ? 'warning' : 'success', title: "\u5F53\u524D\u72B6\u6001", children: [
|
|
2309
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusCard, { status: home.issueCount > 0 || home.unavailableSourceCount > 0 ? 'warning' : 'success', title: "\u5F53\u524D\u72B6\u6001", children: [snapshot.statusReadAtMs !== undefined
|
|
2310
|
+
? _jsx(StaticRow, { title: "\u6700\u8FD1\u6210\u529F\u8BFB\u53D6\u72B6\u6001", meta: new Date(snapshot.statusReadAtMs).toLocaleString('zh-CN', { hour12: false }), compact: true })
|
|
2311
|
+
: null, _jsx(ResultRow, { title: `${home.installedPlugins} 个插件可用`, status: "success" }), _jsx(ResultRow, { title: `${home.issueCount} 项需要处理`, status: home.issueCount > 0 ? 'warning' : 'muted' }), home.unavailableSourceCount > 0 ? _jsx(ResultRow, { title: `${home.unavailableSourceCount} 个来源不可用`, status: "error" }) : null, home.catalogCheckingCount > 0 ? _jsx(ResultRow, { title: `${home.catalogCheckingCount} 个插件来源正在检查`, status: "muted" }) : null, home.catalogUpdateCount > 0 ? _jsx(ResultRow, { title: `${home.catalogUpdateCount} 个插件来源有更新`, status: "warning" }) : null, home.catalogRecoveryCount > 0 ? _jsx(ResultRow, { title: `${home.catalogRecoveryCount} 个插件来源需要检查或恢复`, status: "warning" }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "task", compact: policy.mode === 'compact' })] });
|
|
1976
2312
|
}
|
|
1977
2313
|
else if (route.name === 'pluginList') {
|
|
1978
2314
|
const sourceId = route.sourceId ?? 'all';
|
|
@@ -1990,9 +2326,11 @@ export function ConsoleAppV4(props) {
|
|
|
1990
2326
|
}).length;
|
|
1991
2327
|
breadcrumb = route.mode === 'browse' ? '添加插件' : '我的插件';
|
|
1992
2328
|
title = breadcrumb;
|
|
1993
|
-
description = route.mode === 'browse'
|
|
2329
|
+
description = route.mode === 'browse'
|
|
2330
|
+
? '选择插件;列表末尾可进入批量添加。'
|
|
2331
|
+
: '查看状态;列表末尾可进入批量操作和项目同步。';
|
|
1994
2332
|
context = `${items.length} 个插件`;
|
|
1995
|
-
help = `↑↓ 移动 · Enter 查看 · ←→ 来源 · / 搜索${route.mode === 'browse' ? ' · f 未安装' : ''} · Esc 返回`;
|
|
2333
|
+
help = `↑↓ 移动 · Enter 查看 · m 批量${route.mode === 'installed' ? ' · a 项目全同步' : ''} · ←→ 来源 · / 搜索${route.mode === 'browse' ? ' · f 未安装' : ''} · Esc 返回`;
|
|
1996
2334
|
const sourceError = sourceId !== 'all'
|
|
1997
2335
|
&& props.controller.getSourceOverview(sourceId).health?.state === 'unavailable';
|
|
1998
2336
|
actions = sourceError
|
|
@@ -2002,13 +2340,50 @@ export function ConsoleAppV4(props) {
|
|
|
2002
2340
|
pluginId: item.pluginId,
|
|
2003
2341
|
name: item.displayName,
|
|
2004
2342
|
sourceLabel: item.sourceLabel,
|
|
2005
|
-
description: item.
|
|
2006
|
-
?
|
|
2343
|
+
description: item.configOnly
|
|
2344
|
+
? '来源当前没有这个插件;可以查看已保存设置或定点重置设置层。'
|
|
2007
2345
|
: item.description,
|
|
2346
|
+
...(item.primaryAttention ? { attention: item.primaryAttention.message } : {}),
|
|
2008
2347
|
placementSummary: pluginPlacementSummary(item),
|
|
2009
2348
|
statusLabel: statusText(item),
|
|
2010
2349
|
status: pluginRowStatus(item),
|
|
2011
2350
|
}));
|
|
2351
|
+
if (!sourceError && items.length > 0) {
|
|
2352
|
+
pluginRows.push({
|
|
2353
|
+
pluginId: '@menu:batch',
|
|
2354
|
+
name: route.mode === 'browse' ? '选择多个插件添加…' : '批量操作…',
|
|
2355
|
+
sourceLabel: '',
|
|
2356
|
+
description: route.mode === 'browse'
|
|
2357
|
+
? '选择多个插件,然后统一选择平台和位置'
|
|
2358
|
+
: '选择多个插件进行同步、重新安装、卸载或清理',
|
|
2359
|
+
placementSummary: '',
|
|
2360
|
+
statusLabel: '菜单',
|
|
2361
|
+
status: 'info',
|
|
2362
|
+
menuAction: () => openBatchSelection(route.mode === 'browse' ? 'install' : undefined),
|
|
2363
|
+
});
|
|
2364
|
+
if (route.mode === 'installed') {
|
|
2365
|
+
pluginRows.push({
|
|
2366
|
+
pluginId: '@menu:sync-project',
|
|
2367
|
+
name: '同步当前项目全部插件…',
|
|
2368
|
+
sourceLabel: '',
|
|
2369
|
+
description: '按已保存的项目设置恢复 Codex 和 Claude Code',
|
|
2370
|
+
placementSummary: '',
|
|
2371
|
+
statusLabel: '菜单',
|
|
2372
|
+
status: 'info',
|
|
2373
|
+
menuAction: () => {
|
|
2374
|
+
openBatchSelection();
|
|
2375
|
+
setBatchDraft({
|
|
2376
|
+
action: 'sync',
|
|
2377
|
+
selection: 'currentProjectIntentRoots',
|
|
2378
|
+
selectedPluginIds: [],
|
|
2379
|
+
platform: 'both',
|
|
2380
|
+
scope: 'project',
|
|
2381
|
+
});
|
|
2382
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
2383
|
+
},
|
|
2384
|
+
});
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2012
2387
|
if (route.mode === 'installed' && items.length === 0 && !sourceError) {
|
|
2013
2388
|
title = '还没有添加插件';
|
|
2014
2389
|
description = `从已配置的 ${props.controller.listSources().length} 个来源中浏览可用能力。`;
|
|
@@ -2027,7 +2402,9 @@ export function ConsoleAppV4(props) {
|
|
|
2027
2402
|
const viewport = sourceError || pluginRows.length === 0
|
|
2028
2403
|
? actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(pluginListStaticRows))
|
|
2029
2404
|
: rowViewport({
|
|
2030
|
-
rowHeights: pluginRows.map(() =>
|
|
2405
|
+
rowHeights: pluginRows.map((row) => row.menuAction
|
|
2406
|
+
? 1
|
|
2407
|
+
: row.attention ? 4 : 3),
|
|
2031
2408
|
focus,
|
|
2032
2409
|
requestedOffset: scrollOffset,
|
|
2033
2410
|
rowBudget: pageActionBudget(pluginListStaticRows),
|
|
@@ -2047,6 +2424,126 @@ export function ConsoleAppV4(props) {
|
|
|
2047
2424
|
? _jsx(PluginRows, { rows: pluginRows, focus: focus, viewport: viewport })
|
|
2048
2425
|
: _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit })] }));
|
|
2049
2426
|
}
|
|
2427
|
+
else if (route.name === 'batchSelection') {
|
|
2428
|
+
breadcrumb = '我的插件 / 批量操作';
|
|
2429
|
+
title = batchDraft?.action ? `选择要${batchActionLabel(batchDraft.action)}的插件` : '选择批量操作';
|
|
2430
|
+
description = batchDraft?.action
|
|
2431
|
+
? 'Space 或 Enter 切换当前插件,c 继续。每个根插件及其依赖会在确认页逐目标列出。'
|
|
2432
|
+
: '一次批量操作只使用一种动作。强制清理只在单插件页面提供。';
|
|
2433
|
+
if (!batchDraft?.action) {
|
|
2434
|
+
actions = [
|
|
2435
|
+
{
|
|
2436
|
+
label: '同步当前项目全部插件',
|
|
2437
|
+
description: '从当前项目已保存的插件设置建立方案',
|
|
2438
|
+
run: () => {
|
|
2439
|
+
setBatchDraft({
|
|
2440
|
+
action: 'sync',
|
|
2441
|
+
selection: 'currentProjectIntentRoots',
|
|
2442
|
+
selectedPluginIds: [],
|
|
2443
|
+
platform: 'both',
|
|
2444
|
+
scope: 'project',
|
|
2445
|
+
});
|
|
2446
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
2447
|
+
},
|
|
2448
|
+
},
|
|
2449
|
+
...['install', 'sync', 'reinstall', 'uninstall', 'cleanup'].map((action) => ({
|
|
2450
|
+
label: `选择多个插件并${batchActionLabel(action)}`,
|
|
2451
|
+
description: action === 'cleanup' ? '执行普通清理;保留不属于 AW 管理的内容' : undefined,
|
|
2452
|
+
run: () => {
|
|
2453
|
+
setBatchDraft((current) => current ? { ...current, action } : current);
|
|
2454
|
+
setFocus(0);
|
|
2455
|
+
setScrollOffset(0);
|
|
2456
|
+
},
|
|
2457
|
+
})),
|
|
2458
|
+
];
|
|
2459
|
+
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(1));
|
|
2460
|
+
context = '选择 1 种动作';
|
|
2461
|
+
help = '↑↓ 移动 · Enter 选择 · Esc 放弃';
|
|
2462
|
+
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit });
|
|
2463
|
+
}
|
|
2464
|
+
else {
|
|
2465
|
+
const candidates = props.controller.listPlugins({
|
|
2466
|
+
mode: batchDraft.action === 'install' ? 'browse' : 'installed',
|
|
2467
|
+
}).filter((candidate) => !candidate.configOnly);
|
|
2468
|
+
const selected = new Set(batchDraft.selectedPluginIds);
|
|
2469
|
+
actions = candidates.map((candidate) => ({
|
|
2470
|
+
label: candidate.displayName,
|
|
2471
|
+
description: `${candidate.sourceLabel} · ${pluginPlacementSummary(candidate)}`,
|
|
2472
|
+
selected: selected.has(candidate.pluginId),
|
|
2473
|
+
run: () => setBatchDraft((current) => current ? {
|
|
2474
|
+
...current,
|
|
2475
|
+
selectedPluginIds: current.selectedPluginIds.includes(candidate.pluginId)
|
|
2476
|
+
? current.selectedPluginIds.filter((pluginId) => pluginId !== candidate.pluginId)
|
|
2477
|
+
: [...current.selectedPluginIds, candidate.pluginId],
|
|
2478
|
+
} : current),
|
|
2479
|
+
}));
|
|
2480
|
+
context = `${batchDraft.selectedPluginIds.length}/${candidates.length} 个插件`;
|
|
2481
|
+
help = '↑↓ 移动 · Space/Enter 选择 · a 全选 · c 继续 · Esc 放弃';
|
|
2482
|
+
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(1));
|
|
2483
|
+
content = candidates.length > 0
|
|
2484
|
+
? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "toggle", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit })
|
|
2485
|
+
: _jsx(Callout, { status: "warning", title: "\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u63D2\u4EF6", description: "\u8FD4\u56DE\u63D2\u4EF6\u5217\u8868\u5E76\u68C0\u67E5\u6765\u6E90\u6216\u5B89\u88C5\u72B6\u6001\u3002" });
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
else if (route.name === 'batchPlacement') {
|
|
2489
|
+
breadcrumb = '我的插件 / 批量操作 / 位置';
|
|
2490
|
+
title = batchDraft?.selection === 'currentProjectIntentRoots'
|
|
2491
|
+
? '同步当前项目全部保存目标'
|
|
2492
|
+
: `设置批量${batchDraft?.action ? batchActionLabel(batchDraft.action) : ''}位置`;
|
|
2493
|
+
description = '所有选择使用同一位置和平台。确认页会重新读取状态并冻结实际计划。';
|
|
2494
|
+
if (!batchDraft?.action) {
|
|
2495
|
+
actions = [{ label: '返回批量选择', run: back }];
|
|
2496
|
+
}
|
|
2497
|
+
else {
|
|
2498
|
+
const projectIntent = batchDraft.selection === 'currentProjectIntentRoots';
|
|
2499
|
+
actions = [
|
|
2500
|
+
{
|
|
2501
|
+
label: '当前项目', selected: batchDraft.scope === 'project',
|
|
2502
|
+
run: () => setBatchDraft((current) => current ? { ...current, scope: 'project' } : current),
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
label: '所有项目', selected: batchDraft.scope === 'user',
|
|
2506
|
+
disabled: projectIntent ? '项目全部同步固定使用当前项目' : undefined,
|
|
2507
|
+
run: () => setBatchDraft((current) => current ? { ...current, scope: 'user' } : current),
|
|
2508
|
+
},
|
|
2509
|
+
...['codex', 'claude', 'both'].map((platform) => ({
|
|
2510
|
+
label: platform === 'both' ? 'Codex 和 Claude Code' : platformLabel(platform),
|
|
2511
|
+
selected: batchDraft.platform === platform,
|
|
2512
|
+
run: () => setBatchDraft((current) => current ? { ...current, platform } : current),
|
|
2513
|
+
})),
|
|
2514
|
+
{
|
|
2515
|
+
label: `查看批量${batchActionLabel(batchDraft.action)}确认`,
|
|
2516
|
+
description: `${batchDraft.selection === 'selected' ? `${batchDraft.selectedPluginIds.length} 个所选插件` : '当前项目全部保存目标'} · ${scopeLabel(batchDraft.scope)}`,
|
|
2517
|
+
disabled: batchDraft.selection === 'selected' && batchDraft.selectedPluginIds.length === 0
|
|
2518
|
+
? '请至少选择一个插件' : undefined,
|
|
2519
|
+
run: () => {
|
|
2520
|
+
const intent = {
|
|
2521
|
+
kind: 'pluginWriteBatch',
|
|
2522
|
+
action: batchDraft.action,
|
|
2523
|
+
selection: batchDraft.selection === 'selected'
|
|
2524
|
+
? {
|
|
2525
|
+
kind: 'selected',
|
|
2526
|
+
pluginIds: [...new Set(batchDraft.selectedPluginIds)]
|
|
2527
|
+
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
2528
|
+
}
|
|
2529
|
+
: { kind: 'currentProjectIntentRoots' },
|
|
2530
|
+
platform: batchDraft.platform,
|
|
2531
|
+
scope: batchDraft.scope,
|
|
2532
|
+
};
|
|
2533
|
+
beginReview(intent);
|
|
2534
|
+
setDirty(false);
|
|
2535
|
+
setFocus(0);
|
|
2536
|
+
setScrollOffset(0);
|
|
2537
|
+
},
|
|
2538
|
+
},
|
|
2539
|
+
];
|
|
2540
|
+
}
|
|
2541
|
+
context = batchDraft?.selection === 'selected'
|
|
2542
|
+
? `${batchDraft.selectedPluginIds.length} 个插件`
|
|
2543
|
+
: '保存的项目目标';
|
|
2544
|
+
help = '↑↓ 移动 · Enter 选择 · Esc 返回修改';
|
|
2545
|
+
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' });
|
|
2546
|
+
}
|
|
2050
2547
|
else if (route.name === 'pluginPreview' && plugin) {
|
|
2051
2548
|
breadcrumb = `添加插件 / ${plugin.displayName}`;
|
|
2052
2549
|
context = plugin.sourceLabel;
|
|
@@ -2090,14 +2587,14 @@ export function ConsoleAppV4(props) {
|
|
|
2090
2587
|
if (action.id === 'upgradeAwCli')
|
|
2091
2588
|
return {
|
|
2092
2589
|
label: '升级 AW CLI',
|
|
2093
|
-
description:
|
|
2590
|
+
description: '升级到支持此插件的 AW CLI 版本后重试',
|
|
2094
2591
|
status: 'warning',
|
|
2095
2592
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
2096
2593
|
};
|
|
2097
2594
|
if (action.id === 'capabilityTechnical')
|
|
2098
2595
|
return {
|
|
2099
|
-
label: '
|
|
2100
|
-
description: '
|
|
2596
|
+
label: '查看功能支持详情',
|
|
2597
|
+
description: '查看缺少的功能和安全恢复方向',
|
|
2101
2598
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
2102
2599
|
};
|
|
2103
2600
|
if (action.id === 'sourceProblem')
|
|
@@ -2237,13 +2734,13 @@ export function ConsoleAppV4(props) {
|
|
|
2237
2734
|
description = '插件依赖用于生成,不能在这里取消。';
|
|
2238
2735
|
actions = [
|
|
2239
2736
|
{ label: '返回 Skill 选择', run: back },
|
|
2240
|
-
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () =>
|
|
2737
|
+
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
2241
2738
|
];
|
|
2242
2739
|
const dependencies = overview?.dependencies ?? [];
|
|
2243
2740
|
const visibleDependencies = dependencies.slice(0, showTechnical ? 1 : responsiveBudget(2, 3));
|
|
2244
2741
|
const technicalDependencies = dependencies.slice(0, 1);
|
|
2245
2742
|
const compactTechnicalSummary = policy.mode === 'compact' && showTechnical;
|
|
2246
|
-
content = _jsxs(Box, { flexDirection: "column", children: [compactTechnicalSummary ? _jsxs(SectionCard, { title: "provider \u6458\u8981", meta: `${dependencies.length + 1} 个`, compact: true, children: [_jsx(StaticRow, { title: plugin.displayName, meta: "\u6839 provider", compact: true }), _jsx(StaticRow, { title: `${dependencies.length} 个依赖 providers`, meta: "\u7528\u4E8E\u751F\u6210", compact: true })] }) : _jsx(SectionCard, { title: "\u5F53\u524D\u63D2\u4EF6",
|
|
2743
|
+
content = _jsxs(Box, { flexDirection: "column", children: [compactTechnicalSummary ? _jsxs(SectionCard, { title: "provider \u6458\u8981", meta: `${dependencies.length + 1} 个`, compact: true, children: [_jsx(StaticRow, { title: plugin.displayName, meta: "\u6839 provider", compact: true }), _jsx(StaticRow, { title: `${dependencies.length} 个依赖 providers`, meta: "\u7528\u4E8E\u751F\u6210", compact: true })] }) : _jsx(SectionCard, { title: "\u5F53\u524D\u63D2\u4EF6", children: _jsx(StaticRow, { title: plugin.displayName, description: "\u63D0\u4F9B\u672C\u6B21\u5B89\u88C5\u5165\u53E3", meta: `提供 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.relation === 'requested').length} 个 Skills`, compact: policy.mode === 'compact' }) }), compactTechnicalSummary ? null : _jsxs(SectionCard, { title: "\u4F9D\u8D56\u63D2\u4EF6", meta: `${dependencies.length} 个`, children: [visibleDependencies.map((dependency) => _jsx(StaticRow, { title: dependency.displayName, description: `${dependency.relation === 'direct' ? '当前插件直接需要' : '由依赖插件继续需要'} · ${dependency.requiredBy.map((entry) => entry.displayName).join('、')} 需要它`, meta: `提供 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.providerPluginId === dependency.pluginId).length} 个 Skills`, compact: policy.mode === 'compact' }, dependency.pluginId)), dependencies.length > visibleDependencies.length ? _jsx(StaticRow, { title: `另有 ${dependencies.length - visibleDependencies.length} 个依赖插件`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true }), showTechnical ? _jsx(TechnicalDetails, { children: technicalDependencies.map((entry) => _jsx(StaticRow, { title: entry.displayName, meta: `${entry.pluginId}${compactTechnicalSummary && dependencies.length > technicalDependencies.length ? ` · 另有 ${dependencies.length - technicalDependencies.length} 个依赖` : ''}`, compact: true }, entry.pluginId)) }) : null] });
|
|
2247
2744
|
}
|
|
2248
2745
|
else if (route.name === 'portableAgentMode' && plugin && snapshot.portableDraft) {
|
|
2249
2746
|
breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
|
|
@@ -2260,7 +2757,7 @@ export function ConsoleAppV4(props) {
|
|
|
2260
2757
|
context = '指定';
|
|
2261
2758
|
title = '输入 Agent ID';
|
|
2262
2759
|
description = '多个 Agent 可使用逗号或空格分隔。';
|
|
2263
|
-
help = '输入 Agent ID · Enter 保存 · Esc 返回';
|
|
2760
|
+
help = '输入 Agent ID · Enter 保存 · Ctrl-U 清空输入 · Esc 返回';
|
|
2264
2761
|
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, claude-code, codex", status: "muted", compact: true })] });
|
|
2265
2762
|
}
|
|
2266
2763
|
else if (route.name === 'portableScope' && plugin && snapshot.portableDraft) {
|
|
@@ -2286,7 +2783,7 @@ export function ConsoleAppV4(props) {
|
|
|
2286
2783
|
setScrollOffset(0);
|
|
2287
2784
|
} }] : []),
|
|
2288
2785
|
...(route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible'
|
|
2289
|
-
? [{ label: showTechnical ? '返回问题摘要' : '查看安装说明', run: () =>
|
|
2786
|
+
? [{ label: showTechnical ? '返回问题摘要' : '查看安装说明', run: () => toggleTechnical() }]
|
|
2290
2787
|
: []),
|
|
2291
2788
|
...(route.code === 'portableScopeUnavailable' ? [{ label: '返回 Skill 选择', run: () => {
|
|
2292
2789
|
props.controller.resetPortableFlowToPreview(plugin.pluginId, { name: 'portableSkillSelect', pluginId: plugin.pluginId }, true);
|
|
@@ -2297,7 +2794,7 @@ export function ConsoleAppV4(props) {
|
|
|
2297
2794
|
{ label: '返回插件预览', run: () => props.controller.resetPortableFlowToPreview(plugin.pluginId) },
|
|
2298
2795
|
...(route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible'
|
|
2299
2796
|
? []
|
|
2300
|
-
: [{ label: showTechnical ? '返回问题摘要' : '技术详情', run: () =>
|
|
2797
|
+
: [{ label: showTechnical ? '返回问题摘要' : '技术详情', run: () => toggleTechnical() }]),
|
|
2301
2798
|
];
|
|
2302
2799
|
const blockerActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 4));
|
|
2303
2800
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockerActionViewport.offset, limit: blockerActionViewport.limit })] });
|
|
@@ -2313,12 +2810,13 @@ export function ConsoleAppV4(props) {
|
|
|
2313
2810
|
? dependentCount > 0 ? 1 : 2
|
|
2314
2811
|
: dependentCount > 0 ? 3 : 5;
|
|
2315
2812
|
const dependentLimit = responsiveBudget(2, 4);
|
|
2316
|
-
const dependencyRows = (entries, limit, dependent = false) => {
|
|
2317
|
-
const
|
|
2318
|
-
|
|
2813
|
+
const dependencyRows = (entries, limit, dependent = false, offset = 0) => {
|
|
2814
|
+
const safeOffset = Math.min(offset, Math.max(0, entries.length - 1));
|
|
2815
|
+
const visible = entries.slice(safeOffset, safeOffset + limit);
|
|
2816
|
+
return _jsxs(_Fragment, { children: [safeOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${safeOffset} 项`, status: "muted", compact: true }) : null, visible.map((entry) => {
|
|
2319
2817
|
const state = dependencyStatus(entry);
|
|
2320
2818
|
return _jsx(StaticRow, { title: entry.displayName, description: dependencyRelationDescription(entry), meta: dependent && route.view === 'removeBlocked' ? dependentTargetLabel(entry) : dependencyPlacementLabel(entry), status: state.status, compact: policy.mode === 'compact' }, entry.pluginId);
|
|
2321
|
-
}), entries.length > visible.length ? _jsx(StaticRow, { title:
|
|
2819
|
+
}), entries.length > safeOffset + visible.length ? _jsx(StaticRow, { title: `↓ 后面还有 ${entries.length - safeOffset - visible.length} 项`, status: "muted", compact: true }) : null] });
|
|
2322
2820
|
};
|
|
2323
2821
|
if (route.view === 'removeBlocked') {
|
|
2324
2822
|
breadcrumb = `${plugin.displayName} / 移除`;
|
|
@@ -2326,31 +2824,21 @@ export function ConsoleAppV4(props) {
|
|
|
2326
2824
|
title = `无法移除 ${plugin.displayName}`;
|
|
2327
2825
|
description = '仍有插件依赖它。请先处理依赖者,再重新尝试。';
|
|
2328
2826
|
actions = [
|
|
2329
|
-
...(route.platform && route.scope ? [{
|
|
2330
|
-
label: '强制清理…',
|
|
2331
|
-
description: '依赖者会变为不完整;下一页仍需明确确认',
|
|
2332
|
-
status: 'error',
|
|
2333
|
-
run: () => beginReview({
|
|
2334
|
-
kind: 'pluginWrite',
|
|
2335
|
-
action: 'cleanup',
|
|
2336
|
-
pluginId: plugin.pluginId,
|
|
2337
|
-
platform: route.platform,
|
|
2338
|
-
scope: route.scope,
|
|
2339
|
-
force: true,
|
|
2340
|
-
}),
|
|
2341
|
-
}] : []),
|
|
2342
2827
|
{ label: '查看完整依赖关系', run: () => {
|
|
2343
2828
|
props.controller.replace({ name: 'pluginDependencies', pluginId: plugin.pluginId });
|
|
2344
2829
|
setFocus(0);
|
|
2345
2830
|
setScrollOffset(0);
|
|
2346
2831
|
} },
|
|
2347
2832
|
{ label: '返回插件详情', run: back },
|
|
2348
|
-
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () =>
|
|
2833
|
+
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
2349
2834
|
];
|
|
2350
2835
|
const blockedRemovalViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(5));
|
|
2351
|
-
|
|
2836
|
+
if (!showTechnical && visibleDependents.length > dependentLimit) {
|
|
2837
|
+
help = 'PgUp/PgDn 查看全部被依赖关系 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
2838
|
+
}
|
|
2839
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "error", title: `${dependentCount} 个已安装插件仍在使用`, description: "\u6CA1\u6709\u4FEE\u6539\u4EFB\u4F55\u5B89\u88C5\u3002" }), showTechnical ? null : _jsx(SectionCard, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", meta: "\u53CD\u5411\u5F15\u7528", children: dependencyRows(visibleDependents, dependentLimit, true, batchTargetOffset) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockedRemovalViewport.offset, limit: blockedRemovalViewport.limit }), showTechnical ? _jsx(IssueTechnicalDetails, { details: [{
|
|
2352
2840
|
code: 'dependencyStillRequired',
|
|
2353
|
-
message: '
|
|
2841
|
+
message: '已安装插件仍在使用这个依赖;请先处理依赖者。',
|
|
2354
2842
|
}], compact: policy.mode === 'compact' }) : null] });
|
|
2355
2843
|
}
|
|
2356
2844
|
else {
|
|
@@ -2379,7 +2867,7 @@ export function ConsoleAppV4(props) {
|
|
|
2379
2867
|
go({ name: 'dependencyPlacement', pluginId: plugin.pluginId });
|
|
2380
2868
|
},
|
|
2381
2869
|
}] : []),
|
|
2382
|
-
{ label: '技术详情', description: '显示 canonical pluginId 和安全错误代码', run: () =>
|
|
2870
|
+
{ label: '技术详情', description: '显示 canonical pluginId 和安全错误代码', run: () => toggleTechnical() },
|
|
2383
2871
|
];
|
|
2384
2872
|
const rootStatus = plugin.aggregateStatus === 'installed' ? 'success'
|
|
2385
2873
|
: plugin.aggregateStatus === 'needsAttention' ? 'warning'
|
|
@@ -2396,16 +2884,6 @@ export function ConsoleAppV4(props) {
|
|
|
2396
2884
|
];
|
|
2397
2885
|
const technicalLineBudget = showTechnical ? 4 : 0;
|
|
2398
2886
|
const relationItemBudget = Math.max(relationCategories.length, size.rows - 6 - 1 - technicalLineBudget - 4);
|
|
2399
|
-
const relationBudgets = new Map(relationCategories.map((category) => [category.key, 1]));
|
|
2400
|
-
let remainingRelationLines = Math.max(0, relationItemBudget - relationCategories.length);
|
|
2401
|
-
for (const category of relationCategories) {
|
|
2402
|
-
if (category.key === 'blocker')
|
|
2403
|
-
continue;
|
|
2404
|
-
while (remainingRelationLines > 0 && (relationBudgets.get(category.key) ?? 0) < category.count) {
|
|
2405
|
-
relationBudgets.set(category.key, (relationBudgets.get(category.key) ?? 0) + 1);
|
|
2406
|
-
remainingRelationLines -= 1;
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
2887
|
const prioritizedRelations = (entries) => [...entries].sort((left, right) => {
|
|
2410
2888
|
const priority = (entry) => {
|
|
2411
2889
|
const status = dependencyStatus(entry).status;
|
|
@@ -2413,20 +2891,28 @@ export function ConsoleAppV4(props) {
|
|
|
2413
2891
|
};
|
|
2414
2892
|
return priority(left) - priority(right);
|
|
2415
2893
|
});
|
|
2416
|
-
const
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
return visible.map((entry, index) => {
|
|
2894
|
+
const allRelationRows = [
|
|
2895
|
+
...(overview?.blockers ?? []).map((blocker) => _jsx(StaticRow, { title: `阻塞 · ${blocker.title}`, description: blocker.explanation, status: "error", compact: true }, `blocker:${blocker.code}`)),
|
|
2896
|
+
...prioritizedRelations(overview?.dependencies ?? []).map((entry) => {
|
|
2420
2897
|
const state = dependencyStatus(entry);
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2898
|
+
return _jsx(StaticRow, { title: `必需依赖 · ${entry.displayName}`, description: dependencyRelationDescription(entry), meta: dependencyPlacementLabel(entry), status: state.status, compact: true }, `dependency:${entry.pluginId}`);
|
|
2899
|
+
}),
|
|
2900
|
+
...prioritizedRelations(visibleDependents).map((entry) => {
|
|
2901
|
+
const state = dependencyStatus(entry);
|
|
2902
|
+
return _jsx(StaticRow, { title: `被依赖 · ${entry.displayName}`, description: dependencyRelationDescription(entry), meta: dependentTargetLabel(entry), status: state.status, compact: true }, `dependent:${entry.pluginId}`);
|
|
2903
|
+
}),
|
|
2904
|
+
];
|
|
2905
|
+
const relationLimit = Math.max(1, Math.min(relationItemBudget, showTechnical ? responsiveBudget(1, 6) : responsiveBudget(3, 7)));
|
|
2906
|
+
const relationOffset = Math.min(batchTargetOffset, Math.max(0, allRelationRows.length - 1));
|
|
2907
|
+
const visibleRelationRows = allRelationRows.slice(relationOffset, relationOffset + relationLimit);
|
|
2908
|
+
const hiddenRelationRows = Math.max(0, allRelationRows.length - relationOffset - visibleRelationRows.length);
|
|
2909
|
+
if (useBudgetedRelations && allRelationRows.length > relationLimit) {
|
|
2910
|
+
help = 'PgUp/PgDn 查看全部依赖关系 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
2911
|
+
}
|
|
2912
|
+
content = _jsxs(Box, { flexDirection: "column", children: [useBudgetedRelations ? _jsxs(SectionCard, { title: "\u4F9D\u8D56\u5173\u7CFB", compact: true, children: [_jsx(StaticRow, { title: `根插件 · ${plugin.displayName}`, meta: statusText(plugin), status: rootStatus, compact: true }), relationOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${relationOffset} 项`, status: "muted", compact: true }) : null, visibleRelationRows, hiddenRelationRows > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${hiddenRelationRows} 项`, status: "muted", compact: true }) : null, dependencyCount + dependentCount + blockerCount === 0 ? _jsx(StaticRow, { title: "\u6CA1\u6709\u53EF\u663E\u793A\u7684\u4F9D\u8D56\u5173\u7CFB", status: "muted", compact: true }) : null] }) : _jsxs(_Fragment, { children: [_jsx(SectionCard, { title: "\u4F60\u9009\u62E9\u7684\u63D2\u4EF6", meta: "\u6839\u8282\u70B9", children: _jsx(StaticRow, { title: plugin.displayName, meta: statusText(plugin), status: rootStatus }) }), dependencyCount > 0 ? _jsx(SectionCard, { title: "\u5FC5\u9700\u4F9D\u8D56", meta: "\u4F9D\u8D56\u4F18\u5148", children: dependencyRows(overview?.dependencies ?? [], dependencyLimit) }) : null, dependentCount > 0 ? _jsx(SectionCard, { title: "\u4F7F\u7528\u5B83\u7684\u63D2\u4EF6", meta: "\u53CD\u5411\u5F15\u7528", children: dependencyRows(visibleDependents, dependentLimit, true) }) : null, overview?.blockers.map((blocker) => _jsx(Callout, { status: "error", title: blocker.title, description: blocker.explanation }, blocker.code)), dependencyCount + dependentCount + blockerCount === 0 ? _jsx(Callout, { status: "muted", title: "\u6CA1\u6709\u53EF\u663E\u793A\u7684\u4F9D\u8D56\u5173\u7CFB" }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: useBudgetedRelations, offset: dependencyActionViewport.offset, limit: dependencyActionViewport.limit }), showTechnical ? _jsx(TechnicalDetails, { children: _jsx(StaticRow, { title: `${dependencyCount} 依赖 · ${dependentCount} 被依赖 · 已折叠`, meta: plugin.pluginId, compact: true }) }) : null] });
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
else if (route.name === 'installPlacement' && plugin) {
|
|
2430
2916
|
breadcrumb = `${plugin.displayName} / 添加`;
|
|
2431
2917
|
context = '第 1 步,共 2 步';
|
|
2432
2918
|
title = `你希望在哪里使用 ${plugin.displayName}?`;
|
|
@@ -2474,7 +2960,7 @@ export function ConsoleAppV4(props) {
|
|
|
2474
2960
|
const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
|
|
2475
2961
|
description = hasDependencies
|
|
2476
2962
|
? '根插件必须支持所选位置;每个依赖必须至少有一个合法位置。'
|
|
2477
|
-
: '
|
|
2963
|
+
: '目标平台按所选位置验证;仅含平台共享内容的插件可同时启用两个位置。';
|
|
2478
2964
|
const choices = props.controller.listInstallTargets(plugin.pluginId, scope);
|
|
2479
2965
|
const validSelectedTargets = selectedTargets.filter((platform) => choices.some((choice) => choice.platform === platform && choice.compatible));
|
|
2480
2966
|
const allUnavailable = choices.length > 0 && choices.every((choice) => !choice.compatible);
|
|
@@ -2543,7 +3029,7 @@ export function ConsoleAppV4(props) {
|
|
|
2543
3029
|
const dependencyActions = (draft?.targets ?? []).map((target) => ({
|
|
2544
3030
|
label: `${target.displayName} · ${platformLabel(target.platform)}`,
|
|
2545
3031
|
description: `${target.relation === 'direct' ? '直接依赖' : '间接依赖'} · ${target.explanation}`,
|
|
2546
|
-
meta: `${scopeLabel(target.scope)}${target.assignmentSource === 'consumerPromotion' ? ' ·
|
|
3032
|
+
meta: `${scopeLabel(target.scope)}${target.assignmentSource === 'consumerPromotion' ? ' · 因依赖自动调整' : ''}`,
|
|
2547
3033
|
selected: true,
|
|
2548
3034
|
disabled: target.locked ? '位置由平台或上游约束固定' : undefined,
|
|
2549
3035
|
run: () => go({
|
|
@@ -2563,7 +3049,7 @@ export function ConsoleAppV4(props) {
|
|
|
2563
3049
|
},
|
|
2564
3050
|
{
|
|
2565
3051
|
label: '恢复推荐位置',
|
|
2566
|
-
description: '
|
|
3052
|
+
description: '恢复依赖自动安排的建议位置',
|
|
2567
3053
|
run: () => props.controller.resetDependencyScopes(),
|
|
2568
3054
|
},
|
|
2569
3055
|
{ label: '返回目标平台', run: back },
|
|
@@ -2576,7 +3062,7 @@ export function ConsoleAppV4(props) {
|
|
|
2576
3062
|
const reasonSummary = [
|
|
2577
3063
|
...(defaultCount > 0 ? [`默认 project ${defaultCount} 项`] : []),
|
|
2578
3064
|
...(reusedCount > 0 ? [`复用健康的所有项目安装 ${reusedCount} 项`] : []),
|
|
2579
|
-
...(promotedCount > 0 ? [
|
|
3065
|
+
...(promotedCount > 0 ? [`因依赖自动调整 ${promotedCount} 项`] : []),
|
|
2580
3066
|
].join(';');
|
|
2581
3067
|
content = _jsxs(Box, { flexDirection: "column", children: [policy.mode === 'compact'
|
|
2582
3068
|
? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `根插件 · ${plugin.displayName}`, meta: draft ? scopeLabel(draft.rootScope) : '未就绪', compact: true }), _jsx(StaticRow, { title: `位置原因 · ${reasonSummary || '由平台或上游约束固定'}`, status: userCount > 0 ? 'warning' : 'info', compact: true }), _jsx(StaticRow, { title: "\u8303\u56F4\u63D0\u793A \u00B7 \u6240\u6709\u9879\u76EE\u4F4D\u7F6E\u4F1A\u5F71\u54CD\u5176\u4ED6\u9879\u76EE\u548C\u7528\u6237\u73AF\u5883", status: userCount > 0 ? 'warning' : 'muted', compact: true })] })
|
|
@@ -2596,7 +3082,7 @@ export function ConsoleAppV4(props) {
|
|
|
2596
3082
|
selected: target?.scope === candidateScope,
|
|
2597
3083
|
description: candidateScope === 'user'
|
|
2598
3084
|
? '更宽:对其他项目可见,并会改变你的用户环境'
|
|
2599
|
-
: '
|
|
3085
|
+
: '仅供当前项目使用;适合随项目共享',
|
|
2600
3086
|
disabled: unavailable ? '这个依赖或上游约束不允许此位置' : undefined,
|
|
2601
3087
|
run: async () => {
|
|
2602
3088
|
await props.controller.setDependencyScope(route.dependencyPluginId, route.platform, candidateScope);
|
|
@@ -2605,7 +3091,7 @@ export function ConsoleAppV4(props) {
|
|
|
2605
3091
|
};
|
|
2606
3092
|
});
|
|
2607
3093
|
content = _jsxs(Box, { flexDirection: "column", children: [target?.assignmentSource === 'consumerPromotion'
|
|
2608
|
-
? _jsx(Callout, { status: "warning", title: "
|
|
3094
|
+
? _jsx(Callout, { status: "warning", title: "\u4F9D\u8D56\u9700\u8981\u66F4\u5927\u4F7F\u7528\u8303\u56F4", description: `上游依赖需要至少使用${scopeLabel(target.minimumScope)}。` })
|
|
2609
3095
|
: null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' })] });
|
|
2610
3096
|
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
2611
3097
|
}
|
|
@@ -2616,7 +3102,30 @@ export function ConsoleAppV4(props) {
|
|
|
2616
3102
|
description = plugin.description;
|
|
2617
3103
|
const dependencySummary = plugin.dependencySummary;
|
|
2618
3104
|
const detailMenu = dynamicMenu;
|
|
2619
|
-
actions = detailMenu.map((action) => {
|
|
3105
|
+
actions = plugin.configOnly ? detailMenu.map((action) => {
|
|
3106
|
+
if (action.id === 'settings')
|
|
3107
|
+
return {
|
|
3108
|
+
label: '查看已保存设置',
|
|
3109
|
+
description: '来源恢复前可检查问题,并只对记录中的设置层执行重置。',
|
|
3110
|
+
run: () => {
|
|
3111
|
+
props.controller.beginSavedConfigRecovery(plugin.pluginId);
|
|
3112
|
+
setFocus(0);
|
|
3113
|
+
setScrollOffset(0);
|
|
3114
|
+
},
|
|
3115
|
+
};
|
|
3116
|
+
if (action.id === 'sourceProblem')
|
|
3117
|
+
return {
|
|
3118
|
+
label: '查看来源问题',
|
|
3119
|
+
description: '恢复来源后可重新读取字段定义;不会把此记录当作可安装插件。',
|
|
3120
|
+
status: 'warning',
|
|
3121
|
+
run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }),
|
|
3122
|
+
};
|
|
3123
|
+
return {
|
|
3124
|
+
label: '关于与技术详情',
|
|
3125
|
+
description: '查看保存设置和来源的安全摘要',
|
|
3126
|
+
run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }),
|
|
3127
|
+
};
|
|
3128
|
+
}) : detailMenu.map((action) => {
|
|
2620
3129
|
if (action.id === 'resolveScopeConflict')
|
|
2621
3130
|
return {
|
|
2622
3131
|
label: `保留${scopeLabel(action.keepScope)},移除${scopeLabel(action.scope)} root…`,
|
|
@@ -2657,14 +3166,14 @@ export function ConsoleAppV4(props) {
|
|
|
2657
3166
|
if (action.id === 'upgradeAwCli')
|
|
2658
3167
|
return {
|
|
2659
3168
|
label: '升级 AW CLI',
|
|
2660
|
-
description:
|
|
3169
|
+
description: '升级到支持此插件的 AW CLI 版本后重试',
|
|
2661
3170
|
status: 'warning',
|
|
2662
3171
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
2663
3172
|
};
|
|
2664
3173
|
if (action.id === 'capabilityTechnical')
|
|
2665
3174
|
return {
|
|
2666
|
-
label: '
|
|
2667
|
-
description: '
|
|
3175
|
+
label: '查看功能支持详情',
|
|
3176
|
+
description: '查看缺少的功能和安全恢复方向',
|
|
2668
3177
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
2669
3178
|
};
|
|
2670
3179
|
if (action.id === 'sourceProblem')
|
|
@@ -2696,14 +3205,14 @@ export function ConsoleAppV4(props) {
|
|
|
2696
3205
|
if (action.id === 'upgradeAwCli')
|
|
2697
3206
|
return {
|
|
2698
3207
|
label: '升级 AW CLI',
|
|
2699
|
-
description:
|
|
3208
|
+
description: '升级到支持此插件的 AW CLI 版本后重试',
|
|
2700
3209
|
status: 'warning',
|
|
2701
3210
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
2702
3211
|
};
|
|
2703
3212
|
if (action.id === 'capabilityTechnical')
|
|
2704
3213
|
return {
|
|
2705
|
-
label: '
|
|
2706
|
-
description: '
|
|
3214
|
+
label: '查看功能支持详情',
|
|
3215
|
+
description: '查看缺少的功能和安全恢复方向',
|
|
2707
3216
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
2708
3217
|
};
|
|
2709
3218
|
if (action.id === 'sourceProblem')
|
|
@@ -2725,11 +3234,11 @@ export function ConsoleAppV4(props) {
|
|
|
2725
3234
|
if (action.id === 'addScope')
|
|
2726
3235
|
return {
|
|
2727
3236
|
label: `在${scopeLabel(action.scope)}中启用…`,
|
|
2728
|
-
description: '
|
|
3237
|
+
description: '增加这个使用位置;现有共享内容保持不变',
|
|
2729
3238
|
run: () => beginSecondScope(plugin, action.platform, action.scope),
|
|
2730
3239
|
};
|
|
2731
3240
|
if (action.id === 'about')
|
|
2732
|
-
return { label: '查看支持信息', description: '
|
|
3241
|
+
return { label: '查看支持信息', description: '插件定义与平台支持', run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }) };
|
|
2733
3242
|
if (action.id === 'source')
|
|
2734
3243
|
return { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) };
|
|
2735
3244
|
if (action.id === 'outputCollision')
|
|
@@ -2751,10 +3260,10 @@ export function ConsoleAppV4(props) {
|
|
|
2751
3260
|
run: () => beginScopeConflictRecovery(action),
|
|
2752
3261
|
};
|
|
2753
3262
|
if (action.id === 'refreshAndSync')
|
|
2754
|
-
return { label: `刷新并同步到 ${action.availableVersion}…`, description: '
|
|
3263
|
+
return { label: `刷新并同步到 ${action.availableVersion}…`, description: '先刷新相关来源,再确认新的目录内容', run: () => beginReview({ kind: 'pluginWrite', action: 'refreshAndSync', pluginId: plugin.pluginId, platform: action.platform, scope: action.scope }) };
|
|
2755
3264
|
if (action.id === 'sync')
|
|
2756
3265
|
return {
|
|
2757
|
-
label: '
|
|
3266
|
+
label: '同步已保存的使用位置…',
|
|
2758
3267
|
description: placement?.state === 'installed'
|
|
2759
3268
|
? placement.recovery?.find((entry) => entry.command === 'sync')?.description
|
|
2760
3269
|
?? '进入现有 Review、进度和结果链修复 needsSync 或 drift'
|
|
@@ -2768,15 +3277,55 @@ export function ConsoleAppV4(props) {
|
|
|
2768
3277
|
if (action.id === 'remove')
|
|
2769
3278
|
return { label: `从${scopeLabel(action.scope)}移除…`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, action.platform, action.scope) };
|
|
2770
3279
|
if (action.id === 'cleanup')
|
|
2771
|
-
return { label: `清理${scopeLabel(action.scope)}中的遗留内容…`, description: '只清理这个插件目标的 AW
|
|
3280
|
+
return { label: `清理${scopeLabel(action.scope)}中的遗留内容…`, description: '只清理这个插件目标的 AW 管理内容', status: 'error', run: () => beginReview({ kind: 'pluginWrite', action: 'cleanup', pluginId: plugin.pluginId, platform: action.platform, scope: action.scope }) };
|
|
2772
3281
|
if (action.id === 'technical')
|
|
2773
3282
|
return typedIssue
|
|
2774
3283
|
? { label: '查看对应问题', description: typedIssue.explanation, run: () => go({ name: 'issueDetail', issueId: typedIssue.issueId }) }
|
|
2775
3284
|
: { label: placement?.state === 'installed' && placement.health === 'error' ? '查看安全技术详情' : placement?.state === 'installed' && placement.health === 'incomplete' ? '查看影响' : '查看安装详情', description: '组件与诊断信息', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) };
|
|
2776
3285
|
return { label: '返回插件', run: back };
|
|
2777
3286
|
};
|
|
3287
|
+
const offlineCleanupAction = typedIssue?.actions.find((action) => action.id === 'cleanup');
|
|
3288
|
+
const offlineCleanup = typedIssue !== undefined
|
|
3289
|
+
&& placement?.state === 'installed'
|
|
3290
|
+
&& placement.blockingReason === undefined
|
|
3291
|
+
&& (placement.actualState === 'orphaned' || placement.actualState === 'blocked')
|
|
3292
|
+
&& offlineCleanupAction !== undefined
|
|
3293
|
+
&& !platformMenu.some((action) => action.id === 'cleanup')
|
|
3294
|
+
&& !platformMenu.some((action) => action.id === 'resolveScopeConflict')
|
|
3295
|
+
? {
|
|
3296
|
+
label: `清理${scopeLabel(placement.scope)}中的遗留内容…`,
|
|
3297
|
+
description: '离线时也只清理这个插件目标中可证明属于 AW 的内容;完成后如需恢复,请单独确认重新安装。',
|
|
3298
|
+
status: 'error',
|
|
3299
|
+
run: () => {
|
|
3300
|
+
const intent = issueWriteIntent(typedIssue, offlineCleanupAction);
|
|
3301
|
+
if (intent)
|
|
3302
|
+
beginReview(intent);
|
|
3303
|
+
},
|
|
3304
|
+
}
|
|
3305
|
+
: undefined;
|
|
3306
|
+
const restrictedForceCleanup = placement?.state === 'installed'
|
|
3307
|
+
&& placement.blockingReason === undefined
|
|
3308
|
+
&& (placement.actualState === 'orphaned' || placement.actualState === 'blocked')
|
|
3309
|
+
&& !platformMenu.some((action) => action.id === 'resolveScopeConflict')
|
|
3310
|
+
? {
|
|
3311
|
+
label: '检查受限清理…',
|
|
3312
|
+
description: '只在当前单一目标有精确、专属的 AW 管理恢复证据时进入新的确认;不会清理共享或外来内容。',
|
|
3313
|
+
status: 'warning',
|
|
3314
|
+
run: () => beginRestrictedForceCleanup(plugin.pluginId, route.platform, placement.scope),
|
|
3315
|
+
}
|
|
3316
|
+
: undefined;
|
|
3317
|
+
const platformActions = () => [
|
|
3318
|
+
...platformMenu.map(platformAction),
|
|
3319
|
+
...(offlineCleanup ? [offlineCleanup] : []),
|
|
3320
|
+
...(restrictedForceCleanup ? [restrictedForceCleanup] : []),
|
|
3321
|
+
];
|
|
2778
3322
|
breadcrumb = `${plugin.displayName} / ${platformLabel(route.platform)}`;
|
|
2779
|
-
const installedPlacementRows = placement?.state === 'installed' ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "
|
|
3323
|
+
const installedPlacementRows = placement?.state === 'installed' ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u4F4D\u7F6E", meta: placement.desiredScopes?.map(scopeLabel).join('、') || scopeLabel(placement.scope), compact: true }), _jsx(StaticRow, { title: "\u72B6\u6001", meta: placement.actualState === 'ready' ? '可用'
|
|
3324
|
+
: placement.actualState === 'needsSync' ? '需要同步'
|
|
3325
|
+
: placement.actualState === 'blocked' ? '已阻断'
|
|
3326
|
+
: placement.health === 'incomplete' ? '需要处理' : '暂时不可用', status: placement.actualState === 'ready' ? 'success' : placement.actualState === 'blocked' ? 'error' : 'warning', compact: true }), placement.health !== 'ready'
|
|
3327
|
+
? _jsx(StaticRow, { title: "\u539F\u56E0", meta: placement.blockingReason === 'artifactCollision' ? '输出位置存在冲突内容' : '当前安装与已保存设置不一致', status: "warning", compact: true })
|
|
3328
|
+
: null] }) : null;
|
|
2780
3329
|
if (plugin.aggregateStatus === 'unavailable') {
|
|
2781
3330
|
const sourceOverview = capabilityUnavailable ? undefined : props.controller.getSourceOverview(plugin.sourceId);
|
|
2782
3331
|
const diagnostic = capabilityUnavailable
|
|
@@ -2786,7 +3335,7 @@ export function ConsoleAppV4(props) {
|
|
|
2786
3335
|
? `需要升级 AW CLI 才能管理 ${platformLabel(route.platform)}`
|
|
2787
3336
|
: `暂时无法管理 ${platformLabel(route.platform)}`;
|
|
2788
3337
|
description = '';
|
|
2789
|
-
actions =
|
|
3338
|
+
actions = platformActions();
|
|
2790
3339
|
content = _jsxs(Box, { flexDirection: "column", children: [diagnostic
|
|
2791
3340
|
? _jsx(DiagnosticSummary, { diagnostic: diagnostic, compact: policy.mode === 'compact' })
|
|
2792
3341
|
: null, _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
@@ -2794,7 +3343,7 @@ export function ConsoleAppV4(props) {
|
|
|
2794
3343
|
else if (!placement || placement.state === 'notInstalled') {
|
|
2795
3344
|
title = `尚未添加到 ${platformLabel(route.platform)}`;
|
|
2796
3345
|
description = '此插件支持所列使用位置。';
|
|
2797
|
-
actions =
|
|
3346
|
+
actions = platformActions();
|
|
2798
3347
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "muted", title: "\u672A\u6DFB\u52A0", description: "\u63D2\u4EF6\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8" }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
2799
3348
|
}
|
|
2800
3349
|
else if (placement.state === 'blocked'
|
|
@@ -2803,13 +3352,13 @@ export function ConsoleAppV4(props) {
|
|
|
2803
3352
|
description = placement.state === 'installed'
|
|
2804
3353
|
? '受管内容仍可安全移除;输出位置同时存在冲突内容。'
|
|
2805
3354
|
: '输出位置已有不属于当前插件的内容。';
|
|
2806
|
-
actions =
|
|
3355
|
+
actions = platformActions();
|
|
2807
3356
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "error", title: "\u8F93\u51FA\u51B2\u7A81", description: "AW \u4E0D\u4F1A\u8986\u76D6\u6216\u5220\u9664\u5360\u7528\u5185\u5BB9" }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
2808
3357
|
}
|
|
2809
3358
|
else if (placement.state === 'scopeConflict') {
|
|
2810
3359
|
title = '安装位置冲突';
|
|
2811
3360
|
description = '正常状态只能保留一个位置。';
|
|
2812
|
-
actions =
|
|
3361
|
+
actions = platformActions();
|
|
2813
3362
|
content = _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' });
|
|
2814
3363
|
}
|
|
2815
3364
|
else if (placement.health === 'error') {
|
|
@@ -2834,13 +3383,13 @@ export function ConsoleAppV4(props) {
|
|
|
2834
3383
|
};
|
|
2835
3384
|
title = `${platformLabel(route.platform)} 暂时不可管理`;
|
|
2836
3385
|
description = '';
|
|
2837
|
-
actions =
|
|
3386
|
+
actions = platformActions();
|
|
2838
3387
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
2839
3388
|
}
|
|
2840
3389
|
else if (placement.health === 'incomplete') {
|
|
2841
3390
|
title = `${platformLabel(route.platform)} ${placement.health === 'incomplete' ? '安装不完整' : '暂时不可用'}`;
|
|
2842
3391
|
description = '缺少一项必要内容,插件可能无法稳定启动。';
|
|
2843
|
-
actions =
|
|
3392
|
+
actions = platformActions();
|
|
2844
3393
|
const actualTitle = placement.actualState === 'needsSync' ? '需要同步'
|
|
2845
3394
|
: placement.actualState === 'blocked' ? '当前有安全阻断' : '需要处理';
|
|
2846
3395
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: placement.actualState === 'blocked' ? 'error' : 'warning', title: actualTitle, description: `${scopeLabel(placement.scope)} · ${plugin.sourceLabel}`, children: installedPlacementRows }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
@@ -2849,7 +3398,7 @@ export function ConsoleAppV4(props) {
|
|
|
2849
3398
|
title = `已安装到${scopeLabel(placement.scope)}`;
|
|
2850
3399
|
const installedVersion = placement.revision.installedVersion;
|
|
2851
3400
|
description = `当前检查正常${installedVersion ? `;已安装版本 ${installedVersion}` : ''}。`;
|
|
2852
|
-
actions =
|
|
3401
|
+
actions = platformActions();
|
|
2853
3402
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "success", title: "\u72B6\u6001\u6B63\u5E38", description: `${scopeLabel(placement.scope)}${installedVersion ? ` · 已安装版本 ${installedVersion}` : ''}`, children: installedPlacementRows }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
2854
3403
|
}
|
|
2855
3404
|
}
|
|
@@ -2886,7 +3435,7 @@ export function ConsoleAppV4(props) {
|
|
|
2886
3435
|
? '↑↓ 选择配置 · Enter 修改 · ←→ 分组 · Esc 返回'
|
|
2887
3436
|
: '↑↓ 移动 · Enter 修改 · ←→ 分组 · s 保存 · u 恢复继承 · Esc 返回';
|
|
2888
3437
|
if (!currentConfigPage) {
|
|
2889
|
-
content = _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u8BFB\u53D6\u53EF\u4FE1\u8BBE\u7F6E\u5B9A\u4E49", children: _jsx(ProgressRow, { title: "\u8BFB\u53D6
|
|
3438
|
+
content = _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u8BFB\u53D6\u53EF\u4FE1\u8BBE\u7F6E\u5B9A\u4E49", children: _jsx(ProgressRow, { title: "\u8BFB\u53D6\u63D2\u4EF6\u8BBE\u7F6E\u5B57\u6BB5", state: "active" }) });
|
|
2890
3439
|
}
|
|
2891
3440
|
else {
|
|
2892
3441
|
visibleConfigTabs = configTabs(currentConfigPage);
|
|
@@ -2900,7 +3449,13 @@ export function ConsoleAppV4(props) {
|
|
|
2900
3449
|
? (activeGroup?.fields ?? []).filter((field) => field.sharedNativeRender && !field.writable)
|
|
2901
3450
|
: [];
|
|
2902
3451
|
if (projectSharedNativeFields.length > 0) {
|
|
2903
|
-
description += '
|
|
3452
|
+
description += ' 此设置由个人默认控制平台共享内容;当前项目不可改。更改影响所有项目。';
|
|
3453
|
+
}
|
|
3454
|
+
if (projectSharedNativeFields.some(configFieldCanUnset)) {
|
|
3455
|
+
description += ' 发现不再支持的 project override;实际使用 user/default;请删除旧值。';
|
|
3456
|
+
}
|
|
3457
|
+
if (!readonly && configDraftIssues.length > 0) {
|
|
3458
|
+
description += ' 字段已变化;失效草稿不会保存。';
|
|
2904
3459
|
}
|
|
2905
3460
|
const mutableLayer = {
|
|
2906
3461
|
scope: route.layer.scope,
|
|
@@ -2919,7 +3474,7 @@ export function ConsoleAppV4(props) {
|
|
|
2919
3474
|
})
|
|
2920
3475
|
: () => applyConfigPreset(currentConfigPage, preset.id),
|
|
2921
3476
|
}))
|
|
2922
|
-
: (activeGroup?.fields ?? []).filter((field) => readonly || field
|
|
3477
|
+
: (activeGroup?.fields ?? []).filter((field) => readonly || configFieldCanSet(field) || configFieldCanUnset(field)).map((field) => ({
|
|
2923
3478
|
label: field.title,
|
|
2924
3479
|
description: field.invalidOverrideIssue
|
|
2925
3480
|
? field.invalidOverrideOwnedByLayer
|
|
@@ -2927,7 +3482,9 @@ export function ConsoleAppV4(props) {
|
|
|
2927
3482
|
: field.invalidOverrideIssue
|
|
2928
3483
|
: readonly
|
|
2929
3484
|
? field.sourceLabel === '未设置' ? '没有任何位置提供值' : `来自${field.sourceLabel}`
|
|
2930
|
-
:
|
|
3485
|
+
: !configFieldCanSet(field) && configFieldCanUnset(field)
|
|
3486
|
+
? '不再支持的 project override · 实际使用 user/default · 可删除旧值'
|
|
3487
|
+
: `${field.description ? `${field.description} · ` : ''}当前有效:${effectiveFieldDisplay(field)} · ${field.effectiveSourceLabel}`,
|
|
2931
3488
|
meta: readonly ? effectiveFieldDisplay(field) : currentFieldDisplay(field),
|
|
2932
3489
|
configKey: field.key,
|
|
2933
3490
|
run: readonly
|
|
@@ -2937,7 +3494,7 @@ export function ConsoleAppV4(props) {
|
|
|
2937
3494
|
fieldKey: field.key,
|
|
2938
3495
|
groupId: activeGroup.id,
|
|
2939
3496
|
})
|
|
2940
|
-
: () => go(field.configured ? {
|
|
3497
|
+
: () => go(field.configured || !configFieldCanSet(field) ? {
|
|
2941
3498
|
name: 'configFieldAction',
|
|
2942
3499
|
pluginId: plugin.pluginId,
|
|
2943
3500
|
layer: mutableLayer,
|
|
@@ -2952,17 +3509,21 @@ export function ConsoleAppV4(props) {
|
|
|
2952
3509
|
}),
|
|
2953
3510
|
}));
|
|
2954
3511
|
const operationActions = readonly ? [] : [
|
|
2955
|
-
...
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
:
|
|
2964
|
-
|
|
2965
|
-
|
|
3512
|
+
...(configDraftIssues.length > 0 ? [{
|
|
3513
|
+
label: '放弃已变化字段的草稿',
|
|
3514
|
+
description: `${configDraftIssues.length} 项草稿的字段不存在、不可编辑或不再可删除;不会修改已保存设置。`,
|
|
3515
|
+
status: 'warning',
|
|
3516
|
+
run: discardInvalidConfigDraftChanges,
|
|
3517
|
+
}] : []),
|
|
3518
|
+
...(dirty && configDraftIssues.length === 0 ? [{
|
|
3519
|
+
label: '查看保存汇总',
|
|
3520
|
+
description: '确认变化,再选择只保存或保存并应用',
|
|
3521
|
+
status: 'success',
|
|
3522
|
+
run: () => {
|
|
3523
|
+
setFocus(0);
|
|
3524
|
+
go({ name: 'configSave', pluginId: route.pluginId, layer: route.layer });
|
|
3525
|
+
},
|
|
3526
|
+
}] : []),
|
|
2966
3527
|
...(currentConfigPage.storedCount > 0 ? [{
|
|
2967
3528
|
label: route.layer.scope === 'user'
|
|
2968
3529
|
? '清空个人默认…'
|
|
@@ -2993,29 +3554,47 @@ export function ConsoleAppV4(props) {
|
|
|
2993
3554
|
? activeGroup?.fields[effectiveConfigFocus]
|
|
2994
3555
|
: undefined;
|
|
2995
3556
|
if (!readonly) {
|
|
2996
|
-
const canUnsetFocused = focusedConfigField
|
|
2997
|
-
|
|
2998
|
-
|
|
3557
|
+
const canUnsetFocused = focusedConfigField
|
|
3558
|
+
? configFieldCanUnset(focusedConfigField)
|
|
3559
|
+
&& (!focusedConfigField.invalidOverrideIssue
|
|
3560
|
+
|| focusedConfigField.invalidOverrideOwnedByLayer === true)
|
|
3561
|
+
: false;
|
|
2999
3562
|
help = canUnsetFocused
|
|
3000
|
-
? '↑↓ 移动 · Enter 修改 · ←→ 分组 · s
|
|
3001
|
-
: '↑↓ 移动 · Enter 修改 · ←→ 分组 · s
|
|
3563
|
+
? '↑↓ 移动 · Enter 修改 · ←→ 分组 · s 查看汇总 · u 恢复继承 · Esc 返回'
|
|
3564
|
+
: '↑↓ 移动 · Enter 修改 · ←→ 分组 · s 查看汇总 · Esc 返回';
|
|
3002
3565
|
}
|
|
3003
3566
|
const fieldFocus = tabActions.length > 0
|
|
3004
3567
|
? effectiveConfigFocus < tabActions.length ? effectiveConfigFocus : Math.min(scrollOffset, tabActions.length - 1)
|
|
3005
3568
|
: 0;
|
|
3006
3569
|
const operationRows = operationActions.reduce((rows, action) => rows + (policy.mode === 'compact' || !action.description ? 1 : 2), 0);
|
|
3007
|
-
const viewport = actionViewport(tabActions, policy.mode === 'compact', fieldFocus, scrollOffset, pageActionBudget(1 + operationRows + 3));
|
|
3570
|
+
const viewport = actionViewport(tabActions, policy.mode === 'compact', fieldFocus, scrollOffset, pageActionBudget(1 + operationRows + 3 + (configDraftIssues.length > 0 ? 3 : 0)));
|
|
3008
3571
|
const totalFields = currentConfigPage.groups.reduce((count, group) => count + group.fields.length, 0);
|
|
3009
3572
|
const countLabel = `${totalFields} 项${currentConfigPage.presets.length > 0 ? ` · ${currentConfigPage.presets.length} 个方案` : ''}`;
|
|
3010
3573
|
context = readonly ? countLabel
|
|
3011
3574
|
: dirty ? `${Object.keys(draftUpdates).length + draftRemoves.length} 项未保存` : countLabel;
|
|
3012
|
-
content = _jsxs(Box, { flexDirection: "column", children: [activeTab ? _jsx(ConfigGroupTabs, { tabs: visibleConfigTabs, current: activeTab.id, compact: policy.mode === 'compact', columns: size.columns - 2 }) : null, activeTab && tabActions.length > 0
|
|
3575
|
+
content = _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u5B57\u6BB5\u5DF2\u53D8\u5316", description: "\u6765\u6E90\u6062\u590D\u540E\u5B57\u6BB5\u5B9A\u4E49\u5DF2\u66F4\u65B0\uFF1B\u5B57\u6BB5\u53EF\u80FD\u4E0D\u5B58\u5728\u3001\u4E0D\u53EF\u7F16\u8F91\u6216\u4E0D\u518D\u53EF\u5220\u9664\u3002\u8BF7\u663E\u5F0F\u653E\u5F03\u5931\u6548\u8349\u7A3F\uFF0C\u6216\u8FD4\u56DE\u7EE7\u7EED\u68C0\u67E5\u6765\u6E90\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u6216\u590D\u7528\u65E7\u786E\u8BA4\u3002" }) : null, activeTab ? _jsx(ConfigGroupTabs, { tabs: visibleConfigTabs, current: activeTab.id, compact: policy.mode === 'compact', columns: size.columns - 2 }) : null, activeTab && tabActions.length > 0
|
|
3013
3576
|
? _jsx(SectionCard, { title: activeTab.label, meta: viewport.hiddenBefore > 0 || viewport.hiddenAfter > 0 ? `${viewport.position}/${viewport.total}` : undefined, children: _jsx(SemanticRows, { actions: tabActions, focus: effectiveConfigFocus, kind: "section", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit }) })
|
|
3014
3577
|
: null, operationActions.length > 0
|
|
3015
3578
|
? _jsx(SemanticRows, { actions: actions, focus: effectiveConfigFocus, kind: "action", compact: policy.mode === 'compact', offset: tabActions.length, limit: operationActions.length })
|
|
3016
3579
|
: null] });
|
|
3017
3580
|
}
|
|
3018
3581
|
}
|
|
3582
|
+
else if (route.name === 'configSave' && plugin && currentConfigPage) {
|
|
3583
|
+
const changes = configChanges();
|
|
3584
|
+
const sharedNative = route.layer.scope === 'user'
|
|
3585
|
+
&& [...Object.keys(draftUpdates), ...draftRemoves].some((key) => currentConfigPage.groups.flatMap((group) => group.fields)
|
|
3586
|
+
.some((field) => field.key === key && field.sharedNativeRender));
|
|
3587
|
+
breadcrumb = `${plugin.displayName} / 设置 / 保存汇总`;
|
|
3588
|
+
context = `${changes.length} 项未保存`;
|
|
3589
|
+
title = `保存 ${changes.length} 项设置修改?`;
|
|
3590
|
+
description = '保存后可选择是否立即应用到当前可见安装。';
|
|
3591
|
+
actions = [
|
|
3592
|
+
{ label: '保存并应用', description: sharedNative ? '保存并重新生成 Codex 平台共享 Skill;影响所有启用项目' : '保存设置并同步当前可见安装', status: 'success', run: () => beginConfigSave(true) },
|
|
3593
|
+
{ label: '只保存', description: sharedNative ? '只保存个人默认;平台共享 Skill 将等待同步' : '只保存所选设置层,不应用当前安装', run: () => beginConfigSave(false) },
|
|
3594
|
+
{ label: '返回修改', run: back },
|
|
3595
|
+
];
|
|
3596
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u8BBE\u7F6E\u53D8\u66F4", compact: policy.mode === 'compact', children: changes.map((change) => _jsx(ReviewChangeRow, { title: `~ ${draftRemoves.includes(change.key) ? `删除 ${change.title} 中的值` : change.title}`, description: `${change.before} → ${change.after}`, compact: policy.mode === 'compact' }, change.key)) }), _jsxs(SectionCard, { title: "\u5E94\u7528\u5F71\u54CD", compact: policy.mode === 'compact', children: [_jsx(StaticRow, { title: "\u4FDD\u5B58\u5E76\u5E94\u7528\u53EA\u5904\u7406\u5F53\u524D\u53EF\u89C1\u5B89\u88C5\uFF1B\u5176\u4ED6\u9879\u76EE\u4E0D\u4F1A\u88AB\u81EA\u52A8\u626B\u63CF", compact: true }), _jsx(StaticRow, { title: "\u5176\u4ED6\u8BBE\u7F6E\u5C42\u4FDD\u6301\u4E0D\u53D8", status: "success", compact: true })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
3597
|
+
}
|
|
3019
3598
|
else if (route.name === 'configFieldAction' && plugin && activeField) {
|
|
3020
3599
|
const layer = configLayerLabel(route.layer);
|
|
3021
3600
|
breadcrumb = `${plugin.displayName} / 设置 / ${layer} / ${activeField.title}`;
|
|
@@ -3026,23 +3605,25 @@ export function ConsoleAppV4(props) {
|
|
|
3026
3605
|
? '删除个人默认中的值'
|
|
3027
3606
|
: '删除当前项目中的值';
|
|
3028
3607
|
actions = [
|
|
3029
|
-
{
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
...(!activeField.invalidOverrideIssue || activeField.invalidOverrideOwnedByLayer ? [draftRemoves.includes(activeField.key)
|
|
3608
|
+
...(configFieldCanSet(activeField) ? [{
|
|
3609
|
+
label: '修改值…',
|
|
3610
|
+
run: () => go({
|
|
3611
|
+
name: 'configFieldEditor',
|
|
3612
|
+
pluginId: plugin.pluginId,
|
|
3613
|
+
layer: route.layer,
|
|
3614
|
+
fieldKey: activeField.key,
|
|
3615
|
+
editor: fieldEditor(activeField),
|
|
3616
|
+
...fieldEditorMode(activeField),
|
|
3617
|
+
}),
|
|
3618
|
+
}] : []),
|
|
3619
|
+
...(configFieldCanUnset(activeField) && (!activeField.invalidOverrideIssue || activeField.invalidOverrideOwnedByLayer) ? [draftRemoves.includes(activeField.key)
|
|
3041
3620
|
? { label: '撤销删除', run: () => { cancelDraftRemoval(activeField.key); back(); } }
|
|
3042
3621
|
: { label: deleteLabel, status: 'error', run: () => { removeDraft(activeField.key); back(); } }] : []),
|
|
3043
3622
|
];
|
|
3044
3623
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { children: [activeField.invalidOverrideIssue
|
|
3045
3624
|
? _jsx(StaticRow, { title: "\u5F53\u524D\u8986\u76D6\u65E0\u6548", meta: activeField.invalidOverrideIssue, status: "warning" })
|
|
3625
|
+
: null, !configFieldCanSet(activeField) && configFieldCanUnset(activeField)
|
|
3626
|
+
? _jsx(StaticRow, { title: "\u4E0D\u518D\u652F\u6301\u7684 project override", meta: "\u5B9E\u9645\u4F7F\u7528 user/default\uFF1B\u5220\u9664\u65E7\u503C\u540E\u4FDD\u6301\u7EE7\u627F", status: "warning" })
|
|
3046
3627
|
: null, _jsx(StaticRow, { title: `${layer}中的值`, meta: currentFieldDisplay(activeField) }), _jsx(StaticRow, { title: "\u5F53\u524D\u5B9E\u9645\u751F\u6548", meta: `${effectiveFieldDisplay(activeField)} · 来自${activeField.effectiveSourceLabel}` }), !activeField.invalidOverrideIssue || activeField.invalidOverrideOwnedByLayer
|
|
3047
3628
|
? _jsx(StaticRow, { title: "\u5220\u9664\u540E\u5C06\u6062\u590D", meta: `${activeField.fallbackDisplayValue ?? '未设置'} · 来自${activeField.fallbackSourceLabel ?? '未设置'}` })
|
|
3048
3629
|
: null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
@@ -3053,8 +3634,8 @@ export function ConsoleAppV4(props) {
|
|
|
3053
3634
|
if (showChoices) {
|
|
3054
3635
|
title = `选择${activeField.title}`;
|
|
3055
3636
|
description = activeField.allowCustom
|
|
3056
|
-
? '
|
|
3057
|
-
: '
|
|
3637
|
+
? '先选择插件提供的方案,也可以继续自定义。'
|
|
3638
|
+
: '此字段只允许使用插件提供的方案。';
|
|
3058
3639
|
help = '↑↓ 移动 · Enter 使用 · Esc 取消';
|
|
3059
3640
|
const current = draftUpdates[activeField.key] ?? activeField.draftValue ?? activeField.value;
|
|
3060
3641
|
const booleanCustomValues = activeField.type === 'boolean' && activeField.allowCustom
|
|
@@ -3164,7 +3745,7 @@ export function ConsoleAppV4(props) {
|
|
|
3164
3745
|
}
|
|
3165
3746
|
else {
|
|
3166
3747
|
title = activeField.sensitive ? '输入敏感值' : `输入${activeField.title}`;
|
|
3167
|
-
help = '输入 · Enter 使用 · Ctrl-U
|
|
3748
|
+
help = '输入 · Enter 使用 · Ctrl-U 清空输入 · Esc 取消';
|
|
3168
3749
|
description = route.editor === 'number'
|
|
3169
3750
|
? `允许范围:${activeField.min ?? '不限'}–${activeField.max ?? '不限'}${activeField.unit ? ` ${activeField.unit}` : ''}。`
|
|
3170
3751
|
: activeField.sensitive ? '当前值不会回显;留空不会自动删除旧值。' : activeField.description ?? '';
|
|
@@ -3179,8 +3760,8 @@ export function ConsoleAppV4(props) {
|
|
|
3179
3760
|
breadcrumb = `${plugin.displayName} / 设置 / ${activeField.title} / ${propertyTitle}`;
|
|
3180
3761
|
if (route.stage === 'key') {
|
|
3181
3762
|
title = '输入新键名';
|
|
3182
|
-
description = inputError ?? '
|
|
3183
|
-
help = '输入 · Enter 继续 · Esc 取消';
|
|
3763
|
+
description = inputError ?? '键名和属性数量会按插件规则检查。';
|
|
3764
|
+
help = '输入 · Enter 继续 · Ctrl-U 清空输入 · Esc 取消';
|
|
3184
3765
|
content = _jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: inputValue, columns: Math.max(8, size.columns - 8) }), description: description, status: inputError ? 'error' : undefined });
|
|
3185
3766
|
}
|
|
3186
3767
|
else if (property !== undefined && definition && route.stage === 'action') {
|
|
@@ -3214,7 +3795,7 @@ export function ConsoleAppV4(props) {
|
|
|
3214
3795
|
else if (property !== undefined && definition && route.stage === 'choices') {
|
|
3215
3796
|
title = `选择${propertyTitle}`;
|
|
3216
3797
|
description = definition.type !== 'boolean' && definition.choices?.allowCustom
|
|
3217
|
-
? '
|
|
3798
|
+
? '先选择插件提供的方案,也可以继续自定义。'
|
|
3218
3799
|
: '只显示此属性允许的选择。';
|
|
3219
3800
|
help = '↑↓ 移动 · Enter 使用 · Esc 取消';
|
|
3220
3801
|
const current = activeObjectDraft[property];
|
|
@@ -3266,7 +3847,7 @@ export function ConsoleAppV4(props) {
|
|
|
3266
3847
|
else if (property !== undefined && definition) {
|
|
3267
3848
|
title = activePropertySensitive ? '输入敏感值' : `输入${propertyTitle}`;
|
|
3268
3849
|
description = inputError ?? (activePropertySensitive ? '输入已隐藏。' : definition.description ?? '按 Enter 使用输入值。');
|
|
3269
|
-
help = '输入 · Enter 使用 · Esc 取消';
|
|
3850
|
+
help = '输入 · Enter 使用 · Ctrl-U 清空输入 · Esc 取消';
|
|
3270
3851
|
content = _jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: inputValue, sensitive: activePropertySensitive, columns: Math.max(8, size.columns - 8) }), description: description, status: inputError ? 'error' : undefined });
|
|
3271
3852
|
}
|
|
3272
3853
|
}
|
|
@@ -3277,9 +3858,11 @@ export function ConsoleAppV4(props) {
|
|
|
3277
3858
|
context = '暂时不可编辑';
|
|
3278
3859
|
const diagnostic = configUnavailableDiagnostic(route);
|
|
3279
3860
|
const invalidLayer = (route.invalidStores?.length ?? 0) > 0;
|
|
3861
|
+
const canRecoverSource = configUnavailableCanRecoverSource(route);
|
|
3280
3862
|
title = diagnostic.title;
|
|
3281
3863
|
description = '';
|
|
3282
3864
|
const resetScopes = route.invalidStores?.map(({ scope }) => scope)
|
|
3865
|
+
?? route.storedScopes
|
|
3283
3866
|
?? (route.layer ? [route.layer.scope] : []);
|
|
3284
3867
|
actions = [
|
|
3285
3868
|
...resetScopes.flatMap((scope) => {
|
|
@@ -3298,19 +3881,23 @@ export function ConsoleAppV4(props) {
|
|
|
3298
3881
|
}]
|
|
3299
3882
|
: [];
|
|
3300
3883
|
}),
|
|
3301
|
-
...(!invalidLayer ? [
|
|
3302
|
-
{
|
|
3303
|
-
|
|
3884
|
+
...(!invalidLayer && canRecoverSource ? [
|
|
3885
|
+
{
|
|
3886
|
+
label: '修复插件来源',
|
|
3887
|
+
description: `检查并恢复 ${unavailableSourceId};完成后返回当前设置位置。`,
|
|
3888
|
+
run: () => props.controller.beginConfigSourceRecovery(unavailableSourceId),
|
|
3889
|
+
},
|
|
3304
3890
|
] : []),
|
|
3891
|
+
{ label: showTechnical ? '返回问题摘要' : '技术详情', description: '查看脱敏错误代码和阶段', run: () => toggleTechnical() },
|
|
3305
3892
|
{ label: '返回', run: back },
|
|
3306
3893
|
];
|
|
3307
3894
|
const unsupportedScopes = resetScopes.filter((scope) => configResetAvailability[scope]?.resettable === false);
|
|
3308
3895
|
const unsupportedRows = unsupportedScopes.map((scope) => _jsx(StaticRow, { title: `${scope === 'user' ? '个人默认' : '当前项目'}设置层不能自动重置:特殊节点,请手动移动`, status: "warning", compact: true }, `unsupported-reset:${scope}`));
|
|
3309
3896
|
content = invalidLayer
|
|
3310
|
-
? _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [(route.invalidStores ?? []).flatMap((store) => [
|
|
3311
|
-
_jsx(StaticRow, { title: store.scope
|
|
3312
|
-
_jsx(StaticRow, { title:
|
|
3313
|
-
]), _jsx(StaticRow, { title:
|
|
3897
|
+
? _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u4E0D\u53EF\u8BFB\u53D6\u7684\u8BBE\u7F6E\u5C42", children: (route.invalidStores ?? []).map((store) => _jsx(StaticRow, { title: store.scope === 'user' ? '个人默认设置' : '当前项目设置', meta: "\u9700\u8981\u91CD\u7F6E\u6216\u624B\u52A8\u79FB\u5F00", status: "warning", compact: true }, store.scope)) }), showTechnical ? _jsxs(TechnicalDetails, { children: [(route.invalidStores ?? []).flatMap((store) => [
|
|
3898
|
+
_jsx(StaticRow, { title: scopeLabel(store.scope), meta: store.code, compact: true }, store.scope),
|
|
3899
|
+
_jsx(StaticRow, { title: "path summary", meta: store.pathSummary, compact: true }, `${store.scope}:path`),
|
|
3900
|
+
]), _jsx(StaticRow, { title: "stage", meta: safeDiagnosticCode(route.diagnosticStage, 'read-config'), compact: true })] }) : null, unsupportedRows, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] })
|
|
3314
3901
|
: _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, compact: policy.mode === 'compact' }), unsupportedRows, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
3315
3902
|
}
|
|
3316
3903
|
else if (route.name === 'sourceList') {
|
|
@@ -3340,9 +3927,14 @@ export function ConsoleAppV4(props) {
|
|
|
3340
3927
|
};
|
|
3341
3928
|
}),
|
|
3342
3929
|
{ label: '+ 添加来源', description: 'Git 仓库或本地目录', run: beginAddSource },
|
|
3930
|
+
{
|
|
3931
|
+
label: '检查全部来源',
|
|
3932
|
+
description: '并行检查各来源;结果不会自动启用待检查版本',
|
|
3933
|
+
run: () => props.controller.recheckAllCatalogSources(),
|
|
3934
|
+
},
|
|
3343
3935
|
];
|
|
3344
3936
|
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(8, 12));
|
|
3345
|
-
const sourceCount = Math.max(0, actions.length -
|
|
3937
|
+
const sourceCount = Math.max(0, actions.length - 2);
|
|
3346
3938
|
content = _jsx(Box, { flexDirection: "column", children: _jsx(SemanticRows, { actions: actions, focus: focus, kind: focus < sourceCount ? 'section' : 'action', compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit }) });
|
|
3347
3939
|
}
|
|
3348
3940
|
else if (route.name === 'sourceDetail') {
|
|
@@ -3356,92 +3948,103 @@ export function ConsoleAppV4(props) {
|
|
|
3356
3948
|
const combinedRepairAndMismatch = generationState === 'repairRequired'
|
|
3357
3949
|
&& generation?.configMismatch === true;
|
|
3358
3950
|
const logicalIdentityChanged = generation?.errorCode === 'catalogLogicalIdentityChanged';
|
|
3359
|
-
const activationBlocked = props.controller.
|
|
3951
|
+
const activationBlocked = props.controller.catalogSourceRecoveryBlockReason();
|
|
3360
3952
|
breadcrumb = `插件来源 / ${route.sourceId}`;
|
|
3361
3953
|
context = detail?.type === 'path' ? '本地目录' : 'Git';
|
|
3362
3954
|
title = route.sourceId;
|
|
3363
|
-
description =
|
|
3364
|
-
?
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3955
|
+
description = generationState === 'incompatible'
|
|
3956
|
+
? `当前目录版本与 AW 不兼容;缺少 ${generation?.compatibility?.state === 'unsupported' ? generation.compatibility.missingCapabilities.length : 0} 项功能支持。`
|
|
3957
|
+
: freshness?.state === 'incompatible'
|
|
3958
|
+
? generationState === 'repairRequired'
|
|
3959
|
+
? `待检查版本需要升级 AW 或修改来源后才能使用;损坏的当前启用版本仍可按旧版本精确修复。`
|
|
3960
|
+
: activeVersion
|
|
3961
|
+
? `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能;当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
3962
|
+
: `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能;这个来源没有可用目录版本。`
|
|
3963
|
+
: freshness?.state === 'checkFailed' && activeVersion
|
|
3964
|
+
? `更新检查失败,但当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
3965
|
+
: freshness?.state === 'updateAvailable'
|
|
3966
|
+
? `当前使用 ${activeVersion ?? '未启用'};待检查版本 ${candidateVersion ?? '未知'} 可更新。`
|
|
3967
|
+
: freshness?.state === 'localChangesAvailable'
|
|
3968
|
+
? '本地插件目录内容已变化;确认前仍使用当前启用版本。'
|
|
3969
|
+
: activeVersion ? `当前使用目录版本 ${activeVersion}。`
|
|
3970
|
+
: generationState === 'stateInvalid' ? '目录版本状态无效。确认恢复后可以重新启用这个来源。'
|
|
3971
|
+
: generationState === 'repairRequired' ? combinedRepairAndMismatch
|
|
3972
|
+
? '当前目录版本损坏,且来源配置已变化。验证待检查版本后可精确修复同一版本,或明确切换到新版本。'
|
|
3973
|
+
: '当前目录版本缺失或损坏。可在这里精确修复。'
|
|
3974
|
+
: generationState === 'configMismatch' ? '来源配置与当前启用版本不一致。可验证待检查版本并切换。'
|
|
3975
|
+
: logicalIdentityChanged ? '来源发布身份已变化。请编辑来源,或移除后重新添加。'
|
|
3976
|
+
: '来源没有可用的启用版本。可在这里取得并启用。';
|
|
3381
3977
|
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
3382
|
-
const generationAction = logicalIdentityChanged
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
run: () => beginReview({
|
|
3389
|
-
kind: 'catalogGeneration',
|
|
3390
|
-
action: 'repair',
|
|
3391
|
-
sourceId: route.sourceId,
|
|
3392
|
-
}),
|
|
3393
|
-
}, {
|
|
3394
|
-
label: '使用当前候选替换损坏的 Active…',
|
|
3395
|
-
description: '旧版本无法取得时使用;进入独立影响 Review',
|
|
3396
|
-
disabled: activationBlocked ? `请先处理当前草稿或操作:${activationBlocked}` : undefined,
|
|
3397
|
-
run: () => beginReview({
|
|
3398
|
-
kind: 'catalogGeneration',
|
|
3399
|
-
action: 'replaceDamaged',
|
|
3400
|
-
sourceId: route.sourceId,
|
|
3401
|
-
}),
|
|
3978
|
+
const generationAction = logicalIdentityChanged ? []
|
|
3979
|
+
: generationState === 'incompatible' ? [{
|
|
3980
|
+
label: '查看不兼容详情',
|
|
3981
|
+
description: '显示所需和缺少的功能支持',
|
|
3982
|
+
status: 'error',
|
|
3983
|
+
run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }),
|
|
3402
3984
|
}]
|
|
3403
|
-
: generationState
|
|
3404
|
-
label:
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
disabled: activationBlocked ? `请先处理当前草稿或操作:${activationBlocked}` : undefined,
|
|
3985
|
+
: generationState === 'repairRequired' ? [{
|
|
3986
|
+
label: '重新下载旧版本并精确修复',
|
|
3987
|
+
description: '使用当前启用版本的精确提交;成功后保持此版本',
|
|
3988
|
+
disabled: activationBlocked ? `请先处理:${sourceOperationBlockLabel(activationBlocked)}` : undefined,
|
|
3408
3989
|
run: () => beginReview({
|
|
3409
3990
|
kind: 'catalogGeneration',
|
|
3410
|
-
action:
|
|
3411
|
-
: generationState === 'configMismatch' ? 'update' : 'activate',
|
|
3991
|
+
action: 'repair',
|
|
3412
3992
|
sourceId: route.sourceId,
|
|
3413
3993
|
}),
|
|
3414
|
-
}]
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3994
|
+
}, ...(freshness?.state === 'incompatible' ? [] : [{
|
|
3995
|
+
label: '使用待检查版本替换损坏版本…',
|
|
3996
|
+
description: '旧版本无法取得时使用;进入独立影响确认',
|
|
3997
|
+
disabled: activationBlocked ? `请先处理:${sourceOperationBlockLabel(activationBlocked)}` : undefined,
|
|
3998
|
+
run: () => beginReview({
|
|
3999
|
+
kind: 'catalogGeneration',
|
|
4000
|
+
action: 'replaceDamaged',
|
|
4001
|
+
sourceId: route.sourceId,
|
|
4002
|
+
}),
|
|
4003
|
+
}])]
|
|
4004
|
+
: freshness?.state === 'incompatible' && generationState !== 'ready' ? []
|
|
4005
|
+
: generationState !== 'ready' ? [{
|
|
4006
|
+
label: generationState === 'stateInvalid' ? '恢复状态并启用'
|
|
4007
|
+
: generationState === 'configMismatch' ? '验证并切换' : '获取并启用',
|
|
4008
|
+
description: '先准备只读的待检查版本确认;确认后才切换当前版本',
|
|
4009
|
+
disabled: activationBlocked ? `请先处理:${sourceOperationBlockLabel(activationBlocked)}` : undefined,
|
|
4010
|
+
run: () => beginReview({
|
|
4011
|
+
kind: 'catalogGeneration',
|
|
4012
|
+
action: generationState === 'stateInvalid' ? 'recoverState'
|
|
4013
|
+
: generationState === 'configMismatch' ? 'update' : 'activate',
|
|
4014
|
+
sourceId: route.sourceId,
|
|
4015
|
+
}),
|
|
3422
4016
|
}]
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
:
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
4017
|
+
: freshness?.state === 'updateAvailable' || freshness?.state === 'localChangesAvailable'
|
|
4018
|
+
? [{
|
|
4019
|
+
label: freshness.state === 'localChangesAvailable'
|
|
4020
|
+
? '应用本地内容变化…' : `更新到 ${candidateVersion ?? '待检查版本'}…`,
|
|
4021
|
+
description: '进入确认;确认前不会切换当前版本',
|
|
4022
|
+
disabled: activationBlocked ? `请先处理:${sourceOperationBlockLabel(activationBlocked)}` : undefined,
|
|
4023
|
+
run: () => beginReview({ kind: 'catalogGeneration', action: 'update', sourceId: route.sourceId }),
|
|
4024
|
+
}]
|
|
4025
|
+
: freshness?.state === 'versionConflict'
|
|
4026
|
+
|| freshness?.state === 'rollbackDetected'
|
|
4027
|
+
|| freshness?.state === 'incompatible'
|
|
4028
|
+
? [{
|
|
4029
|
+
label: freshness.state === 'versionConflict' ? '查看版本冲突'
|
|
4030
|
+
: freshness.state === 'rollbackDetected' ? '查看回退详情' : '查看不兼容详情',
|
|
4031
|
+
description: '普通更新已阻止;当前启用版本保持不变',
|
|
4032
|
+
status: 'error',
|
|
4033
|
+
run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }),
|
|
4034
|
+
}]
|
|
4035
|
+
: [{
|
|
4036
|
+
label: freshness?.state === 'checking' ? '正在检查…' : '重新检查',
|
|
4037
|
+
description: '轻量检查这个来源;提交发生变化时才完整验证',
|
|
4038
|
+
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
4039
|
+
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
4040
|
+
}];
|
|
3439
4041
|
actions = [
|
|
3440
4042
|
...generationAction,
|
|
3441
|
-
{ label: '编辑来源…', description: '
|
|
3442
|
-
...(
|
|
4043
|
+
{ label: '编辑来源…', description: '集中修改地址、引用、子目录和自动检查', run: () => openSourceEdit(route.sourceId) },
|
|
4044
|
+
...(generationState === 'incompatible'
|
|
4045
|
+
|| (freshness?.state === 'incompatible' && generationState !== 'ready') ? [] : [{
|
|
3443
4046
|
label: generationState === 'ready' ? '设为主来源…' : '启用并设为主来源…',
|
|
3444
|
-
description: generationState === 'ready' ? '
|
|
4047
|
+
description: generationState === 'ready' ? '只改变默认插件来源' : '先启用确认的待检查版本,再设为主来源',
|
|
3445
4048
|
disabled: detail?.primary ? '当前已经是主来源'
|
|
3446
4049
|
: logicalIdentityChanged ? '请编辑来源,或移除后重新添加' : undefined,
|
|
3447
4050
|
run: () => generationState === 'ready'
|
|
@@ -3456,7 +4059,7 @@ export function ConsoleAppV4(props) {
|
|
|
3456
4059
|
}),
|
|
3457
4060
|
}]),
|
|
3458
4061
|
{ label: '移除来源…', description: '移除来源配置', status: 'error', disabled: detail?.primary ? '主来源不能直接移除' : undefined, run: () => beginReview({ kind: 'sourceMutation', action: 'remove', sourceId: route.sourceId, changes: [{ title: `移除来源 ${route.sourceId}` }] }) },
|
|
3459
|
-
{ label: '技术详情', description: '
|
|
4062
|
+
{ label: '技术详情', description: '查看脱敏错误代码和当前版本证据', run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }) },
|
|
3460
4063
|
...(generationState !== 'ready'
|
|
3461
4064
|
|| freshness?.state === 'updateAvailable'
|
|
3462
4065
|
|| freshness?.state === 'localChangesAvailable'
|
|
@@ -3464,7 +4067,7 @@ export function ConsoleAppV4(props) {
|
|
|
3464
4067
|
|| freshness?.state === 'rollbackDetected'
|
|
3465
4068
|
|| freshness?.state === 'incompatible' ? [{
|
|
3466
4069
|
label: freshness?.state === 'checking' ? '正在检查…' : '重新检查',
|
|
3467
|
-
description: '
|
|
4070
|
+
description: '轻量检查这个来源;提交发生变化时才完整验证',
|
|
3468
4071
|
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
3469
4072
|
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
3470
4073
|
}] : []),
|
|
@@ -3477,7 +4080,7 @@ export function ConsoleAppV4(props) {
|
|
|
3477
4080
|
];
|
|
3478
4081
|
const sourceActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 5));
|
|
3479
4082
|
const freshnessLabel = catalogSourceFreshnessLabel(freshness);
|
|
3480
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "
|
|
4083
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u5F53\u524D\u76EE\u5F55\u7248\u672C", status: generationState === 'incompatible' ? 'error' : activeVersion ? 'success' : 'warning', compact: policy.mode === 'compact', children: [_jsx(StaticRow, { title: activeVersion ? `版本 ${activeVersion}` : '未启用', meta: activeVersion ? generation?.active?.digestShort : catalogGenerationStateLabel(generationState), status: generationState === 'incompatible' ? 'error' : undefined, compact: true }), candidateVersion ? _jsx(StaticRow, { title: `待检查版本 ${candidateVersion}`, meta: freshnessLabel, status: freshness?.state === 'updateAvailable' || freshness?.state === 'localChangesAvailable' ? 'warning' : undefined, compact: true }) : _jsx(StaticRow, { title: freshnessLabel, status: freshness?.state === 'checkFailed' ? 'warning' : undefined, compact: true })] }), _jsxs(SectionCard, { title: "\u6765\u6E90\u914D\u7F6E", compact: policy.mode === 'compact', children: [_jsx(StaticRow, { title: "\u4F4D\u7F6E", meta: detail?.locationSummary ?? '不可用', compact: true }), _jsx(StaticRow, { title: "\u5F15\u7528", meta: generation?.active?.requestedRef ?? detail?.explicitRef ?? detail?.requestedRef ?? '本地目录', compact: true }), _jsx(StaticRow, { title: "\u81EA\u52A8\u68C0\u67E5\u66F4\u65B0", meta: detail?.autoCheck === false ? '关闭' : '开启', compact: true })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: sourceActionViewport.offset, limit: sourceActionViewport.limit })] });
|
|
3481
4084
|
}
|
|
3482
4085
|
else if (route.name === 'sourceTechnical') {
|
|
3483
4086
|
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
@@ -3501,9 +4104,9 @@ export function ConsoleAppV4(props) {
|
|
|
3501
4104
|
context = '只读';
|
|
3502
4105
|
title = '来源技术诊断';
|
|
3503
4106
|
description = '';
|
|
3504
|
-
help = 'Enter 返回 · Esc 返回';
|
|
4107
|
+
help = 'PgUp/PgDn 查看全部技术详情 · Enter 返回 · Esc 返回';
|
|
3505
4108
|
actions = [{ label: '返回来源详情', run: back }];
|
|
3506
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: true, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4109
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: true, compact: policy.mode === 'compact', offset: batchTargetOffset, limit: responsiveBudget(5, 10) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
3507
4110
|
}
|
|
3508
4111
|
else if (route.name === 'sourceAdd' && sourceDraft?.owner === route.owner) {
|
|
3509
4112
|
breadcrumb = sourceDraft.mode === 'add' ? '插件来源 / 添加' : `插件来源 / ${sourceDraft.sourceId} / 编辑`;
|
|
@@ -3516,6 +4119,7 @@ export function ConsoleAppV4(props) {
|
|
|
3516
4119
|
description = '来源类型创建后不能修改。';
|
|
3517
4120
|
const selectType = (type, autoCheck) => {
|
|
3518
4121
|
setSourceDraft({ ...sourceDraft, type, autoCheck });
|
|
4122
|
+
setDirty(true);
|
|
3519
4123
|
go({ name: 'sourceAdd', step: sourceDraft.sourceId ? 'location' : 'id', owner: route.owner });
|
|
3520
4124
|
};
|
|
3521
4125
|
actions = [
|
|
@@ -3532,8 +4136,9 @@ export function ConsoleAppV4(props) {
|
|
|
3532
4136
|
const useRefPreset = async (refPolicy, ref) => {
|
|
3533
4137
|
const next = { ...sourceDraft, refPolicy, ref };
|
|
3534
4138
|
setSourceDraft(next);
|
|
3535
|
-
|
|
3536
|
-
|
|
4139
|
+
setDirty(true);
|
|
4140
|
+
if (route.returnToDraft)
|
|
4141
|
+
acceptSourceFieldAndBack();
|
|
3537
4142
|
else
|
|
3538
4143
|
go({ name: 'sourceAdd', step: 'subdir', owner: route.owner });
|
|
3539
4144
|
};
|
|
@@ -3541,7 +4146,12 @@ export function ConsoleAppV4(props) {
|
|
|
3541
4146
|
{ label: '跟随 AW CLI', description: `当前 CLI ${snapshot.cliChannel === 'beta' ? 'beta' : 'release'} → catalog/${snapshot.cliChannel === 'beta' ? 'beta' : 'latest'}`, selected: sourceDraft.refPolicy === 'cliChannel', run: () => { const policy = catalogRefPolicyForPreset('followCli'); return useRefPreset(policy.type, ''); } },
|
|
3542
4147
|
{ label: 'Release', description: '固定读取 catalog/latest', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref === CATALOG_LATEST_REF, run: () => { const policy = catalogRefPolicyForPreset('release'); return useRefPreset(policy.type, policy.type === 'explicit' ? policy.ref : ''); } },
|
|
3543
4148
|
{ label: 'Beta', description: '固定读取 catalog/beta', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref === CATALOG_BETA_REF, run: () => { const policy = catalogRefPolicyForPreset('beta'); return useRefPreset(policy.type, policy.type === 'explicit' ? policy.ref : ''); } },
|
|
3544
|
-
{ label: '指定 Git ref…', description: 'branch、tag、完整 ref 或 commit', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref !== CATALOG_LATEST_REF && sourceDraft.ref !== CATALOG_BETA_REF, run: () => {
|
|
4149
|
+
{ label: '指定 Git ref…', description: 'branch、tag、完整 ref 或 commit', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref !== CATALOG_LATEST_REF && sourceDraft.ref !== CATALOG_BETA_REF, run: () => {
|
|
4150
|
+
if (route.returnToDraft)
|
|
4151
|
+
props.controller.replace({ name: 'sourceAdd', step: 'ref', owner: route.owner, returnToDraft: true });
|
|
4152
|
+
else
|
|
4153
|
+
go({ name: 'sourceAdd', step: 'ref', owner: route.owner });
|
|
4154
|
+
} },
|
|
3545
4155
|
];
|
|
3546
4156
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u8DDF\u968F\u542B\u4E49\uFF1A\u5F53\u524D CLI ", snapshot.cliChannel === 'beta' ? 'beta → catalog/beta' : 'release → catalog/latest'] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' })] });
|
|
3547
4157
|
}
|
|
@@ -3558,60 +4168,115 @@ export function ConsoleAppV4(props) {
|
|
|
3558
4168
|
}
|
|
3559
4169
|
else if (route.name === 'sourceEdit') {
|
|
3560
4170
|
const detail = props.controller.getSourceOverview(route.sourceId).detail;
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
4171
|
+
const activeDraft = route.owner && sourceDraft?.owner === route.owner ? sourceDraft : undefined;
|
|
4172
|
+
const startDraftField = (step) => {
|
|
4173
|
+
let draft = activeDraft;
|
|
4174
|
+
if (!draft) {
|
|
4175
|
+
const source = props.controller.getConfiguredSource(route.sourceId);
|
|
4176
|
+
if (!source)
|
|
4177
|
+
return;
|
|
4178
|
+
const owner = `source-update:${route.sourceId}:${Date.now()}`;
|
|
4179
|
+
draft = sourceDraftFromSource(source, owner);
|
|
4180
|
+
setSourceDraft(draft);
|
|
4181
|
+
props.controller.replace({ name: 'sourceEdit', sourceId: route.sourceId, owner });
|
|
4182
|
+
}
|
|
4183
|
+
go({ name: 'sourceAdd', step, owner: draft.owner, returnToDraft: true });
|
|
3571
4184
|
};
|
|
3572
|
-
const
|
|
3573
|
-
|
|
3574
|
-
|
|
4185
|
+
const draft = activeDraft;
|
|
4186
|
+
breadcrumb = `插件来源 / ${route.sourceId} / 编辑`;
|
|
4187
|
+
title = draft?.mode === 'add' ? `检查新来源 ${draft.sourceId}` : `编辑 ${route.sourceId}`;
|
|
4188
|
+
description = '地址、引用、子目录和自动检查组成一个草稿;保存前统一 Review。';
|
|
4189
|
+
const locationSummary = draft
|
|
4190
|
+
? projectCatalogSourceLocation(draft.location)
|
|
4191
|
+
: detail?.locationSummary ?? '不可用';
|
|
4192
|
+
const setOfficialLocation = (location) => {
|
|
4193
|
+
const current = draft ?? (() => {
|
|
4194
|
+
const source = props.controller.getConfiguredSource(route.sourceId);
|
|
4195
|
+
if (!source)
|
|
4196
|
+
return undefined;
|
|
4197
|
+
return sourceDraftFromSource(source, `source-update:${route.sourceId}:${Date.now()}`);
|
|
4198
|
+
})();
|
|
4199
|
+
if (!current || current.type !== 'git' || current.location === location)
|
|
3575
4200
|
return;
|
|
3576
|
-
const
|
|
3577
|
-
const next = {
|
|
3578
|
-
owner,
|
|
3579
|
-
mode: 'update',
|
|
3580
|
-
sourceId: route.sourceId,
|
|
3581
|
-
type: 'git',
|
|
3582
|
-
location,
|
|
3583
|
-
refPolicy: detail.refPolicy ?? 'cliChannel',
|
|
3584
|
-
ref: detail.explicitRef ?? '',
|
|
3585
|
-
subdir: detail.subdir ?? '',
|
|
3586
|
-
autoCheck: detail.autoCheck,
|
|
3587
|
-
editField: 'location',
|
|
3588
|
-
};
|
|
4201
|
+
const next = { ...current, location };
|
|
3589
4202
|
setSourceDraft(next);
|
|
3590
4203
|
setDirty(true);
|
|
3591
|
-
|
|
4204
|
+
props.controller.replace({ name: 'sourceEdit', sourceId: route.sourceId, owner: next.owner });
|
|
3592
4205
|
};
|
|
3593
|
-
actions =
|
|
3594
|
-
...(route.sourceId === 'official' ? [
|
|
3595
|
-
{ label: 'HTTPS', description: '使用系统 Git credential helper', selected:
|
|
3596
|
-
{ label: 'SSH', description: '使用本机 SSH key;Git 连接使用 BatchMode', selected:
|
|
3597
|
-
] : [{ label: 'Git
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
{ label: '
|
|
3603
|
-
|
|
4206
|
+
actions = [
|
|
4207
|
+
...(route.sourceId === 'official' && (draft?.type === 'git' || !draft && detail?.type === 'git') ? [
|
|
4208
|
+
{ label: 'HTTPS', description: '使用系统 Git credential helper', selected: locationSummary === projectCatalogSourceLocation(OFFICIAL_CATALOG_SOURCE_URL), run: () => setOfficialLocation(OFFICIAL_CATALOG_SOURCE_URL) },
|
|
4209
|
+
{ label: 'SSH', description: '使用本机 SSH key;Git 连接使用 BatchMode', selected: locationSummary === projectCatalogSourceLocation(OFFICIAL_CATALOG_SOURCE_SSH_URL), run: () => setOfficialLocation(OFFICIAL_CATALOG_SOURCE_SSH_URL) },
|
|
4210
|
+
] : [{ label: draft?.type === 'path' || detail?.type === 'path' ? '本地目录' : 'Git 仓库地址', description: locationSummary, run: () => startDraftField('location') }]),
|
|
4211
|
+
...(draft?.type === 'git' || !draft && detail?.type === 'git' ? [
|
|
4212
|
+
{ label: '版本引用', description: draft ? draft.refPolicy === 'explicit' ? `固定:${draft.ref}` : '跟随 AW 版本通道' : detail?.refPolicy === 'explicit' ? `固定:${detail.explicitRef}` : '跟随 AW 版本通道', run: () => startDraftField('refPolicy') },
|
|
4213
|
+
{ label: 'Catalog 子目录', description: draft?.subdir || detail?.subdir || '未设置', run: () => startDraftField('subdir') },
|
|
4214
|
+
] : []),
|
|
4215
|
+
{ label: '自动检查更新', description: (draft?.autoCheck ?? detail?.autoCheck) ? '已开启' : '已关闭', selected: draft?.autoCheck ?? detail?.autoCheck, run: () => {
|
|
4216
|
+
if (!draft) {
|
|
4217
|
+
const source = props.controller.getConfiguredSource(route.sourceId);
|
|
4218
|
+
if (!source)
|
|
4219
|
+
return;
|
|
4220
|
+
const owner = `source-update:${route.sourceId}:${Date.now()}`;
|
|
4221
|
+
const next = sourceDraftFromSource(source, owner);
|
|
4222
|
+
next.autoCheck = !next.autoCheck;
|
|
4223
|
+
setSourceDraft(next);
|
|
4224
|
+
setDirty(true);
|
|
4225
|
+
props.controller.replace({ name: 'sourceEdit', sourceId: route.sourceId, owner });
|
|
4226
|
+
return;
|
|
4227
|
+
}
|
|
4228
|
+
setSourceDraft({ ...draft, autoCheck: !draft.autoCheck });
|
|
4229
|
+
setDirty(true);
|
|
4230
|
+
} },
|
|
4231
|
+
{ label: '查看修改汇总', description: draft && sourceDraftChanges(draft).length > 0 ? `${sourceDraftChanges(draft).length} 项未保存` : '尚无修改', disabled: draft && sourceDraftChanges(draft).length > 0 ? undefined : '请先修改来源设置', status: 'success', run: () => {
|
|
4232
|
+
if (!draft)
|
|
4233
|
+
return;
|
|
4234
|
+
setFocus(0);
|
|
4235
|
+
go({ name: 'sourceSave', owner: draft.owner });
|
|
4236
|
+
} },
|
|
3604
4237
|
];
|
|
3605
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: "section", compact: policy.mode === 'compact' }), route.sourceId === 'official' && detail?.type === 'git' ? _jsxs(SectionCard, { title: "\u6765\u6E90\u8EAB\u4EFD", children: [_jsx(StaticRow, { title: "\u903B\u8F91 Catalog", meta: "lesliechan721-arch/agent-workflow" }), _jsx(StaticRow, { title: "source identity", meta: "HTTPS / SSH \u4FDD\u6301\u4E00\u81F4" }), _jsx(StaticRow, { title: "\u660E\u6587 HTTP", meta: "\u4E0D\u652F\u6301", status: "warning" })] }) : null] });
|
|
4238
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u6765\u6E90\u8349\u7A3F", compact: policy.mode === 'compact', children: [_jsx(StaticRow, { title: "\u6765\u6E90 ID", meta: draft?.sourceId ?? route.sourceId, compact: true }), _jsx(StaticRow, { title: "\u7C7B\u578B", meta: (draft?.type ?? detail?.type) === 'git' ? 'Git 仓库' : '本地目录', compact: true })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "section", compact: policy.mode === 'compact' }), route.sourceId === 'official' && detail?.type === 'git' ? _jsxs(SectionCard, { title: "\u6765\u6E90\u8EAB\u4EFD", children: [_jsx(StaticRow, { title: "\u903B\u8F91 Catalog", meta: "lesliechan721-arch/agent-workflow" }), _jsx(StaticRow, { title: "source identity", meta: "HTTPS / SSH \u4FDD\u6301\u4E00\u81F4" }), _jsx(StaticRow, { title: "\u660E\u6587 HTTP", meta: "\u4E0D\u652F\u6301", status: "warning" })] }) : null] });
|
|
4239
|
+
}
|
|
4240
|
+
else if (route.name === 'sourceSave' && sourceDraft?.owner === route.owner) {
|
|
4241
|
+
const changes = sourceDraftChanges(sourceDraft);
|
|
4242
|
+
breadcrumb = `插件来源 / ${sourceDraft.sourceId} / 修改汇总`;
|
|
4243
|
+
context = `${changes.length} 项未保存`;
|
|
4244
|
+
title = sourceDraft.mode === 'add' ? `保存新来源 ${sourceDraft.sourceId}?` : `保存 ${sourceDraft.sourceId} 的修改?`;
|
|
4245
|
+
description = '选择是否在保存后验证候选并启用。保存成功与启用成功会分别报告。';
|
|
4246
|
+
actions = [
|
|
4247
|
+
{ label: '保存并启用', description: '保存来源配置,再验证并切换当前目录版本', status: 'success', run: () => beginSourceReview(sourceDraft, true) },
|
|
4248
|
+
{ label: '只保存', description: '只保存来源配置;当前目录版本保持不变', run: () => beginSourceReview(sourceDraft, false) },
|
|
4249
|
+
{ label: '返回修改', run: back },
|
|
4250
|
+
];
|
|
4251
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u6765\u6E90\u53D8\u5316", compact: policy.mode === 'compact', children: changes.map((change) => _jsx(ReviewChangeRow, { title: `~ ${change.title}`, description: `${change.before ?? '未设置'} → ${change.after}`, compact: true }, change.title)) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4252
|
+
}
|
|
4253
|
+
else if (route.name === 'scriptTimeout' && pendingReviewIntent) {
|
|
4254
|
+
breadcrumb = '高级选项 / 安装前检查脚本';
|
|
4255
|
+
context = '毫秒';
|
|
4256
|
+
title = '设置脚本超时';
|
|
4257
|
+
description = `输入 ${SCRIPT_TIMEOUT_MIN_MS}–${SCRIPT_TIMEOUT_MAX_MS} 毫秒。默认 ${SCRIPT_TIMEOUT_DEFAULT_MS} 毫秒。`;
|
|
4258
|
+
help = '输入 · Enter 应用并重新准备 Review · Ctrl-U 清空 · Esc 取消';
|
|
4259
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: scriptTimeoutInput, columns: Math.max(8, size.columns - 8) }), description: scriptTimeoutError ?? '此值将绑定本次 Review。', status: scriptTimeoutError ? 'error' : undefined }) });
|
|
3606
4260
|
}
|
|
3607
4261
|
else if (route.name === 'sourceRemoveBlocked') {
|
|
3608
4262
|
breadcrumb = `插件来源 / ${route.sourceId} / 移除`;
|
|
3609
4263
|
context = '已阻止';
|
|
3610
4264
|
title = `无法移除 ${route.sourceId}`;
|
|
3611
4265
|
description = sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整,不能把未知引用当作零。' : '仍有内容依赖这个来源。';
|
|
3612
|
-
actions = [{ label: sourceDependenciesExpanded ? '收起依赖内容' : '查看依赖内容', description: '只显示插件、来源、平台、位置和类别聚合', run: () =>
|
|
4266
|
+
actions = [{ label: sourceDependenciesExpanded ? '收起依赖内容' : '查看依赖内容', description: '只显示插件、来源、平台、位置和类别聚合', run: () => {
|
|
4267
|
+
setSourceDependenciesExpanded((value) => !value);
|
|
4268
|
+
setBatchTargetOffset(0);
|
|
4269
|
+
} }, { label: '返回来源详情', run: back }];
|
|
3613
4270
|
const countLabel = (value, unit) => value === undefined ? '未知' : `${value} ${unit}`;
|
|
3614
|
-
|
|
4271
|
+
const referenceLimit = Math.max(1, pageActionBudget(10));
|
|
4272
|
+
const references = sourceReferenceSummary?.dependencies ?? [];
|
|
4273
|
+
const referenceOffset = Math.min(batchTargetOffset, Math.max(0, references.length - 1));
|
|
4274
|
+
const visibleReferences = references.slice(referenceOffset, referenceOffset + referenceLimit);
|
|
4275
|
+
const hiddenReferences = Math.max(0, references.length - referenceOffset - visibleReferences.length);
|
|
4276
|
+
if (sourceDependenciesExpanded && references.length > referenceLimit) {
|
|
4277
|
+
help = 'PgUp/PgDn 查看全部引用 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4278
|
+
}
|
|
4279
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "error", title: sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整' : '发现活动引用' }), _jsxs(SectionCard, { title: "\u5F15\u7528\u6C47\u603B", children: [_jsx(StaticRow, { title: "\u5B89\u88C5\u4F4D\u7F6E", meta: countLabel(sourceReferenceSummary?.installationCount, '个') }), _jsx(StaticRow, { title: "\u63D2\u4EF6\u8BBE\u7F6E", meta: countLabel(sourceReferenceSummary?.configCount, '份') }), _jsx(StaticRow, { title: "\u751F\u547D\u5468\u671F\u610F\u56FE", meta: countLabel(sourceReferenceSummary?.lifecycleCount, '项') }), _jsx(StaticRow, { title: "Marketplace", meta: countLabel(sourceReferenceSummary?.marketplaceCount, '个') }), sourceDependenciesExpanded && referenceOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${referenceOffset} 项`, status: "muted", compact: true }) : null, sourceDependenciesExpanded ? visibleReferences.map((dependency, index) => _jsx(StaticRow, { title: `${dependency.category} · ${dependency.pluginId ?? dependency.sourceId ?? '身份未知'}`, meta: `${dependency.platform ? platformLabel(dependency.platform) : ''}${dependency.scope ? ` · ${scopeLabel(dependency.scope)}` : ''}` }, `${dependency.category}:${dependency.pluginId ?? dependency.sourceId ?? referenceOffset + index}`)) : null, sourceDependenciesExpanded && hiddenReferences > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${hiddenReferences} 项`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
3615
4280
|
}
|
|
3616
4281
|
else if (route.name === 'issueList') {
|
|
3617
4282
|
const issues = props.controller.listUserIssues();
|
|
@@ -3623,7 +4288,7 @@ export function ConsoleAppV4(props) {
|
|
|
3623
4288
|
: '当前状态检查没有发现需要处理的安装、来源或设置问题。';
|
|
3624
4289
|
if (issues.length === 0) {
|
|
3625
4290
|
actions = [
|
|
3626
|
-
{ label: '返回首页', run: () =>
|
|
4291
|
+
{ label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
3627
4292
|
{ label: '浏览插件', run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
3628
4293
|
];
|
|
3629
4294
|
}
|
|
@@ -3706,7 +4371,7 @@ export function ConsoleAppV4(props) {
|
|
|
3706
4371
|
go({ name: 'pluginDependencies', pluginId: entry.pluginId });
|
|
3707
4372
|
}
|
|
3708
4373
|
else
|
|
3709
|
-
|
|
4374
|
+
toggleTechnical();
|
|
3710
4375
|
};
|
|
3711
4376
|
const issueHasTechnicalDetails = Boolean(issue.diagnostic.technical);
|
|
3712
4377
|
actions = issue.actions.filter((entry) => entry.id !== 'viewTechnical' || issueHasTechnicalDetails).map((entry) => ({
|
|
@@ -3718,7 +4383,7 @@ export function ConsoleAppV4(props) {
|
|
|
3718
4383
|
}));
|
|
3719
4384
|
const issuePlugin = issue.pluginId ? props.controller.getPluginOverview(issue.pluginId) : undefined;
|
|
3720
4385
|
const issueOverview = issue.pluginId ? props.controller.getPluginDependencyOverview(issue.pluginId) : undefined;
|
|
3721
|
-
const issueDiagnosticSummary = _jsx(DiagnosticSummary, { diagnostic: issue.diagnostic, technical: showTechnical, compact: policy.mode === 'compact' || issue.category.startsWith('dependency')
|
|
4386
|
+
const issueDiagnosticSummary = _jsx(DiagnosticSummary, { diagnostic: issue.diagnostic, technical: showTechnical, offset: showTechnical ? batchTargetOffset : undefined, limit: showTechnical ? responsiveBudget(5, 10) : undefined, compact: policy.mode === 'compact' || issue.category.startsWith('dependency')
|
|
3722
4387
|
|| issue.category === 'scopeConflict' });
|
|
3723
4388
|
const issueActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(6));
|
|
3724
4389
|
const compactTechnicalActionViewport = policy.mode === 'compact' && showTechnical
|
|
@@ -3736,7 +4401,7 @@ export function ConsoleAppV4(props) {
|
|
|
3736
4401
|
actions = [
|
|
3737
4402
|
{ label: '查看完整依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
|
|
3738
4403
|
{ label: '返回插件详情', run: () => go({ name: 'pluginDetail', pluginId: issuePlugin.pluginId }) },
|
|
3739
|
-
...(issueHasTechnicalDetails ? [{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () =>
|
|
4404
|
+
...(issueHasTechnicalDetails ? [{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() }] : []),
|
|
3740
4405
|
];
|
|
3741
4406
|
const dependencyBlockedViewport = policy.mode === 'compact' && showTechnical
|
|
3742
4407
|
? { offset: Math.min(Math.max(0, focus - 1), Math.max(0, actions.length - 2)), limit: 2 }
|
|
@@ -3778,7 +4443,7 @@ export function ConsoleAppV4(props) {
|
|
|
3778
4443
|
go({ name: 'installTargets', pluginId: issuePlugin.pluginId });
|
|
3779
4444
|
} },
|
|
3780
4445
|
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
|
|
3781
|
-
...(issueHasTechnicalDetails ? [{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () =>
|
|
4446
|
+
...(issueHasTechnicalDetails ? [{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() }] : []),
|
|
3782
4447
|
];
|
|
3783
4448
|
const unsupportedTargetViewport = policy.mode === 'compact' && showTechnical
|
|
3784
4449
|
? { offset: Math.min(Math.max(0, focus - 1), Math.max(0, actions.length - 2)), limit: 2 }
|
|
@@ -3806,24 +4471,105 @@ export function ConsoleAppV4(props) {
|
|
|
3806
4471
|
: null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: issueActionViewport.offset, limit: issueActionViewport.limit })] });
|
|
3807
4472
|
}
|
|
3808
4473
|
}
|
|
4474
|
+
else if (route.name === 'operationHistory') {
|
|
4475
|
+
const records = snapshot.operationHistorySummaries;
|
|
4476
|
+
breadcrumb = '操作记录';
|
|
4477
|
+
context = `本次会话 · ${records.length} 项`;
|
|
4478
|
+
title = records.length === 0 ? '还没有操作记录' : '最近操作';
|
|
4479
|
+
description = records.length === 0
|
|
4480
|
+
? '完成操作或遇到问题后,安全摘要会保留在这里。'
|
|
4481
|
+
: '记录只包含安全摘要,不携带可重放的确认票据。';
|
|
4482
|
+
actions = records.length === 0
|
|
4483
|
+
? [{ label: '返回首页', run: () => requestSessionNavigation('home') }]
|
|
4484
|
+
: [
|
|
4485
|
+
...records.map((record) => ({
|
|
4486
|
+
label: record.message,
|
|
4487
|
+
description: `${operationHistoryState(record).label} · ${record.diagnosticCount} 项安全诊断`,
|
|
4488
|
+
meta: operationHistoryMeta(record),
|
|
4489
|
+
status: operationHistoryState(record).status,
|
|
4490
|
+
run: () => go({ name: 'operationDiagnostic', recordId: record.recordId }),
|
|
4491
|
+
})),
|
|
4492
|
+
{ label: '返回上一页', run: back },
|
|
4493
|
+
];
|
|
4494
|
+
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(4));
|
|
4495
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: records.some((record) => record.state !== 'succeeded') ? 'warning' : 'success', title: records.length === 0 ? '没有可回看的安全结果' : '操作结果与问题', description: "\u9009\u62E9\u4E00\u9879\u53EF\u67E5\u770B\u5B8C\u6574\u5B89\u5168\u8BE6\u60C5\u6216\u62A5\u544A\u9884\u89C8\u3002", compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit })] });
|
|
4496
|
+
}
|
|
4497
|
+
else if (route.name === 'operationDiagnostic') {
|
|
4498
|
+
const detail = props.controller.getOperationDiagnostic(route.recordId);
|
|
4499
|
+
breadcrumb = '操作记录 / 技术详情';
|
|
4500
|
+
context = '只读 · 已脱敏';
|
|
4501
|
+
title = detail ? '安全技术详情' : '操作记录已不可用';
|
|
4502
|
+
description = detail
|
|
4503
|
+
? '可滚动查看所有目标、阶段、代码和已保留的安全原因。'
|
|
4504
|
+
: '这项会话记录已经不再可用。';
|
|
4505
|
+
const preview = detail ? props.controller.previewOperationReport(route.recordId) : undefined;
|
|
4506
|
+
actions = detail ? [
|
|
4507
|
+
preview
|
|
4508
|
+
? { label: '预览安全报告', description: '确认内容后选择本地保存位置', run: () => go({ name: 'operationReportPreview', recordId: route.recordId }) }
|
|
4509
|
+
: { label: '报告当前不可用', disabled: '无法生成这项记录的报告' },
|
|
4510
|
+
{ label: '返回操作记录', run: back },
|
|
4511
|
+
] : [{ label: '返回操作记录', run: back }];
|
|
4512
|
+
const detailLineLimit = policy.mode === 'compact' ? 2 : Math.max(1, pageActionBudget(5));
|
|
4513
|
+
content = detail ? _jsxs(Box, { flexDirection: "column", children: [_jsx(OperationDiagnosticScreen, { detail: detail, compact: policy.mode === 'compact', offset: scrollOffset, limit: detailLineLimit }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] }) : _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "warning", title: "\u8BB0\u5F55\u5DF2\u4E0D\u53EF\u7528", description: "\u8FD4\u56DE\u64CD\u4F5C\u8BB0\u5F55\u540E\u53EF\u67E5\u770B\u4ECD\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u7684\u7ED3\u679C\u3002" }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4514
|
+
}
|
|
4515
|
+
else if (route.name === 'operationReportPreview') {
|
|
4516
|
+
const preview = props.controller.previewOperationReport(route.recordId);
|
|
4517
|
+
breadcrumb = '操作记录 / 报告预览';
|
|
4518
|
+
context = '只读预览';
|
|
4519
|
+
title = reportSaveMode ? '选择报告保存位置' : preview ? '安全报告预览' : '报告已不可用';
|
|
4520
|
+
description = reportSaveMode
|
|
4521
|
+
? '输入一个新的本地文件路径,然后按 Enter 保存。'
|
|
4522
|
+
: preview
|
|
4523
|
+
? '保存前请确认报告内容。不会自动上传或发送。'
|
|
4524
|
+
: '这项会话报告已经不再可用。';
|
|
4525
|
+
if (reportSaveMode && preview) {
|
|
4526
|
+
help = 'Enter 保存 · Ctrl-U 清空输入 · Esc 返回报告预览';
|
|
4527
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(InputCard, { title: "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84", focused: true, compact: policy.mode === 'compact', children: _jsx(InputDisplay, { value: reportSavePath, placeholder: "/tmp/aw-operation-report.md", columns: Math.max(12, size.columns - 10) }) }), reportSaveError ? _jsx(Callout, { status: "error", title: "\u62A5\u544A\u672A\u4FDD\u5B58", description: reportSaveError, compact: policy.mode === 'compact' }) : null, _jsx(StaticRow, { title: "\u4EC5\u4FDD\u5B58\u5F53\u524D\u9884\u89C8\u4E2D\u7684\u5B89\u5168\u5185\u5BB9\u3002", status: "muted", compact: true })] });
|
|
4528
|
+
}
|
|
4529
|
+
else if (preview) {
|
|
4530
|
+
actions = [
|
|
4531
|
+
{ label: '选择本地保存位置…', description: '保存前输入明确的文件路径', run: () => {
|
|
4532
|
+
setReportSaveMode(true);
|
|
4533
|
+
setReportSaveError(undefined);
|
|
4534
|
+
setReportSaveResult(undefined);
|
|
4535
|
+
} },
|
|
4536
|
+
{ label: '保存到临时目录', description: '使用唯一的临时文件名保存当前安全报告', run: () => {
|
|
4537
|
+
void props.controller.saveOperationReport(route.recordId).then(() => {
|
|
4538
|
+
setReportSaveResult('报告已保存到临时目录。');
|
|
4539
|
+
setReportSaveError(undefined);
|
|
4540
|
+
}).catch(() => {
|
|
4541
|
+
setReportSaveError('报告未保存。请检查保存位置后重试。');
|
|
4542
|
+
});
|
|
4543
|
+
} },
|
|
4544
|
+
{ label: '返回技术详情', run: back },
|
|
4545
|
+
];
|
|
4546
|
+
const reportLineLimit = policy.mode === 'compact' ? 2 : Math.max(1, pageActionBudget(5));
|
|
4547
|
+
const reportActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(5));
|
|
4548
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(OperationReportPreviewScreen, { preview: preview, compact: policy.mode === 'compact', offset: scrollOffset, limit: reportLineLimit }), reportSaveResult ? _jsx(Callout, { status: "success", title: "\u62A5\u544A\u5DF2\u4FDD\u5B58", description: reportSaveResult, compact: policy.mode === 'compact' }) : null, reportSaveError ? _jsx(Callout, { status: "error", title: "\u62A5\u544A\u672A\u4FDD\u5B58", description: reportSaveError, compact: policy.mode === 'compact' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: reportActionViewport.offset, limit: reportActionViewport.limit })] });
|
|
4549
|
+
}
|
|
4550
|
+
else {
|
|
4551
|
+
actions = [{ label: '返回技术详情', run: back }];
|
|
4552
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "warning", title: "\u62A5\u544A\u5DF2\u4E0D\u53EF\u7528", description: "\u8FD4\u56DE\u6280\u672F\u8BE6\u60C5\u540E\u53EF\u67E5\u770B\u4ECD\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u7684\u8BB0\u5F55\u3002" }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
3809
4555
|
else if (route.name === 'reviewPreparing' && snapshot.reviewPreparation) {
|
|
3810
4556
|
const preparation = snapshot.reviewPreparation;
|
|
3811
|
-
const labels = preparation.
|
|
4557
|
+
const labels = preparation.operationKind === 'portableInstall'
|
|
3812
4558
|
? ['通用 Skills / 安装', '正在准备通用 Skills 安装确认']
|
|
3813
|
-
: preparation.
|
|
4559
|
+
: preparation.operationKind === 'configSave' || preparation.operationKind === 'configReset'
|
|
3814
4560
|
? ['设置', '正在准备设置确认']
|
|
3815
|
-
: preparation.
|
|
4561
|
+
: preparation.operationKind === 'sourceMutation' || preparation.operationKind === 'catalogGeneration'
|
|
3816
4562
|
? ['插件来源', '正在准备来源变更确认']
|
|
3817
4563
|
: ['插件操作', '正在准备插件操作确认'];
|
|
3818
4564
|
breadcrumb = `${labels[0]} / 准备确认`;
|
|
3819
4565
|
context = '只读';
|
|
3820
4566
|
title = preparation.state === 'failed' ? '无法准备确认信息' : labels[1];
|
|
3821
|
-
const refreshPreparation = preparation.
|
|
3822
|
-
&& preparation.
|
|
4567
|
+
const refreshPreparation = preparation.operationKind === 'pluginWrite'
|
|
4568
|
+
&& preparation.stages.some((stage) => stage.id === 'refresh');
|
|
3823
4569
|
description = preparation.state === 'failed'
|
|
3824
4570
|
? preparation.message ?? '无法准备确认信息。请返回检查选择,或重试。'
|
|
3825
4571
|
: refreshPreparation
|
|
3826
|
-
? '
|
|
4572
|
+
? '先刷新相关来源的当前状态,再读取并验证新的同步计划;不会修改插件安装或设置。'
|
|
3827
4573
|
: '这个阶段只读取和验证状态,不会修改任何安装或设置。';
|
|
3828
4574
|
help = preparation.state === 'running'
|
|
3829
4575
|
? 'Esc 取消 · Ctrl-C 完成后退出'
|
|
@@ -3837,11 +4583,13 @@ export function ConsoleAppV4(props) {
|
|
|
3837
4583
|
{ label: '返回修改', run: back },
|
|
3838
4584
|
] : [];
|
|
3839
4585
|
const stageState = (state) => state === 'running' ? 'active' : state === 'completed' ? 'complete' : state === 'failed' ? 'error' : 'pending';
|
|
3840
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u51C6\u5907\u6B65\u9AA4", children: preparation.stages.map((stage) => _jsx(ProgressRow, { title: stage.label, state: stageState(stage.state), meta: stage.state === 'running' ? '进行中' : stage.state === 'completed' ? '完成' : stage.state === 'failed' ? '失败' : '未开始', compact: policy.mode === 'compact' }, stage.id)) }), preparation.
|
|
3841
|
-
? _jsx(Callout, { status: "error", title:
|
|
4586
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u51C6\u5907\u6B65\u9AA4", children: preparation.stages.map((stage) => _jsx(ProgressRow, { title: stage.label, state: stageState(stage.state), meta: stage.state === 'running' ? '进行中' : stage.state === 'completed' ? '完成' : stage.state === 'failed' ? '失败' : '未开始', compact: policy.mode === 'compact' }, stage.id)) }), preparation.subject ? _jsx(StaticRow, { title: "\u5F53\u524D", meta: preparation.subject, status: "muted" }) : null, preparation.code && preparation.message
|
|
4587
|
+
? _jsx(Callout, { status: "error", title: "\u786E\u8BA4\u4FE1\u606F\u672A\u51C6\u5907\u5B8C\u6210", description: preparation.message }) : null, showTechnical && preparation.code
|
|
4588
|
+
? _jsx(TechnicalDetails, { children: _jsx(StaticRow, { title: "failure code", meta: preparation.code, compact: true }) }) : null, actions.length > 0 ? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' }) : null] });
|
|
3842
4589
|
}
|
|
3843
4590
|
else if (route.name === 'review' && snapshot.activeReview) {
|
|
3844
4591
|
const review = snapshot.activeReview;
|
|
4592
|
+
const batchReview = review.operationKind === 'pluginWriteBatch';
|
|
3845
4593
|
const sharedImpactConfirmed = !review.requiresConfirmation
|
|
3846
4594
|
|| confirmedSharedReviewToken === review.reviewToken;
|
|
3847
4595
|
breadcrumb = `${review.subject.name} / ${review.title}`;
|
|
@@ -3849,17 +4597,21 @@ export function ConsoleAppV4(props) {
|
|
|
3849
4597
|
description = snapshot.result?.state === 'stale'
|
|
3850
4598
|
? '当前状态已变化。请检查新的变化后再次确认。'
|
|
3851
4599
|
: review.requiresConfirmation ? '' : review.description;
|
|
4600
|
+
if (batchReview) {
|
|
4601
|
+
context = `${review.batch?.requestedPluginIds.length ?? 0} 个根插件 · ${review.batch?.groupCount ?? 0} 组`;
|
|
4602
|
+
help = 'PgUp/PgDn 查看全部根插件和依赖 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4603
|
+
}
|
|
3852
4604
|
actions = [
|
|
3853
4605
|
...(review.requiresConfirmation ? [{
|
|
3854
|
-
label: sharedImpactConfirmed ? '
|
|
3855
|
-
description: review.sharedImpact.warning ?? '
|
|
4606
|
+
label: sharedImpactConfirmed ? '已确认共享影响' : '确认共享影响',
|
|
4607
|
+
description: review.sharedImpact.warning ?? '此操作会修改多个插件共同使用的 Codex 平台内容',
|
|
3856
4608
|
selected: sharedImpactConfirmed,
|
|
3857
4609
|
run: () => {
|
|
3858
4610
|
setConfirmedSharedReviewToken(review.reviewToken);
|
|
3859
4611
|
setFocus(1);
|
|
3860
4612
|
},
|
|
3861
4613
|
}] : []),
|
|
3862
|
-
{ label: review.primaryLabel, description: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, disabled: sharedImpactConfirmed ? undefined : '
|
|
4614
|
+
{ label: review.primaryLabel, description: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, disabled: sharedImpactConfirmed ? undefined : '请先确认共享影响', run: () => {
|
|
3863
4615
|
if (operationBusy.current)
|
|
3864
4616
|
return;
|
|
3865
4617
|
operationBusy.current = true;
|
|
@@ -3870,6 +4622,23 @@ export function ConsoleAppV4(props) {
|
|
|
3870
4622
|
operationBusy.current = false;
|
|
3871
4623
|
});
|
|
3872
4624
|
} },
|
|
4625
|
+
...(sharedImpactConfirmed && (pendingReviewIntent?.kind === 'pluginWrite'
|
|
4626
|
+
&& (pendingReviewIntent.action === 'install'
|
|
4627
|
+
|| pendingReviewIntent.action === 'refreshAndSync'
|
|
4628
|
+
|| pendingReviewIntent.action === 'reinstall'
|
|
4629
|
+
|| pendingReviewIntent.action === 'sync')
|
|
4630
|
+
|| pendingReviewIntent?.kind === 'pluginWriteBatch'
|
|
4631
|
+
&& (pendingReviewIntent.action === 'install'
|
|
4632
|
+
|| pendingReviewIntent.action === 'sync'
|
|
4633
|
+
|| pendingReviewIntent.action === 'reinstall')
|
|
4634
|
+
|| pendingReviewIntent?.kind === 'configSave' && pendingReviewIntent.apply) ? [{
|
|
4635
|
+
label: '高级选项:脚本超时',
|
|
4636
|
+
description: `${pendingReviewIntent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS} 毫秒`,
|
|
4637
|
+
run: () => {
|
|
4638
|
+
back();
|
|
4639
|
+
go({ name: 'scriptTimeout', owner: review.reviewToken });
|
|
4640
|
+
},
|
|
4641
|
+
}] : []),
|
|
3873
4642
|
{ label: '返回修改', run: review.operationKind === 'portableInstall' && snapshot.portableDraft
|
|
3874
4643
|
? async () => {
|
|
3875
4644
|
if (await refreshPortableDraft(snapshot.portableDraft.pluginId))
|
|
@@ -3882,13 +4651,13 @@ export function ConsoleAppV4(props) {
|
|
|
3882
4651
|
const groupTitle = {
|
|
3883
4652
|
requested: review.operationKind === 'portableInstall' ? '已选择 Skills · 当前插件' : '你选择的插件',
|
|
3884
4653
|
dependencies: review.operationKind === 'portableInstall' ? '已选择 Skills · 来自依赖插件' : '必需依赖',
|
|
3885
|
-
projectIntent: '
|
|
3886
|
-
userIntent: '
|
|
3887
|
-
sharedCodexNative: '
|
|
3888
|
-
userActivation: '
|
|
3889
|
-
projectActivation: '
|
|
3890
|
-
awExtensions: 'AW
|
|
3891
|
-
runtime: '
|
|
4654
|
+
projectIntent: '当前项目使用设置',
|
|
4655
|
+
userIntent: '所有项目使用设置',
|
|
4656
|
+
sharedCodexNative: 'Codex 平台共享内容',
|
|
4657
|
+
userActivation: '所有项目启用位置',
|
|
4658
|
+
projectActivation: '当前项目启用位置',
|
|
4659
|
+
awExtensions: 'AW 管理附加能力',
|
|
4660
|
+
runtime: '执行设置',
|
|
3892
4661
|
portableTarget: '安装目标',
|
|
3893
4662
|
portableInputs: '准备内容',
|
|
3894
4663
|
portableWarnings: '覆盖冲突',
|
|
@@ -3934,7 +4703,7 @@ export function ConsoleAppV4(props) {
|
|
|
3934
4703
|
allocations.set(group.key, current + added);
|
|
3935
4704
|
remainingRows -= added;
|
|
3936
4705
|
}
|
|
3937
|
-
const
|
|
4706
|
+
const standardChangeGroups = visibleSpecs.map((group) => {
|
|
3938
4707
|
const visibleCount = allocations.get(group.key) ?? 1;
|
|
3939
4708
|
const hiddenCount = group.changes.length - visibleCount;
|
|
3940
4709
|
return _jsxs(Box, { flexDirection: "column", children: [!compressedReview && group.label ? _jsx(Text, { bold: true, color: tuiTheme.colors.muted, children: group.label }) : null, group.changes.slice(0, visibleCount).map((change, index) => {
|
|
@@ -3952,12 +4721,23 @@ export function ConsoleAppV4(props) {
|
|
|
3952
4721
|
return _jsx(ReviewChangeRow, { title: `${category}${reviewSymbol(change)} ${change.title}${description ? ` · ${description}` : ''}`, meta: count, status: changeStatus(change), compact: true }, `${change.title}:${index}`);
|
|
3953
4722
|
})] }, group.key);
|
|
3954
4723
|
});
|
|
4724
|
+
const batchChangeLimit = Math.max(1, responsiveBudget(6, 10) - actions.length);
|
|
4725
|
+
const batchChanges = review.changes.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
4726
|
+
const batchHiddenBefore = batchTargetOffset;
|
|
4727
|
+
const prioritizedReviewChanges = [
|
|
4728
|
+
...review.changes.filter((change) => change.kind === 'warning'),
|
|
4729
|
+
...review.changes.filter((change) => change.kind !== 'warning'),
|
|
4730
|
+
];
|
|
4731
|
+
const pagedReviewChanges = prioritizedReviewChanges.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
4732
|
+
const changeGroups = batchReview || review.changes.length > staticRowBudget
|
|
4733
|
+
? _jsxs(Box, { flexDirection: "column", children: [batchHiddenBefore > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${batchHiddenBefore} 项`, status: "muted", compact: true }) : null, (batchReview ? batchChanges : pagedReviewChanges).map((change, index) => _jsx(ReviewChangeRow, { title: `${change.sourceId ? `${change.sourceId} · ` : ''}${change.relation === 'requested' ? '根插件' : change.relation === 'direct' ? '直接依赖' : change.relation === 'transitive' ? '间接依赖' : ''} ${reviewSymbol(change)} ${change.title}`, meta: change.description, status: changeStatus(change), compact: true }, `${change.batchGroupId ?? ''}:${change.pluginId ?? change.title}:${index}`)), Math.max(0, review.changes.length - batchTargetOffset - (batchReview ? batchChanges : pagedReviewChanges).length) > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${Math.max(0, review.changes.length - batchTargetOffset - (batchReview ? batchChanges : pagedReviewChanges).length)} 项`, status: "muted", compact: true }) : null] })
|
|
4734
|
+
: standardChangeGroups;
|
|
3955
4735
|
const technicalDetails = review.technicalDetails ?? [];
|
|
3956
4736
|
const technicalDetailLimit = responsiveBudget(1, 3);
|
|
3957
|
-
const visibleTechnicalDetails = technicalDetails.slice(
|
|
3958
|
-
const hiddenTechnicalDetails = technicalDetails.length - visibleTechnicalDetails.length;
|
|
4737
|
+
const visibleTechnicalDetails = technicalDetails.slice(batchTargetOffset, batchTargetOffset + technicalDetailLimit);
|
|
4738
|
+
const hiddenTechnicalDetails = Math.max(0, technicalDetails.length - batchTargetOffset - visibleTechnicalDetails.length);
|
|
3959
4739
|
const technicalCardRows = 3 + visibleTechnicalDetails.length;
|
|
3960
|
-
const technicalChangeBudget = Math.max(1, size.rows -
|
|
4740
|
+
const technicalChangeBudget = Math.max(1, size.rows - 8 - 3 - (review.equivalentCommand ? 1 : 0) - actions.length - technicalCardRows);
|
|
3961
4741
|
const prioritizedTechnicalChanges = [
|
|
3962
4742
|
...review.changes.filter((change) => change.kind === 'warning'),
|
|
3963
4743
|
...review.changes.filter((change) => change.kind !== 'warning'
|
|
@@ -3968,15 +4748,19 @@ export function ConsoleAppV4(props) {
|
|
|
3968
4748
|
&& change.group !== 'portableTarget'
|
|
3969
4749
|
&& (change.group === undefined || !technicalGroups.has(change.group))),
|
|
3970
4750
|
];
|
|
3971
|
-
const visibleTechnicalChanges = prioritizedTechnicalChanges.slice(
|
|
3972
|
-
const hiddenTechnicalChanges = review.changes.length - visibleTechnicalChanges.length;
|
|
4751
|
+
const visibleTechnicalChanges = prioritizedTechnicalChanges.slice(batchTargetOffset, batchTargetOffset + technicalChangeBudget);
|
|
4752
|
+
const hiddenTechnicalChanges = Math.max(0, review.changes.length - batchTargetOffset - visibleTechnicalChanges.length);
|
|
3973
4753
|
const technicalChanges = visibleTechnicalChanges.map((change, index) => {
|
|
3974
4754
|
const category = change.group ? groupTitle[change.group] : undefined;
|
|
3975
4755
|
return _jsx(ReviewChangeRow, { title: `${category ? `${category} · ` : ''}${reviewSymbol(change)} ${change.title}${change.description ? ` · ${change.description}` : ''}`, meta: index === visibleTechnicalChanges.length - 1 && hiddenTechnicalChanges > 0
|
|
3976
4756
|
? `另有 ${hiddenTechnicalChanges} 项` : undefined, status: changeStatus(change), compact: true }, `${change.title}:${index}`);
|
|
3977
4757
|
});
|
|
3978
|
-
const showEquivalentCommand = Boolean(review.equivalentCommand && showTechnical);
|
|
3979
|
-
|
|
4758
|
+
const showEquivalentCommand = Boolean(review.equivalentCommand && showTechnical && !review.requiresConfirmation);
|
|
4759
|
+
if (review.changes.length > staticRowBudget || showTechnical
|
|
4760
|
+
&& (review.changes.length > technicalChangeBudget || technicalDetails.length > technicalDetailLimit)) {
|
|
4761
|
+
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4762
|
+
}
|
|
4763
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: review.requiresConfirmation ? undefined : review.subject.name, meta: review.requiresConfirmation ? undefined : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : 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, review.requiresConfirmation ? _jsx(StaticRow, { title: "! \u9700\u8981\u786E\u8BA4\u5171\u4EAB\u5F71\u54CD", meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" }), showTechnical ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, detail)) }) : null] }) });
|
|
3980
4764
|
}
|
|
3981
4765
|
else if (route.name === 'progress') {
|
|
3982
4766
|
const portableOperation = snapshot.activeReview?.operationKind === 'portableInstall';
|
|
@@ -3992,18 +4776,18 @@ export function ConsoleAppV4(props) {
|
|
|
3992
4776
|
const catalogGenerationOperation = snapshot.activeReview?.operationKind === 'catalogGeneration';
|
|
3993
4777
|
const catalogSourcesRecovery = snapshot.activeReview?.operationKind === 'catalogSourcesRecovery';
|
|
3994
4778
|
title = portableOperation ? '正在安装通用 Skills' : issueOperation ? '正在处理问题'
|
|
3995
|
-
: catalogGenerationOperation ? '
|
|
3996
|
-
: catalogSourcesRecovery ? '
|
|
4779
|
+
: catalogGenerationOperation ? '正在验证并启用目录版本'
|
|
4780
|
+
: catalogSourcesRecovery ? '正在恢复插件来源配置'
|
|
3997
4781
|
: sourceOperation ? '正在保存来源配置'
|
|
3998
4782
|
: configOperation ? configApply ? '正在保存并应用设置' : '正在保存设置'
|
|
3999
4783
|
: '正在执行插件操作';
|
|
4000
|
-
description = issueOperation ? '
|
|
4001
|
-
: catalogGenerationOperation ? '
|
|
4784
|
+
description = issueOperation ? '完成后会重新读取状态并检查问题。'
|
|
4785
|
+
: catalogGenerationOperation ? '正在验证并应用已确认的目录版本操作。'
|
|
4002
4786
|
: catalogSourcesRecovery ? '正在备份损坏配置并恢复官方默认来源。'
|
|
4003
4787
|
: sourceOperation ? '只原子保存来源配置,不访问网络。'
|
|
4004
4788
|
: configOperation ? configApply ? '先保存所选层,再重新应用当前可见安装位置。' : '只保存所选设置层。'
|
|
4005
4789
|
: '请勿关闭当前终端。';
|
|
4006
|
-
|
|
4790
|
+
const batchProgress = snapshot.activeReview?.operationKind === 'pluginWriteBatch';
|
|
4007
4791
|
const progress = snapshot.progress;
|
|
4008
4792
|
const elapsedSeconds = Math.max(0, Math.floor(((progress.elapsedMs
|
|
4009
4793
|
?? (progress.startedAtMs === undefined ? 0 : Date.now() - progress.startedAtMs))) / 1_000));
|
|
@@ -4053,8 +4837,11 @@ export function ConsoleAppV4(props) {
|
|
|
4053
4837
|
: 0;
|
|
4054
4838
|
const progressTargetBudget = progressTargetAndMessageBudget - dependencyMessageLimit;
|
|
4055
4839
|
const progressTargetLimit = prefixRowLimit(prioritizedProgressTargets.map(() => policy.mode === 'compact' ? 1 : 2), progressTargetBudget);
|
|
4056
|
-
|
|
4057
|
-
|
|
4840
|
+
help = prioritizedProgressTargets.length > progressTargetLimit
|
|
4841
|
+
? 'PgUp/PgDn 查看全部目标 · Ctrl-C 完成后退出' : 'Ctrl-C 完成后退出';
|
|
4842
|
+
const progressTargetOffset = Math.min(batchTargetOffset, Math.max(0, prioritizedProgressTargets.length - 1));
|
|
4843
|
+
const visibleProgressTargets = prioritizedProgressTargets.slice(progressTargetOffset, progressTargetOffset + progressTargetLimit);
|
|
4844
|
+
const hiddenProgressTargets = Math.max(0, snapshot.progressTargets.length - progressTargetOffset - visibleProgressTargets.length);
|
|
4058
4845
|
const targetStates = snapshot.progressTargets.map((entry) => entry.state);
|
|
4059
4846
|
const executionComplete = targetStates.length > 0
|
|
4060
4847
|
&& targetStates.every((state) => state === 'completed' || state === 'kept');
|
|
@@ -4102,13 +4889,13 @@ export function ConsoleAppV4(props) {
|
|
|
4102
4889
|
return step === 2 ? '正在安装' : step === 3 ? '正在验证' : step === 1 ? '正在生成' : '正在检查';
|
|
4103
4890
|
};
|
|
4104
4891
|
const compactPortableProgress = policy.mode === 'compact' && showTechnical;
|
|
4105
|
-
content = portableOperation ? _jsxs(StatusCard, { status: failedProgress ? 'error' : 'info', title: "Skills CLI", children: [_jsx(ProgressRow, { title: "\u68C0\u67E5\u63D2\u4EF6\u548C\u4F9D\u8D56", description: `${snapshot.activeReview?.subject.name ?? '当前插件'} · ${portableDraft?.dependencySummary?.directCount ?? 0} 个直接依赖`, meta: portableProgressMeta(0), state: portableProgressState(0), compact: compactPortableProgress }), _jsx(ProgressRow, { title: "\u751F\u6210\u5DF2\u9009\u5019\u9009 Skills", description: `${portableProviderCount}
|
|
4892
|
+
content = portableOperation ? _jsxs(StatusCard, { status: failedProgress ? 'error' : 'info', title: "Skills CLI", children: [_jsx(ProgressRow, { title: "\u68C0\u67E5\u63D2\u4EF6\u548C\u4F9D\u8D56", description: `${snapshot.activeReview?.subject.name ?? '当前插件'} · ${portableDraft?.dependencySummary?.directCount ?? 0} 个直接依赖`, meta: portableProgressMeta(0), state: portableProgressState(0), compact: compactPortableProgress }), _jsx(ProgressRow, { title: "\u751F\u6210\u5DF2\u9009\u5019\u9009 Skills", description: `${portableProviderCount} 个来源或依赖插件提供 ${portableSelectedCount} 个 Skills`, meta: portableProgressMeta(1), state: portableProgressState(1), compact: compactPortableProgress }), _jsx(ProgressRow, { title: `安装已选择的 ${portableSelectedCount} 个 Skills`, description: `Agent:${portableDraft?.agentMode === 'explicit' ? portableDraft.requestedAgents.join('、') : 'Skills CLI 默认选择'}`, meta: portableProgressMeta(2), state: portableProgressState(2), compact: compactPortableProgress }), _jsx(ProgressRow, { title: "\u9A8C\u8BC1\u7ED3\u679C", description: "\u9010\u9879\u786E\u8BA4\u6240\u9009 Skill \u7684\u5B89\u88C5\u76EE\u6807", meta: portableProgressMeta(3), state: portableProgressState(3), compact: compactPortableProgress }), showTechnical ? _jsxs(TechnicalDetails, { children: [technicalMessages.map((message, index) => _jsx(StaticRow, { title: message, compact: true }, `${index}:${message}`)), hiddenTechnicalMessages > 0 ? _jsx(StaticRow, { title: `另有 ${hiddenTechnicalMessages} 条技术消息`, status: "muted", compact: true }) : null] }) : null] }) : _jsxs(StatusCard, { status: "info", children: [dependencyProgress ? _jsxs(_Fragment, { children: [_jsx(ProgressRow, { title: "\u68C0\u67E5\u4F9D\u8D56\u5173\u7CFB", state: relationStep }), _jsx(ProgressRow, { title: "\u751F\u6210\u6267\u884C\u8BA1\u5212", state: planStep }), _jsx(ProgressRow, { title: "\u6267\u884C\u4F9D\u8D56\u95ED\u5305", description: progress.current?.target ? `当前:${progress.current.target}` : undefined, meta: `${elapsedSeconds}s`, state: closureStep, compact: policy.mode === 'compact' }), _jsx(ProgressRow, { title: "\u9A8C\u8BC1\u5B8C\u6574\u7ED3\u679C", state: verificationStep })] }) : _jsxs(_Fragment, { children: [_jsx(ProgressRow, { title: sourceOperation ? '检查来源操作输入' : configOperation ? '验证设置值' : '检查兼容性', state: !currentPhase ? 'pending' : preflightActive ? 'active' : 'complete' }), _jsx(ProgressRow, { title: progress.current?.phase || (sourceOperation ? '保存来源配置' : configOperation ? '保存设置层并重新应用' : '连接到所选目标平台'), description: progress.current?.target ? `当前:${progress.current.target}` : undefined, meta: `${elapsedSeconds}s`, state: failedProgress ? 'error' : executionActive ? 'active' : validationActive ? 'complete' : 'pending', compact: policy.mode === 'compact' }), _jsx(ProgressRow, { title: "\u9A8C\u8BC1\u7ED3\u679C", state: failedProgress ? 'error' : validationActive ? 'active' : 'pending' })] }), dependencyProgress ? _jsxs(SectionCard, { title: "\u6267\u884C\u76EE\u6807", meta: `${snapshot.progressTargets.length} 个目标`, children: [progressTargetOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${progressTargetOffset} 个目标`, status: "muted", compact: true }) : null, visibleProgressTargets.map((entry) => {
|
|
4106
4893
|
const state = progressLabel(entry);
|
|
4107
4894
|
const relation = entry.relation === 'requested' ? '你选择的插件' : entry.relation === 'direct' ? '直接依赖' : '间接依赖';
|
|
4108
4895
|
const title = props.controller.getPluginOverview(entry.pluginId)?.displayName
|
|
4109
4896
|
?? entry.pluginId.split('/').at(-1);
|
|
4110
4897
|
return _jsx(ResultRow, { title: title, description: relation, meta: state.label, status: state.status ?? 'muted', compact: policy.mode === 'compact' }, `${entry.pluginId}:${entry.platform}:${entry.scope}`);
|
|
4111
|
-
}), hiddenProgressTargets > 0 ? _jsx(StaticRow, { title:
|
|
4898
|
+
}), hiddenProgressTargets > 0 ? _jsx(StaticRow, { title: `${batchProgress ? '↓ 后面还有' : '另有'} ${hiddenProgressTargets} 个目标`, status: "muted", compact: true }) : null] }) : null, !showTechnical && latestMessage && (!dependencyProgress || dependencyMessageLimit > 0) ? _jsx(StaticRow, { title: latestMessage, status: "muted", compact: true }) : null, showTechnical ? (dependencyProgress
|
|
4112
4899
|
? dependencyMessageLimit > 0 ? technicalMessages.slice(-dependencyMessageLimit) : []
|
|
4113
4900
|
: technicalMessages).map((message, index) => _jsx(StaticRow, { title: message, compact: true }, `${index}:${message}`)) : null, exitAfterBusy ? _jsx(Callout, { status: "warning", title: "\u64CD\u4F5C\u5B8C\u6210\u540E\u9000\u51FA" }) : null] });
|
|
4114
4901
|
}
|
|
@@ -4122,6 +4909,7 @@ export function ConsoleAppV4(props) {
|
|
|
4122
4909
|
: result.state === 'blocked' ? result.summary : undefined;
|
|
4123
4910
|
const action = resultSummary?.action;
|
|
4124
4911
|
const operationKind = 'operationKind' in result ? result.operationKind : undefined;
|
|
4912
|
+
const batchOperation = operationKind === 'pluginWriteBatch';
|
|
4125
4913
|
const portableOperation = operationKind === 'portableInstall';
|
|
4126
4914
|
const portableReceipt = result.state === 'succeeded' || result.state === 'partial'
|
|
4127
4915
|
? result.summary.portable : undefined;
|
|
@@ -4144,68 +4932,79 @@ export function ConsoleAppV4(props) {
|
|
|
4144
4932
|
cleanup: '✓ 插件清理完成',
|
|
4145
4933
|
sync: '✓ 插件同步完成',
|
|
4146
4934
|
}[action ?? 'install'];
|
|
4147
|
-
title =
|
|
4148
|
-
? blocked ? '×
|
|
4149
|
-
: partial ? '!
|
|
4150
|
-
:
|
|
4151
|
-
? '×
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
:
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4935
|
+
title = batchOperation
|
|
4936
|
+
? blocked ? '× 批量操作没有完成'
|
|
4937
|
+
: partial ? '! 批量操作只完成了一部分' : `✓ 批量${batchActionLabel(action)}完成`
|
|
4938
|
+
: portableOperation
|
|
4939
|
+
? blocked ? '× 通用 Skills 安装被阻止'
|
|
4940
|
+
: partial ? '! 通用 Skills 结果未完全完成' : '✓ 通用 Skills 已安装'
|
|
4941
|
+
: blocked && result.writeMayHaveCompleted
|
|
4942
|
+
? '× 写入结果未确认'
|
|
4943
|
+
: issueOperation
|
|
4944
|
+
? blocked || partial ? '× 问题仍未解决' : '✓ 问题已处理'
|
|
4945
|
+
: sourceOperation
|
|
4946
|
+
? blocked ? '× 来源操作未完成'
|
|
4947
|
+
: operationKind === 'catalogSourcesRecovery' ? '✓ Catalog 来源配置已恢复'
|
|
4948
|
+
: resultSummary?.catalogAction === 'update'
|
|
4949
|
+
? resultSummary.catalogGeneration?.activeChanged
|
|
4950
|
+
&& resultSummary.catalogGeneration.outcome === 'activated'
|
|
4951
|
+
? '✓ Catalog 已更新' : '✓ Catalog 无需更新'
|
|
4952
|
+
: resultSummary?.catalogAction === 'repair' ? '✓ Catalog 已修复'
|
|
4953
|
+
: resultSummary?.catalogAction === 'replaceDamaged' ? '✓ 损坏的 Catalog 已替换'
|
|
4954
|
+
: resultSummary?.catalogAction === 'recoverState' ? '✓ Catalog 状态已恢复'
|
|
4955
|
+
: operationKind === 'catalogGeneration' ? '✓ Catalog 已启用'
|
|
4956
|
+
: '✓ 来源配置已保存'
|
|
4957
|
+
: configOperation
|
|
4958
|
+
? blocked
|
|
4959
|
+
? '× 设置未确认保存'
|
|
4960
|
+
: partial ? resultSummary?.configApplySkippedReason === 'invalidConfig'
|
|
4961
|
+
? '! 设置已保存,但未应用'
|
|
4962
|
+
: '! 设置已保存,但部分应用失败'
|
|
4963
|
+
: resultSummary?.configApplied ? '✓ 设置已保存并应用' : '✓ 设置已保存'
|
|
4964
|
+
: partial ? '! 插件操作只完成了一部分'
|
|
4965
|
+
: blocked ? result.code === 'operationFailed' ? '× 插件操作没有完成' : '× 插件操作未完成'
|
|
4966
|
+
: completedTitle;
|
|
4967
|
+
description = batchOperation
|
|
4177
4968
|
? blocked
|
|
4178
|
-
?
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
?
|
|
4182
|
-
:
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
:
|
|
4969
|
+
? '批量写入被阻止。已完成和未完成目标按实际结果保留。'
|
|
4970
|
+
: partial
|
|
4971
|
+
? (resultSummary?.batch?.retryTargets?.length ?? 0) > 0
|
|
4972
|
+
? `${resultSummary?.batch?.retryTargets?.length ?? 0} 个根目标失败。成功目标不会重放。`
|
|
4973
|
+
: '失败目标无法精确定位。请查看本次记录后重新操作。'
|
|
4974
|
+
: `${resultSummary?.batch?.requestedPluginIds.length ?? 0} 个根插件已完成。`
|
|
4975
|
+
: portableOperation
|
|
4976
|
+
? blocked
|
|
4977
|
+
? result.code === 'operationReviewExpired' || result.code === 'reviewTicketExpired'
|
|
4978
|
+
? 'Review 已过期,写入未开始。请返回修改并重新确认。'
|
|
4979
|
+
: result.code === 'operationReviewStale'
|
|
4980
|
+
? '安装证据已变化,写入被阻止。请返回修改并重新确认。'
|
|
4981
|
+
: result.writeMayHaveCompleted
|
|
4982
|
+
? '写入结果无法确认。请检查外部 Skills 状态后重试。'
|
|
4983
|
+
: '安装在写入前被阻止。请返回修改后重试。'
|
|
4984
|
+
: portableInstallFailed ? 'Skills CLI 安装失败,可能已有部分目标写入。可以修改 Agent 或使用原选择重试。' : partial ? '无法确认全部已选择 Skills。' : '所选 Skills 已通过安装验证。'
|
|
4985
|
+
: blocked && result.writeMayHaveCompleted
|
|
4986
|
+
? result.message
|
|
4987
|
+
: issueOperation
|
|
4197
4988
|
? blocked
|
|
4198
|
-
? result.
|
|
4199
|
-
? '
|
|
4200
|
-
: '
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4989
|
+
? result.code === 'issueVerificationFailed'
|
|
4990
|
+
? '写入可能已完成,但当前无法确认。请重新检查状态。'
|
|
4991
|
+
: result.code === 'issueStillPresent'
|
|
4992
|
+
? '写入已完成,但重新检查后原问题仍然存在。'
|
|
4993
|
+
: '操作被安全阻止。可返回问题详情后重试。'
|
|
4994
|
+
: '状态已重新加载;原问题已确认消失。'
|
|
4995
|
+
: sourceOperation
|
|
4996
|
+
? blocked
|
|
4997
|
+
? result.writeMayHaveCompleted
|
|
4998
|
+
? '来源配置可能已保存。请重新检查来源列表。'
|
|
4999
|
+
: '来源操作未执行。草稿已保留,可返回修改。'
|
|
5000
|
+
: partial ? resultSummary?.message ?? '来源配置已保存,但 Catalog 尚未启用。可立即重试。'
|
|
5001
|
+
: resultSummary?.message ?? '来源配置已保存。'
|
|
5002
|
+
: blocked ? result.message
|
|
5003
|
+
: configOperation
|
|
5004
|
+
? partial
|
|
5005
|
+
? resultSummary?.message ?? '设置层已经保存;平台应用失败不会回滚设置。'
|
|
5006
|
+
: '设置层已经更新。'
|
|
5007
|
+
: '状态已刷新。';
|
|
4209
5008
|
const pluginId = resultSummary?.pluginId
|
|
4210
5009
|
?? ('pluginId' in result ? result.pluginId : undefined);
|
|
4211
5010
|
const configResultPluginId = pluginId ?? ('returnRoute' in result
|
|
@@ -4254,7 +5053,8 @@ export function ConsoleAppV4(props) {
|
|
|
4254
5053
|
: undefined;
|
|
4255
5054
|
const retryAction = failedPluginId && failedTarget && failedTargetState
|
|
4256
5055
|
? selectLifecycleRetryAction({
|
|
4257
|
-
operationKind: operationKind === 'catalogSourcesRecovery'
|
|
5056
|
+
operationKind: operationKind === 'catalogSourcesRecovery' || operationKind === 'pluginWriteBatch'
|
|
5057
|
+
? undefined : operationKind,
|
|
4258
5058
|
originalAction: action,
|
|
4259
5059
|
target: {
|
|
4260
5060
|
pluginId: failedPluginId,
|
|
@@ -4274,131 +5074,146 @@ export function ConsoleAppV4(props) {
|
|
|
4274
5074
|
&& (candidate.platform ?? failedIssue.platform) === failedTarget?.platform
|
|
4275
5075
|
&& (candidate.scope ?? failedIssue.scope) === failedTarget?.scope)
|
|
4276
5076
|
: undefined;
|
|
4277
|
-
actions =
|
|
4278
|
-
?
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
5077
|
+
actions = batchOperation
|
|
5078
|
+
? [
|
|
5079
|
+
...((partial || blocked) && (resultSummary?.batch?.retryTargets?.length ?? 0) > 0 ? [{
|
|
5080
|
+
label: '仅重试失败目标…',
|
|
5081
|
+
description: '重新读取失败根目标及其依赖,并进入新的确认页',
|
|
5082
|
+
run: () => {
|
|
5083
|
+
props.controller.retryFailedPluginWriteBatch();
|
|
5084
|
+
setFocus(0);
|
|
5085
|
+
setScrollOffset(0);
|
|
5086
|
+
},
|
|
5087
|
+
}] : []),
|
|
5088
|
+
{ label: '返回我的插件', run: () => leaveResult() },
|
|
5089
|
+
{ label: '查看本次记录', run: () => result.operationRecordId
|
|
5090
|
+
&& go({ name: 'operationDiagnostic', recordId: result.operationRecordId }) },
|
|
5091
|
+
]
|
|
5092
|
+
: portableOperation
|
|
5093
|
+
? blocked || partial ? [
|
|
5094
|
+
{ label: blocked || partial ? '返回修改' : portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: async () => {
|
|
5095
|
+
if (!pluginId) {
|
|
5096
|
+
leaveResult();
|
|
5097
|
+
return;
|
|
5098
|
+
}
|
|
5099
|
+
if (!blocked && !partial) {
|
|
5100
|
+
props.controller.discardPortableDraft();
|
|
5101
|
+
leaveResult();
|
|
5102
|
+
return;
|
|
5103
|
+
}
|
|
5104
|
+
if (!await refreshPortableDraft(pluginId))
|
|
5105
|
+
return;
|
|
5106
|
+
if (portableInstallFailed && portableReceipt?.agentMode === 'explicit') {
|
|
5107
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableAgentInput', pluginId }, true);
|
|
5108
|
+
return;
|
|
5109
|
+
}
|
|
5110
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
|
|
5111
|
+
} },
|
|
5112
|
+
{ label: blocked || partial ? '重试' : '再安装其他 Skills…', run: async () => {
|
|
5113
|
+
if (!pluginId)
|
|
5114
|
+
return;
|
|
5115
|
+
if (blocked || partial) {
|
|
5116
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
|
|
5117
|
+
await beginPortableReview(pluginId, snapshot.portableDraft?.scope ?? 'project');
|
|
5118
|
+
return;
|
|
5119
|
+
}
|
|
5120
|
+
props.controller.resetPortableFlowToPreview(pluginId);
|
|
5121
|
+
await beginPortable(pluginId);
|
|
5122
|
+
} },
|
|
5123
|
+
{ label: portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: () => { if (pluginId)
|
|
5124
|
+
props.controller.resetPortableFlowToPreview(pluginId); } },
|
|
5125
|
+
] : [
|
|
5126
|
+
{ label: portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: () => {
|
|
4285
5127
|
props.controller.discardPortableDraft();
|
|
4286
5128
|
leaveResult();
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
{ label: blocked || partial ? '重试' : '再安装其他 Skills…', run: async () => {
|
|
4298
|
-
if (!pluginId)
|
|
4299
|
-
return;
|
|
4300
|
-
if (blocked || partial) {
|
|
4301
|
-
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
|
|
4302
|
-
await beginPortableReview(pluginId, snapshot.portableDraft?.scope ?? 'project');
|
|
4303
|
-
return;
|
|
4304
|
-
}
|
|
4305
|
-
props.controller.resetPortableFlowToPreview(pluginId);
|
|
4306
|
-
await beginPortable(pluginId);
|
|
4307
|
-
} },
|
|
4308
|
-
{ label: portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: () => { if (pluginId)
|
|
4309
|
-
props.controller.resetPortableFlowToPreview(pluginId); } },
|
|
4310
|
-
] : [
|
|
4311
|
-
{ label: portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: () => {
|
|
4312
|
-
props.controller.discardPortableDraft();
|
|
4313
|
-
leaveResult();
|
|
4314
|
-
} },
|
|
4315
|
-
{ label: '再安装其他 Skills…', run: async () => {
|
|
4316
|
-
if (!pluginId)
|
|
4317
|
-
return;
|
|
4318
|
-
props.controller.resetPortableFlowToPreview(pluginId);
|
|
4319
|
-
await beginPortable(pluginId);
|
|
4320
|
-
} },
|
|
4321
|
-
]
|
|
4322
|
-
: issueOperation && !dependencyRecovery
|
|
4323
|
-
? blocked || partial
|
|
4324
|
-
? [
|
|
4325
|
-
{ label: '返回问题详情', run: () => blocked
|
|
4326
|
-
? leaveResult()
|
|
4327
|
-
: failedIssue
|
|
4328
|
-
? leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId })
|
|
4329
|
-
: pluginId && failedTarget
|
|
4330
|
-
? leaveResult({ name: 'platform', pluginId, platform: failedTarget.platform })
|
|
4331
|
-
: leaveResult() },
|
|
4332
|
-
{ label: '重新检查问题', run: () => leaveResult({ name: 'issueList' }) },
|
|
4333
|
-
]
|
|
4334
|
-
: [
|
|
4335
|
-
{ label: '查看问题列表', run: () => leaveResult({ name: 'issueList' }) },
|
|
4336
|
-
{ label: '返回首页', run: () => leaveResult({ name: 'home' }) },
|
|
4337
|
-
]
|
|
4338
|
-
: sourceOperation
|
|
4339
|
-
? [{ label: blocked ? '返回修改' : partial ? '现在启用 / 重试' : '查看来源', run: () => blocked ? leaveResult() : leaveResult({ name: 'sourceList' }) }, { label: '返回首页', run: () => leaveResult({ name: 'home' }) }]
|
|
4340
|
-
: configOperation && partial
|
|
5129
|
+
} },
|
|
5130
|
+
{ label: '再安装其他 Skills…', run: async () => {
|
|
5131
|
+
if (!pluginId)
|
|
5132
|
+
return;
|
|
5133
|
+
props.controller.resetPortableFlowToPreview(pluginId);
|
|
5134
|
+
await beginPortable(pluginId);
|
|
5135
|
+
} },
|
|
5136
|
+
]
|
|
5137
|
+
: issueOperation && !dependencyRecovery
|
|
5138
|
+
? blocked || partial
|
|
4341
5139
|
? [
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
{ label:
|
|
4354
|
-
{ label: '返回设置执行 unset / reset', description: '显式删除顶层字段或整个设置层', run: () => leaveResult() },
|
|
4355
|
-
{ label: '保留设置并退出', run: () => requestExit({ code: 1, reason: 'partial' }) },
|
|
5140
|
+
{ label: '返回问题详情', run: () => blocked
|
|
5141
|
+
? leaveResult()
|
|
5142
|
+
: failedIssue
|
|
5143
|
+
? leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId })
|
|
5144
|
+
: pluginId && failedTarget
|
|
5145
|
+
? leaveResult({ name: 'platform', pluginId, platform: failedTarget.platform })
|
|
5146
|
+
: leaveResult() },
|
|
5147
|
+
{ label: '重新检查问题', run: () => leaveResult({ name: 'issueList' }) },
|
|
5148
|
+
]
|
|
5149
|
+
: [
|
|
5150
|
+
{ label: '查看问题列表', run: () => leaveResult({ name: 'issueList' }) },
|
|
5151
|
+
{ label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
4356
5152
|
]
|
|
4357
|
-
|
|
5153
|
+
: sourceOperation
|
|
5154
|
+
? [{ label: blocked ? '返回修改' : partial ? '现在启用 / 重试' : '查看来源', run: () => blocked ? props.controller.resumeResultDraft() : leaveResult() }, { label: '返回首页', run: () => requestSessionNavigation('home') }]
|
|
5155
|
+
: configOperation && partial
|
|
4358
5156
|
? [
|
|
4359
|
-
|
|
4360
|
-
|
|
5157
|
+
...(configRetryAvailable ? [{
|
|
5158
|
+
label: '重试应用',
|
|
5159
|
+
description: '先重新进入 Review,并重新采集当前证据',
|
|
5160
|
+
run: () => beginReview({
|
|
5161
|
+
kind: 'configSave',
|
|
5162
|
+
pluginId: configResultPluginId,
|
|
5163
|
+
scope: resultSummary.configScope,
|
|
5164
|
+
apply: true,
|
|
5165
|
+
retryApply: true,
|
|
5166
|
+
}),
|
|
5167
|
+
}] : []),
|
|
5168
|
+
{ label: invalidConfigResult ? '返回设置' : '返回修改', description: '重新打开已保存的设置层', run: () => leaveResult() },
|
|
5169
|
+
{ label: '返回设置删除字段或重置设置层', description: '显式删除顶层字段或整个设置层', run: () => leaveResult() },
|
|
5170
|
+
{ label: '保留设置并退出', run: () => requestExit({ code: 1, reason: 'partial' }) },
|
|
4361
5171
|
]
|
|
4362
|
-
:
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
if (
|
|
4372
|
-
leaveResult(
|
|
4373
|
-
|
|
4374
|
-
|
|
5172
|
+
: configOperation && blocked
|
|
5173
|
+
? [
|
|
5174
|
+
{ label: '返回设置', run: () => props.controller.resumeResultDraft() },
|
|
5175
|
+
{ label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
5176
|
+
]
|
|
5177
|
+
: [{ label: partial || blockedDependencyFailure ? '查看失败项' : successfulConfigOperation && configOrigin
|
|
5178
|
+
? configOrigin?.name === 'pluginPreview' ? '返回插件预览' : '返回插件详情'
|
|
5179
|
+
: configOperation ? '查看插件'
|
|
5180
|
+
: action === 'uninstall' || action === 'cleanup' ? '返回插件详情' : '查看插件', run: () => {
|
|
5181
|
+
if (successfulConfigOperation && configOrigin) {
|
|
5182
|
+
leaveResult();
|
|
5183
|
+
return;
|
|
5184
|
+
}
|
|
5185
|
+
if ((partial || blockedDependencyFailure) && failedPluginId && failedTarget) {
|
|
5186
|
+
if (failedIssue)
|
|
5187
|
+
leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId });
|
|
5188
|
+
else if (failedPluginPlatforms?.includes(failedTarget.platform)) {
|
|
5189
|
+
leaveResult({ name: 'platform', pluginId: failedPluginId, platform: failedTarget.platform });
|
|
5190
|
+
}
|
|
5191
|
+
else if (snapshot.viewModel?.plugins.some((entry) => entry.pluginId === failedPluginId)) {
|
|
5192
|
+
leaveResult({ name: 'pluginDetail', pluginId: failedPluginId });
|
|
5193
|
+
}
|
|
5194
|
+
else
|
|
5195
|
+
leaveResult({ name: 'pluginList', mode: 'installed' });
|
|
4375
5196
|
}
|
|
4376
|
-
else if (snapshot.viewModel?.plugins.some((entry) => entry.pluginId ===
|
|
4377
|
-
leaveResult({ name: 'pluginDetail', pluginId
|
|
5197
|
+
else if (pluginId && snapshot.viewModel?.plugins.some((entry) => entry.pluginId === pluginId)) {
|
|
5198
|
+
leaveResult({ name: 'pluginDetail', pluginId });
|
|
4378
5199
|
}
|
|
4379
5200
|
else
|
|
4380
5201
|
leaveResult({ name: 'pluginList', mode: 'installed' });
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
pluginId: failedPluginId,
|
|
4397
|
-
platform: failedTarget.platform,
|
|
4398
|
-
scope: failedTarget.scope,
|
|
4399
|
-
});
|
|
4400
|
-
},
|
|
4401
|
-
}] : []), { label: '返回首页', run: () => leaveResult({ name: 'home' }) }];
|
|
5202
|
+
} }, ...((partial || blockedDependencyFailure) && failedPluginId && failedTarget && retryAction ? [{
|
|
5203
|
+
label: '重试失败部分…',
|
|
5204
|
+
run: () => {
|
|
5205
|
+
const issueIntent = failedIssue && retryIssueAction
|
|
5206
|
+
? issueWriteIntent(failedIssue, retryIssueAction)
|
|
5207
|
+
: undefined;
|
|
5208
|
+
return beginReview(issueIntent ?? {
|
|
5209
|
+
kind: 'pluginWrite',
|
|
5210
|
+
action: retryAction,
|
|
5211
|
+
pluginId: failedPluginId,
|
|
5212
|
+
platform: failedTarget.platform,
|
|
5213
|
+
scope: failedTarget.scope,
|
|
5214
|
+
});
|
|
5215
|
+
},
|
|
5216
|
+
}] : []), { label: '返回首页', run: () => requestSessionNavigation('home') }];
|
|
4402
5217
|
const visibleMessages = resultSummary?.messages?.slice(policy.mode === 'compact' ? -2 : -4) ?? [];
|
|
4403
5218
|
const visibleChanges = resultSummary?.changes?.slice(0, policy.mode === 'compact' ? 2 : 4) ?? [];
|
|
4404
5219
|
const resultTargetName = (target) => target.pluginId
|
|
@@ -4419,17 +5234,19 @@ export function ConsoleAppV4(props) {
|
|
|
4419
5234
|
+ visibleChanges.length
|
|
4420
5235
|
+ (partial && hasPreservedTarget && resultTargets.some((target) => target.state === 'skipped') ? 1 : 0);
|
|
4421
5236
|
const resultTargetLimit = prefixRowLimit(prioritizedTargets.map(() => policy.mode === 'compact' ? 1 : 2), pageActionBudget() - 3 - resultActionViewport.limit - resultExtraRows);
|
|
4422
|
-
const
|
|
4423
|
-
const
|
|
5237
|
+
const resultTargetOffset = Math.min(batchTargetOffset, Math.max(0, prioritizedTargets.length - 1));
|
|
5238
|
+
const visibleResultTargets = prioritizedTargets.slice(resultTargetOffset, resultTargetOffset + resultTargetLimit);
|
|
5239
|
+
const hiddenResultTargets = Math.max(0, resultTargets.length - resultTargetOffset - visibleResultTargets.length);
|
|
4424
5240
|
const portableMissingSkills = new Set(portableReceipt?.missing.map((entry) => entry.skill) ?? []);
|
|
4425
5241
|
const portableSelectedSkills = portableReceipt?.selectedSkills
|
|
4426
|
-
?? snapshot.
|
|
5242
|
+
?? snapshot.portableResultSkills
|
|
4427
5243
|
?? [];
|
|
4428
5244
|
const prioritizedPortableSkills = [...portableSelectedSkills].sort((left, right) => Number(portableMissingSkills.has(right)) - Number(portableMissingSkills.has(left)));
|
|
4429
5245
|
const portableTechnicalSummary = showTechnical && (partial || blocked);
|
|
4430
5246
|
const portableSkillLimit = showTechnical ? 1 : responsiveBudget(2, 6);
|
|
4431
|
-
const
|
|
4432
|
-
const
|
|
5247
|
+
const portableSkillOffset = Math.min(batchTargetOffset, Math.max(0, prioritizedPortableSkills.length - 1));
|
|
5248
|
+
const visiblePortableSkills = prioritizedPortableSkills.slice(portableSkillOffset, portableSkillOffset + portableSkillLimit);
|
|
5249
|
+
const hiddenPortableSkills = Math.max(0, prioritizedPortableSkills.length - portableSkillOffset - visiblePortableSkills.length);
|
|
4433
5250
|
const portableRows = visiblePortableSkills.map((skill) => {
|
|
4434
5251
|
const missing = portableMissingSkills.has(skill);
|
|
4435
5252
|
const status = blocked || portableReceipt?.verificationUnknown ? 'warning'
|
|
@@ -4457,11 +5274,15 @@ export function ConsoleAppV4(props) {
|
|
|
4457
5274
|
: resultSummary?.resetDisposition === 'quarantinedDirectory'
|
|
4458
5275
|
? `设置路径是目录;已隔离为 ${resultSummary.quarantineBasename ?? '安全隔离目录'}`
|
|
4459
5276
|
: undefined;
|
|
4460
|
-
|
|
5277
|
+
if (batchOperation || resultTargets.length > resultTargetLimit
|
|
5278
|
+
|| prioritizedPortableSkills.length > portableSkillLimit) {
|
|
5279
|
+
help = 'PgUp/PgDn 查看全部项目 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
5280
|
+
}
|
|
5281
|
+
content = portableOperation ? _jsxs(Box, { flexDirection: "column", children: [portableTechnicalSummary ? _jsxs(_Fragment, { children: [portableSkillOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${portableSkillOffset} 个 Skills`, status: "muted", compact: true }) : null, portableRows, hiddenPortableSkills > 0 ? _jsx(StaticRow, { title: `另有 ${hiddenPortableSkills} 个 Skills`, status: "muted", compact: true }) : null, portableReceipt?.recovery ? _jsx(StaticRow, { title: "\u5B89\u5168\u6062\u590D", meta: portableReceipt.recovery, status: "warning", compact: true }) : null, _jsx(TechnicalDetails, { children: compactTechnicalRows })] }) : _jsx(_Fragment, { children: _jsxs(ResultCard, { status: resultStatus, title: blocked ? '安装未开始或结果未确认' : partial ? '部分 Skills 未确认' : '通用 Skills 安装结果', children: [portableSkillOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${portableSkillOffset} 个 Skills`, status: "muted", compact: true }) : null, portableRows, hiddenPortableSkills > 0 ? _jsx(StaticRow, { title: `另有 ${hiddenPortableSkills} 个 Skills`, status: "muted", compact: true }) : null, portableReceipt ? _jsx(StaticRow, { title: `Agent:${portableReceipt.agentMode === 'automatic' ? 'Skills CLI 默认选择' : portableReceipt.requestedAgents.join('、')}`, meta: `范围:${scopeLabel(portableReceipt.scope)}`, compact: true }) : null, portableReceipt?.recovery ? policy.mode === 'compact'
|
|
4461
5282
|
? _jsx(StaticRow, { title: "\u5B89\u5168\u6062\u590D", meta: portableReceipt.recovery, status: "warning", compact: true })
|
|
4462
5283
|
: _jsx(Callout, { status: "warning", title: "\u5B89\u5168\u6062\u590D", description: portableReceipt.recovery }) : null, showTechnical && portableReceipt ? _jsxs(TechnicalDetails, { children: [portableReceipt.failureCode ? _jsx(StaticRow, { title: "failure code", meta: portableReceipt.failureCode, compact: true }) : null, _jsx(StaticRow, { title: "verification missing", meta: String(portableReceipt.missing.length), compact: true }), _jsx(StaticRow, { title: "Skills CLI version", meta: portableReceipt.skillsCliVersion, compact: true }), partial || blocked
|
|
4463
5284
|
? _jsx(StaticRow, { title: "\u53E6\u6709\u6280\u672F\u5B57\u6BB5\u5DF2\u6298\u53E0", meta: "materialization digest \u00B7 elapsed", status: "muted", compact: true })
|
|
4464
|
-
: _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "materialization digest", meta: portableReceipt.materializationDigest, compact: true }), resultSummary?.elapsedMs !== undefined ? _jsx(StaticRow, { title: "elapsed", meta: `${resultSummary.elapsedMs} ms`, compact: true }) : null] })] }) : null, showTechnical && !portableReceipt ? _jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: "failure code", meta: 'code' in result ? result.code : 'operationFailed', compact: true }), _jsx(StaticRow, { title: "\u5176\u4F59\u6280\u672F\u5B57\u6BB5", meta: "\u672C\u6B21\u5199\u5165\u6CA1\u6709\u751F\u6210\u5B89\u5168 receipt", status: "muted", compact: true })] }) : null] }) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] }) : _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: blockedDependencyFailure ? '依赖安装没有完成' : blocked ?
|
|
5285
|
+
: _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "materialization digest", meta: portableReceipt.materializationDigest, compact: true }), resultSummary?.elapsedMs !== undefined ? _jsx(StaticRow, { title: "elapsed", meta: `${resultSummary.elapsedMs} ms`, compact: true }) : null] })] }) : null, showTechnical && !portableReceipt ? _jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: "failure code", meta: 'code' in result ? result.code : 'operationFailed', compact: true }), _jsx(StaticRow, { title: "\u5176\u4F59\u6280\u672F\u5B57\u6BB5", meta: "\u672C\u6B21\u5199\u5165\u6CA1\u6709\u751F\u6210\u5B89\u5168 receipt", status: "muted", compact: true })] }) : null] }) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] }) : _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: blockedDependencyFailure ? '依赖安装没有完成' : blocked ? result.writeMayHaveCompleted ? '结果未确认' : '操作未完成' : sourceOperation ? '来源配置写入已完成' : dependencyTargets ? partial ? '依赖安装没有全部完成' : '操作已完成' : resultSummary?.message ?? '状态已变化,请重新确认', children: [resetDispositionText ? _jsx(StaticRow, { title: resetDispositionText, status: "success", compact: true }) : null, resultTargetOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${resultTargetOffset} 个目标`, status: "muted", compact: true }) : null, visibleResultTargets.map((target) => { const state = operationTargetStateLabel(target.state); const relation = target.relation === 'requested' ? '你选择的插件' : target.relation === 'direct' ? '直接依赖' : target.relation === 'transitive' ? '间接依赖' : undefined; return _jsx(ResultRow, { title: resultTargetName(target), description: relation, meta: state.label, status: state.status ?? 'muted', compact: policy.mode === 'compact' }, `${target.pluginId ?? ''}:${target.platform}:${target.scope}`); }), hiddenResultTargets > 0 ? _jsx(StaticRow, { title: "\u2193 \u540E\u9762\u8FD8\u6709", meta: `${hiddenResultTargets} 个目标`, status: "muted", compact: true }) : null, partial && hasPreservedTarget && resultTargets.some((target) => target.state === 'skipped') ? _jsx(StaticRow, { title: "\u672A\u6267\u884C\u7684\u76EE\u6807\u6CA1\u6709\u5199\u5165\uFF1B\u5DF2\u5B8C\u6210\u6216\u5DF2\u4FDD\u7559\u7684\u76EE\u6807\u4FDD\u6301\u53EF\u7528\u3002", status: "muted", compact: true }) : null, configOperation && partial && !invalidConfigResult ? _jsx(Callout, { status: "warning", title: "\u6062\u590D\u6307\u5F15", description: "\u5982\u9700\u6E05\u7406\u6B8B\u7559\uFF0C\u8BF7\u8FD4\u56DE\u63D2\u4EF6\u8BE6\u60C5\u9009\u62E9\u6E05\u7406\uFF1B\u7136\u540E\u9009\u62E9\u91CD\u65B0\u5B89\u88C5\u6216\u540C\u6B65\u3002\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u5220\u9664\u73B0\u6709\u5185\u5BB9\u3002" }) : null, resultSummary?.elapsedMs !== undefined ? _jsx(StaticRow, { title: "\u5DF2\u7528\u65F6", meta: `${Math.floor(resultSummary.elapsedMs / 1_000)}s` }) : null, visibleMessages.map((message, index) => _jsx(StaticRow, { title: productResultMessage(message), status: "muted", compact: true }, `${index}:${message}`))] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] });
|
|
4465
5286
|
}
|
|
4466
5287
|
else if (route.name === 'pluginAbout' && plugin) {
|
|
4467
5288
|
breadcrumb = `${plugin.displayName} / 关于`;
|
|
@@ -4487,17 +5308,89 @@ export function ConsoleAppV4(props) {
|
|
|
4487
5308
|
const capabilityRowCount = plugin.capabilityUnsupported ? 4 : 0;
|
|
4488
5309
|
const componentRowBudget = Math.max(1, detailRowBudget - 2 - plugin.installedPlacements.length - capabilityRowCount);
|
|
4489
5310
|
const componentLimit = prioritizedComponents.length > componentRowBudget
|
|
4490
|
-
? Math.max(1, componentRowBudget -
|
|
5311
|
+
? Math.max(1, componentRowBudget - 2)
|
|
4491
5312
|
: componentRowBudget;
|
|
4492
|
-
const
|
|
4493
|
-
const
|
|
4494
|
-
|
|
5313
|
+
const componentOffset = Math.min(batchTargetOffset, Math.max(0, prioritizedComponents.length - 1));
|
|
5314
|
+
const visibleComponents = prioritizedComponents.slice(componentOffset, componentOffset + componentLimit);
|
|
5315
|
+
const hiddenComponents = Math.max(0, prioritizedComponents.length - componentOffset - visibleComponents.length);
|
|
5316
|
+
if (prioritizedComponents.length > componentLimit) {
|
|
5317
|
+
help = 'PgUp/PgDn 查看全部组件 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
5318
|
+
}
|
|
5319
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: "canonical pluginId", meta: plugin.pluginId }), _jsx(StaticRow, { title: "\u6765\u6E90", meta: plugin.sourceId }), plugin.capabilityUnsupported ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "failure code", meta: "awCapabilityUnsupported" }), _jsx(StaticRow, { title: "capability \u72B6\u6001", meta: "\u5F53\u524D AW CLI \u4E0D\u652F\u6301", status: "error" }), _jsx(StaticRow, { title: "\u7F3A\u5C11 capability", meta: plugin.missingCapabilities?.join('、') ?? 'Status 未提供具体 capability' }), _jsx(StaticRow, { title: "\u6062\u590D\u65B9\u5411", meta: plugin.capabilityRecovery ?? '升级 AW CLI 到支持所需 capability 的版本后重试。' })] }) : null, plugin.installedPlacements.map((placement) => _jsx(StaticRow, { title: `${platformLabel(placement.platform)} target`, meta: placementStatus(placement).value }, placement.platform)), componentOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${componentOffset} 个组件`, status: "muted", compact: true }) : null, visibleComponents.map((component) => _jsx(StaticRow, { title: `组件 · ${component.state === 'absent' ? '缺失' : component.state}${component.errorCode ? ` · ${component.errorCode}` : ''} · ${component.key} · ${platformLabel(component.platform)} · ${scopeLabel(component.scope)}` }, `${component.platform}:${component.scope}:${component.key}`)), hiddenComponents > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${hiddenComponents} 个组件`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4495
5320
|
}
|
|
4496
5321
|
else {
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
5322
|
+
const missingPlugin = definition.entity === 'plugin' && plugin === undefined;
|
|
5323
|
+
const missingIssue = definition.entity === 'issue' && issue === undefined;
|
|
5324
|
+
const missingConfigField = definition.rendererFamily === 'config'
|
|
5325
|
+
&& configRoute !== undefined
|
|
5326
|
+
&& configRoute.name !== 'configFields'
|
|
5327
|
+
&& configRoute.name !== 'configSave'
|
|
5328
|
+
&& currentConfigPage !== undefined
|
|
5329
|
+
&& activeField === undefined;
|
|
5330
|
+
let unavailable;
|
|
5331
|
+
switch (route.name) {
|
|
5332
|
+
case 'pluginPreview':
|
|
5333
|
+
case 'pluginDependencies':
|
|
5334
|
+
case 'installPlacement':
|
|
5335
|
+
case 'installTargets':
|
|
5336
|
+
case 'dependencyPlacement':
|
|
5337
|
+
case 'dependencyPlacementEditor':
|
|
5338
|
+
case 'pluginDetail':
|
|
5339
|
+
case 'platform':
|
|
5340
|
+
case 'pluginAbout':
|
|
5341
|
+
case 'pluginTechnical':
|
|
5342
|
+
case 'configLayer':
|
|
5343
|
+
unavailable = missingPlugin ? 'plugin' : 'flow';
|
|
5344
|
+
break;
|
|
5345
|
+
case 'portableSkillSelect':
|
|
5346
|
+
case 'portableDependencies':
|
|
5347
|
+
case 'portableAgentMode':
|
|
5348
|
+
case 'portableAgentInput':
|
|
5349
|
+
case 'portableScope':
|
|
5350
|
+
case 'portableBlocker':
|
|
5351
|
+
unavailable = missingPlugin ? 'plugin' : 'flow';
|
|
5352
|
+
break;
|
|
5353
|
+
case 'configFields':
|
|
5354
|
+
case 'configSave':
|
|
5355
|
+
case 'configFieldAction':
|
|
5356
|
+
case 'configFieldEditor':
|
|
5357
|
+
case 'configObjectPropertyEditor':
|
|
5358
|
+
unavailable = missingPlugin ? 'plugin' : missingConfigField ? 'config' : 'flow';
|
|
5359
|
+
break;
|
|
5360
|
+
case 'issueDetail':
|
|
5361
|
+
unavailable = missingIssue ? 'issue' : 'flow';
|
|
5362
|
+
break;
|
|
5363
|
+
case 'sourceAdd':
|
|
5364
|
+
case 'sourceSave':
|
|
5365
|
+
case 'scriptTimeout':
|
|
5366
|
+
case 'reviewPreparing':
|
|
5367
|
+
case 'review':
|
|
5368
|
+
case 'result':
|
|
5369
|
+
unavailable = 'flow';
|
|
5370
|
+
break;
|
|
5371
|
+
case 'home':
|
|
5372
|
+
throw new Error(`TUI renderer invariant failed for ${route.name}`);
|
|
5373
|
+
default:
|
|
5374
|
+
unreachableTuiRoute(route);
|
|
5375
|
+
}
|
|
5376
|
+
breadcrumb = unavailable === 'plugin' ? '插件不可用'
|
|
5377
|
+
: unavailable === 'issue' ? '问题不可用'
|
|
5378
|
+
: unavailable === 'config' ? '设置已变化' : '操作已变化';
|
|
5379
|
+
title = unavailable === 'plugin' ? '这个插件已不在当前目录中'
|
|
5380
|
+
: unavailable === 'issue' ? '这个问题已经变化'
|
|
5381
|
+
: unavailable === 'config' ? '这个设置字段已不再可用' : '这个操作已结束或变化';
|
|
5382
|
+
description = '状态已变化。请返回列表并重新选择。';
|
|
5383
|
+
actions = [{
|
|
5384
|
+
label: unavailable === 'plugin' ? '返回插件列表'
|
|
5385
|
+
: unavailable === 'issue' ? '返回问题列表'
|
|
5386
|
+
: unavailable === 'config' ? '返回设置列表' : '返回首页',
|
|
5387
|
+
run: () => unavailable === 'flow'
|
|
5388
|
+
? requestSessionNavigation('home')
|
|
5389
|
+
: go(unavailable === 'plugin'
|
|
5390
|
+
? { name: 'pluginList', mode: 'installed' }
|
|
5391
|
+
: unavailable === 'issue' ? { name: 'issueList' }
|
|
5392
|
+
: { name: 'configFields', pluginId: configRoute.pluginId, layer: configRoute.layer }),
|
|
5393
|
+
}];
|
|
4501
5394
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' });
|
|
4502
5395
|
}
|
|
4503
5396
|
if (canOpenHelp && supportsTechnicalDetails) {
|
|
@@ -4508,6 +5401,11 @@ export function ConsoleAppV4(props) {
|
|
|
4508
5401
|
else if (canOpenHelp && !help.includes('? 帮助')) {
|
|
4509
5402
|
help = `? 帮助 · ${help}`;
|
|
4510
5403
|
}
|
|
5404
|
+
if (props.onSessionNavigation && !isCharacterInputMode(route, searchFocus)) {
|
|
5405
|
+
help = busy
|
|
5406
|
+
? `h/H 完成后导航 · Esc 撤销等待 · ${help}`
|
|
5407
|
+
: `h 首页 · H 重载 · ${help}`;
|
|
5408
|
+
}
|
|
4511
5409
|
const targetActionSignature = route.name === 'installTargets' && route.view !== 'blocked'
|
|
4512
5410
|
? actions.map((action) => String(Boolean(action.disabled))).join(',')
|
|
4513
5411
|
: '';
|
|
@@ -4544,13 +5442,75 @@ export function ConsoleAppV4(props) {
|
|
|
4544
5442
|
void pending.catch((error) => {
|
|
4545
5443
|
if (error instanceof Error && error.name === 'AbortError')
|
|
4546
5444
|
return;
|
|
4547
|
-
|
|
5445
|
+
showSafeUiFailure(error, 'tuiActionFailed', 'ui-action');
|
|
4548
5446
|
});
|
|
4549
5447
|
}
|
|
4550
|
-
catch {
|
|
4551
|
-
|
|
5448
|
+
catch (error) {
|
|
5449
|
+
showSafeUiFailure(error, 'tuiActionFailed', 'ui-action');
|
|
4552
5450
|
}
|
|
4553
5451
|
};
|
|
5452
|
+
function requestSessionNavigation(navigation, discardDrafts = false) {
|
|
5453
|
+
if (!props.onSessionNavigation) {
|
|
5454
|
+
if (navigation !== 'home')
|
|
5455
|
+
return;
|
|
5456
|
+
if (!props.controller.goHome({ discardDrafts })) {
|
|
5457
|
+
setDiscardDraftConfirmation(props.controller.externalDrafts);
|
|
5458
|
+
setDiscardForSessionNavigation(navigation);
|
|
5459
|
+
setOverlayFocus(0);
|
|
5460
|
+
setOverlay('sessionNavigation');
|
|
5461
|
+
return;
|
|
5462
|
+
}
|
|
5463
|
+
resetRootUiState();
|
|
5464
|
+
if (discardDrafts)
|
|
5465
|
+
clearExternalDraftValues();
|
|
5466
|
+
return;
|
|
5467
|
+
}
|
|
5468
|
+
const draftConfirmation = discardDrafts ? discardDraftConfirmation : undefined;
|
|
5469
|
+
void props.onSessionNavigation(navigation, {
|
|
5470
|
+
discardDrafts,
|
|
5471
|
+
...(draftConfirmation ? { draftConfirmation } : {}),
|
|
5472
|
+
}).then((result) => {
|
|
5473
|
+
if (result === 'draftBlocked') {
|
|
5474
|
+
setDiscardDraftConfirmation(props.controller.externalDrafts);
|
|
5475
|
+
setDiscardForSessionNavigation(navigation);
|
|
5476
|
+
setOverlayFocus(0);
|
|
5477
|
+
setOverlay('sessionNavigation');
|
|
5478
|
+
return;
|
|
5479
|
+
}
|
|
5480
|
+
if (result === 'completed') {
|
|
5481
|
+
if (navigation === 'home')
|
|
5482
|
+
resetRootUiState();
|
|
5483
|
+
if (discardDrafts)
|
|
5484
|
+
clearExternalDraftValues();
|
|
5485
|
+
setDiscardDraftConfirmation(undefined);
|
|
5486
|
+
}
|
|
5487
|
+
}).catch((error) => showSafeUiFailure(error, 'tuiSessionNavigationFailed', 'session-navigation'));
|
|
5488
|
+
}
|
|
5489
|
+
function clearExternalDraftValues() {
|
|
5490
|
+
setDirty(false);
|
|
5491
|
+
setDraftUpdates({});
|
|
5492
|
+
setDraftRemoves([]);
|
|
5493
|
+
setObjectDrafts({});
|
|
5494
|
+
setChangedObjectDraftKeys([]);
|
|
5495
|
+
setObjectError(undefined);
|
|
5496
|
+
setSourceDraft(undefined);
|
|
5497
|
+
setBatchDraft(undefined);
|
|
5498
|
+
}
|
|
5499
|
+
function continueEditingFromSessionNavigation() {
|
|
5500
|
+
if (props.pendingSessionNavigation)
|
|
5501
|
+
props.onCancelPendingSessionNavigation?.();
|
|
5502
|
+
setOverlay(undefined);
|
|
5503
|
+
setDiscardForSessionNavigation(undefined);
|
|
5504
|
+
setDiscardDraftConfirmation(undefined);
|
|
5505
|
+
}
|
|
5506
|
+
useEffect(() => {
|
|
5507
|
+
if (!props.pendingSessionNavigation || !props.pendingSessionNavigationDrafts)
|
|
5508
|
+
return;
|
|
5509
|
+
setDiscardForSessionNavigation(props.pendingSessionNavigation);
|
|
5510
|
+
setDiscardDraftConfirmation(props.pendingSessionNavigationDrafts);
|
|
5511
|
+
setOverlayFocus(0);
|
|
5512
|
+
setOverlay('sessionNavigation');
|
|
5513
|
+
}, [props.pendingSessionNavigation, props.pendingSessionNavigationDrafts]);
|
|
4554
5514
|
useInput((input, key) => {
|
|
4555
5515
|
if (!overlay && route.name === 'reviewPreparing' && snapshot.reviewPreparation?.state === 'failed'
|
|
4556
5516
|
&& (key.escape || input.toLowerCase() === 'b')) {
|
|
@@ -4558,7 +5518,11 @@ export function ConsoleAppV4(props) {
|
|
|
4558
5518
|
return;
|
|
4559
5519
|
}
|
|
4560
5520
|
if (preparationRunning) {
|
|
4561
|
-
|
|
5521
|
+
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: false });
|
|
5522
|
+
if (sessionNavigation) {
|
|
5523
|
+
requestSessionNavigation(sessionNavigation);
|
|
5524
|
+
}
|
|
5525
|
+
else if (key.escape || input.toLowerCase() === 'b') {
|
|
4562
5526
|
back();
|
|
4563
5527
|
}
|
|
4564
5528
|
else if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
@@ -4567,7 +5531,13 @@ export function ConsoleAppV4(props) {
|
|
|
4567
5531
|
return;
|
|
4568
5532
|
}
|
|
4569
5533
|
if (busy) {
|
|
4570
|
-
|
|
5534
|
+
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: false });
|
|
5535
|
+
if ((key.pageUp || key.pageDown) && route.name === 'progress') {
|
|
5536
|
+
const total = snapshot.progressTargets.length;
|
|
5537
|
+
const pageRows = 1;
|
|
5538
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5539
|
+
}
|
|
5540
|
+
else if (overlay === 'help') {
|
|
4571
5541
|
if (key.escape || key.return || input.toLowerCase() === 'b' || input === '?') {
|
|
4572
5542
|
setOverlay(undefined);
|
|
4573
5543
|
}
|
|
@@ -4575,6 +5545,12 @@ export function ConsoleAppV4(props) {
|
|
|
4575
5545
|
setExitAfterBusy(true);
|
|
4576
5546
|
}
|
|
4577
5547
|
}
|
|
5548
|
+
else if (key.escape && props.pendingSessionNavigation) {
|
|
5549
|
+
props.onCancelPendingSessionNavigation?.();
|
|
5550
|
+
}
|
|
5551
|
+
else if (sessionNavigation) {
|
|
5552
|
+
requestSessionNavigation(sessionNavigation);
|
|
5553
|
+
}
|
|
4578
5554
|
else if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
4579
5555
|
setExitAfterBusy(true);
|
|
4580
5556
|
}
|
|
@@ -4583,7 +5559,7 @@ export function ConsoleAppV4(props) {
|
|
|
4583
5559
|
setOverlay('help');
|
|
4584
5560
|
}
|
|
4585
5561
|
else if (!tooSmall && supportsTechnicalDetails && input.toLowerCase() === 'd') {
|
|
4586
|
-
|
|
5562
|
+
toggleTechnical();
|
|
4587
5563
|
}
|
|
4588
5564
|
return;
|
|
4589
5565
|
}
|
|
@@ -4598,25 +5574,49 @@ export function ConsoleAppV4(props) {
|
|
|
4598
5574
|
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
4599
5575
|
return;
|
|
4600
5576
|
}
|
|
4601
|
-
if (
|
|
5577
|
+
if (overlay === 'help' && input.toLowerCase() === 'o') {
|
|
4602
5578
|
setOverlay(undefined);
|
|
5579
|
+
go({ name: 'operationHistory' });
|
|
5580
|
+
return;
|
|
5581
|
+
}
|
|
5582
|
+
if (key.escape || input.toLowerCase() === 'b' || input === '?') {
|
|
5583
|
+
if (overlay === 'sessionNavigation') {
|
|
5584
|
+
continueEditingFromSessionNavigation();
|
|
5585
|
+
}
|
|
5586
|
+
else
|
|
5587
|
+
setOverlay(undefined);
|
|
4603
5588
|
return;
|
|
4604
5589
|
}
|
|
4605
5590
|
if (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow) {
|
|
4606
|
-
|
|
4607
|
-
|
|
5591
|
+
const count = overlay === 'help' && props.onSessionNavigation ? 3 : overlay === 'help' ? 1 : 2;
|
|
5592
|
+
const direction = key.leftArrow || key.upArrow ? -1 : 1;
|
|
5593
|
+
setOverlayFocus((value) => moveIndex(count, value, direction));
|
|
4608
5594
|
return;
|
|
4609
5595
|
}
|
|
4610
5596
|
if (key.return) {
|
|
4611
|
-
if (overlay === 'help'
|
|
5597
|
+
if (overlay === 'help') {
|
|
4612
5598
|
setOverlay(undefined);
|
|
5599
|
+
if (props.onSessionNavigation && overlayFocus === 1)
|
|
5600
|
+
requestSessionNavigation('home');
|
|
5601
|
+
else if (props.onSessionNavigation && overlayFocus === 2)
|
|
5602
|
+
requestSessionNavigation('reload');
|
|
5603
|
+
}
|
|
5604
|
+
else if (overlayFocus === 0) {
|
|
5605
|
+
if (overlay === 'sessionNavigation')
|
|
5606
|
+
continueEditingFromSessionNavigation();
|
|
5607
|
+
else
|
|
5608
|
+
setOverlay(undefined);
|
|
5609
|
+
}
|
|
4613
5610
|
else if (overlay === 'discard') {
|
|
4614
5611
|
setOverlay(undefined);
|
|
4615
5612
|
setDirty(false);
|
|
4616
5613
|
setDraftUpdates({});
|
|
4617
5614
|
setDraftRemoves([]);
|
|
5615
|
+
setObjectDrafts({});
|
|
5616
|
+
setChangedObjectDraftKeys([]);
|
|
4618
5617
|
const discardedSourceDraft = sourceDraft;
|
|
4619
5618
|
setSourceDraft(undefined);
|
|
5619
|
+
setBatchDraft(undefined);
|
|
4620
5620
|
if (discardForExit) {
|
|
4621
5621
|
requestExit({ code: 0, reason: 'user' });
|
|
4622
5622
|
}
|
|
@@ -4629,6 +5629,12 @@ export function ConsoleAppV4(props) {
|
|
|
4629
5629
|
back();
|
|
4630
5630
|
}
|
|
4631
5631
|
}
|
|
5632
|
+
else if (overlay === 'sessionNavigation' && discardForSessionNavigation) {
|
|
5633
|
+
const navigation = discardForSessionNavigation;
|
|
5634
|
+
setOverlay(undefined);
|
|
5635
|
+
setDiscardForSessionNavigation(undefined);
|
|
5636
|
+
requestSessionNavigation(navigation, true);
|
|
5637
|
+
}
|
|
4632
5638
|
else
|
|
4633
5639
|
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
4634
5640
|
}
|
|
@@ -4638,6 +5644,111 @@ export function ConsoleAppV4(props) {
|
|
|
4638
5644
|
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
4639
5645
|
return;
|
|
4640
5646
|
}
|
|
5647
|
+
if (route.name === 'operationReportPreview' && reportSaveMode) {
|
|
5648
|
+
if (key.escape) {
|
|
5649
|
+
setReportSaveMode(false);
|
|
5650
|
+
setReportSaveError(undefined);
|
|
5651
|
+
return;
|
|
5652
|
+
}
|
|
5653
|
+
if (key.return) {
|
|
5654
|
+
const savePath = reportSavePath.trim();
|
|
5655
|
+
if (!savePath) {
|
|
5656
|
+
setReportSaveError('请输入要保存到的本地文件路径。');
|
|
5657
|
+
return;
|
|
5658
|
+
}
|
|
5659
|
+
void props.controller.saveOperationReport(route.recordId, savePath).then(() => {
|
|
5660
|
+
setReportSaveMode(false);
|
|
5661
|
+
setReportSaveResult('报告已保存到你选择的位置。');
|
|
5662
|
+
setReportSaveError(undefined);
|
|
5663
|
+
}).catch(() => {
|
|
5664
|
+
setReportSaveMode(false);
|
|
5665
|
+
setReportSaveError('报告未保存。请检查保存位置后重试。');
|
|
5666
|
+
});
|
|
5667
|
+
return;
|
|
5668
|
+
}
|
|
5669
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)) {
|
|
5670
|
+
setReportSavePath('');
|
|
5671
|
+
setReportSaveError(undefined);
|
|
5672
|
+
return;
|
|
5673
|
+
}
|
|
5674
|
+
if (key.backspace || key.delete)
|
|
5675
|
+
setReportSavePath(removeLastGrapheme);
|
|
5676
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
5677
|
+
setReportSavePath((value) => value + input);
|
|
5678
|
+
setReportSaveError(undefined);
|
|
5679
|
+
return;
|
|
5680
|
+
}
|
|
5681
|
+
if ((key.pageUp || key.pageDown)
|
|
5682
|
+
&& route.name === 'review'
|
|
5683
|
+
&& snapshot.activeReview) {
|
|
5684
|
+
const total = Math.max(snapshot.activeReview.changes.length, showTechnical ? snapshot.activeReview.technicalDetails?.length ?? 0 : 0);
|
|
5685
|
+
const pageRows = 1;
|
|
5686
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5687
|
+
return;
|
|
5688
|
+
}
|
|
5689
|
+
if ((key.pageUp || key.pageDown)
|
|
5690
|
+
&& route.name === 'result'
|
|
5691
|
+
&& snapshot.result) {
|
|
5692
|
+
const total = 'summary' in snapshot.result
|
|
5693
|
+
? Math.max(snapshot.result.summary?.targets?.length ?? 0, snapshot.result.operationKind === 'portableInstall'
|
|
5694
|
+
? snapshot.portableResultSkills?.length ?? 0
|
|
5695
|
+
: 0)
|
|
5696
|
+
: 0;
|
|
5697
|
+
const pageRows = 1;
|
|
5698
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5699
|
+
return;
|
|
5700
|
+
}
|
|
5701
|
+
if ((key.pageUp || key.pageDown) && route.name === 'pluginTechnical' && plugin) {
|
|
5702
|
+
const total = plugin.technicalComponents.length;
|
|
5703
|
+
const pageRows = 1;
|
|
5704
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5705
|
+
return;
|
|
5706
|
+
}
|
|
5707
|
+
if ((key.pageUp || key.pageDown) && route.name === 'issueDetail' && issue && showTechnical) {
|
|
5708
|
+
const total = diagnosticDetailRowCount(issue.diagnostic);
|
|
5709
|
+
const pageRows = responsiveBudget(5, 10);
|
|
5710
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5711
|
+
return;
|
|
5712
|
+
}
|
|
5713
|
+
if ((key.pageUp || key.pageDown) && route.name === 'sourceTechnical') {
|
|
5714
|
+
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
5715
|
+
const diagnostic = sourceDiagnostic(route.sourceId, overview.health?.state ?? overview.source?.state, overview.detail, props.controller.getCatalogGeneration(route.sourceId), props.controller.getCatalogFreshness(route.sourceId));
|
|
5716
|
+
const total = diagnostic ? diagnosticDetailRowCount(diagnostic) : 0;
|
|
5717
|
+
const pageRows = responsiveBudget(5, 10);
|
|
5718
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5719
|
+
return;
|
|
5720
|
+
}
|
|
5721
|
+
if ((key.pageUp || key.pageDown) && route.name === 'sourceRemoveBlocked'
|
|
5722
|
+
&& sourceDependenciesExpanded) {
|
|
5723
|
+
const total = sourceReferenceSummary?.dependencies.length ?? 0;
|
|
5724
|
+
const pageRows = Math.max(1, pageActionBudget(10));
|
|
5725
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5726
|
+
return;
|
|
5727
|
+
}
|
|
5728
|
+
if ((key.pageUp || key.pageDown) && route.name === 'pluginDependencies' && plugin) {
|
|
5729
|
+
const overview = dependencyOverview(plugin);
|
|
5730
|
+
const dependents = route.view === 'removeBlocked' && route.platform && route.scope
|
|
5731
|
+
? props.controller.getBlockingDependents(plugin.pluginId, route.platform, route.scope)
|
|
5732
|
+
: overview?.dependents ?? [];
|
|
5733
|
+
const total = route.view === 'removeBlocked'
|
|
5734
|
+
? dependents.length
|
|
5735
|
+
: (overview?.blockers.length ?? 0) + (overview?.dependencies.length ?? 0) + dependents.length;
|
|
5736
|
+
const pageRows = 1;
|
|
5737
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5738
|
+
return;
|
|
5739
|
+
}
|
|
5740
|
+
if ((route.name === 'operationDiagnostic' || route.name === 'operationReportPreview')
|
|
5741
|
+
&& (key.pageUp || key.pageDown)) {
|
|
5742
|
+
const detail = route.name === 'operationDiagnostic'
|
|
5743
|
+
? props.controller.getOperationDiagnostic(route.recordId) : undefined;
|
|
5744
|
+
const preview = route.name === 'operationReportPreview'
|
|
5745
|
+
? props.controller.previewOperationReport(route.recordId) : undefined;
|
|
5746
|
+
const total = detail ? operationDiagnosticLineCount(detail)
|
|
5747
|
+
: preview ? operationReportLineCount(preview) : 0;
|
|
5748
|
+
const pageRows = policy.mode === 'compact' ? 2 : Math.max(1, pageActionBudget(5));
|
|
5749
|
+
setScrollOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
5750
|
+
return;
|
|
5751
|
+
}
|
|
4641
5752
|
if (route.name === 'portableAgentInput') {
|
|
4642
5753
|
if (key.escape) {
|
|
4643
5754
|
back();
|
|
@@ -4656,6 +5767,11 @@ export function ConsoleAppV4(props) {
|
|
|
4656
5767
|
}
|
|
4657
5768
|
return;
|
|
4658
5769
|
}
|
|
5770
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)) {
|
|
5771
|
+
setPortableAgentInput('');
|
|
5772
|
+
setInputError(undefined);
|
|
5773
|
+
return;
|
|
5774
|
+
}
|
|
4659
5775
|
if (key.backspace || key.delete)
|
|
4660
5776
|
setPortableAgentInput(removeLastGrapheme);
|
|
4661
5777
|
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
@@ -4663,14 +5779,43 @@ export function ConsoleAppV4(props) {
|
|
|
4663
5779
|
setInputError(undefined);
|
|
4664
5780
|
return;
|
|
4665
5781
|
}
|
|
5782
|
+
if (route.name === 'scriptTimeout') {
|
|
5783
|
+
if (key.escape) {
|
|
5784
|
+
const intent = pendingReviewIntentRef.current;
|
|
5785
|
+
back();
|
|
5786
|
+
if (intent)
|
|
5787
|
+
beginReview(intent);
|
|
5788
|
+
return;
|
|
5789
|
+
}
|
|
5790
|
+
const pendingIntent = pendingReviewIntentRef.current;
|
|
5791
|
+
if (key.return && pendingIntent) {
|
|
5792
|
+
try {
|
|
5793
|
+
const scriptTimeoutMs = parseScriptTimeoutMs(scriptTimeoutInput);
|
|
5794
|
+
const intent = { ...pendingIntent, scriptTimeoutMs };
|
|
5795
|
+
setScriptTimeoutError(undefined);
|
|
5796
|
+
back();
|
|
5797
|
+
beginReview(intent);
|
|
5798
|
+
}
|
|
5799
|
+
catch (error) {
|
|
5800
|
+
setScriptTimeoutError(error instanceof Error ? error.message : '脚本超时无效。');
|
|
5801
|
+
}
|
|
5802
|
+
return;
|
|
5803
|
+
}
|
|
5804
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl))
|
|
5805
|
+
setScriptTimeoutInput('');
|
|
5806
|
+
else if (key.backspace || key.delete)
|
|
5807
|
+
setScriptTimeoutInput(removeLastGrapheme);
|
|
5808
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
5809
|
+
setScriptTimeoutInput((value) => value + input);
|
|
5810
|
+
setScriptTimeoutError(undefined);
|
|
5811
|
+
return;
|
|
5812
|
+
}
|
|
4666
5813
|
if (route.name === 'sourceAdd'
|
|
4667
5814
|
&& route.step !== 'type'
|
|
4668
5815
|
&& route.step !== 'refPolicy'
|
|
4669
5816
|
&& sourceDraft?.owner === route.owner) {
|
|
4670
5817
|
if (key.escape) {
|
|
4671
|
-
|
|
4672
|
-
setDiscardForExit(false);
|
|
4673
|
-
setOverlay('discard');
|
|
5818
|
+
back();
|
|
4674
5819
|
return;
|
|
4675
5820
|
}
|
|
4676
5821
|
if (key.return) {
|
|
@@ -4715,35 +5860,35 @@ export function ConsoleAppV4(props) {
|
|
|
4715
5860
|
next.sourceId = sourceInput;
|
|
4716
5861
|
else if (route.step === 'location')
|
|
4717
5862
|
next.location = sourceInput;
|
|
4718
|
-
else if (route.step === 'ref')
|
|
5863
|
+
else if (route.step === 'ref') {
|
|
5864
|
+
next.refPolicy = 'explicit';
|
|
4719
5865
|
next.ref = sourceInput;
|
|
5866
|
+
}
|
|
4720
5867
|
else
|
|
4721
5868
|
next.subdir = sourceInput === '' ? '' : path.posix.normalize(sourceInput).replace(/^\.\//u, '').replace(/\/$/u, '');
|
|
4722
5869
|
setSourceDraft(next);
|
|
5870
|
+
setDirty(true);
|
|
4723
5871
|
setSourceInputError(undefined);
|
|
4724
|
-
if (route.
|
|
5872
|
+
if (route.returnToDraft)
|
|
5873
|
+
acceptSourceFieldAndBack();
|
|
5874
|
+
else if (route.step === 'id')
|
|
4725
5875
|
go({ name: 'sourceAdd', step: 'location', owner: route.owner });
|
|
4726
5876
|
else if (route.step === 'location') {
|
|
4727
|
-
if (next.
|
|
4728
|
-
void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
|
|
4729
|
-
else if (next.type === 'git')
|
|
5877
|
+
if (next.type === 'git')
|
|
4730
5878
|
go({ name: 'sourceAdd', step: 'refPolicy', owner: route.owner });
|
|
4731
5879
|
else
|
|
4732
|
-
|
|
5880
|
+
go({ name: 'sourceEdit', sourceId: next.sourceId, owner: route.owner });
|
|
4733
5881
|
}
|
|
4734
5882
|
else if (route.step === 'ref') {
|
|
4735
|
-
|
|
4736
|
-
void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
|
|
4737
|
-
else
|
|
4738
|
-
go({ name: 'sourceAdd', step: 'subdir', owner: route.owner });
|
|
5883
|
+
go({ name: 'sourceAdd', step: 'subdir', owner: route.owner });
|
|
4739
5884
|
}
|
|
4740
|
-
else if (next.mode === 'update')
|
|
4741
|
-
void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
|
|
4742
5885
|
else
|
|
4743
|
-
|
|
5886
|
+
go({ name: 'sourceEdit', sourceId: next.sourceId, owner: route.owner });
|
|
4744
5887
|
return;
|
|
4745
5888
|
}
|
|
4746
|
-
if (
|
|
5889
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl))
|
|
5890
|
+
setSourceInput('');
|
|
5891
|
+
else if (key.backspace || key.delete)
|
|
4747
5892
|
setSourceInput(removeLastGrapheme);
|
|
4748
5893
|
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
4749
5894
|
setSourceInput((value) => value + input);
|
|
@@ -4758,8 +5903,8 @@ export function ConsoleAppV4(props) {
|
|
|
4758
5903
|
return;
|
|
4759
5904
|
}
|
|
4760
5905
|
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)) {
|
|
4761
|
-
|
|
4762
|
-
|
|
5906
|
+
setInputValue('');
|
|
5907
|
+
setInputError(undefined);
|
|
4763
5908
|
return;
|
|
4764
5909
|
}
|
|
4765
5910
|
if (key.return && activeField) {
|
|
@@ -4780,7 +5925,9 @@ export function ConsoleAppV4(props) {
|
|
|
4780
5925
|
back();
|
|
4781
5926
|
return;
|
|
4782
5927
|
}
|
|
4783
|
-
if (
|
|
5928
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl))
|
|
5929
|
+
setInputValue('');
|
|
5930
|
+
else if (key.backspace || key.delete)
|
|
4784
5931
|
setInputValue(removeLastGrapheme);
|
|
4785
5932
|
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
4786
5933
|
setInputValue((value) => value + input);
|
|
@@ -4795,6 +5942,11 @@ export function ConsoleAppV4(props) {
|
|
|
4795
5942
|
back();
|
|
4796
5943
|
return;
|
|
4797
5944
|
}
|
|
5945
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)) {
|
|
5946
|
+
setInputValue('');
|
|
5947
|
+
setInputError(undefined);
|
|
5948
|
+
return;
|
|
5949
|
+
}
|
|
4798
5950
|
if (key.return && route.stage === 'key') {
|
|
4799
5951
|
const propertyKey = inputValue;
|
|
4800
5952
|
if (Object.hasOwn(activeObjectDraft, propertyKey)
|
|
@@ -4880,7 +6032,9 @@ export function ConsoleAppV4(props) {
|
|
|
4880
6032
|
setScrollOffset(0);
|
|
4881
6033
|
return;
|
|
4882
6034
|
}
|
|
4883
|
-
if (
|
|
6035
|
+
if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl))
|
|
6036
|
+
setQuery('');
|
|
6037
|
+
else if (key.backspace || key.delete)
|
|
4884
6038
|
setQuery(removeLastGrapheme);
|
|
4885
6039
|
else if (input && !key.ctrl && !key.meta && input >= ' ')
|
|
4886
6040
|
setQuery((value) => value + input);
|
|
@@ -4894,26 +6048,50 @@ export function ConsoleAppV4(props) {
|
|
|
4894
6048
|
|| route.name === 'configObjectPropertyEditor')) {
|
|
4895
6049
|
return;
|
|
4896
6050
|
}
|
|
4897
|
-
|
|
6051
|
+
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: isCharacterInputMode(route, searchFocus) });
|
|
6052
|
+
const globalAction = resolveTuiGlobalKeyAction({ input, key }, { mode: 'menu', allowBack: true });
|
|
6053
|
+
if (sessionNavigation) {
|
|
6054
|
+
requestSessionNavigation(sessionNavigation);
|
|
6055
|
+
}
|
|
6056
|
+
else if (route.name === 'sourceAdd' && key.escape && route.step !== 'type') {
|
|
6057
|
+
back();
|
|
6058
|
+
}
|
|
6059
|
+
else if (input.toLowerCase() === 'o'
|
|
6060
|
+
&& (route.name === 'home'
|
|
6061
|
+
|| route.name === 'issueList'
|
|
6062
|
+
|| route.name === 'issueDetail'
|
|
6063
|
+
|| route.name === 'result')) {
|
|
6064
|
+
go({ name: 'operationHistory' });
|
|
6065
|
+
}
|
|
6066
|
+
else if (canOpenHelp && globalAction === 'help') {
|
|
4898
6067
|
setOverlayFocus(0);
|
|
4899
6068
|
setOverlay('help');
|
|
4900
6069
|
}
|
|
4901
|
-
else if (
|
|
6070
|
+
else if (globalAction === 'exit') {
|
|
4902
6071
|
setOverlayFocus(0);
|
|
4903
6072
|
setDiscardForExit(dirty);
|
|
4904
6073
|
setOverlay(dirty ? 'discard' : 'exit');
|
|
4905
6074
|
}
|
|
4906
6075
|
else if (input.toLowerCase() === 'd' && supportsTechnicalDetails) {
|
|
4907
|
-
|
|
6076
|
+
toggleTechnical();
|
|
4908
6077
|
}
|
|
4909
|
-
else if (
|
|
6078
|
+
else if (globalAction === 'back'
|
|
4910
6079
|
&& (route.name === 'configFieldEditor'
|
|
4911
6080
|
|| route.name === 'configObjectPropertyEditor'
|
|
4912
6081
|
|| route.name === 'configFieldAction')) {
|
|
4913
6082
|
back();
|
|
4914
6083
|
}
|
|
4915
|
-
else if (
|
|
4916
|
-
|
|
6084
|
+
else if (globalAction === 'back' && route.name === 'batchPlacement') {
|
|
6085
|
+
back();
|
|
6086
|
+
}
|
|
6087
|
+
else if (globalAction === 'back'
|
|
6088
|
+
&& (route.name === 'operationHistory'
|
|
6089
|
+
|| route.name === 'operationDiagnostic'
|
|
6090
|
+
|| route.name === 'operationReportPreview')) {
|
|
6091
|
+
back();
|
|
6092
|
+
}
|
|
6093
|
+
else if (globalAction === 'back') {
|
|
6094
|
+
if (dirty && route.name !== 'review') {
|
|
4917
6095
|
setOverlayFocus(0);
|
|
4918
6096
|
setDiscardForExit(false);
|
|
4919
6097
|
setOverlay('discard');
|
|
@@ -4941,15 +6119,53 @@ export function ConsoleAppV4(props) {
|
|
|
4941
6119
|
setFocus(retained >= 0 ? retained : 0);
|
|
4942
6120
|
setScrollOffset(0);
|
|
4943
6121
|
}
|
|
6122
|
+
else if (route.name === 'pluginList' && input.toLowerCase() === 'm') {
|
|
6123
|
+
openBatchSelection(route.mode === 'browse' ? 'install' : undefined);
|
|
6124
|
+
}
|
|
6125
|
+
else if (route.name === 'pluginList' && route.mode === 'installed' && input.toLowerCase() === 'a') {
|
|
6126
|
+
openBatchSelection();
|
|
6127
|
+
setBatchDraft({
|
|
6128
|
+
action: 'sync',
|
|
6129
|
+
selection: 'currentProjectIntentRoots',
|
|
6130
|
+
selectedPluginIds: [],
|
|
6131
|
+
platform: 'both',
|
|
6132
|
+
scope: 'project',
|
|
6133
|
+
});
|
|
6134
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
6135
|
+
}
|
|
6136
|
+
else if (route.name === 'batchSelection' && batchDraft?.action && input.toLowerCase() === 'a') {
|
|
6137
|
+
const candidates = props.controller.listPlugins({
|
|
6138
|
+
mode: batchDraft.action === 'install' ? 'browse' : 'installed',
|
|
6139
|
+
}).filter((candidate) => !candidate.configOnly);
|
|
6140
|
+
setBatchDraft((current) => current ? {
|
|
6141
|
+
...current,
|
|
6142
|
+
selectedPluginIds: current.selectedPluginIds.length === candidates.length
|
|
6143
|
+
? [] : candidates.map((candidate) => candidate.pluginId),
|
|
6144
|
+
} : current);
|
|
6145
|
+
}
|
|
6146
|
+
else if (route.name === 'batchSelection' && batchDraft?.action && input.toLowerCase() === 'c') {
|
|
6147
|
+
if (batchDraft.selectedPluginIds.length > 0) {
|
|
6148
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
6149
|
+
setFocus(0);
|
|
6150
|
+
setScrollOffset(0);
|
|
6151
|
+
}
|
|
6152
|
+
else
|
|
6153
|
+
setActionError('请至少选择一个插件。');
|
|
6154
|
+
}
|
|
6155
|
+
else if (route.name === 'batchSelection' && batchDraft?.action && input === ' ') {
|
|
6156
|
+
runAction(actions[effectiveFocus]);
|
|
6157
|
+
}
|
|
4944
6158
|
else if (route.name === 'configFields' && route.layer.scope !== 'effective' && input.toLowerCase() === 's') {
|
|
4945
|
-
if (dirty)
|
|
4946
|
-
|
|
6159
|
+
if (dirty && configDraftIssues.length === 0) {
|
|
6160
|
+
setFocus(0);
|
|
6161
|
+
go({ name: 'configSave', pluginId: route.pluginId, layer: { scope: route.layer.scope } });
|
|
6162
|
+
}
|
|
4947
6163
|
}
|
|
4948
6164
|
else if (route.name === 'configFields' && route.layer.scope !== 'effective' && input.toLowerCase() === 'u') {
|
|
4949
6165
|
const key = actions[focus]?.configKey;
|
|
4950
6166
|
const field = currentConfigPage?.groups.flatMap((group) => group.fields)
|
|
4951
6167
|
.find((candidate) => candidate.key === key);
|
|
4952
|
-
if (field
|
|
6168
|
+
if (field && configFieldCanUnset(field) && (!field.invalidOverrideIssue || field.invalidOverrideOwnedByLayer)) {
|
|
4953
6169
|
removeDraft(field.key);
|
|
4954
6170
|
}
|
|
4955
6171
|
}
|
|
@@ -4963,13 +6179,10 @@ export function ConsoleAppV4(props) {
|
|
|
4963
6179
|
setScrollOffset(0);
|
|
4964
6180
|
}
|
|
4965
6181
|
else if (input.toLowerCase() === 'r' && route.name === 'sourceDetail') {
|
|
4966
|
-
void props.controller.
|
|
6182
|
+
void props.controller.recheckCatalogSource(route.sourceId);
|
|
4967
6183
|
}
|
|
4968
6184
|
else if (input.toLowerCase() === 'r' && route.name === 'sourceList') {
|
|
4969
|
-
|
|
4970
|
-
if (source) {
|
|
4971
|
-
void props.controller.reloadCatalog({ sourceId: source.id });
|
|
4972
|
-
}
|
|
6185
|
+
void props.controller.recheckAllCatalogSources();
|
|
4973
6186
|
}
|
|
4974
6187
|
else if (route.name === 'pluginList' && (key.leftArrow || key.rightArrow || input === '[' || input === ']')) {
|
|
4975
6188
|
const tabs = ['all', ...props.controller.listSources().map((source) => source.id)];
|
|
@@ -5011,7 +6224,7 @@ export function ConsoleAppV4(props) {
|
|
|
5011
6224
|
const viewport = route.name === 'portableSkillSelect' && snapshot.portableDraft
|
|
5012
6225
|
? portableSkillCandidateViewport(actions.slice(0, snapshot.portableDraft.candidates.length), snapshot.portableDraft.candidates, policy.mode === 'compact', next, scrollOffset, pageActionBudget(actions.length - snapshot.portableDraft.candidates.length
|
|
5013
6226
|
+ (portableVariantCandidates.length > 0 ? 3 : 0)))
|
|
5014
|
-
: actionViewport(actions, policy.mode === 'compact', next, scrollOffset, pageActionBudget(
|
|
6227
|
+
: actionViewport(actions, policy.mode === 'compact', next, scrollOffset, pageActionBudget(actionViewportStaticRows));
|
|
5015
6228
|
setScrollOffset(viewport.offset);
|
|
5016
6229
|
return next;
|
|
5017
6230
|
});
|
|
@@ -5030,15 +6243,19 @@ export function ConsoleAppV4(props) {
|
|
|
5030
6243
|
const viewport = route.name === 'portableSkillSelect' && snapshot.portableDraft
|
|
5031
6244
|
? portableSkillCandidateViewport(actions.slice(0, snapshot.portableDraft.candidates.length), snapshot.portableDraft.candidates, policy.mode === 'compact', next, scrollOffset, pageActionBudget(actions.length - snapshot.portableDraft.candidates.length
|
|
5032
6245
|
+ (portableVariantCandidates.length > 0 ? 3 : 0)))
|
|
5033
|
-
: actionViewport(actions, policy.mode === 'compact', next, scrollOffset, pageActionBudget(
|
|
6246
|
+
: actionViewport(actions, policy.mode === 'compact', next, scrollOffset, pageActionBudget(actionViewportStaticRows));
|
|
5034
6247
|
setScrollOffset(viewport.offset);
|
|
5035
6248
|
return next;
|
|
5036
6249
|
});
|
|
5037
6250
|
}
|
|
5038
6251
|
else if (key.return && route.name === 'pluginList' && pluginRows[focus]) {
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
6252
|
+
const row = pluginRows[focus];
|
|
6253
|
+
if (row.menuAction)
|
|
6254
|
+
row.menuAction();
|
|
6255
|
+
else
|
|
6256
|
+
go(route.mode === 'browse'
|
|
6257
|
+
? { name: 'pluginPreview', pluginId: row.pluginId }
|
|
6258
|
+
: { name: 'pluginDetail', pluginId: row.pluginId });
|
|
5042
6259
|
}
|
|
5043
6260
|
else if (key.return) {
|
|
5044
6261
|
runAction(actions[effectiveFocus]);
|
|
@@ -5052,15 +6269,26 @@ export function ConsoleAppV4(props) {
|
|
|
5052
6269
|
const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, help: help, size: size, children: content }));
|
|
5053
6270
|
if (!overlay)
|
|
5054
6271
|
return base;
|
|
5055
|
-
const helpRows = helpRowsForRoute(route, actions.length, supportsTechnicalDetails, busy);
|
|
6272
|
+
const helpRows = helpRowsForRoute(route, actions.length, supportsTechnicalDetails, busy, props.onSessionNavigation !== undefined);
|
|
5056
6273
|
const overlayActions = overlay === 'help'
|
|
5057
|
-
? [
|
|
6274
|
+
? [
|
|
6275
|
+
{ label: '关闭' },
|
|
6276
|
+
...(props.onSessionNavigation ? [
|
|
6277
|
+
{ label: '返回首页' },
|
|
6278
|
+
{ label: '返回首页并重新加载' },
|
|
6279
|
+
] : []),
|
|
6280
|
+
]
|
|
5058
6281
|
: [
|
|
5059
|
-
{ label: overlay === 'discard' ? '继续编辑' : '继续使用' },
|
|
5060
|
-
{ label: overlay === 'discard' ? '放弃修改' : '退出', status: 'error' },
|
|
6282
|
+
{ label: overlay === 'discard' || overlay === 'sessionNavigation' ? '继续编辑' : '继续使用' },
|
|
6283
|
+
{ label: overlay === 'discard' || overlay === 'sessionNavigation' ? '放弃修改' : '退出', status: 'error' },
|
|
5061
6284
|
];
|
|
5062
|
-
return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: overlay === 'help' ? null : base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", children: _jsxs(ReviewCard, { title: overlay === 'help' ? '当前页快捷键'
|
|
6285
|
+
return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: overlay === 'help' ? null : base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", children: _jsxs(ReviewCard, { title: overlay === 'help' ? '当前页快捷键'
|
|
6286
|
+
: overlay === 'discard' || overlay === 'sessionNavigation' ? '放弃未保存修改?'
|
|
6287
|
+
: '退出控制台?', children: [overlay === 'help'
|
|
5063
6288
|
? _jsx(_Fragment, { children: helpRows.map(([key, value]) => _jsx(StaticRow, { title: `${key} ${value}`, compact: true }, key)) })
|
|
5064
|
-
: _jsx(StaticRow, { title: overlay === 'discard' ? '放弃后将返回上一页。'
|
|
6289
|
+
: _jsx(StaticRow, { title: overlay === 'discard' ? '放弃后将返回上一页。'
|
|
6290
|
+
: overlay === 'sessionNavigation'
|
|
6291
|
+
? `放弃后将${discardForSessionNavigation === 'reload' ? '重新读取本地状态' : '返回首页'}。`
|
|
6292
|
+
: '进行中的写操作会先完成。', status: "muted" }), _jsx(SemanticRows, { actions: overlayActions, focus: overlayFocus, kind: "action", compact: true })] }) })] }));
|
|
5065
6293
|
}
|
|
5066
6294
|
//# sourceMappingURL=console-app-v4.js.map
|