@lesliechan721/aw 0.1.0-beta.35 → 0.1.0-beta.37
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/src/commands/operation-plan.d.ts +19 -0
- package/dist/src/commands/operation-plan.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +1 -0
- package/dist/src/commands/rebuild-command.js +890 -8
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +12 -0
- package/dist/src/commands/rebuild-integration.js +88 -2
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/status-v6.js +109 -19
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/wizard.d.ts +5 -1
- package/dist/src/commands/wizard.js +4 -2
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.js +3 -0
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/index.js +57 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/tui/components.d.ts +20 -1
- package/dist/src/tui/components.js +41 -20
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.js +1323 -1165
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/content-item-card.d.ts +13 -0
- package/dist/src/tui/content-item-card.js +51 -0
- package/dist/src/tui/content-item-card.js.map +1 -0
- package/dist/src/tui/controller-runtime.js +18 -2
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +3 -0
- package/dist/src/tui/controller.js +57 -8
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph-view.d.ts +39 -0
- package/dist/src/tui/dependency-graph-view.js +121 -0
- package/dist/src/tui/dependency-graph-view.js.map +1 -0
- package/dist/src/tui/dependency-graph.js +16 -19
- package/dist/src/tui/dependency-graph.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +55 -0
- package/dist/src/tui/entity-cards.js +220 -0
- package/dist/src/tui/entity-cards.js.map +1 -0
- package/dist/src/tui/input-display.js +10 -1
- package/dist/src/tui/input-display.js.map +1 -1
- package/dist/src/tui/model.d.ts +2 -0
- package/dist/src/tui/model.js +10 -2
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +2 -18
- package/dist/src/tui/operation-diagnostic-screens.js +16 -69
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -1
- package/dist/src/tui/operation-review-planner.js +44 -3
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/plugin-content.d.ts +8 -0
- package/dist/src/tui/plugin-content.js +409 -65
- package/dist/src/tui/plugin-content.js.map +1 -1
- package/dist/src/tui/plugin-detail.d.ts +52 -0
- package/dist/src/tui/plugin-detail.js +165 -0
- package/dist/src/tui/plugin-detail.js.map +1 -0
- package/dist/src/tui/query-header.d.ts +30 -0
- package/dist/src/tui/query-header.js +40 -0
- package/dist/src/tui/query-header.js.map +1 -0
- package/dist/src/tui/reader.d.ts +37 -0
- package/dist/src/tui/reader.js +47 -0
- package/dist/src/tui/reader.js.map +1 -0
- package/dist/src/tui/router.d.ts +10 -0
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +2 -0
- package/dist/src/tui/source-detail-card.d.ts +29 -0
- package/dist/src/tui/source-detail-card.js +45 -0
- package/dist/src/tui/source-detail-card.js.map +1 -0
- package/dist/src/tui/wizard.js +26 -4
- package/dist/src/tui/wizard.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +1 -0
- package/dist/src/utils/local-marketplace.js +22 -0
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/package.json +4 -4
|
@@ -5,7 +5,6 @@ import { presentConfigValue, safeConfigText } from './config-value-presentation.
|
|
|
5
5
|
import { Box, Text, useInput, useWindowSize } from 'ink';
|
|
6
6
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import stringWidth from 'string-width';
|
|
9
8
|
import { validatePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
10
9
|
import { CATALOG_BETA_REF, CATALOG_LATEST_REF, OFFICIAL_CATALOG_SOURCE_SSH_URL, OFFICIAL_CATALOG_SOURCE_URL, } from '../constants.js';
|
|
11
10
|
import { CATALOG_SOURCE_ID_PATTERN } from '../utils/catalog-sources.js';
|
|
@@ -14,17 +13,23 @@ import { normalizePortableAgents, SKILLS_CLI_SUPPORTED_VERSION_LABEL, } from '..
|
|
|
14
13
|
import { selectLifecycleRetryAction, } from '../commands/operation-planner.js';
|
|
15
14
|
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, SCRIPT_TIMEOUT_MAX_MS, SCRIPT_TIMEOUT_MIN_MS, } from '../commands/options.js';
|
|
16
15
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
17
|
-
import { ActionRow, ConfigTile, configTileHeight, HomeNavTile, KeyCap, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
16
|
+
import { ActionRow, ConfigTile, configTileHeight, HomeNavTile, KeyCap, ObjectCardFrame, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
18
17
|
import { safeDiagnosticCode, sanitizeDiagnosticText, } from './diagnostics.js';
|
|
19
18
|
import { extractPortableError, TuiConfigInvalidStoreError, userIssueKey, } from './controller.js';
|
|
20
19
|
import { pluginWriteTaskVersionEvidence } from './plugin-write-task.js';
|
|
21
20
|
import { catalogSourceFreshnessLabel, catalogSourceListPresentation, } from './catalog-sources/screens.js';
|
|
22
|
-
import {
|
|
21
|
+
import { operationDiagnosticText, operationHistoryMeta, operationHistoryState, } from './operation-diagnostic-screens.js';
|
|
23
22
|
import { copyToClipboard } from './clipboard.js';
|
|
24
23
|
import { resolveTuiSizePolicy } from './layout.js';
|
|
25
24
|
import { buildDependencyGraphProjection, buildUnifiedDependencyGraphProjection, } from './dependency-graph.js';
|
|
25
|
+
import { DependencyGraphView, projectDependencyGraphView } from './dependency-graph-view.js';
|
|
26
26
|
import { platformLabel, scopeLabel } from './glossary.js';
|
|
27
27
|
import { InputDisplay, removeLastGrapheme } from './input-display.js';
|
|
28
|
+
import { QueryHeader, querySearchWidth } from './query-header.js';
|
|
29
|
+
import { Pager, Reader, paginateWrappedRows, readerPage } from './reader.js';
|
|
30
|
+
import { PluginDetailCard, ProjectRootCard, EntityPlacementRow, entityPlacementRowHeight, entityCardWindowHeight, pluginDetailCardLayout, projectRootCardLayout } from './entity-cards.js';
|
|
31
|
+
import { ContentItemCard, contentItemCardLayout, contentItemCardWindowHeight, contentItemHasReader, contentTypeLabels } from './content-item-card.js';
|
|
32
|
+
import { SourceDetailCard, sourceDetailCardLayout } from './source-detail-card.js';
|
|
28
33
|
import { catalogRefPolicyForPreset } from './source-ref-presets.js';
|
|
29
34
|
import { buildPlatformActions, configObjectHasSensitiveChild, hasInstalledPlacement, configValuesEqual, } from './model.js';
|
|
30
35
|
import { resolveTuiGlobalKeyAction, resolveTuiSessionNavigationKey } from './keys.js';
|
|
@@ -35,6 +40,17 @@ export const CONSOLE_V4_SCREEN_IDS = TUI_SCREEN_IDS;
|
|
|
35
40
|
const PLUGIN_FILTERS = [
|
|
36
41
|
'all', 'installed', 'uninstalled', 'updateAvailable', 'attention', 'project', 'user',
|
|
37
42
|
];
|
|
43
|
+
const PROJECT_STATUS_FILTERS = ['all', 'pending', 'blocked', 'unknown', 'ready'];
|
|
44
|
+
const PROJECT_STATUS_LABELS = {
|
|
45
|
+
all: '全部', pending: '待同步', blocked: '受阻', unknown: '未确认', ready: '就绪',
|
|
46
|
+
};
|
|
47
|
+
function queryMatches(query, ...values) {
|
|
48
|
+
const normalized = query.trim().toLocaleLowerCase();
|
|
49
|
+
return !normalized || values.some((value) => value?.toLocaleLowerCase().includes(normalized));
|
|
50
|
+
}
|
|
51
|
+
function contentMatches(query, item) {
|
|
52
|
+
return queryMatches(query, item.name, item.descriptionState === 'issue' ? undefined : item.description);
|
|
53
|
+
}
|
|
38
54
|
function pluginFilterLabel(filter) {
|
|
39
55
|
return {
|
|
40
56
|
all: '全部',
|
|
@@ -65,13 +81,11 @@ function productResultMessage(message) {
|
|
|
65
81
|
return message;
|
|
66
82
|
}
|
|
67
83
|
function pluginMenuActionId(action) {
|
|
68
|
-
return action
|
|
69
|
-
? `${action.id}:${action.platform}`
|
|
70
|
-
: action.id === '
|
|
71
|
-
? `${action.id}:${action.platform}
|
|
72
|
-
: action.id
|
|
73
|
-
? `${action.id}:${action.pluginId}:${action.platform}:${action.scope}`
|
|
74
|
-
: action.id;
|
|
84
|
+
return 'scope' in action && 'platform' in action
|
|
85
|
+
? `${action.id}:${action.platform}:${action.scope}`
|
|
86
|
+
: action.id === 'platform' || action.id === 'scopeConflict'
|
|
87
|
+
? `${action.id}:${action.platform}`
|
|
88
|
+
: action.id;
|
|
75
89
|
}
|
|
76
90
|
function batchTargetKey(target) {
|
|
77
91
|
return [target.pluginId, target.platform, target.scope].join('\0');
|
|
@@ -85,58 +99,12 @@ function batchPluginVersionSummary(plugin) {
|
|
|
85
99
|
? changes[0]
|
|
86
100
|
: `${updates.length} 个位置版本不同(Enter 查看)`;
|
|
87
101
|
}
|
|
88
|
-
function scopedPluginPlacementConclusion(placement, location) {
|
|
89
|
-
if (placement.blockingReason === 'artifactCollision' || placement.actualState === 'blocked') {
|
|
90
|
-
return { text: `${location}暂时不可用`, status: 'error' };
|
|
91
|
-
}
|
|
92
|
-
if (placement.actualState === 'needsSync' || placement.health === 'incomplete') {
|
|
93
|
-
return { text: `${location}需要同步`, status: 'warning' };
|
|
94
|
-
}
|
|
95
|
-
if (placement.health === 'error')
|
|
96
|
-
return { text: `${location}状态未知`, status: 'warning' };
|
|
97
|
-
return { text: `${location}可用`, status: 'success' };
|
|
98
|
-
}
|
|
99
|
-
function pluginPlacementConclusions(placement) {
|
|
100
|
-
if (placement.state === 'notInstalled')
|
|
101
|
-
return [{ text: '未添加', status: 'muted' }];
|
|
102
|
-
if (placement.state === 'blocked')
|
|
103
|
-
return [{ text: '无法添加:输出位置有冲突', status: 'error' }];
|
|
104
|
-
if (placement.state === 'scopeConflict')
|
|
105
|
-
return [{ text: '使用位置冲突,需要选择一个位置', status: 'warning' }];
|
|
106
|
-
if (!placement.scopedPlacements?.length) {
|
|
107
|
-
const location = placement.desiredScopes?.map(scopeLabel).join('、') || scopeLabel(placement.scope);
|
|
108
|
-
return [scopedPluginPlacementConclusion(placement, location)];
|
|
109
|
-
}
|
|
110
|
-
const grouped = new Map();
|
|
111
|
-
for (const scoped of placement.scopedPlacements) {
|
|
112
|
-
const conclusion = scopedPluginPlacementConclusion({
|
|
113
|
-
platform: placement.platform,
|
|
114
|
-
state: 'installed',
|
|
115
|
-
scope: scoped.scope,
|
|
116
|
-
health: scoped.health,
|
|
117
|
-
revision: scoped.revision,
|
|
118
|
-
desiredScopes: [scoped.scope],
|
|
119
|
-
...(scoped.actualState ? { actualState: scoped.actualState } : {}),
|
|
120
|
-
...(scoped.recovery ? { recovery: scoped.recovery } : {}),
|
|
121
|
-
...(scoped.blockingReason ? { blockingReason: scoped.blockingReason } : {}),
|
|
122
|
-
}, '');
|
|
123
|
-
const key = `${conclusion.status}\0${conclusion.text}`;
|
|
124
|
-
const current = grouped.get(key);
|
|
125
|
-
if (current)
|
|
126
|
-
current.scopes.push(scoped.scope);
|
|
127
|
-
else
|
|
128
|
-
grouped.set(key, { scopes: [scoped.scope], status: conclusion.status, suffix: conclusion.text });
|
|
129
|
-
}
|
|
130
|
-
return [...grouped.values()].map((entry) => ({
|
|
131
|
-
text: `${entry.scopes.map(scopeLabel).join('、')}${entry.suffix}`,
|
|
132
|
-
status: entry.status,
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
102
|
function isCharacterInputMode(route, searchMode) {
|
|
103
|
+
if (searchMode)
|
|
104
|
+
return true;
|
|
136
105
|
if (routeDefinition(route).keymap !== 'menuOrTextInput')
|
|
137
106
|
return false;
|
|
138
|
-
return
|
|
139
|
-
|| route.name === 'portableAgentInput'
|
|
107
|
+
return route.name === 'portableAgentInput'
|
|
140
108
|
|| route.name === 'configFieldEditor'
|
|
141
109
|
&& (route.editor === 'string' || route.editor === 'number')
|
|
142
110
|
&& route.mode !== 'choices'
|
|
@@ -154,56 +122,13 @@ function supportsScriptTimeout(intent) {
|
|
|
154
122
|
|| intent?.kind === 'pluginWriteTask'
|
|
155
123
|
|| (intent?.kind === 'configSave' || intent?.kind === 'configSaveBatch') && intent.apply;
|
|
156
124
|
}
|
|
157
|
-
function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (!busy && actionCount > 0)
|
|
162
|
-
rows.push(['Enter', '打开、选择或继续']);
|
|
163
|
-
if (!busy && route.name !== 'home')
|
|
164
|
-
rows.push(['Esc / b', '返回上一页']);
|
|
165
|
-
if (route.name === 'pluginList') {
|
|
166
|
-
rows.push(['←/→ · [ / ]', '切换来源']);
|
|
167
|
-
rows.push(['/', '进入搜索']);
|
|
168
|
-
rows.push(['f', '切换插件状态']);
|
|
169
|
-
rows.push(['m', '原位选择多个插件']);
|
|
170
|
-
}
|
|
171
|
-
if (route.name === 'portableSkillSelect') {
|
|
172
|
-
rows.push(['Space', '切换当前 Skill']);
|
|
173
|
-
rows.push(['a', '定位下方操作 / 回到原项']);
|
|
174
|
-
}
|
|
175
|
-
if (route.name === 'batchSelection') {
|
|
176
|
-
rows.push(['Space', '切换当前插件']);
|
|
177
|
-
rows.push(['a', '定位下方操作 / 回到原项']);
|
|
178
|
-
rows.push(['c', '继续选择任务或查看汇总']);
|
|
179
|
-
}
|
|
180
|
-
if (route.name === 'configFields' && route.layer.scope !== 'effective') {
|
|
181
|
-
rows.push(['s', '查看保存汇总']);
|
|
182
|
-
rows.push(['u', '恢复当前字段继承']);
|
|
183
|
-
}
|
|
184
|
-
if (route.name === 'sourceList' || route.name === 'sourceDetail' || route.name === 'sourceMore') {
|
|
185
|
-
rows.push(['r', '检查来源']);
|
|
186
|
-
}
|
|
187
|
-
if (supportsTechnicalDetails) {
|
|
188
|
-
rows.push(['d', route.name === 'progress' ? '打开技术进度' : '打开技术详情']);
|
|
189
|
-
}
|
|
190
|
-
if (sessionNavigation && !busy && !isCharacterInputMode(route, false)) {
|
|
191
|
-
rows.push(['h / H', '返回首页 / 重新读取本地状态']);
|
|
192
|
-
}
|
|
193
|
-
if (sessionNavigation && busy) {
|
|
194
|
-
rows.push(['h / H', '操作成功后返回首页或重新读取']);
|
|
195
|
-
rows.push(['Esc', '撤销等待中的页面跳转']);
|
|
196
|
-
}
|
|
197
|
-
if (route.name === 'pluginList' || route.name === 'pluginDetail'
|
|
198
|
-
|| route.name === 'batchSelection' || route.name === 'installTargets') {
|
|
199
|
-
rows.push(['状态', '✓已装 ○未装 !异常 ↑更新 ?未知']);
|
|
200
|
-
}
|
|
201
|
-
rows.push(['q / Ctrl-C', busy ? '操作完成后退出' : '退出控制台']);
|
|
202
|
-
rows.push(['? / o', '关闭帮助 / 查看操作记录']);
|
|
203
|
-
return rows;
|
|
125
|
+
function helpTextForPage(help, busy) {
|
|
126
|
+
return [...help.split(' · ').filter((part) => part !== '? 帮助' && !/^(q|Ctrl-C) /u.test(part)),
|
|
127
|
+
`q / Ctrl-C ${busy ? '操作完成后退出' : '退出控制台'}`,
|
|
128
|
+
...(!busy ? ['o 查看操作记录'] : []), 'Esc / ? 关闭帮助'].join('\n');
|
|
204
129
|
}
|
|
205
130
|
function projectDependencyLines(root) {
|
|
206
|
-
return root?.dependencies.flatMap((dependency) => dependency.targets.map((target) => `依赖 ${dependency.displayName} · ${dependency.pluginId} · ${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} ·
|
|
131
|
+
return root?.dependencies.flatMap((dependency) => dependency.targets.map((target) => `依赖 ${dependency.displayName} · ${dependency.pluginId} · ${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} · 目录参考 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'}${target.reasonCodes.length ? ` · ${target.reasonCodes.map((code) => safeDiagnosticCode(code, 'projectTargetUnknown')).join('、')}` : ''}`)) ?? [];
|
|
207
132
|
}
|
|
208
133
|
function configOwnerKey(route) {
|
|
209
134
|
return route.pluginId;
|
|
@@ -253,14 +178,6 @@ function pluginWriteTaskTargetKey(candidate) {
|
|
|
253
178
|
function dependencyGraphContextKey(pluginId, direction) {
|
|
254
179
|
return `${pluginId}\0${direction}`;
|
|
255
180
|
}
|
|
256
|
-
function unifiedDependencyGraphActionIds(projection) {
|
|
257
|
-
return [
|
|
258
|
-
...projection.rows.map((row) => `row:${row.branch}:${row.id}`),
|
|
259
|
-
...projection.dependents.scopeSummary.unknownConsumerPluginIds
|
|
260
|
-
.map((pluginId) => `unverified:${pluginId}`),
|
|
261
|
-
'technical',
|
|
262
|
-
];
|
|
263
|
-
}
|
|
264
181
|
function statusText(plugin) {
|
|
265
182
|
if (plugin.configOnly)
|
|
266
183
|
return '已保存设置';
|
|
@@ -283,21 +200,6 @@ function statusText(plugin) {
|
|
|
283
200
|
: '来源不可用',
|
|
284
201
|
}[plugin.aggregateStatus];
|
|
285
202
|
}
|
|
286
|
-
function pluginUpdateDescription(plugin) {
|
|
287
|
-
const targets = (plugin.update?.targets ?? []).filter((target) => target.availableVersion !== undefined);
|
|
288
|
-
if (targets.length === 0) {
|
|
289
|
-
const incompatibleCandidate = plugin.update?.targets.find((target) => target.candidateVersion !== undefined && (target.missingCapabilities?.length ?? 0) > 0);
|
|
290
|
-
return incompatibleCandidate
|
|
291
|
-
? `候选版本缺少 ${incompatibleCandidate.missingCapabilities.length} 项 AW 功能支持;当前启用版本可继续使用。`
|
|
292
|
-
: undefined;
|
|
293
|
-
}
|
|
294
|
-
if (targets.some((target) => target.requiresCatalogActivation)) {
|
|
295
|
-
return plugin.update?.lastSuccessful
|
|
296
|
-
? `上次成功检查:${plugin.update.checkedAt ? new Date(plugin.update.checkedAt).toLocaleString('zh-CN', { hour12: false }) : '时间未知'};需先更新来源目录。`
|
|
297
|
-
: '已验证待检查版本;需先更新来源目录。';
|
|
298
|
-
}
|
|
299
|
-
return '当前启用目录已验证;可直接进入更新确认。';
|
|
300
|
-
}
|
|
301
203
|
function pluginPlatformUnknown(plugin, platform) {
|
|
302
204
|
const placements = plugin.installedPlacements.filter((entry) => entry.platform === platform);
|
|
303
205
|
return plugin.sourceUnavailable || placements.length === 0
|
|
@@ -435,18 +337,6 @@ function relativeCheckTime(value, now = Date.now()) {
|
|
|
435
337
|
function parseConfigNumberInput(input) {
|
|
436
338
|
return input.trim().length === 0 ? undefined : Number(input);
|
|
437
339
|
}
|
|
438
|
-
function changeDetailLines(changes, columns) {
|
|
439
|
-
const valueLines = (label, value) => [
|
|
440
|
-
label,
|
|
441
|
-
...wrapConsoleText(value ?? '未设置', Math.max(1, columns - 4)).map((line) => ` ${line}`),
|
|
442
|
-
];
|
|
443
|
-
return changes.flatMap((change) => [
|
|
444
|
-
`修改项 · ${change.title}`,
|
|
445
|
-
...valueLines('修改前', change.before),
|
|
446
|
-
...valueLines('修改后', change.after),
|
|
447
|
-
...(change.children ?? []).flatMap((child) => [child.property, ...valueLines('修改前', child.before), ...valueLines('修改后', child.after)]),
|
|
448
|
-
]);
|
|
449
|
-
}
|
|
450
340
|
function changeDetailText(changes) {
|
|
451
341
|
return changes.flatMap((change) => [
|
|
452
342
|
`修改项 · ${change.title}`,
|
|
@@ -533,6 +423,15 @@ function sourceLocationSummary(type, value) {
|
|
|
533
423
|
return value.startsWith('ssh://') || value.includes('@') ? 'SSH 远程来源' : '远程来源';
|
|
534
424
|
}
|
|
535
425
|
}
|
|
426
|
+
function matchingSources(controller, query, type) {
|
|
427
|
+
return controller.listSources().filter((source) => {
|
|
428
|
+
const configured = controller.getConfiguredSource(source.id);
|
|
429
|
+
const detail = controller.getSourceOverview(source.id).detail;
|
|
430
|
+
const sourceType = configured?.type ?? detail?.type;
|
|
431
|
+
return (type === 'all' || sourceType === type)
|
|
432
|
+
&& queryMatches(query, source.id, detail?.locationSummary, configured?.type === 'git' ? configured.url : configured?.path);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
536
435
|
function portableBlockerDiagnostic(route) {
|
|
537
436
|
const cliMissing = route.code === 'portableSkillsCliMissing';
|
|
538
437
|
const cliIncompatible = route.code === 'portableSkillsCliIncompatible';
|
|
@@ -811,33 +710,6 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
811
710
|
},
|
|
812
711
|
};
|
|
813
712
|
}
|
|
814
|
-
function capabilityDiagnostic(plugin) {
|
|
815
|
-
const missingCapabilities = plugin.missingCapabilities?.join('、')
|
|
816
|
-
?? 'Status 未提供具体 capability';
|
|
817
|
-
const recovery = '升级 AW CLI 到支持此插件的版本后重试。';
|
|
818
|
-
return {
|
|
819
|
-
severity: 'error',
|
|
820
|
-
title: '当前 AW 版本不支持此插件',
|
|
821
|
-
userMessage: '当前 AW CLI 不能安装或重新安装这个插件。来源保持可用。',
|
|
822
|
-
impact: [
|
|
823
|
-
{ state: 'blocked', message: '安装和重新安装会保持阻断,直到 AW CLI 升级。' },
|
|
824
|
-
{ state: 'preserved', message: '已安装插件和已保存设置不会被删除。' },
|
|
825
|
-
],
|
|
826
|
-
nextStep: recovery,
|
|
827
|
-
technical: {
|
|
828
|
-
code: 'awCapabilityUnsupported',
|
|
829
|
-
stage: 'check-capability',
|
|
830
|
-
context: [{ key: 'pluginId', value: plugin.pluginId }],
|
|
831
|
-
checks: [
|
|
832
|
-
`确认插件需要的 capability:${missingCapabilities}。`,
|
|
833
|
-
'升级 AW CLI 后重新检查当前状态。',
|
|
834
|
-
],
|
|
835
|
-
},
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
function terminalTextWidth(value) {
|
|
839
|
-
return [...value].reduce((width, character) => width + (character.codePointAt(0) > 0xff ? 2 : 1), 0);
|
|
840
|
-
}
|
|
841
713
|
function SemanticRows(props) {
|
|
842
714
|
const offset = props.offset ?? 0;
|
|
843
715
|
const actions = props.limit === undefined
|
|
@@ -847,9 +719,11 @@ function SemanticRows(props) {
|
|
|
847
719
|
if (action.role === 'navigation')
|
|
848
720
|
return null;
|
|
849
721
|
const index = visibleIndex + offset;
|
|
722
|
+
// The legacy label suffix is display metadata, never an action or permission lookup.
|
|
723
|
+
const keyHint = action.label.match(/ \[([a-z/?])\]$/u);
|
|
850
724
|
const rowProps = {
|
|
851
|
-
title:
|
|
852
|
-
|
|
725
|
+
title: keyHint ? action.label.slice(0, -keyHint[0].length) : action.label,
|
|
726
|
+
keyHint: keyHint?.[1],
|
|
853
727
|
description: action.description,
|
|
854
728
|
meta: action.meta ?? (props.compact ? action.disabled : undefined),
|
|
855
729
|
status: action.status,
|
|
@@ -879,7 +753,8 @@ function SequenceRows(props) {
|
|
|
879
753
|
];
|
|
880
754
|
const viewport = rowViewport({
|
|
881
755
|
rowHeights: props.actions.map((action, index) => action.role === 'navigation' ? 0 : index < props.itemCount
|
|
882
|
-
? action.configTile ? configTileHeight({ width: props.columns, ...action.configTile
|
|
756
|
+
? action.configTile ? configTileHeight({ width: props.columns, ...action.configTile,
|
|
757
|
+
focused: !props.inactive && index === props.focus, bodyRows: Math.max(1, props.rowBudget - 3) })
|
|
883
758
|
: props.skillRows ? 2 + (action.exception ? 1 : 0)
|
|
884
759
|
: props.wrapValues ? configLines(action).length : props.cards ? objectCardRowLayout({
|
|
885
760
|
width: props.columns, description: action.disabled ?? action.description ?? '',
|
|
@@ -895,7 +770,7 @@ function SequenceRows(props) {
|
|
|
895
770
|
const index = viewport.offset + visibleIndex;
|
|
896
771
|
const focused = !props.inactive && index === props.focus;
|
|
897
772
|
if (index < props.itemCount && action.configTile)
|
|
898
|
-
return _jsx(ConfigTile, { width: props.columns, title: action.label, focused: focused, ...action.configTile }, action.configKey);
|
|
773
|
+
return _jsx(ConfigTile, { width: props.columns, title: action.label, focused: focused, ...action.configTile, bodyRows: Math.max(1, props.rowBudget - 3), page: focused ? props.configPage : 1 }, action.configKey);
|
|
899
774
|
if (index < props.itemCount && props.skillRows)
|
|
900
775
|
return _jsxs(Box, { flexDirection: "column", backgroundColor: focused ? tuiTheme.colors.selectionBackground : undefined, children: [_jsxs(Text, { bold: focused, color: tuiTheme.roles.primaryValue, wrap: "truncate-end", children: [focused ? '›' : ' ', " [", action.selected ? 'x' : ' ', "] ", action.label] }), _jsxs(Text, { color: tuiTheme.roles.secondary, wrap: "truncate-end", children: [" ", action.description || '暂无简介'] }), action.exception ? _jsxs(Text, { color: tuiTheme.roles.warning, wrap: "truncate-end", children: [" ! ", action.exception] }) : null] }, action.actionId ?? action.label);
|
|
901
776
|
if (index < props.itemCount && props.wrapValues)
|
|
@@ -919,36 +794,6 @@ function IssueTechnicalDetails(props) {
|
|
|
919
794
|
_jsx(StaticRow, { title: detail.message, meta: index === visible.length - 1 && hidden > 0 ? `另有 ${hidden} 项` : undefined, compact: true }, `${detail.code}:${index}:message`),
|
|
920
795
|
]) });
|
|
921
796
|
}
|
|
922
|
-
function SourceTabs(props) {
|
|
923
|
-
const filter = { mode: props.mode, query: props.query, installFilter: props.installFilter };
|
|
924
|
-
const tabs = [{ id: 'all', label: '全部', count: props.controller.listPlugins(filter).length },
|
|
925
|
-
...props.controller.listSources().map((source) => ({
|
|
926
|
-
...source,
|
|
927
|
-
count: props.controller.listPlugins({ ...filter, sourceId: source.id }).length,
|
|
928
|
-
}))];
|
|
929
|
-
const index = Math.max(0, tabs.findIndex((tab) => tab.id === props.current));
|
|
930
|
-
const badge = (state, count) => state === 'checking' ? ' …'
|
|
931
|
-
: state === 'update' ? ' ↑'
|
|
932
|
-
: state === 'recovery' ? ' ?'
|
|
933
|
-
: state === 'error' ? ' !' : ` ${count ?? ''}`;
|
|
934
|
-
const fullText = `来源 ${tabs.map((tab) => {
|
|
935
|
-
const state = 'state' in tab ? tab.state : 'ready';
|
|
936
|
-
return `${tab.id === props.current ? '[' : ''}${tab.label}${badge(state, tab.count)}${tab.id === props.current ? ']' : ''} `;
|
|
937
|
-
}).join('')}`;
|
|
938
|
-
if (props.compact || terminalTextWidth(`${fullText}←→ 切换`) > props.columns) {
|
|
939
|
-
const tab = tabs[index];
|
|
940
|
-
const neighbour = tabs[(index + 1) % tabs.length];
|
|
941
|
-
return _jsxs(Text, { wrap: "truncate-end", children: ["\u6765\u6E90 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", tab.label, "]"] }), " \u2190\u2192 \u5207\u6362 ", tabs.length > 1 ? neighbour?.label : ''] });
|
|
942
|
-
}
|
|
943
|
-
return (_jsxs(Text, { wrap: "truncate-end", children: ["\u6765\u6E90 ", tabs.map((tab) => {
|
|
944
|
-
const active = tab.id === props.current;
|
|
945
|
-
const state = 'state' in tab ? tab.state : 'ready';
|
|
946
|
-
return (_jsxs(Text, { bold: active, color: active ? tuiTheme.colors.accent
|
|
947
|
-
: state === 'error' ? tuiTheme.statusColors.error
|
|
948
|
-
: state === 'update' || state === 'recovery' ? tuiTheme.statusColors.warning
|
|
949
|
-
: tuiTheme.colors.muted, children: [active ? '[' : '', tab.label, badge(state, tab.count), active ? ']' : '', ' '] }, tab.id));
|
|
950
|
-
}), "\u2190\u2192 \u5207\u6362"] }));
|
|
951
|
-
}
|
|
952
797
|
function configTabs(page) {
|
|
953
798
|
return [
|
|
954
799
|
...page.groups.filter((group) => group.fields.length > 0).map((group) => ({
|
|
@@ -962,18 +807,6 @@ function configTabs(page) {
|
|
|
962
807
|
: []),
|
|
963
808
|
];
|
|
964
809
|
}
|
|
965
|
-
function ConfigGroupTabs(props) {
|
|
966
|
-
const index = Math.max(0, props.tabs.findIndex((tab) => tab.id === props.current));
|
|
967
|
-
const fullText = `分组 ${props.tabs.map((tab) => `${tab.id === props.current ? '[' : ''}${tab.label} ${tab.count}${tab.id === props.current ? ']' : ''} `).join('')}`;
|
|
968
|
-
if (props.compact || terminalTextWidth(fullText) > props.columns) {
|
|
969
|
-
const tab = props.tabs[index];
|
|
970
|
-
return _jsxs(Text, { wrap: "truncate-end", children: ["\u5206\u7EC4 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", tab.label, " ", tab.count, "]"] }), " ", index + 1, " / ", props.tabs.length, " \u2190 \u2192"] });
|
|
971
|
-
}
|
|
972
|
-
return (_jsxs(Text, { wrap: "truncate-end", children: ["\u5206\u7EC4 ", props.tabs.map((tab) => {
|
|
973
|
-
const active = tab.id === props.current;
|
|
974
|
-
return _jsxs(Text, { bold: active, color: active ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: [active ? '[' : '', tab.label, " ", tab.count, active ? ']' : '', ' '] }, tab.id);
|
|
975
|
-
}), "\u2190 \u2192"] }));
|
|
976
|
-
}
|
|
977
810
|
function placementStatus(placement) {
|
|
978
811
|
if (placement.state === 'notInstalled') {
|
|
979
812
|
return { value: '未添加', status: 'muted', description: '选择使用位置后添加' };
|
|
@@ -1049,22 +882,41 @@ function dependencyRelationDescription(item) {
|
|
|
1049
882
|
const requiredBy = item.requiredBy.map((entry) => entry.displayName).join('、');
|
|
1050
883
|
return `${item.relation === 'direct' ? '直接依赖' : '间接依赖'}${requiredBy ? ` · ${requiredBy} 需要它` : ''}`;
|
|
1051
884
|
}
|
|
1052
|
-
function
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
885
|
+
function dependencyEvidenceLines(row, subject) {
|
|
886
|
+
const references = row.edge?.availablePlacementReferences ?? [];
|
|
887
|
+
const placementLines = subject?.placements.map((placement) => `安装位置 · ${platformLabel(placement.platform)} · ${scopeLabel(placement.scope)} · 实际安装版本 ${placement.installedVersion ?? '未确认'} · 观察 ${placement.observation === 'unknown' ? '未确认' : '已检查'}`) ?? [];
|
|
888
|
+
const referenceLines = (reference) => [
|
|
889
|
+
`consumer · ${reference.consumer.pluginId}`,
|
|
890
|
+
`consumer · platform=${platformLabel(reference.consumer.platform)} · scope=${reference.consumer.scope}`,
|
|
891
|
+
`consumer · project=${reference.consumer.project ?? 'none'}`,
|
|
892
|
+
`dependency · ${reference.dependency.pluginId}`,
|
|
893
|
+
`dependency · platform=${platformLabel(reference.dependency.platform)} · scope=${reference.dependency.scope}`,
|
|
894
|
+
`dependency · project=${reference.dependency.project ?? 'none'}`,
|
|
895
|
+
];
|
|
896
|
+
const availablePath = row.relationship === 'directAvailable'
|
|
897
|
+
|| row.relationship === 'indirectAvailable';
|
|
898
|
+
return [
|
|
899
|
+
`pluginId · ${row.pluginId}`,
|
|
900
|
+
`目录版本 · ${subject?.catalogVersion ?? '未确认'}`,
|
|
901
|
+
...(placementLines.length ? placementLines : ['安装位置 · 本次无位置事实']),
|
|
902
|
+
`relationship · ${row.relationship}`,
|
|
903
|
+
`图路径 · ${row.pathPluginIds.join(' → ')}`,
|
|
904
|
+
`直接边声明 · ${row.edge?.declared ? '已声明' : '未确认'}`,
|
|
905
|
+
`直接边已验证位置 · ${references.length} 个`,
|
|
906
|
+
...references.flatMap((reference, index) => [
|
|
907
|
+
`直接边引用 ${index + 1}`,
|
|
908
|
+
...referenceLines(reference),
|
|
909
|
+
]),
|
|
910
|
+
...(availablePath && row.pathReferences.length
|
|
911
|
+
? row.pathReferences.flatMap((path, index) => [
|
|
912
|
+
`连续已验证路径 ${index + 1} · ${path.length} 段`,
|
|
913
|
+
...path.flatMap((reference, hop) => [
|
|
914
|
+
`连续路径 ${index + 1} · 第 ${hop + 1} 段`,
|
|
915
|
+
...referenceLines(reference),
|
|
916
|
+
]),
|
|
917
|
+
])
|
|
918
|
+
: ['连续路径 · 未验证']),
|
|
919
|
+
];
|
|
1068
920
|
}
|
|
1069
921
|
function isCatalogSourcesBootstrapFlow(snapshot) {
|
|
1070
922
|
const route = snapshot.router.current.route;
|
|
@@ -1127,6 +979,7 @@ export function ConsoleAppV4(props) {
|
|
|
1127
979
|
const [snapshot, setSnapshot] = useState(() => props.controller.snapshot);
|
|
1128
980
|
const [overlay, setOverlay] = useState();
|
|
1129
981
|
const [overlayFocus, setOverlayFocus] = useState(0);
|
|
982
|
+
const [helpPage, setHelpPage] = useState(1);
|
|
1130
983
|
const [operationConfirmation, setOperationConfirmation] = useState();
|
|
1131
984
|
const [sourceTransportOverlay, setSourceTransportOverlay] = useState();
|
|
1132
985
|
const [projectReminderOpen, setProjectReminderOpen] = useState(false);
|
|
@@ -1134,7 +987,10 @@ export function ConsoleAppV4(props) {
|
|
|
1134
987
|
const [discardForSessionNavigation, setDiscardForSessionNavigation] = useState();
|
|
1135
988
|
const [discardDraftConfirmation, setDiscardDraftConfirmation] = useState();
|
|
1136
989
|
const [focus, setFocus] = useState(0);
|
|
990
|
+
const [graphEvidenceOpen, setGraphEvidenceOpen] = useState(false);
|
|
991
|
+
const [graphPortableSelectionOpen, setGraphPortableSelectionOpen] = useState(false);
|
|
1137
992
|
const actionJumpAnchor = useRef(undefined);
|
|
993
|
+
const detailActionAnchor = useRef(undefined);
|
|
1138
994
|
const [searchCursor, setSearchCursor] = useState(0);
|
|
1139
995
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
1140
996
|
const [searchFocus, setSearchFocus] = useState(false);
|
|
@@ -1173,6 +1029,8 @@ export function ConsoleAppV4(props) {
|
|
|
1173
1029
|
const [batchDraft, setBatchDraft] = useState();
|
|
1174
1030
|
const batchSelectionAnchor = useRef(undefined);
|
|
1175
1031
|
const [batchTargetOffset, setBatchTargetOffset] = useState(0);
|
|
1032
|
+
const [configCardPage, setConfigCardPage] = useState();
|
|
1033
|
+
const [contentReader, setContentReader] = useState();
|
|
1176
1034
|
const [portableResultDetailOpen, setPortableResultDetailOpen] = useState(false);
|
|
1177
1035
|
const [portableResultDetailOffset, setPortableResultDetailOffset] = useState(0);
|
|
1178
1036
|
const [portableRecheckBusy, setPortableRecheckBusy] = useState(false);
|
|
@@ -1204,7 +1062,6 @@ export function ConsoleAppV4(props) {
|
|
|
1204
1062
|
const pluginListFocusId = useRef(undefined);
|
|
1205
1063
|
const listFocusAnchor = useRef(undefined);
|
|
1206
1064
|
const dependencyGraphContexts = useRef(new Map());
|
|
1207
|
-
const selectedDependencyReferenceIndex = useRef(0);
|
|
1208
1065
|
const externalDraftRevision = useRef({ config: 0, source: 0, batch: 0 });
|
|
1209
1066
|
const previousExternalDraftValues = useRef({
|
|
1210
1067
|
configDrafts, configCandidate, arrayDrafts, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
@@ -1214,6 +1071,14 @@ export function ConsoleAppV4(props) {
|
|
|
1214
1071
|
const exitSent = useRef(false);
|
|
1215
1072
|
const operationBusy = useRef(false);
|
|
1216
1073
|
const route = snapshot.router.current.route;
|
|
1074
|
+
const platformFacts = route.name === 'platform'
|
|
1075
|
+
? props.controller.getPluginDetailSubject(route.pluginId)?.placements
|
|
1076
|
+
.filter((entry) => entry.platform === route.platform) ?? []
|
|
1077
|
+
: [];
|
|
1078
|
+
const platformContext = snapshot.router.current.context;
|
|
1079
|
+
const platformSelectedScope = route.name === 'platform'
|
|
1080
|
+
&& platformFacts.some((entry) => entry.scope === platformContext.platformSelectedScope)
|
|
1081
|
+
? platformContext.platformSelectedScope : undefined;
|
|
1217
1082
|
if (route.name === 'configFields') {
|
|
1218
1083
|
configOverviewAnchors.current.set(route.pluginId, {
|
|
1219
1084
|
overviewFrameId: snapshot.router.currentFrameId,
|
|
@@ -1246,12 +1111,40 @@ export function ConsoleAppV4(props) {
|
|
|
1246
1111
|
setBatchTargetOffset(0);
|
|
1247
1112
|
};
|
|
1248
1113
|
const plugin = currentPlugin(props.controller, route);
|
|
1114
|
+
const detailSubject = route.name === 'pluginDetail'
|
|
1115
|
+
? props.controller.getPluginDetailSubject(route.pluginId) : undefined;
|
|
1249
1116
|
const issue = route.name === 'issueDetail'
|
|
1250
1117
|
? props.controller.listUserIssues().find((entry) => entry.issueId === route.issueId)
|
|
1251
1118
|
: undefined;
|
|
1252
1119
|
const busy = route.name === 'progress';
|
|
1253
1120
|
const preparationRunning = route.name === 'reviewPreparing'
|
|
1254
1121
|
&& snapshot.reviewPreparation?.state === 'running';
|
|
1122
|
+
useEffect(() => {
|
|
1123
|
+
if (route.name !== 'pluginDependencyNode' || route.view === 'references')
|
|
1124
|
+
return;
|
|
1125
|
+
props.controller.replace({ name: 'pluginDetail', pluginId: route.nodePluginId });
|
|
1126
|
+
}, [props.controller, route.name,
|
|
1127
|
+
route.name === 'pluginDependencyNode' ? route.nodePluginId : '',
|
|
1128
|
+
route.name === 'pluginDependencyNode' ? route.view : '']);
|
|
1129
|
+
useEffect(() => {
|
|
1130
|
+
if (route.name !== 'platform')
|
|
1131
|
+
return;
|
|
1132
|
+
const selected = snapshot.router.current.context.platformSelectedScope;
|
|
1133
|
+
if (!snapshot.router.current.context.platformSelectionInitialized) {
|
|
1134
|
+
props.controller.updatePageContext({
|
|
1135
|
+
platformSelectionInitialized: true,
|
|
1136
|
+
platformSelectedScope: platformFacts.length === 1 ? platformFacts[0].scope : undefined,
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
else if (selected && !platformFacts.some((entry) => entry.scope === selected)) {
|
|
1140
|
+
props.controller.updatePageContext({ platformSelectedScope: undefined });
|
|
1141
|
+
setActionError('已选位置不再存在;请重新选择位置。');
|
|
1142
|
+
}
|
|
1143
|
+
}, [props.controller, route.name, route.name === 'platform' ? route.pluginId : '',
|
|
1144
|
+
route.name === 'platform' ? route.platform : '', snapshot.viewModel,
|
|
1145
|
+
snapshot.router.current.context.platformSelectionInitialized,
|
|
1146
|
+
snapshot.router.current.context.platformSelectedScope,
|
|
1147
|
+
platformFacts.map((entry) => entry.scope).join('\0')]);
|
|
1255
1148
|
useEffect(() => {
|
|
1256
1149
|
if (!busy)
|
|
1257
1150
|
return;
|
|
@@ -1752,9 +1645,11 @@ export function ConsoleAppV4(props) {
|
|
|
1752
1645
|
return () => { current = false; };
|
|
1753
1646
|
}, [props.controller, route.name, snapshot.result, catalogStoreRefresh]);
|
|
1754
1647
|
useEffect(() => {
|
|
1755
|
-
if (route.name !== 'pluginDependencies')
|
|
1648
|
+
if (route.name !== 'pluginDependencies' && route.name !== 'portableDependencies')
|
|
1649
|
+
return;
|
|
1650
|
+
if (route.name === 'pluginDependencies' && route.view === 'removeBlocked')
|
|
1756
1651
|
return;
|
|
1757
|
-
const key = dependencyGraphContextKey(route.pluginId, 'dependencies');
|
|
1652
|
+
const key = dependencyGraphContextKey(route.pluginId, route.name === 'portableDependencies' ? 'portable' : 'dependencies');
|
|
1758
1653
|
const stored = dependencyGraphContexts.current.get(key);
|
|
1759
1654
|
const overviews = props.controller.listPluginDependencyOverviews();
|
|
1760
1655
|
const knownPluginIds = new Set(overviews.flatMap((overview) => [
|
|
@@ -1770,32 +1665,31 @@ export function ConsoleAppV4(props) {
|
|
|
1770
1665
|
rootPluginId: route.pluginId,
|
|
1771
1666
|
...(expandedPluginIds ? { expandedPluginIds } : {}),
|
|
1772
1667
|
});
|
|
1773
|
-
const
|
|
1668
|
+
const layout = projectDependencyGraphView(projection, { columns: size.columns - 2,
|
|
1669
|
+
rowBudget: Math.max(1, size.rows - 7),
|
|
1670
|
+
focusRowId: stored?.focusedRowId,
|
|
1671
|
+
scrollAnchorRowId: stored?.scrollAnchorRowId });
|
|
1774
1672
|
const normalizedExpanded = expandedPluginIds ?? new Set([
|
|
1775
1673
|
...projection.dependencies.defaultExpandedPluginIds,
|
|
1776
1674
|
...projection.dependents.defaultExpandedPluginIds,
|
|
1777
1675
|
]);
|
|
1778
|
-
const
|
|
1779
|
-
|
|
1780
|
-
: 0;
|
|
1781
|
-
const nextFocus = focusedIndex >= 0 ? focusedIndex : 0;
|
|
1782
|
-
const anchorIndex = stored?.scrollAnchorActionId
|
|
1783
|
-
? actionIds.indexOf(stored.scrollAnchorActionId)
|
|
1784
|
-
: 0;
|
|
1785
|
-
const nextScroll = Math.max(0, anchorIndex >= 0 ? Math.min(anchorIndex, nextFocus) : 0);
|
|
1676
|
+
const nextFocus = layout.focusIndex;
|
|
1677
|
+
const nextScroll = layout.viewport.offset;
|
|
1786
1678
|
setExpandedDependencyPluginIds(normalizedExpanded);
|
|
1787
1679
|
setFocus(nextFocus);
|
|
1788
1680
|
setScrollOffset(nextScroll);
|
|
1789
1681
|
dependencyGraphContexts.current.set(key, {
|
|
1790
1682
|
expandedPluginIds: normalizedExpanded,
|
|
1791
|
-
|
|
1792
|
-
|
|
1683
|
+
focusedRowId: layout.focusRowId,
|
|
1684
|
+
scrollAnchorRowId: layout.scrollAnchorRowId,
|
|
1793
1685
|
});
|
|
1794
1686
|
}, [
|
|
1795
1687
|
props.controller,
|
|
1796
1688
|
route.name,
|
|
1797
|
-
route.name === 'pluginDependencies' ? route.pluginId : '',
|
|
1689
|
+
route.name === 'pluginDependencies' || route.name === 'portableDependencies' ? route.pluginId : '',
|
|
1798
1690
|
snapshot.viewModel,
|
|
1691
|
+
size.columns,
|
|
1692
|
+
size.rows,
|
|
1799
1693
|
]);
|
|
1800
1694
|
useEffect(() => {
|
|
1801
1695
|
if (route.name !== 'pluginList' || snapshot.phase !== 'ready'
|
|
@@ -1813,8 +1707,9 @@ export function ConsoleAppV4(props) {
|
|
|
1813
1707
|
}, [props.controller, route.name, route.name === 'pluginList' ? route.mode : '',
|
|
1814
1708
|
route.name === 'pluginList' ? route.sourceId : '', snapshot.phase]);
|
|
1815
1709
|
useLayoutEffect(() => {
|
|
1816
|
-
setBatchTargetOffset(0);
|
|
1817
|
-
}, [
|
|
1710
|
+
setBatchTargetOffset(snapshot.router.current.context.entityLineOffset ?? 0);
|
|
1711
|
+
}, [snapshot.router.currentFrameId, route.name,
|
|
1712
|
+
route.name === 'review' || route.name === 'result' ? route.operationId : '']);
|
|
1818
1713
|
const activeTopField = route.name === 'configFieldEditor'
|
|
1819
1714
|
|| route.name === 'configObjectPropertyEditor'
|
|
1820
1715
|
|| route.name === 'configFieldAction'
|
|
@@ -1980,29 +1875,30 @@ export function ConsoleAppV4(props) {
|
|
|
1980
1875
|
: [];
|
|
1981
1876
|
const portableVisibleCandidates = route.name === 'portableSkillSelect' && snapshot.portableDraft
|
|
1982
1877
|
? portableSelectionView === 'selected'
|
|
1983
|
-
? snapshot.portableDraft.candidates.filter((candidate) => candidate.selected
|
|
1984
|
-
|
|
1878
|
+
? snapshot.portableDraft.candidates.filter((candidate) => candidate.selected
|
|
1879
|
+
&& queryMatches(query, candidate.name, candidate.shortDescription
|
|
1880
|
+
?? candidate.description.split(/[。!?\n]/u)[0]))
|
|
1881
|
+
: snapshot.portableDraft.candidates.filter((candidate) => queryMatches(query, candidate.name, candidate.shortDescription ?? candidate.description.split(/[。!?\n]/u)[0]))
|
|
1985
1882
|
: [];
|
|
1986
|
-
const supportsTechnicalDetails =
|
|
1987
|
-
||
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
&& issue
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
&&
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
const canOpenHelp = !tooSmall
|
|
2005
|
-
&& !isCharacterInputMode(route, searchFocus);
|
|
1883
|
+
const supportsTechnicalDetails = !(route.name === 'pluginDependencies' && route.view !== 'removeBlocked'
|
|
1884
|
+
|| route.name === 'pluginDependencyNode' || route.name === 'portableDependencies')
|
|
1885
|
+
&& (snapshot.phase === 'error'
|
|
1886
|
+
|| route.name === 'projectStatus'
|
|
1887
|
+
|| Boolean(route.name === 'reviewPreparing' && snapshot.reviewPreparation?.code)
|
|
1888
|
+
|| Boolean(route.name === 'progress' && snapshot.progress.messages.length > 0)
|
|
1889
|
+
|| Boolean(route.name === 'review' && snapshot.activeReview?.technicalDetails?.length)
|
|
1890
|
+
|| Boolean(route.name === 'issueDetail' && issue
|
|
1891
|
+
&& issue.diagnostic.technical)
|
|
1892
|
+
|| Boolean(route.name === 'pluginDependencies' && plugin)
|
|
1893
|
+
|| route.name === 'catalogStoreRules'
|
|
1894
|
+
|| portableVariantCandidates.length > 0
|
|
1895
|
+
|| Boolean(route.name === 'portableBlocker' && plugin)
|
|
1896
|
+
|| Boolean(route.name === 'configUnavailable' && plugin)
|
|
1897
|
+
|| Boolean(route.name === 'result' && snapshot.result && 'operationKind' in snapshot.result
|
|
1898
|
+
&& snapshot.result.operationKind === 'portableInstall'));
|
|
1899
|
+
const characterInput = isCharacterInputMode(route, searchFocus)
|
|
1900
|
+
|| route.name === 'operationReportPreview' && reportSaveMode;
|
|
1901
|
+
const canOpenHelp = !tooSmall && !characterInput && !preparationRunning;
|
|
2006
1902
|
useEffect(() => {
|
|
2007
1903
|
props.onScreenChange?.(screenId);
|
|
2008
1904
|
}, [props.onScreenChange, screenId]);
|
|
@@ -2017,6 +1913,9 @@ export function ConsoleAppV4(props) {
|
|
|
2017
1913
|
let content;
|
|
2018
1914
|
let actions = [];
|
|
2019
1915
|
let readingText;
|
|
1916
|
+
let readerNavigation;
|
|
1917
|
+
const helpBodyRows = Math.max(1, size.rows - (props.onSessionNavigation ? 10 : 8));
|
|
1918
|
+
let configCardNavigation;
|
|
2020
1919
|
let confirmationText;
|
|
2021
1920
|
let referenceDetailPagination;
|
|
2022
1921
|
let confirmationPagination;
|
|
@@ -2028,26 +1927,23 @@ export function ConsoleAppV4(props) {
|
|
|
2028
1927
|
let movePluginListSource;
|
|
2029
1928
|
let choosePluginListFilter;
|
|
2030
1929
|
let choosePluginListSource;
|
|
1930
|
+
let toggleProjectStatusFilter;
|
|
1931
|
+
let moveProjectSection;
|
|
1932
|
+
let moveSourceType;
|
|
1933
|
+
let moveContentType;
|
|
1934
|
+
let entityCardNavigation;
|
|
1935
|
+
let graphLayout;
|
|
1936
|
+
let graphProjection;
|
|
1937
|
+
let graphRowBudget = 1;
|
|
2031
1938
|
let returnToPreviousInstallPlatform;
|
|
2032
1939
|
let visibleConfigTabs = [];
|
|
2033
1940
|
let activeConfigTabId;
|
|
2034
1941
|
const dynamicMenu = plugin && route.name === 'platform'
|
|
2035
|
-
? buildPlatformActions(plugin, route.platform)
|
|
1942
|
+
? buildPlatformActions(plugin, route.platform).filter((action) => action.id === 'addScope' || !('scope' in action) || action.scope === platformSelectedScope)
|
|
2036
1943
|
: undefined;
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
const retainedDynamicFocus = dynamicActionIds
|
|
2041
|
-
&& previousDynamicFocus?.routeKey === routeKey
|
|
2042
|
-
&& previousDynamicFocus.actionIds !== dynamicActionSignature
|
|
2043
|
-
&& previousDynamicFocus.focusedActionId !== undefined
|
|
2044
|
-
? dynamicActionIds.indexOf(previousDynamicFocus.focusedActionId)
|
|
2045
|
-
: -1;
|
|
2046
|
-
const effectiveFocus = dynamicActionIds
|
|
2047
|
-
&& previousDynamicFocus?.routeKey === routeKey
|
|
2048
|
-
&& previousDynamicFocus.actionIds !== dynamicActionSignature
|
|
2049
|
-
? retainedDynamicFocus >= 0 ? retainedDynamicFocus : 0
|
|
2050
|
-
: focus;
|
|
1944
|
+
let dynamicActionIds;
|
|
1945
|
+
let dynamicActionSignature;
|
|
1946
|
+
let effectiveFocus = focus;
|
|
2051
1947
|
const pageActionBudget = (staticRows = 0) => contentRowBudget({
|
|
2052
1948
|
terminalRows: size.rows,
|
|
2053
1949
|
description: Boolean(actionError ?? description),
|
|
@@ -2056,6 +1952,56 @@ export function ConsoleAppV4(props) {
|
|
|
2056
1952
|
+ Math.max(0, wrapConsoleText(actionError ?? description ?? '', size.columns - 2).length - 1)
|
|
2057
1953
|
+ Math.max(0, wrapConsoleText(title, size.columns - 2).length - 1),
|
|
2058
1954
|
});
|
|
1955
|
+
const detailCardContent = (subject, completionLines = []) => {
|
|
1956
|
+
const cardWidth = size.columns - 2;
|
|
1957
|
+
const card = pluginDetailCardLayout(subject, cardWidth);
|
|
1958
|
+
const availableRows = pageActionBudget(completionLines.length);
|
|
1959
|
+
// Compact actions use one row each; paging adds one row inside the card.
|
|
1960
|
+
const actionRows = Math.min(3, actions.filter((action) => action.role !== 'navigation').length);
|
|
1961
|
+
const needsPaging = card.fullHeight > availableRows - actionRows;
|
|
1962
|
+
const lineLimit = needsPaging ? Math.max(1, availableRows - actionRows - 3) : undefined;
|
|
1963
|
+
const pageCount = lineLimit ? Math.max(1, Math.ceil(card.factRows / lineLimit)) : 1;
|
|
1964
|
+
const page = lineLimit ? Math.min(pageCount, Math.floor(batchTargetOffset / lineLimit) + 1) : 1;
|
|
1965
|
+
const lineOffset = lineLimit ? (page - 1) * lineLimit : 0;
|
|
1966
|
+
if (lineLimit)
|
|
1967
|
+
readerNavigation = { page, pageCount, move: (direction) => {
|
|
1968
|
+
setBatchTargetOffset((Math.min(pageCount, Math.max(1, page + direction)) - 1) * lineLimit);
|
|
1969
|
+
} };
|
|
1970
|
+
const cardHeight = entityCardWindowHeight(card, lineOffset, lineLimit);
|
|
1971
|
+
currentActionViewportLineBudget = Math.max(1, pageActionBudget(completionLines.length) - cardHeight);
|
|
1972
|
+
const viewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
1973
|
+
help = [
|
|
1974
|
+
...(actions.length > 0 ? ['↑↓ 选择动作'] : []),
|
|
1975
|
+
...(needsPaging ? ['←→ 翻信息'] : []),
|
|
1976
|
+
...(actions[focus]?.run && !actions[focus]?.disabled ? ['Enter 选择'] : []),
|
|
1977
|
+
'Esc 返回',
|
|
1978
|
+
].join(' · ');
|
|
1979
|
+
return _jsxs(Box, { flexDirection: "column", children: [completionLines.map((line, index) => _jsx(Text, { color: tuiTheme.statusColors.success, children: line }, `completion:${index}`)), _jsx(PluginDetailCard, { subject: subject, width: cardWidth, lineOffset: lineOffset, lineLimit: lineLimit }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: viewport.offset, limit: viewport.limit })] });
|
|
1980
|
+
};
|
|
1981
|
+
const readingPage = (text, columns, rows, offset = 'batch') => {
|
|
1982
|
+
const current = offset === 'scroll' ? scrollOffset
|
|
1983
|
+
: offset === 'portable' ? portableResultDetailOffset : batchTargetOffset;
|
|
1984
|
+
const page = readerPage({ text, columns, rows, page: current + 1 });
|
|
1985
|
+
readerNavigation = {
|
|
1986
|
+
page: page.page,
|
|
1987
|
+
pageCount: page.pageCount,
|
|
1988
|
+
move: (direction) => {
|
|
1989
|
+
const next = Math.min(page.pageCount, Math.max(1, page.page + direction)) - 1;
|
|
1990
|
+
if (offset === 'scroll')
|
|
1991
|
+
setScrollOffset(next);
|
|
1992
|
+
else if (offset === 'portable')
|
|
1993
|
+
setPortableResultDetailOffset(next);
|
|
1994
|
+
else
|
|
1995
|
+
setBatchTargetOffset(next);
|
|
1996
|
+
},
|
|
1997
|
+
};
|
|
1998
|
+
return _jsx(Reader, { text: text, columns: columns, rows: rows, page: page.page });
|
|
1999
|
+
};
|
|
2000
|
+
const pagedItems = (total, rows, offset = batchTargetOffset) => paginateWrappedRows({
|
|
2001
|
+
lines: Array.from({ length: total }, (_, index) => String(index)),
|
|
2002
|
+
rows,
|
|
2003
|
+
page: Math.floor(offset / Math.max(1, rows)) + 1,
|
|
2004
|
+
});
|
|
2059
2005
|
const dependencyPlacementStaticRows = (policy.mode === 'compact' ? 3 : 10)
|
|
2060
2006
|
+ (snapshot.installPlacementDraft?.exactRootTargets?.length ?? 0);
|
|
2061
2007
|
const actionViewportStaticRows = route.name === 'dependencyPlacement'
|
|
@@ -2064,17 +2010,11 @@ export function ConsoleAppV4(props) {
|
|
|
2064
2010
|
: route.name === 'operationHistory' ? 4
|
|
2065
2011
|
: route.name === 'operationReportPreview' ? 5 : 0;
|
|
2066
2012
|
let currentActionViewportLineBudget = pageActionBudget(actionViewportStaticRows);
|
|
2067
|
-
let portableResultDetailPagination;
|
|
2068
2013
|
const responsiveBudget = (compactRows, standardRows) => {
|
|
2069
2014
|
const baseline = policy.mode === 'compact' ? 18 : 24;
|
|
2070
2015
|
const base = policy.mode === 'compact' ? compactRows : standardRows;
|
|
2071
2016
|
return base + Math.max(0, size.rows - baseline);
|
|
2072
2017
|
};
|
|
2073
|
-
const operationDetailPageRows = responsiveBudget(3, 6);
|
|
2074
|
-
const operationReportPageRows = responsiveBudget(3, 4);
|
|
2075
|
-
const sourceTechnicalPageRows = Math.max(1, contentRowBudget({
|
|
2076
|
-
terminalRows: size.rows, description: false, compact: policy.mode === 'compact', staticRows: 6,
|
|
2077
|
-
}));
|
|
2078
2018
|
const resolveSourceTechnicalDiagnostic = (sourceId) => {
|
|
2079
2019
|
const overview = props.controller.getSourceOverview(sourceId);
|
|
2080
2020
|
const freshness = props.controller.getCatalogFreshness(sourceId);
|
|
@@ -2122,8 +2062,10 @@ export function ConsoleAppV4(props) {
|
|
|
2122
2062
|
};
|
|
2123
2063
|
};
|
|
2124
2064
|
const moveBatchTargetPage = (total, pageRows, direction) => {
|
|
2125
|
-
|
|
2126
|
-
|
|
2065
|
+
setBatchTargetOffset((value) => {
|
|
2066
|
+
const page = pagedItems(total, pageRows, value);
|
|
2067
|
+
return pagedItems(total, pageRows, (page.page + direction - 1) * pageRows).offset;
|
|
2068
|
+
});
|
|
2127
2069
|
};
|
|
2128
2070
|
const pluginListRowIds = (_mode, items, _queryValue = query, _sourceUnavailable = false) => {
|
|
2129
2071
|
if (items.length === 0)
|
|
@@ -2132,19 +2074,31 @@ export function ConsoleAppV4(props) {
|
|
|
2132
2074
|
};
|
|
2133
2075
|
const go = (next) => {
|
|
2134
2076
|
props.controller.updatePageContext({
|
|
2135
|
-
focusIndex: focus,
|
|
2077
|
+
focusIndex: route.name === 'projectStatus' || route.name === 'platform' ? effectiveFocus : focus,
|
|
2136
2078
|
scrollOffset,
|
|
2079
|
+
entityLineOffset: entityCardNavigation || route.name === 'pluginDetail' ? batchTargetOffset : undefined,
|
|
2137
2080
|
query,
|
|
2138
2081
|
installFilter,
|
|
2139
2082
|
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
2140
2083
|
focusedEntityId: route.name === 'pluginList'
|
|
2141
2084
|
? pluginRows[focus]?.pluginId
|
|
2142
|
-
: route.name === '
|
|
2143
|
-
: route.name === '
|
|
2085
|
+
: route.name === 'pluginContent' ? actions[focus]?.actionId?.replace(/^content:/u, '')
|
|
2086
|
+
: route.name === 'batchSelection' ? actions[focus]?.pluginId
|
|
2087
|
+
: route.name === 'projectStatus' ? actions[effectiveFocus]?.pluginId
|
|
2088
|
+
: route.name === 'sourceList' ? actions[focus]?.actionId?.replace(/^source:/u, '')
|
|
2089
|
+
: route.name === 'portableSkillSelect' ? actions[focus]?.actionId : undefined,
|
|
2090
|
+
...(route.name === 'projectStatus' || route.name === 'platform'
|
|
2091
|
+
? { focusedActionId: actions[effectiveFocus]?.actionId } : {}),
|
|
2144
2092
|
});
|
|
2145
2093
|
props.controller.push(next);
|
|
2094
|
+
if (route.name === 'pluginContent')
|
|
2095
|
+
setContentReader(undefined);
|
|
2146
2096
|
setFocus(0);
|
|
2097
|
+
if (next.name === 'pluginDetail' || next.name === 'sourceDetail'
|
|
2098
|
+
|| next.name === 'projectStatus')
|
|
2099
|
+
setBatchTargetOffset(0);
|
|
2147
2100
|
setScrollOffset(0);
|
|
2101
|
+
setQuery('');
|
|
2148
2102
|
setSearchFocus(false);
|
|
2149
2103
|
if (next.name === 'pluginList') {
|
|
2150
2104
|
setInstallFilter(next.mode === 'installed' ? 'installed' : 'all');
|
|
@@ -2170,6 +2124,13 @@ export function ConsoleAppV4(props) {
|
|
|
2170
2124
|
setFocus(portableResultDetailReturnFocus.current);
|
|
2171
2125
|
setPortableResultDetailOpen(false);
|
|
2172
2126
|
setPortableResultDetailOffset(0);
|
|
2127
|
+
if (route.name === 'projectStatus' && dynamicActionIds && dynamicActionSignature !== undefined) {
|
|
2128
|
+
const recheck = actions.findIndex((action) => action.actionId === 'project-recheck');
|
|
2129
|
+
dynamicMenuFocus.current = { routeKey, actionIds: dynamicActionSignature,
|
|
2130
|
+
focusedActionId: 'project-recheck' };
|
|
2131
|
+
setFocus(Math.max(0, recheck));
|
|
2132
|
+
setScrollOffset(0);
|
|
2133
|
+
}
|
|
2173
2134
|
setShowTechnical((value) => !value);
|
|
2174
2135
|
if (!(route.name === 'result' && snapshot.result
|
|
2175
2136
|
&& 'operationKind' in snapshot.result
|
|
@@ -2198,9 +2159,25 @@ export function ConsoleAppV4(props) {
|
|
|
2198
2159
|
: Math.min(restoredFocus, Math.max(0, rowIds.length - 1));
|
|
2199
2160
|
}
|
|
2200
2161
|
else if (restoredFrame.route.name === 'projectStatus') {
|
|
2201
|
-
const
|
|
2202
|
-
|
|
2203
|
-
|
|
2162
|
+
const actionId = restored.focusedActionId
|
|
2163
|
+
?? (restored.focusedEntityId ? `project-root:${restored.focusedEntityId}` : 'project-recheck');
|
|
2164
|
+
dynamicMenuFocus.current = {
|
|
2165
|
+
routeKey: JSON.stringify(restoredFrame.route), actionIds: '', focusedActionId: actionId,
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
else if (restoredFrame.route.name === 'platform') {
|
|
2169
|
+
dynamicMenuFocus.current = {
|
|
2170
|
+
routeKey: JSON.stringify(restoredFrame.route), actionIds: '',
|
|
2171
|
+
focusedActionId: restored.focusedActionId,
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
else if (restoredFrame.route.name === 'pluginContent') {
|
|
2175
|
+
const overview = props.controller.getPluginContentOverview(restoredFrame.route.pluginId);
|
|
2176
|
+
const items = (overview?.items ?? []).filter((item) => contentMatches(restored.query ?? '', item)
|
|
2177
|
+
&& (restored.contentTypeTab === undefined || restored.contentTypeTab === 'all'
|
|
2178
|
+
|| item.type === restored.contentTypeTab));
|
|
2179
|
+
const index = items.findIndex((item) => item.id === restored.focusedEntityId);
|
|
2180
|
+
itemCount = items.length + 2;
|
|
2204
2181
|
restoredFocus = index >= 0 ? index : Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2205
2182
|
}
|
|
2206
2183
|
else if (restoredFrame.route.name === 'batchSelection' && batchDraft) {
|
|
@@ -2210,7 +2187,7 @@ export function ConsoleAppV4(props) {
|
|
|
2210
2187
|
restoredFocus = entityIndex >= 0 ? entityIndex : Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2211
2188
|
}
|
|
2212
2189
|
else if (restoredFrame.route.name === 'sourceList') {
|
|
2213
|
-
const sources = props.controller.
|
|
2190
|
+
const sources = matchingSources(props.controller, restored.query ?? '', restored.sourceTypeTab ?? 'all');
|
|
2214
2191
|
itemCount = sources.length + 3;
|
|
2215
2192
|
const entityIndex = restored.focusedEntityId
|
|
2216
2193
|
? sources.findIndex((source) => source.id === restored.focusedEntityId)
|
|
@@ -2219,13 +2196,25 @@ export function ConsoleAppV4(props) {
|
|
|
2219
2196
|
? entityIndex
|
|
2220
2197
|
: Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2221
2198
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2199
|
+
else if (restoredFrame.route.name === 'portableSkillSelect' && snapshot.portableDraft) {
|
|
2200
|
+
const candidates = snapshot.portableDraft.candidates.filter((candidate) => queryMatches(restored.query ?? '', candidate.name, candidate.shortDescription ?? candidate.description.split(/[。!?\n]/u)[0]));
|
|
2201
|
+
const entityIndex = candidates.findIndex((candidate) => `skill:${candidate.providerPluginId}:${candidate.name}` === restored.focusedEntityId);
|
|
2202
|
+
itemCount = candidates.length + 2;
|
|
2203
|
+
restoredFocus = entityIndex >= 0 ? entityIndex : Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2204
|
+
}
|
|
2205
|
+
const dynamicMenuRoute = restoredFrame.route.name === 'projectStatus' || restoredFrame.route.name === 'platform';
|
|
2206
|
+
let restoredScroll = dynamicMenuRoute
|
|
2207
|
+
? restored.scrollOffset ?? 0
|
|
2208
|
+
: Math.min(restored.scrollOffset ?? 0, Math.max(0, (itemCount ?? 1) - 1));
|
|
2209
|
+
if (!dynamicMenuRoute) {
|
|
2210
|
+
if (restoredFocus < restoredScroll)
|
|
2211
|
+
restoredScroll = restoredFocus;
|
|
2212
|
+
else if (restoredFocus >= restoredScroll + 4)
|
|
2213
|
+
restoredScroll = Math.max(0, restoredFocus - 3);
|
|
2214
|
+
}
|
|
2227
2215
|
setFocus(restoredFocus);
|
|
2228
2216
|
setScrollOffset(restoredScroll);
|
|
2217
|
+
setBatchTargetOffset(restored.entityLineOffset ?? 0);
|
|
2229
2218
|
setQuery(restored.query ?? '');
|
|
2230
2219
|
setInstallFilter(restored.installFilter ?? 'all');
|
|
2231
2220
|
setSearchFocus(false);
|
|
@@ -2241,6 +2230,7 @@ export function ConsoleAppV4(props) {
|
|
|
2241
2230
|
props.controller.beginConfigFlow(pluginId);
|
|
2242
2231
|
setFocus(0);
|
|
2243
2232
|
setScrollOffset(0);
|
|
2233
|
+
setQuery('');
|
|
2244
2234
|
setSearchFocus(false);
|
|
2245
2235
|
setShowTechnical(false);
|
|
2246
2236
|
setActionError(undefined);
|
|
@@ -2260,6 +2250,8 @@ export function ConsoleAppV4(props) {
|
|
|
2260
2250
|
setActionError(undefined);
|
|
2261
2251
|
};
|
|
2262
2252
|
const back = () => {
|
|
2253
|
+
if (route.name === 'pluginContent')
|
|
2254
|
+
setContentReader(undefined);
|
|
2263
2255
|
if (route.name === 'sourceEdit'
|
|
2264
2256
|
&& sourceDraft
|
|
2265
2257
|
&& sourceDraft.owner === route.owner
|
|
@@ -2302,11 +2294,13 @@ export function ConsoleAppV4(props) {
|
|
|
2302
2294
|
? snapshot.result.returnRoute
|
|
2303
2295
|
: undefined);
|
|
2304
2296
|
props.controller.updatePageContext({
|
|
2305
|
-
focusIndex: focus,
|
|
2297
|
+
focusIndex: route.name === 'projectStatus' || route.name === 'platform' ? effectiveFocus : focus,
|
|
2306
2298
|
scrollOffset,
|
|
2307
2299
|
query,
|
|
2308
2300
|
installFilter,
|
|
2309
2301
|
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
2302
|
+
...(route.name === 'projectStatus' || route.name === 'platform'
|
|
2303
|
+
? { focusedActionId: actions[effectiveFocus]?.actionId } : {}),
|
|
2310
2304
|
});
|
|
2311
2305
|
props.controller.startReviewPreparation(intent, underlyingResultRoute);
|
|
2312
2306
|
setFocus(0);
|
|
@@ -2404,9 +2398,10 @@ export function ConsoleAppV4(props) {
|
|
|
2404
2398
|
: draft.action === undefined
|
|
2405
2399
|
&& !props.controller.pluginHasInstallableTarget(candidate.plugin.pluginId)
|
|
2406
2400
|
&& props.controller.listPluginWriteBatchTargets('uninstall', [candidate.plugin.pluginId]).length === 0
|
|
2401
|
+
&& props.controller.listPluginWriteBatchTargets('cleanup', [candidate.plugin.pluginId]).length === 0
|
|
2407
2402
|
&& !batchTaskPluginIds.has(candidate.plugin.pluginId)
|
|
2408
2403
|
? '没有可用于批量处理的实际目标'
|
|
2409
|
-
: draft.action === 'install' && candidate.plugin.
|
|
2404
|
+
: draft.action === 'install' && candidate.plugin.catalogEntryMissing
|
|
2410
2405
|
? '来源中没有可信插件定义'
|
|
2411
2406
|
: draft.action === 'install' && candidate.plugin.sourceUnavailable
|
|
2412
2407
|
? '插件来源不可用'
|
|
@@ -2425,13 +2420,71 @@ export function ConsoleAppV4(props) {
|
|
|
2425
2420
|
...(byId.get(pluginId) ? { plugin: byId.get(pluginId) } : {}),
|
|
2426
2421
|
}) !== undefined);
|
|
2427
2422
|
};
|
|
2428
|
-
const persistUnifiedDependencyGraphContext = (pluginId,
|
|
2429
|
-
|
|
2430
|
-
dependencyGraphContexts.current.set(dependencyGraphContextKey(pluginId, 'dependencies'), {
|
|
2423
|
+
const persistUnifiedDependencyGraphContext = (pluginId, layout, expandedPluginIds, focusIndex, offset, portable = false) => {
|
|
2424
|
+
dependencyGraphContexts.current.set(dependencyGraphContextKey(pluginId, portable ? 'portable' : 'dependencies'), {
|
|
2431
2425
|
expandedPluginIds: new Set(expandedPluginIds),
|
|
2432
|
-
...(
|
|
2433
|
-
...(
|
|
2426
|
+
...(layout.rows[focusIndex] ? { focusedRowId: layout.rows[focusIndex].id } : {}),
|
|
2427
|
+
...(layout.rows[offset] ? { scrollAnchorRowId: layout.rows[offset].id } : {}),
|
|
2428
|
+
});
|
|
2429
|
+
};
|
|
2430
|
+
const renderDependencyGraph = (pluginId, portable) => {
|
|
2431
|
+
graphProjection = buildUnifiedDependencyGraphProjection({
|
|
2432
|
+
overviews: props.controller.listPluginDependencyOverviews(),
|
|
2433
|
+
rootPluginId: pluginId,
|
|
2434
|
+
...(expandedDependencyPluginIds ? { expandedPluginIds: expandedDependencyPluginIds } : {}),
|
|
2435
|
+
});
|
|
2436
|
+
graphRowBudget = pageActionBudget(3
|
|
2437
|
+
+ Number(portable && snapshot.portableDraft !== undefined)
|
|
2438
|
+
+ (graphProjection.status.state === 'invalid' ? 4 : 0));
|
|
2439
|
+
const ordered = projectDependencyGraphView(graphProjection, {
|
|
2440
|
+
columns: size.columns - 2, rowBudget: graphRowBudget,
|
|
2434
2441
|
});
|
|
2442
|
+
const currentId = ordered.rows[focus]?.id;
|
|
2443
|
+
const anchorId = ordered.rows[scrollOffset]?.id;
|
|
2444
|
+
graphLayout = projectDependencyGraphView(graphProjection, {
|
|
2445
|
+
columns: size.columns - 2, rowBudget: graphRowBudget,
|
|
2446
|
+
focusRowId: currentId, scrollAnchorRowId: anchorId,
|
|
2447
|
+
});
|
|
2448
|
+
const layout = graphLayout;
|
|
2449
|
+
const projection = graphProjection;
|
|
2450
|
+
actions = layout.rows.map((row) => ({
|
|
2451
|
+
actionId: row.id,
|
|
2452
|
+
label: row.node.displayName,
|
|
2453
|
+
run: () => {
|
|
2454
|
+
persistUnifiedDependencyGraphContext(pluginId, layout, expandedDependencyPluginIds ?? new Set([
|
|
2455
|
+
...projection.dependencies.defaultExpandedPluginIds,
|
|
2456
|
+
...projection.dependents.defaultExpandedPluginIds,
|
|
2457
|
+
]), focus, layout.viewport.offset, portable);
|
|
2458
|
+
setGraphEvidenceOpen(false);
|
|
2459
|
+
setGraphPortableSelectionOpen(false);
|
|
2460
|
+
go({ name: 'pluginDetail', pluginId: row.node.pluginId });
|
|
2461
|
+
},
|
|
2462
|
+
}));
|
|
2463
|
+
const selected = layout.rows[focus] ?? layout.rows[layout.focusIndex];
|
|
2464
|
+
if (portable && graphPortableSelectionOpen && snapshot.portableDraft) {
|
|
2465
|
+
readingText = [
|
|
2466
|
+
`本次选择 ${snapshot.portableDraft.selectedCount} 个 Skills`,
|
|
2467
|
+
...snapshot.portableDraft.candidates.filter((candidate) => candidate.selected)
|
|
2468
|
+
.map((candidate) => `${candidate.name} · ${candidate.providerDisplayName} · ${candidate.providerPluginId}`),
|
|
2469
|
+
'安装位置在最终汇总中确认。',
|
|
2470
|
+
].join('\n');
|
|
2471
|
+
content = _jsx(Box, { flexDirection: "column", children: readingPage(readingText, size.columns - 2, pageActionBudget(2)) });
|
|
2472
|
+
help = '←→/PgUp/PgDn 翻页 · y 复制全文 · Esc 返回关系图';
|
|
2473
|
+
}
|
|
2474
|
+
else if (graphEvidenceOpen && selected?.node.edge) {
|
|
2475
|
+
readingText = [
|
|
2476
|
+
`${selected.node.displayName} · ${selected.node.edge.declared ? '已声明' : '当前已验证'}`,
|
|
2477
|
+
...dependencyEvidenceLines(selected.node, props.controller.getPluginDetailSubject(selected.node.pluginId)),
|
|
2478
|
+
].join('\n');
|
|
2479
|
+
content = _jsx(Box, { flexDirection: "column", children: readingPage(readingText, size.columns - 2, pageActionBudget(2)) });
|
|
2480
|
+
help = '←→/PgUp/PgDn 翻页 · y 复制全文 · Esc 返回关系图';
|
|
2481
|
+
}
|
|
2482
|
+
else {
|
|
2483
|
+
const selectedHasEdge = selected?.node.edge !== undefined;
|
|
2484
|
+
help = `↑↓ 选择节点 · → 展开 · ← 收起 · Enter 插件详情${selectedHasEdge ? ' · r 引用证据' : ''}${portable ? ' · p 已选 Skills' : ''} · Esc 返回`;
|
|
2485
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: "\u68C0\u67E5\u8303\u56F4", value: "\u5F53\u524D\u9879\u76EE\u548C\u4E2A\u4EBA\u4F4D\u7F6E\uFF1B\u5176\u4ED6\u9879\u76EE\u672A\u786E\u8BA4", status: "muted", compact: true }), projection.status.state === 'invalid'
|
|
2486
|
+
? _jsx(Callout, { status: "error", title: "\u4F9D\u8D56\u5173\u7CFB\u65E0\u6548", description: projection.status.message }) : null, _jsx(DependencyGraphView, { projection: projection, columns: size.columns - 2, rowBudget: graphRowBudget, focusRowId: layout.focusRowId, scrollAnchorRowId: layout.scrollAnchorRowId }), selectedHasEdge ? _jsxs(Box, { width: size.columns - 2, justifyContent: "space-between", children: [_jsx(Text, { color: tuiTheme.colors.muted, children: "\u67E5\u770B\u5F53\u524D\u8FB9\u7684\u5B8C\u6574\u4F4D\u7F6E\u5F15\u7528" }), _jsx(KeyCap, { children: "r" })] }) : null, portable && snapshot.portableDraft ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u672C\u6B21\u9009\u62E9 ", snapshot.portableDraft.selectedCount, " \u4E2A Skills\uFF1B\u5B89\u88C5\u4F4D\u7F6E\u5728\u6700\u7EC8\u6C47\u603B\u786E\u8BA4\u3002"] }) : null] });
|
|
2487
|
+
}
|
|
2435
2488
|
};
|
|
2436
2489
|
const toggleBatchPlugin = (pluginId) => {
|
|
2437
2490
|
setBatchDraft((current) => current ? {
|
|
@@ -2441,19 +2494,6 @@ export function ConsoleAppV4(props) {
|
|
|
2441
2494
|
: [...current.selectedPluginIds, pluginId],
|
|
2442
2495
|
} : current);
|
|
2443
2496
|
};
|
|
2444
|
-
const prepareCurrentProjectPlugins = () => {
|
|
2445
|
-
if (props.controller.listCurrentProjectIntentRoots().length === 0) {
|
|
2446
|
-
setActionError('当前项目没有已保存的插件清单。');
|
|
2447
|
-
return;
|
|
2448
|
-
}
|
|
2449
|
-
beginReview({
|
|
2450
|
-
kind: 'pluginWriteBatch',
|
|
2451
|
-
action: 'sync',
|
|
2452
|
-
selection: { kind: 'currentProjectIntentRoots' },
|
|
2453
|
-
platform: 'both',
|
|
2454
|
-
scope: 'project',
|
|
2455
|
-
});
|
|
2456
|
-
};
|
|
2457
2497
|
const prepareCurrentProjectTargets = (targets) => {
|
|
2458
2498
|
if (targets.length === 0) {
|
|
2459
2499
|
setActionError('当前项目没有已确认可同步的目标。');
|
|
@@ -3427,7 +3467,7 @@ export function ConsoleAppV4(props) {
|
|
|
3427
3467
|
const plugins = props.controller.listPlugins({ mode: 'browse' });
|
|
3428
3468
|
const attention = new Set(plugins.filter((entry) => entry.primaryAttention).map((entry) => entry.pluginId)).size;
|
|
3429
3469
|
const unknown = new Set(plugins.filter((entry) => entry.supportedPlatforms.some((platform) => pluginPlatformUnknown(entry, platform))).map((entry) => entry.pluginId)).size;
|
|
3430
|
-
title = '';
|
|
3470
|
+
title = '管理 Agent Workflows 插件';
|
|
3431
3471
|
description = undefined;
|
|
3432
3472
|
context = undefined;
|
|
3433
3473
|
const pluginState = [home.pluginUpdateCount ? `↑ ${home.pluginUpdateCount} 可更新` : '',
|
|
@@ -3438,13 +3478,15 @@ export function ConsoleAppV4(props) {
|
|
|
3438
3478
|
: [inventory.counts.blocked ? `! ${inventory.counts.blocked} 受阻` : '',
|
|
3439
3479
|
inventory.counts.pending ? `↻ ${inventory.counts.pending} 待同步` : '',
|
|
3440
3480
|
inventory.counts.unknown ? `? ${inventory.counts.unknown} 未确认` : ''].filter(Boolean).join(' · ') || '✓ 全部就绪';
|
|
3481
|
+
const visibleProjectState = snapshot.projectInventoryRefresh.state === 'error'
|
|
3482
|
+
? `! 项目检查失败,显示上次检查结果 · ${projectState}` : projectState;
|
|
3441
3483
|
const sourceState = [home.catalogUpdateCount ? `↑ ${home.catalogUpdateCount} 有更新` : '',
|
|
3442
3484
|
home.catalogRecoveryCount ? `! ${home.catalogRecoveryCount} 检查失败` : '',
|
|
3443
3485
|
home.unavailableSourceCount ? `! ${home.unavailableSourceCount} 不可用` : '',
|
|
3444
3486
|
home.catalogCheckingCount ? `${home.catalogCheckingCount} 检查中` : ''].filter(Boolean).join(' · ') || '✓ 当前可用';
|
|
3445
3487
|
actions = [
|
|
3446
3488
|
{ label: '插件', description: pluginState, run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
3447
|
-
{ label: '项目插件', description:
|
|
3489
|
+
{ label: '项目插件', description: visibleProjectState, run: () => go({ name: 'projectStatus' }) },
|
|
3448
3490
|
{ label: '插件来源', description: sourceState, run: () => go({ name: 'sourceList' }) },
|
|
3449
3491
|
];
|
|
3450
3492
|
content = _jsx(Box, { flexDirection: "column", children: actions.map((action, index) => _jsx(Box, { marginBottom: 1, children: _jsx(HomeNavTile, { width: size.columns - 2, title: action.label, focused: !projectReminderOpen && focus === index, children: _jsx(Text, { wrap: "wrap", children: action.description?.split(' · ').map((state, stateIndex) => _jsxs(Text, { color: state.startsWith('↑') ? tuiTheme.roles.change : state.startsWith('!') || state.startsWith('↻') ? tuiTheme.roles.warning
|
|
@@ -3453,54 +3495,123 @@ export function ConsoleAppV4(props) {
|
|
|
3453
3495
|
else if (route.name === 'projectStatus') {
|
|
3454
3496
|
const inventory = snapshot.projectInventory;
|
|
3455
3497
|
const section = snapshot.router.current.context.projectSection ?? 'declared';
|
|
3498
|
+
const statusFilter = snapshot.router.current.context.projectStatusFilter ?? 'all';
|
|
3456
3499
|
const stateLabel = { blocked: '受阻', pending: '待同步', unknown: '未确认', ready: '就绪' };
|
|
3457
|
-
const
|
|
3500
|
+
const searchedRoots = props.controller.listProjectInventoryRoots(section).filter((root) => queryMatches(query, root.displayName, root.pluginId, root.sourceId));
|
|
3501
|
+
const roots = searchedRoots.filter((root) => statusFilter === 'all' || root.state === statusFilter);
|
|
3502
|
+
const statusCounts = Object.fromEntries(PROJECT_STATUS_FILTERS.map((filter) => [filter,
|
|
3503
|
+
filter === 'all' ? searchedRoots.length : searchedRoots.filter((root) => root.state === filter).length,
|
|
3504
|
+
]));
|
|
3505
|
+
toggleProjectStatusFilter = () => {
|
|
3506
|
+
const available = PROJECT_STATUS_FILTERS.filter((filter) => filter === 'all' || filter === statusFilter || statusCounts[filter] > 0);
|
|
3507
|
+
const next = available[(available.indexOf(statusFilter) + 1) % available.length];
|
|
3508
|
+
props.controller.updatePageContext({ projectStatusFilter: next });
|
|
3509
|
+
dynamicMenuFocus.current = undefined;
|
|
3510
|
+
actionJumpAnchor.current = undefined;
|
|
3511
|
+
setFocus(0);
|
|
3512
|
+
setScrollOffset(0);
|
|
3513
|
+
};
|
|
3514
|
+
moveProjectSection = () => {
|
|
3515
|
+
const next = section === 'declared' ? 'extraInstalled' : 'declared';
|
|
3516
|
+
props.controller.updatePageContext({ projectSection: next });
|
|
3517
|
+
dynamicMenuFocus.current = undefined;
|
|
3518
|
+
actionJumpAnchor.current = undefined;
|
|
3519
|
+
setFocus(0);
|
|
3520
|
+
setScrollOffset(0);
|
|
3521
|
+
};
|
|
3458
3522
|
breadcrumb = '项目插件';
|
|
3459
3523
|
title = showTechnical ? '项目状态安全详情' : inventory?.projectName ?? '项目插件';
|
|
3460
3524
|
context = undefined;
|
|
3461
|
-
description =
|
|
3462
|
-
|
|
3463
|
-
|
|
3525
|
+
description = snapshot.projectInventoryRefresh.state === 'error'
|
|
3526
|
+
? '项目检查失败;以下显示上次检查结果。可重新检查。'
|
|
3527
|
+
: inventory?.declarationState === 'notProject' ? '当前不是项目;没有项目同步目标。'
|
|
3528
|
+
: inventory?.declarationState === 'absent' ? '未配置项目插件。'
|
|
3464
3529
|
: inventory?.declarationState === 'invalid' || inventory?.declarationState === 'unavailable' ? '项目声明需要检查。' : undefined;
|
|
3465
3530
|
actions = roots.map((root) => ({
|
|
3466
3531
|
actionId: `project-root:${root.pluginId}`, pluginId: root.pluginId,
|
|
3467
3532
|
label: root.displayName,
|
|
3468
|
-
description: [root.targets.map((target) => `${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} · ${stateLabel[target.state]}`).join(';'),
|
|
3469
|
-
...(root.dependencies.some((dependency) => dependency.targets.some((target) => target.location === 'sharedUser'))
|
|
3470
|
-
? [`${root.dependencies.filter((dependency) => dependency.targets.some((target) => target.location === 'sharedUser')).length} 个依赖共用个人安装`] : [])].join('\n'),
|
|
3471
|
-
maxDescriptionRows: 2,
|
|
3472
|
-
sourceLabel: root.sourceId,
|
|
3473
3533
|
run: () => go({ name: 'pluginDetail', pluginId: root.pluginId }),
|
|
3474
3534
|
}));
|
|
3475
3535
|
const syncTargets = inventory?.syncTargets ?? [];
|
|
3476
3536
|
const syncCount = new Set(syncTargets.map((target) => target.pluginId)).size;
|
|
3537
|
+
const projectRestoreIds = [...new Set(props.controller.listPluginWriteTaskCandidates('restore')
|
|
3538
|
+
.filter((candidate) => candidate.target.scope === 'project')
|
|
3539
|
+
.map((candidate) => candidate.target.pluginId))];
|
|
3477
3540
|
bottomActions = [
|
|
3478
3541
|
...(inventory?.projectRoot && syncCount > 0 ? [{ actionId: 'project-sync-actionable',
|
|
3479
|
-
label:
|
|
3480
|
-
|
|
3481
|
-
...(
|
|
3542
|
+
label: `同步全部可处理声明(${syncCount}) [c]`, status: 'info',
|
|
3543
|
+
run: () => prepareCurrentProjectTargets(syncTargets) }] : []),
|
|
3544
|
+
...(projectRestoreIds.length > 0 ? [{ actionId: 'project-restore',
|
|
3545
|
+
label: `修复当前项目(${projectRestoreIds.length})`,
|
|
3546
|
+
run: () => openSelectedPluginTask('restore', projectRestoreIds, 'project') }] : []),
|
|
3547
|
+
...(() => {
|
|
3548
|
+
const issues = props.controller.listUserIssues().filter((issue) => props.controller.listProjectInventoryRoots('declared').some((root) => issueMatchesTarget(issue, root.pluginId)));
|
|
3549
|
+
if (issues.length === 0)
|
|
3550
|
+
return [];
|
|
3551
|
+
const focusedRoot = roots[focus];
|
|
3552
|
+
const selectedIssue = focusedRoot
|
|
3553
|
+
? issues.find((issue) => issueMatchesTarget(issue, focusedRoot.pluginId)) : undefined;
|
|
3554
|
+
const issue = selectedIssue ?? (issues.length === 1 ? issues[0] : undefined);
|
|
3555
|
+
return [{ actionId: 'project-open-issues',
|
|
3556
|
+
label: issue ? '查看对应问题' : '查看需要处理',
|
|
3557
|
+
run: () => go(issue
|
|
3558
|
+
? { name: 'issueDetail', issueId: issue.issueId } : { name: 'issueList' }) }];
|
|
3559
|
+
})(),
|
|
3560
|
+
...(!showTechnical && (inventory?.counts.blocked || inventory?.counts.unknown || snapshot.projectIntentIssue) ? [{ actionId: 'project-error-details', label: '错误详情', run: toggleTechnical }] : []),
|
|
3482
3561
|
{ actionId: 'project-recheck', label: snapshot.projectInventoryRefresh.state === 'checking' ? '正在检查项目' : '重新检查项目 [r]',
|
|
3483
3562
|
disabled: snapshot.projectInventoryRefresh.state === 'checking' ? '检查中' : undefined,
|
|
3484
3563
|
run: async () => { await props.controller.refreshProjectInventory(); } },
|
|
3485
3564
|
];
|
|
3486
3565
|
const technicalText = [snapshot.projectIntentIssue ? `${safeDiagnosticCode(snapshot.projectIntentIssue.code, 'projectIntentUnknown')} · ${sanitizeDiagnosticText(snapshot.projectIntentIssue.message)}` : '',
|
|
3487
|
-
...
|
|
3566
|
+
...props.controller.listProjectInventoryRoots(section).flatMap((root) => [...projectDependencyLines(root), ...root.targets.flatMap((target) => [
|
|
3488
3567
|
`${root.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
3489
|
-
|
|
3568
|
+
`目录参考 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'} · ${stateLabel[target.state]}`,
|
|
3490
3569
|
...target.reasonCodes.map((code) => `原因 · ${safeDiagnosticCode(code, 'projectTargetUnknown')}`)
|
|
3491
3570
|
])])].filter(Boolean).join('\n');
|
|
3492
3571
|
const itemCount = showTechnical ? 0 : actions.length;
|
|
3493
3572
|
actions = [...(showTechnical ? [] : actions), ...bottomActions];
|
|
3573
|
+
dynamicActionIds = actions.map((action) => action.actionId);
|
|
3574
|
+
dynamicActionSignature = dynamicActionIds.join('\0');
|
|
3575
|
+
const previousProjectFocus = dynamicMenuFocus.current;
|
|
3576
|
+
if (previousProjectFocus?.routeKey === routeKey
|
|
3577
|
+
&& previousProjectFocus.actionIds !== dynamicActionSignature) {
|
|
3578
|
+
const retained = previousProjectFocus.focusedActionId
|
|
3579
|
+
? dynamicActionIds.indexOf(previousProjectFocus.focusedActionId) : -1;
|
|
3580
|
+
effectiveFocus = retained >= 0 ? retained
|
|
3581
|
+
: itemCount > 0 ? 0 : dynamicActionIds.indexOf('project-recheck');
|
|
3582
|
+
}
|
|
3494
3583
|
help = '↑↓ 移动 · Enter 查看 · Esc 返回';
|
|
3495
3584
|
if (showTechnical) {
|
|
3496
3585
|
readingText = technicalText;
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3586
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(technicalText, size.columns - 2, Math.max(1, pageActionBudget(4) - 1)), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: true })] });
|
|
3587
|
+
}
|
|
3588
|
+
else {
|
|
3589
|
+
const cardWidth = size.columns - 2;
|
|
3590
|
+
const lineLimit = Math.max(1, Math.min(3, pageActionBudget(3) - 4));
|
|
3591
|
+
const cardSubjects = roots.map((root) => props.controller.getPluginDetailSubject(root.pluginId));
|
|
3592
|
+
const cardLayouts = roots.map((root, index) => projectRootCardLayout(root, cardSubjects[index], cardWidth));
|
|
3593
|
+
entityCardNavigation = { itemCount, factRows: cardLayouts.map((card) => card.factRows), lineLimit };
|
|
3594
|
+
const emptyHint = roots.length === 0 && inventory?.declarationState === 'ready'
|
|
3595
|
+
&& Boolean(searchedRoots.length > 0 || query || statusFilter !== 'all');
|
|
3596
|
+
const projectViewport = rowViewport({
|
|
3597
|
+
rowHeights: [...cardLayouts.map((card, index) => entityCardWindowHeight(card, index === effectiveFocus ? batchTargetOffset : 0, lineLimit)), ...bottomActions.map(() => 1)],
|
|
3598
|
+
focus: effectiveFocus, requestedOffset: scrollOffset,
|
|
3599
|
+
rowBudget: pageActionBudget(3 + Number(emptyHint)),
|
|
3600
|
+
});
|
|
3601
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, tabs: { items: [
|
|
3602
|
+
{ id: 'declared', label: '项目声明', count: props.controller.listProjectInventoryRoots('declared').filter((root) => queryMatches(query, root.displayName, root.pluginId, root.sourceId)).length },
|
|
3603
|
+
{ id: 'extraInstalled', label: '额外安装', count: props.controller.listProjectInventoryRoots('extraInstalled').filter((root) => queryMatches(query, root.displayName, root.pluginId, root.sourceId)).length },
|
|
3604
|
+
], currentId: section }, search: { value: query, placeholder: '名称或来源', input: searchFocus
|
|
3605
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: querySearchWidth(size.columns - 2) }) : undefined }, filter: { label: PROJECT_STATUS_LABELS[statusFilter], count: statusCounts[statusFilter],
|
|
3606
|
+
currentZero: statusCounts[statusFilter] === 0 && statusFilter !== 'all' } }), emptyHint
|
|
3607
|
+
? _jsx(Text, { color: tuiTheme.roles.secondary, children: "\u5F53\u524D\u6761\u4EF6\u65E0\u5339\u914D\uFF1B\u641C\u7D22\u548C\u7B5B\u9009\u4FDD\u6301\u539F\u6837\u3002" }) : null, _jsx(ContentViewport, { children: actions.slice(projectViewport.offset, projectViewport.offset + projectViewport.limit)
|
|
3608
|
+
.map((action, visibleIndex) => {
|
|
3609
|
+
const index = projectViewport.offset + visibleIndex;
|
|
3610
|
+
if (index >= itemCount)
|
|
3611
|
+
return _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : effectiveFocus, kind: "action", compact: true, offset: index, limit: 1 }, action.actionId);
|
|
3612
|
+
return _jsx(ProjectRootCard, { root: roots[index], subject: cardSubjects[index], width: cardWidth, focused: !searchFocus && effectiveFocus === index, lineOffset: effectiveFocus === index ? batchTargetOffset : 0, lineLimit: lineLimit }, action.actionId);
|
|
3613
|
+
}) })] });
|
|
3501
3614
|
}
|
|
3502
|
-
else
|
|
3503
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(KeyCap, { children: "f" }), " ", section === 'declared' ? '项目声明' : '额外安装', " \u00B7 ", roots.length] }), _jsxs(Text, { children: ["\u641C\u7D22 ", _jsx(KeyCap, { children: "/" }), " ", searchFocus ? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: size.columns - 14 }) : query || '名称或来源'] }), roots.length === 0 ? _jsx(Text, { color: tuiTheme.roles.secondary, children: "\u672C\u6761\u4EF6\u65E0\u5339\u914D" }) : null, _jsx(SequenceRows, { actions: actions, itemCount: itemCount, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2 + (roots.length === 0 ? 1 : 0)), compact: policy.mode === 'compact', cards: true, inactive: searchFocus })] });
|
|
3504
3615
|
}
|
|
3505
3616
|
else if (route.name === 'pluginList') {
|
|
3506
3617
|
const sourceId = route.sourceId ?? 'all';
|
|
@@ -3518,8 +3629,15 @@ export function ConsoleAppV4(props) {
|
|
|
3518
3629
|
installFilter: filter,
|
|
3519
3630
|
}).length,
|
|
3520
3631
|
]));
|
|
3632
|
+
const sourceTabs = [{ id: 'all', label: '全部', count: props.controller.listPlugins({
|
|
3633
|
+
mode: route.mode, ...(query ? { query } : {}), installFilter,
|
|
3634
|
+
}).length }, ...props.controller.listSources().map((source) => ({
|
|
3635
|
+
id: source.id, label: source.label,
|
|
3636
|
+
count: props.controller.listPlugins({ mode: route.mode, sourceId: source.id,
|
|
3637
|
+
...(query ? { query } : {}), installFilter }).length,
|
|
3638
|
+
}))];
|
|
3521
3639
|
breadcrumb = '插件';
|
|
3522
|
-
title = `插件 · ${items.length}
|
|
3640
|
+
title = `插件 · ${items.length} 个`;
|
|
3523
3641
|
description = undefined;
|
|
3524
3642
|
context = `${items.length} 个插件`;
|
|
3525
3643
|
help = '↑↓ 移动 · Enter 查看 · Esc 返回';
|
|
@@ -3578,9 +3696,6 @@ export function ConsoleAppV4(props) {
|
|
|
3578
3696
|
.filter((candidate) => candidate.eligible && listedPluginIds.has(candidate.target.pluginId));
|
|
3579
3697
|
const recommendedUpdatePluginIds = [...new Set(recommendedUpdateCandidates
|
|
3580
3698
|
.map((candidate) => candidate.target.pluginId))];
|
|
3581
|
-
const projectRestoreCandidates = props.controller.listPluginWriteTaskCandidates('restore')
|
|
3582
|
-
.filter((candidate) => candidate.target.scope === 'project');
|
|
3583
|
-
const projectIntentRoots = props.controller.listCurrentProjectIntentRoots();
|
|
3584
3699
|
const menuActions = [
|
|
3585
3700
|
...(props.controller.listUserIssues().length > 0 ? [{ actionId: 'plugin-list-issues',
|
|
3586
3701
|
label: '需要处理', run: () => go({ name: 'issueList' }) }] : []),
|
|
@@ -3588,29 +3703,13 @@ export function ConsoleAppV4(props) {
|
|
|
3588
3703
|
actionId: 'plugin-list-update',
|
|
3589
3704
|
label: `更新 ${recommendedUpdatePluginIds.length} 个`,
|
|
3590
3705
|
description: '使用已验证版本和现有安装位置,直接查看任务确认',
|
|
3591
|
-
status: 'warning',
|
|
3592
|
-
run: () => openRecommendedUpdateTask(recommendedUpdatePluginIds),
|
|
3593
|
-
}] : []),
|
|
3594
|
-
...(projectIntentRoots.length > 0 ? [{
|
|
3595
|
-
actionId: 'plugin-list-project',
|
|
3596
|
-
label: '项目清单',
|
|
3597
|
-
description: `${projectIntentRoots.length} 个已保存根插件;允许补齐当前项目缺少的安装`,
|
|
3598
3706
|
status: 'info',
|
|
3599
|
-
run:
|
|
3600
|
-
}] : []),
|
|
3601
|
-
...(projectRestoreCandidates.length > 0 ? [{
|
|
3602
|
-
actionId: 'plugin-list-repair-project',
|
|
3603
|
-
label: '修复项目',
|
|
3604
|
-
description: '严格按已确认的现有版本修复异常,不改变插件版本',
|
|
3605
|
-
status: 'warning',
|
|
3606
|
-
run: () => openSelectedPluginTask('restore', [...new Set(projectRestoreCandidates
|
|
3607
|
-
.map((candidate) => candidate.target.pluginId))], 'project'),
|
|
3707
|
+
run: () => openRecommendedUpdateTask(recommendedUpdatePluginIds),
|
|
3608
3708
|
}] : []),
|
|
3609
3709
|
{
|
|
3610
3710
|
actionId: 'plugin-list-multi',
|
|
3611
3711
|
label: '选择多个插件 [m]',
|
|
3612
3712
|
description: '先选择插件,再选择安装、更新、恢复、卸载或维护任务',
|
|
3613
|
-
status: 'info',
|
|
3614
3713
|
run: () => openBatchSelection(),
|
|
3615
3714
|
},
|
|
3616
3715
|
];
|
|
@@ -3712,9 +3811,9 @@ export function ConsoleAppV4(props) {
|
|
|
3712
3811
|
}).height : 1),
|
|
3713
3812
|
focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(3),
|
|
3714
3813
|
});
|
|
3715
|
-
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
3716
|
-
? _jsx(InputDisplay, { value: query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns:
|
|
3717
|
-
|
|
3814
|
+
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, tabs: { label: '来源', items: sourceTabs, currentId: sourceId }, search: { value: query, placeholder: '名称或用途', input: searchFocus
|
|
3815
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: querySearchWidth(size.columns - 2) }) : undefined }, filter: { label: pluginFilterLabel(installFilter), count: filterCounts[installFilter],
|
|
3816
|
+
currentZero: installFilter !== 'all' && filterCounts[installFilter] === 0 } }), sourceError
|
|
3718
3817
|
? _jsx(Callout, { status: "error", title: `${sourceId} 暂时无法提供插件`, description: "\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })
|
|
3719
3818
|
: route.mode === 'installed' && items.length === 0 && !query && sourceId === 'all'
|
|
3720
3819
|
? installFilter === 'updateAvailable'
|
|
@@ -3741,11 +3840,21 @@ export function ConsoleAppV4(props) {
|
|
|
3741
3840
|
const selected = new Set(batchDraft.selectedPluginIds);
|
|
3742
3841
|
const currentIds = new Set(candidates.map((candidate) => candidate.pluginId));
|
|
3743
3842
|
const hiddenSelected = batchDraft.selectedPluginIds.filter((pluginId) => !currentIds.has(pluginId)).length;
|
|
3843
|
+
const batchSearchDraft = { ...batchDraft, showSelected: false };
|
|
3844
|
+
const batchFilterCounts = Object.fromEntries(PLUGIN_FILTERS.map((filter) => [filter,
|
|
3845
|
+
batchCandidates({ ...batchSearchDraft, installFilter: filter }).length,
|
|
3846
|
+
]));
|
|
3847
|
+
const batchSourceTabs = [{ id: 'all', label: '全部', count: batchCandidates({ ...batchSearchDraft,
|
|
3848
|
+
sourceId: undefined }).length }, ...props.controller.listSources().map((source) => ({
|
|
3849
|
+
id: source.id, label: source.label,
|
|
3850
|
+
count: batchCandidates({ ...batchSearchDraft, sourceId: source.id }).length,
|
|
3851
|
+
}))];
|
|
3744
3852
|
const candidateActions = candidates.map((candidate) => {
|
|
3745
3853
|
const blockReason = batchCandidateBlockReason(batchDraft, candidate);
|
|
3746
3854
|
const isSelected = selected.has(candidate.pluginId);
|
|
3747
3855
|
const plugin = candidate.plugin;
|
|
3748
3856
|
return {
|
|
3857
|
+
actionId: `batch:${candidate.pluginId}`,
|
|
3749
3858
|
label: plugin
|
|
3750
3859
|
? plugin.displayName
|
|
3751
3860
|
: `${candidate.pluginId}(已不可用,可取消选择)`,
|
|
@@ -3858,9 +3967,10 @@ export function ConsoleAppV4(props) {
|
|
|
3858
3967
|
help = batchDraft.showSelectionTools
|
|
3859
3968
|
? '↑↓ 移动 · Enter 执行 · Esc 返回选择'
|
|
3860
3969
|
: '↑↓ 移动 · Space 选择 · Enter 查看 · Esc 返回';
|
|
3861
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
3862
|
-
|
|
3863
|
-
|
|
3970
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, tabs: { label: '来源', items: batchSourceTabs,
|
|
3971
|
+
currentId: batchDraft.showSelected ? 'all' : batchDraft.sourceId ?? 'all' }, search: { value: batchDraft.query, placeholder: '名称或用途', input: searchFocus
|
|
3972
|
+
? _jsx(InputDisplay, { value: batchDraft.query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: querySearchWidth(size.columns - 2) }) : undefined }, filter: { label: pluginFilterLabel(batchDraft.installFilter), count: batchFilterCounts[batchDraft.installFilter],
|
|
3973
|
+
currentZero: batchDraft.installFilter !== 'all' && batchFilterCounts[batchDraft.installFilter] === 0 }, selection: { total: selected.size, hidden: hiddenSelected } }), _jsx(SequenceRows, { actions: actions, itemCount: batchDraft.showSelectionTools ? 0 : candidateActions.length, focus: batchFocus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(4), compact: policy.mode === 'compact', cards: true, inactive: searchFocus })] });
|
|
3864
3974
|
}
|
|
3865
3975
|
else {
|
|
3866
3976
|
const selectedIds = batchDraft.selectedPluginIds;
|
|
@@ -3897,9 +4007,9 @@ export function ConsoleAppV4(props) {
|
|
|
3897
4007
|
const uninstallTargets = props.controller.listPluginWriteBatchTargets('uninstall', selectedIds);
|
|
3898
4008
|
if (batchDraft.stage === 'maintenance') {
|
|
3899
4009
|
actions = [
|
|
3900
|
-
{ label: '同步已保存使用设置', description: `重新应用 ${syncTargets.length} 个已保存或现场目标`, run: () => startExistingBatch('sync') },
|
|
3901
|
-
{ label: '重新安装', description: `重新生成 ${props.controller.listPluginWriteBatchTargets('reinstall', selectedIds).length} 个已保存或现场目标`, run: () => startExistingBatch('reinstall') },
|
|
3902
|
-
{ label: '清理遗留内容', description: `${props.controller.listPluginWriteBatchTargets('cleanup', selectedIds).length} 个已核验位置;保留不属于插件或被共用的内容`, run: () => startExistingBatch('cleanup') },
|
|
4010
|
+
{ label: '同步已保存使用设置', status: 'info', description: `重新应用 ${syncTargets.length} 个已保存或现场目标`, run: () => startExistingBatch('sync') },
|
|
4011
|
+
{ label: '重新安装', status: 'info', description: `重新生成 ${props.controller.listPluginWriteBatchTargets('reinstall', selectedIds).length} 个已保存或现场目标`, run: () => startExistingBatch('reinstall') },
|
|
4012
|
+
{ label: '清理遗留内容', status: 'error', description: `${props.controller.listPluginWriteBatchTargets('cleanup', selectedIds).length} 个已核验位置;保留不属于插件或被共用的内容`, run: () => startExistingBatch('cleanup') },
|
|
3903
4013
|
{
|
|
3904
4014
|
role: 'navigation', label: '返回任务选择',
|
|
3905
4015
|
run: () => moveBatchStage('task'),
|
|
@@ -3917,7 +4027,7 @@ export function ConsoleAppV4(props) {
|
|
|
3917
4027
|
...(updates.length > 0 ? [{
|
|
3918
4028
|
label: `更新所选中的 ${updateRoots} 个可更新插件`,
|
|
3919
4029
|
description: '使用已验证版本和已经添加的位置',
|
|
3920
|
-
status: '
|
|
4030
|
+
status: 'info',
|
|
3921
4031
|
run: () => openSelectedPluginTask('update', selectedIds),
|
|
3922
4032
|
}] : []),
|
|
3923
4033
|
...(restores.length > 0 ? [{
|
|
@@ -4245,8 +4355,10 @@ export function ConsoleAppV4(props) {
|
|
|
4245
4355
|
title = '选择要安装的 Skills';
|
|
4246
4356
|
description = undefined;
|
|
4247
4357
|
const visibleCandidates = portableSelectionView === 'selected'
|
|
4248
|
-
?
|
|
4249
|
-
:
|
|
4358
|
+
? portableVisibleCandidates.filter((candidate) => candidate.selected)
|
|
4359
|
+
: portableVisibleCandidates;
|
|
4360
|
+
const hiddenSelected = draft.candidates.filter((candidate) => candidate.selected
|
|
4361
|
+
&& !visibleCandidates.includes(candidate)).length;
|
|
4250
4362
|
const continuePortableSelection = () => {
|
|
4251
4363
|
if (!draft.canContinue) {
|
|
4252
4364
|
setPortableEmptySelectionOpen(true);
|
|
@@ -4255,6 +4367,7 @@ export function ConsoleAppV4(props) {
|
|
|
4255
4367
|
go({ name: 'portableAgentMode', pluginId: plugin.pluginId });
|
|
4256
4368
|
};
|
|
4257
4369
|
const candidateActions = visibleCandidates.map((candidate) => ({
|
|
4370
|
+
actionId: `skill:${candidate.providerPluginId}:${candidate.name}`,
|
|
4258
4371
|
label: draft.candidates.filter((entry) => entry.name === candidate.name).length > 1
|
|
4259
4372
|
? `${candidate.name} · ${candidate.providerDisplayName}` : candidate.name,
|
|
4260
4373
|
selected: candidate.selected,
|
|
@@ -4307,16 +4420,18 @@ export function ConsoleAppV4(props) {
|
|
|
4307
4420
|
const technicalVariant = focusedVariant ?? visibleVariant ?? variantCandidates[0];
|
|
4308
4421
|
help = portableToolsOpen ? '↑↓ 移动 · Enter 选择 · Esc 返回'
|
|
4309
4422
|
: '↑↓ 移动 · Space/Enter 切换 · Esc 返回';
|
|
4310
|
-
content =
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
?
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4423
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, search: { value: query, placeholder: '名称或短描述', input: searchFocus
|
|
4424
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: querySearchWidth(size.columns - 2) }) : undefined }, selection: { total: draft.selectedCount, hidden: hiddenSelected } }), _jsxs(ContentViewport, { children: [_jsx(SequenceRows, { actions: actions, itemCount: portableToolsOpen ? 0 : candidateActions.length, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2 + (variantCandidates.length > 0 ? 3 : 0) + (!draft.canContinue ? 1 : 0)
|
|
4425
|
+
+ (showTechnical && technicalVariant ? 5 : 0)), compact: policy.mode === 'compact', skillRows: true, inactive: portableEmptySelectionOpen }), !portableToolsOpen && variantCandidates.length > 0
|
|
4426
|
+
? showTechnical
|
|
4427
|
+
? _jsx(StaticRow, { title: `! ${variantCandidates.length} 个 Skill 存在范围变体`, status: "warning", compact: true })
|
|
4428
|
+
: _jsx(Callout, { status: "warning", title: `! ${variantCandidates.length} 个 Skill 在不同安装范围会生成不同内容。选择范围后会重新确认最终内容。` })
|
|
4429
|
+
: null, !portableToolsOpen && visibleCandidates.length === 0
|
|
4430
|
+
? _jsx(StaticRow, { title: query ? '当前搜索没有匹配 Skill'
|
|
4431
|
+
: portableSelectionView === 'selected' ? '没有已选 Skill' : '当前没有可选 Skill', description: query ? '清除搜索后可查看其他候选' : '选择“查看全部候选”后可继续勾选', status: "muted", compact: true })
|
|
4432
|
+
: null, !portableToolsOpen && !draft.canContinue
|
|
4433
|
+
? _jsx(StaticRow, { title: "\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A Skill", status: "warning", compact: true })
|
|
4434
|
+
: null, !portableToolsOpen && showTechnical && technicalVariant ? _jsxs(TechnicalDetails, { title: "\u8303\u56F4\u53D8\u4F53", children: [_jsx(StaticRow, { title: `${technicalVariant.name} · ${portableScopeVariantLabel('project', technicalVariant.scopeDetails.project)}`, compact: true }, `${technicalVariant.name}:project`), _jsx(StaticRow, { title: `${technicalVariant.name} · ${portableScopeVariantLabel('user', technicalVariant.scopeDetails.user)}`, compact: true }, `${technicalVariant.name}:user`), variantCandidates.length > 1 ? _jsx(StaticRow, { title: `另有 ${variantCandidates.length - 1} 个 Skill 的范围变体已折叠`, status: "muted", compact: true }) : null] }) : null] })] });
|
|
4320
4435
|
}
|
|
4321
4436
|
else if (route.name === 'portablePreparing' && plugin) {
|
|
4322
4437
|
breadcrumb = `${plugin.displayName} / 通用 Skills`;
|
|
@@ -4327,31 +4442,18 @@ export function ConsoleAppV4(props) {
|
|
|
4327
4442
|
help = 'Esc 取消检查并返回目标平台';
|
|
4328
4443
|
}
|
|
4329
4444
|
else if (route.name === 'portableDependencies' && plugin && snapshot.portableDraft) {
|
|
4330
|
-
const overview = dependencyOverview(plugin);
|
|
4331
|
-
const dependencies = overview?.dependencies ?? [];
|
|
4332
4445
|
breadcrumb = `${plugin.displayName} / 通用 Skills / 依赖`;
|
|
4333
|
-
context = `${
|
|
4446
|
+
context = `${snapshot.portableDraft.selectedCount} 个 Skills`;
|
|
4334
4447
|
title = '本次生成依赖';
|
|
4335
|
-
description = '
|
|
4336
|
-
|
|
4337
|
-
`${entry.displayName} · ${entry.pluginId}`,
|
|
4338
|
-
`${entry.relation === 'direct' ? '直接依赖' : '间接依赖'} · ${entry.requiredBy.map((parent) => parent.displayName).join('、')} 需要它`,
|
|
4339
|
-
`提供 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.providerPluginId === entry.pluginId).length} 个 Skills`,
|
|
4340
|
-
])];
|
|
4341
|
-
readingText = fields.join('\n');
|
|
4342
|
-
const lines = fields.flatMap((field) => wrapConsoleText(field, size.columns - 2));
|
|
4343
|
-
const pageRows = pageActionBudget(2);
|
|
4344
|
-
referenceDetailPagination = { total: lines.length, pageRows };
|
|
4345
|
-
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
4346
|
-
actions = [{ label: '返回 Skills 选择', run: back }];
|
|
4347
|
-
content = _jsxs(Box, { flexDirection: "column", children: [lines.slice(offset, offset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsxs(Text, { color: tuiTheme.roles.secondary, children: [offset + 1, "\u2013", Math.min(lines.length, offset + pageRows), " / ", lines.length] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
4448
|
+
description = '关系图显示已知依赖;所选 Skills 和安装位置仍以最终汇总为准。';
|
|
4449
|
+
renderDependencyGraph(plugin.pluginId, true);
|
|
4348
4450
|
}
|
|
4349
4451
|
else if (route.name === 'portableAgentMode' && plugin && snapshot.portableDraft) {
|
|
4350
4452
|
breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
|
|
4351
4453
|
title = '安装到哪些 Agent?';
|
|
4352
4454
|
description = '可以自动选择检测到的 Agent,也可以限制到指定 Agent。';
|
|
4353
4455
|
actions = [
|
|
4354
|
-
{ label: '
|
|
4456
|
+
{ label: '自动选择可用 Agent', description: '按当前环境确定可用 Agent,不承诺全部 Agent 可用', meta: '默认', selected: snapshot.portableDraft.agentMode === 'automatic', run: () => { props.controller.setPortableAgentMode('automatic'); go({ name: 'portableScope', pluginId: plugin.pluginId }); } },
|
|
4355
4457
|
{ label: '指定 Agent', description: '输入一个或多个 Agent ID', selected: snapshot.portableDraft.agentMode === 'explicit', run: () => go({ name: 'portableAgentInput', pluginId: plugin.pluginId }) },
|
|
4356
4458
|
];
|
|
4357
4459
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' });
|
|
@@ -4430,21 +4532,29 @@ export function ConsoleAppV4(props) {
|
|
|
4430
4532
|
const blockerActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 4));
|
|
4431
4533
|
content = _jsxs(Box, { flexDirection: "column", children: [showPortableRequirements ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u5728 PATH \u4E2D\u63D0\u4F9B\u517C\u5BB9\u7684 skills \u53EF\u6267\u884C\u6587\u4EF6\u3002", compact: true }), _jsx(StaticRow, { title: `当前 AW adapter 支持:${SKILLS_CLI_SUPPORTED_VERSION_LABEL}`, compact: true }), _jsx(StaticRow, { title: "\u51C6\u5907\u5B8C\u6210\u540E\u8FD4\u56DE\u95EE\u9898\u6458\u8981\u5E76\u91CD\u65B0\u68C0\u67E5\u3002", compact: true }), _jsx(StaticRow, { title: "\u4E0D\u4F1A\u81EA\u52A8\u8FD0\u884C npx latest\u3001sudo \u6216\u5728\u7EBF\u5B89\u88C5\u811A\u672C\u3002", status: "warning", compact: true }), _jsx(StaticRow, { title: "\u9009\u5B9A\u63D2\u4EF6\u3001\u5B89\u88C5\u76EE\u6807\u548C Portable \u8349\u7A3F\u4FDD\u6301\u4E0D\u53D8\u3002", status: "success", compact: true }), _jsx(StaticRow, { title: "AW \u4E0D\u4F1A\u5728\u6B64\u9875\u5B89\u88C5\u5916\u90E8\u4F9D\u8D56\u3002", status: "warning", compact: true })] }) : _jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, columns: size.columns, offset: showTechnical ? batchTargetOffset : undefined, limit: showTechnical ? technicalPageRows : undefined, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockerActionViewport.offset, limit: blockerActionViewport.limit })] });
|
|
4432
4534
|
}
|
|
4535
|
+
else if (route.name === 'pluginDependencies' && !plugin
|
|
4536
|
+
&& route.view !== 'removeBlocked' && props.controller.getPluginDetailSubject(route.pluginId)) {
|
|
4537
|
+
const subject = props.controller.getPluginDetailSubject(route.pluginId);
|
|
4538
|
+
breadcrumb = `${subject.displayName} / 依赖关系`;
|
|
4539
|
+
title = '已知依赖关系';
|
|
4540
|
+
description = 'A → B 表示 A 需要 B;未验证关系不表示当前可用。';
|
|
4541
|
+
context = subject.relations.checkState === 'checked' ? '已检查' : '部分未确认';
|
|
4542
|
+
renderDependencyGraph(route.pluginId, false);
|
|
4543
|
+
}
|
|
4433
4544
|
else if (route.name === 'pluginDependencies' && plugin) {
|
|
4434
4545
|
const overview = dependencyOverview(plugin);
|
|
4435
4546
|
const visibleDependents = route.view === 'removeBlocked' && route.platform && route.scope
|
|
4436
4547
|
? props.controller.getBlockingDependents(plugin.pluginId, route.platform, route.scope)
|
|
4437
4548
|
: overview?.dependents ?? [];
|
|
4438
|
-
const dependencyCount = overview?.dependencies.length ?? 0;
|
|
4439
4549
|
const dependentCount = visibleDependents.length;
|
|
4440
4550
|
const dependentLimit = responsiveBudget(1, 3);
|
|
4441
|
-
const dependencyRows = (entries, limit, dependent = false
|
|
4442
|
-
const
|
|
4443
|
-
const visible = entries.slice(
|
|
4444
|
-
return _jsxs(_Fragment, { children: [
|
|
4551
|
+
const dependencyRows = (entries, limit, dependent = false) => {
|
|
4552
|
+
const page = pagedItems(entries.length, limit);
|
|
4553
|
+
const visible = entries.slice(page.offset, page.offset + limit);
|
|
4554
|
+
return _jsxs(_Fragment, { children: [visible.map((entry) => {
|
|
4445
4555
|
const state = dependencyStatus(entry);
|
|
4446
4556
|
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);
|
|
4447
|
-
}),
|
|
4557
|
+
}), page.pageCount > 1 ? _jsx(Pager, { page: page.page, pageCount: page.pageCount, columns: size.columns - 4 }) : null] });
|
|
4448
4558
|
};
|
|
4449
4559
|
if (route.view === 'removeBlocked') {
|
|
4450
4560
|
if (!showTechnical)
|
|
@@ -4470,251 +4580,44 @@ export function ConsoleAppV4(props) {
|
|
|
4470
4580
|
if (!showTechnical && visibleDependents.length > dependentLimit) {
|
|
4471
4581
|
help = '←→ 查看全部被依赖关系 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4472
4582
|
}
|
|
4473
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: `${dependentCount} 个已安装插件仍在使用`, status: "error", compact: true }), _jsx(StaticRow, { title: "\u6CA1\u6709\u4FEE\u6539\u4EFB\u4F55\u5B89\u88C5\u3002", status: "muted", compact: true }), showTechnical ? null : _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", meta: "\u53CD\u5411\u5F15\u7528", compact: true }), dependencyRows(visibleDependents, dependentLimit, true
|
|
4583
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: `${dependentCount} 个已安装插件仍在使用`, status: "error", compact: true }), _jsx(StaticRow, { title: "\u6CA1\u6709\u4FEE\u6539\u4EFB\u4F55\u5B89\u88C5\u3002", status: "muted", compact: true }), showTechnical ? null : _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", meta: "\u53CD\u5411\u5F15\u7528", compact: true }), dependencyRows(visibleDependents, dependentLimit, true)] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockedRemovalViewport.offset, limit: blockedRemovalViewport.limit }), showTechnical ? _jsx(IssueTechnicalDetails, { details: [{
|
|
4474
4584
|
code: 'dependencyStillRequired',
|
|
4475
4585
|
message: '已安装插件仍在使用这个依赖;请先处理依赖者。',
|
|
4476
4586
|
}], compact: policy.mode === 'compact' }) : null] });
|
|
4477
4587
|
}
|
|
4478
4588
|
else {
|
|
4479
4589
|
breadcrumb = `${plugin.displayName} / 依赖关系`;
|
|
4480
|
-
const unified = buildUnifiedDependencyGraphProjection({
|
|
4481
|
-
overviews: props.controller.listPluginDependencyOverviews(),
|
|
4482
|
-
rootPluginId: plugin.pluginId,
|
|
4483
|
-
...(expandedDependencyPluginIds ? { expandedPluginIds: expandedDependencyPluginIds } : {}),
|
|
4484
|
-
});
|
|
4485
|
-
const visibleRows = unified.rows;
|
|
4486
4590
|
context = '上下游';
|
|
4487
4591
|
title = hasInstalledPlacement(plugin) ? '依赖与当前可用关系' : '安装所需插件';
|
|
4488
|
-
description = 'A → B 表示 A 需要 B
|
|
4489
|
-
|
|
4490
|
-
const state = row.depth === 0
|
|
4491
|
-
? { value: statusText(plugin), status: plugin.aggregateStatus === 'needsAttention'
|
|
4492
|
-
? 'warning'
|
|
4493
|
-
: plugin.aggregateStatus === 'unavailable' ? 'error'
|
|
4494
|
-
: plugin.aggregateStatus === 'available' ? 'muted' : 'success' }
|
|
4495
|
-
: dependencyGraphRowStatus(row);
|
|
4496
|
-
const branch = row.depth === 0 ? ''
|
|
4497
|
-
: `${row.ancestorHasNextSibling.map((next) => next ? '│ ' : ' ').join('')}${row.lastChild ? '└─' : '├─'} `;
|
|
4498
|
-
const compactBranch = policy.mode === 'compact' && row.depth > 2 ? '… └─ ' : branch;
|
|
4499
|
-
return {
|
|
4500
|
-
label: row.depth === 0
|
|
4501
|
-
? `${row.label}(当前插件)`
|
|
4502
|
-
: `${row.branch === 'dependents' ? '↑ 使用者 ' : '↓ 所需 '}${compactBranch}${row.label}${row.sharedWithRowId ? '(共用,见上方)' : ''}`,
|
|
4503
|
-
description: row.hiddenDescendantCount > 0
|
|
4504
|
-
? `另有 ${row.hiddenDescendantCount} 个${row.branch === 'dependencies' ? '所需插件' : '当前可用使用者'}`
|
|
4505
|
-
: row.depth === 0 ? '当前插件 ★'
|
|
4506
|
-
: row.branch === 'dependencies' ? '当前插件所需' : '需要当前插件',
|
|
4507
|
-
meta: state.value,
|
|
4508
|
-
status: state.status,
|
|
4509
|
-
run: () => {
|
|
4510
|
-
const direction = row.branch === 'dependents' ? 'dependents' : 'dependencies';
|
|
4511
|
-
persistUnifiedDependencyGraphContext(plugin.pluginId, unified, expandedDependencyPluginIds ?? new Set([
|
|
4512
|
-
...unified.dependencies.defaultExpandedPluginIds,
|
|
4513
|
-
...unified.dependents.defaultExpandedPluginIds,
|
|
4514
|
-
]), focus, scrollOffset);
|
|
4515
|
-
go({
|
|
4516
|
-
name: 'pluginDependencyNode',
|
|
4517
|
-
pluginId: plugin.pluginId,
|
|
4518
|
-
nodePluginId: row.pluginId,
|
|
4519
|
-
direction,
|
|
4520
|
-
nodePathPluginIds: row.pathPluginIds,
|
|
4521
|
-
});
|
|
4522
|
-
},
|
|
4523
|
-
};
|
|
4524
|
-
});
|
|
4525
|
-
const unverifiedConsumerActions = unified.dependents.scopeSummary.unknownConsumerPluginIds
|
|
4526
|
-
.map((pluginId) => {
|
|
4527
|
-
const declared = overview?.dependents.find((entry) => entry.pluginId === pluginId);
|
|
4528
|
-
return {
|
|
4529
|
-
label: declared?.displayName ?? pluginId,
|
|
4530
|
-
description: '有依赖声明,但本次尚未确认依赖可用',
|
|
4531
|
-
meta: declared?.aggregateStatus === 'notInstalled' ? '仅声明 · 未安装' : '仅声明 · 未验证',
|
|
4532
|
-
status: 'muted',
|
|
4533
|
-
run: () => {
|
|
4534
|
-
persistUnifiedDependencyGraphContext(plugin.pluginId, unified, expandedDependencyPluginIds ?? new Set([
|
|
4535
|
-
...unified.dependencies.defaultExpandedPluginIds,
|
|
4536
|
-
...unified.dependents.defaultExpandedPluginIds,
|
|
4537
|
-
]), focus, scrollOffset);
|
|
4538
|
-
go({
|
|
4539
|
-
name: 'pluginDependencyNode',
|
|
4540
|
-
pluginId: plugin.pluginId,
|
|
4541
|
-
nodePluginId: pluginId,
|
|
4542
|
-
direction: 'dependents',
|
|
4543
|
-
});
|
|
4544
|
-
},
|
|
4545
|
-
};
|
|
4546
|
-
});
|
|
4547
|
-
actions = [
|
|
4548
|
-
...rowActions,
|
|
4549
|
-
...unverifiedConsumerActions,
|
|
4550
|
-
{ label: showTechnical ? '隐藏检查详情' : '查看检查详情', run: () => toggleTechnical() },
|
|
4551
|
-
];
|
|
4552
|
-
const relationViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, showTechnical ? responsiveBudget(2, 5) : responsiveBudget(5, 9));
|
|
4553
|
-
const focusedGraphRow = focus < rowActions.length ? visibleRows[focus] : undefined;
|
|
4554
|
-
help = '↑↓ 选择 · → 展开 · ← 收起 · Enter 节点详情 · Esc 返回';
|
|
4555
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: "\u68C0\u67E5\u8303\u56F4", value: "\u53EA\u68C0\u67E5\u5F53\u524D\u9879\u76EE\u548C\u4E2A\u4EBA\u4F4D\u7F6E\uFF1B\u5176\u4ED6\u9879\u76EE\u672A\u68C0\u67E5", status: "muted", compact: true }), unified.status.state === 'invalid'
|
|
4556
|
-
? _jsx(Callout, { status: "error", title: "\u4F9D\u8D56\u5173\u7CFB\u65E0\u6548", description: unified.status.message }) : null, unverifiedConsumerActions.length > 0
|
|
4557
|
-
? _jsx(StaticRow, { title: `未验证声明者 · ${unverifiedConsumerActions.length} 个`, description: "\u4EE5\u4E0B\u58F0\u660E\u8005\u4E0D\u8BA1\u5165\u5F53\u524D\u53EF\u7528\u5173\u7CFB", status: "muted", compact: true })
|
|
4558
|
-
: null, focusedGraphRow ? _jsx(StaticRow, { title: "\u7528\u9014", value: props.controller
|
|
4559
|
-
.getPluginOverview(focusedGraphRow.pluginId)?.description ?? '当前目录没有用途说明', compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: relationViewport.offset, limit: relationViewport.limit }), showTechnical ? _jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: `${dependencyCount} 个依赖 · ${unified.dependents.scopeSummary.availableConsumerPluginIds.length} 个当前可用使用者`, meta: plugin.pluginId, compact: true }), _jsx(StaticRow, { title: `未验证声明者 · ${unified.dependents.scopeSummary.unknownConsumerPluginIds.length} 个`, compact: true })] }) : null] });
|
|
4592
|
+
description = 'A → B 表示 A 需要 B;实线已验证,虚线未验证。';
|
|
4593
|
+
renderDependencyGraph(plugin.pluginId, false);
|
|
4560
4594
|
}
|
|
4561
4595
|
}
|
|
4562
|
-
else if (route.name === 'pluginDependencyNode' &&
|
|
4563
|
-
|
|
4564
|
-
const
|
|
4596
|
+
else if (route.name === 'pluginDependencyNode' && route.view === 'references'
|
|
4597
|
+
&& props.controller.getPluginDetailSubject(route.pluginId)) {
|
|
4598
|
+
const root = props.controller.getPluginDetailSubject(route.pluginId);
|
|
4599
|
+
const node = props.controller.getPluginDetailSubject(route.nodePluginId);
|
|
4565
4600
|
const nodeProjection = buildDependencyGraphProjection({
|
|
4566
4601
|
overviews: props.controller.listPluginDependencyOverviews(),
|
|
4567
|
-
rootPluginId:
|
|
4602
|
+
rootPluginId: route.pluginId,
|
|
4568
4603
|
direction: route.direction,
|
|
4569
|
-
expandedPluginIds: nodePathPluginIds,
|
|
4604
|
+
expandedPluginIds: route.nodePathPluginIds ?? [route.pluginId, route.nodePluginId],
|
|
4570
4605
|
focusPluginId: route.nodePluginId,
|
|
4571
4606
|
});
|
|
4572
|
-
const selectedRow = nodeProjection.rows.find((row) => row.pathPluginIds.join('\0') === nodePathPluginIds
|
|
4607
|
+
const selectedRow = nodeProjection.rows.find((row) => row.pathPluginIds.join('\0') === route.nodePathPluginIds?.join('\0'))
|
|
4573
4608
|
?? nodeProjection.rows.find((row) => row.pluginId === route.nodePluginId);
|
|
4574
|
-
const
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
const pathPluginIds = selectedRow?.pathPluginIds ?? nodePathPluginIds;
|
|
4587
|
-
const intermediateNames = pathPluginIds.slice(1, -1).reverse().map((pluginId) => props.controller.getPluginOverview(pluginId)?.displayName ?? pluginId);
|
|
4588
|
-
const declaringPluginId = pathPluginIds.at(-2) ?? plugin.pluginId;
|
|
4589
|
-
const declaringPluginName = props.controller.getPluginOverview(declaringPluginId)?.displayName
|
|
4590
|
-
?? declaringPluginId;
|
|
4591
|
-
description = relationship === 'root'
|
|
4592
|
-
? '这是当前关系图的根插件。'
|
|
4593
|
-
: relationship === 'directAvailable'
|
|
4594
|
-
? route.direction === 'dependencies'
|
|
4595
|
-
? `${plugin.displayName} 在本次检查的位置需要此插件,所需依赖当前可用。`
|
|
4596
|
-
: `${node?.displayName ?? route.nodePluginId} 在本次检查的位置需要当前插件,所需依赖当前可用。`
|
|
4597
|
-
: relationship === 'indirectAvailable'
|
|
4598
|
-
? route.direction === 'dependencies'
|
|
4599
|
-
? `${plugin.displayName} 通过 ${intermediateNames.join('、')} 需要 ${node?.displayName ?? route.nodePluginId},且各段依赖当前可用。`
|
|
4600
|
-
: `${node?.displayName ?? route.nodePluginId} 通过 ${intermediateNames.join('、')} 需要 ${plugin.displayName},且各段依赖当前可用。`
|
|
4601
|
-
: relationship === 'unknown'
|
|
4602
|
-
? '本次检查的位置不能组成连续路径,不能显示这项间接关系。'
|
|
4603
|
-
: relationship === 'declared'
|
|
4604
|
-
? route.direction === 'dependencies'
|
|
4605
|
-
? `${declaringPluginName} 声明需要 ${node?.displayName ?? route.nodePluginId},但本次尚未确认依赖可用。`
|
|
4606
|
-
: `${node?.displayName ?? route.nodePluginId} 声明需要当前插件,但本次尚未确认依赖可用。`
|
|
4607
|
-
: '当前数据不足,不能判断这项依赖关系。';
|
|
4608
|
-
const references = selectedRow?.edge?.availablePlacementReferences
|
|
4609
|
-
?? item?.availablePlacementReferences ?? [];
|
|
4610
|
-
const referenceActions = references.map((reference) => {
|
|
4611
|
-
const consumerName = props.controller.getPluginOverview(reference.consumer.pluginId)?.displayName
|
|
4612
|
-
?? reference.consumer.pluginId.split('/').at(-1);
|
|
4613
|
-
return {
|
|
4614
|
-
label: dependencyReferenceUsageLabel(reference, consumerName),
|
|
4615
|
-
meta: '依赖当前可用',
|
|
4616
|
-
};
|
|
4617
|
-
});
|
|
4618
|
-
if (route.view === 'references') {
|
|
4619
|
-
breadcrumb = `${plugin.displayName} / 依赖关系 / ${node?.displayName ?? route.nodePluginId} / 使用位置`;
|
|
4620
|
-
context = showTechnical ? '技术详情' : '使用位置';
|
|
4621
|
-
title = `当前可用依赖位置 ${references.length} 个`;
|
|
4622
|
-
description = showTechnical
|
|
4623
|
-
? '显示当前选择的完整引用字段。'
|
|
4624
|
-
: '这些位置的所需依赖当前可用,且本次检查发现它们同时满足依赖声明和安装位置。';
|
|
4625
|
-
if (!showTechnical && references[focus])
|
|
4626
|
-
selectedDependencyReferenceIndex.current = focus;
|
|
4627
|
-
const selectedReferenceIndex = Math.min(selectedDependencyReferenceIndex.current, Math.max(0, references.length - 1));
|
|
4628
|
-
const selectedReference = references[selectedReferenceIndex];
|
|
4629
|
-
const referenceIdLines = (label, pluginId) => {
|
|
4630
|
-
const prefix = `${label} · `;
|
|
4631
|
-
return wrapConsoleText(pluginId, Math.max(1, size.columns - 10 - stringWidth(prefix)))
|
|
4632
|
-
.map((line) => `${prefix}${line}`);
|
|
4633
|
-
};
|
|
4634
|
-
const referenceDetailLines = selectedReference ? [
|
|
4635
|
-
...referenceIdLines('consumer', selectedReference.consumer.pluginId),
|
|
4636
|
-
`consumer · platform=${platformLabel(selectedReference.consumer.platform)} · scope=${selectedReference.consumer.scope}`,
|
|
4637
|
-
`consumer · project=${selectedReference.consumer.project ?? 'none'}`,
|
|
4638
|
-
...referenceIdLines('dependency', selectedReference.dependency.pluginId),
|
|
4639
|
-
`dependency · platform=${platformLabel(selectedReference.dependency.platform)} · scope=${selectedReference.dependency.scope}`,
|
|
4640
|
-
`dependency · project=${selectedReference.dependency.project ?? 'none'}`,
|
|
4641
|
-
] : [];
|
|
4642
|
-
if (showTechnical && selectedReference)
|
|
4643
|
-
readingText = [
|
|
4644
|
-
`consumer · ${selectedReference.consumer.pluginId}`,
|
|
4645
|
-
`consumer · platform=${platformLabel(selectedReference.consumer.platform)} · scope=${selectedReference.consumer.scope}`,
|
|
4646
|
-
`consumer · project=${selectedReference.consumer.project ?? 'none'}`,
|
|
4647
|
-
`dependency · ${selectedReference.dependency.pluginId}`,
|
|
4648
|
-
`dependency · platform=${platformLabel(selectedReference.dependency.platform)} · scope=${selectedReference.dependency.scope}`,
|
|
4649
|
-
`dependency · project=${selectedReference.dependency.project ?? 'none'}`,
|
|
4650
|
-
].join('\n');
|
|
4651
|
-
const referenceDetailPageRows = referenceDetailLines.length <= 6
|
|
4652
|
-
? 6
|
|
4653
|
-
: responsiveBudget(4, 8);
|
|
4654
|
-
const referenceDetailOffset = Math.min(batchTargetOffset, Math.max(0, referenceDetailLines.length - 1));
|
|
4655
|
-
const visibleReferenceDetailLines = referenceDetailLines.slice(referenceDetailOffset, referenceDetailOffset + referenceDetailPageRows);
|
|
4656
|
-
referenceDetailPagination = showTechnical ? {
|
|
4657
|
-
total: referenceDetailLines.length,
|
|
4658
|
-
pageRows: referenceDetailPageRows,
|
|
4659
|
-
} : undefined;
|
|
4660
|
-
actions = [
|
|
4661
|
-
...(showTechnical && referenceActions[selectedReferenceIndex]
|
|
4662
|
-
? [referenceActions[selectedReferenceIndex]] : referenceActions),
|
|
4663
|
-
{ role: 'navigation', label: '返回节点详情', run: back },
|
|
4664
|
-
];
|
|
4665
|
-
const referenceViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(3, 6));
|
|
4666
|
-
help = showTechnical
|
|
4667
|
-
? '←→ 查看当前位置详情 · ↑↓ 移动 · Enter 选择 · d 隐藏技术详情 · Esc 返回节点详情'
|
|
4668
|
-
: '↑↓ 查看全部位置 · d 技术详情 · Enter 返回节点详情 · Esc 返回节点详情';
|
|
4669
|
-
content = _jsxs(Box, { flexDirection: "column", children: [showTechnical ? null
|
|
4670
|
-
: _jsx(StaticRow, { title: "\u672C\u6B21\u68C0\u67E5", value: "\u53EA\u68C0\u67E5\u5F53\u524D\u9879\u76EE\u548C\u4E2A\u4EBA\u4F4D\u7F6E\uFF1B\u5176\u4ED6\u9879\u76EE\u672A\u68C0\u67E5", status: "muted", compact: true }), showTechnical && referenceDetailOffset > 0
|
|
4671
|
-
? _jsx(StaticRow, { title: `↑ 前面还有 ${referenceDetailOffset} 行`, status: "muted", compact: true }) : null, showTechnical ? visibleReferenceDetailLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${referenceDetailOffset + index}:${line}`)) : null, showTechnical && referenceDetailOffset + visibleReferenceDetailLines.length < referenceDetailLines.length
|
|
4672
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${referenceDetailLines.length - referenceDetailOffset - visibleReferenceDetailLines.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: referenceViewport.offset, limit: referenceViewport.limit })] });
|
|
4673
|
-
}
|
|
4674
|
-
else {
|
|
4675
|
-
actions = [
|
|
4676
|
-
...(references.length > 0 ? [{
|
|
4677
|
-
label: `查看全部位置(${references.length})`,
|
|
4678
|
-
description: '查看每个位置的当前可用依赖',
|
|
4679
|
-
run: () => go({ ...route, view: 'references' }),
|
|
4680
|
-
}] : []),
|
|
4681
|
-
...(route.nodePluginId !== plugin.pluginId ? [{
|
|
4682
|
-
label: '以此节点查看关系',
|
|
4683
|
-
description: '打开局部子树;面包屑保留来路',
|
|
4684
|
-
run: () => go({
|
|
4685
|
-
name: 'pluginDependencies',
|
|
4686
|
-
pluginId: route.nodePluginId,
|
|
4687
|
-
direction: route.direction,
|
|
4688
|
-
}),
|
|
4689
|
-
}] : []),
|
|
4690
|
-
{ role: 'navigation', label: '返回关系图', run: back },
|
|
4691
|
-
];
|
|
4692
|
-
const reason = description;
|
|
4693
|
-
description = undefined;
|
|
4694
|
-
const summary = [
|
|
4695
|
-
`用途:${node?.description ?? '当前目录没有用途说明'}`,
|
|
4696
|
-
`为何需要:${reason}`,
|
|
4697
|
-
`影响:${state?.value ?? (node ? statusText(node) : '尚未检查当前状态')}`,
|
|
4698
|
-
...(showTechnical ? [`pluginId · ${route.nodePluginId}`, `relationship · ${relationship ?? 'unknown'}`] : []),
|
|
4699
|
-
];
|
|
4700
|
-
readingText = [title, ...summary].join('\n');
|
|
4701
|
-
const summaryLines = summary.flatMap((line) => wrapConsoleText(line, size.columns - 2));
|
|
4702
|
-
const actionCount = actions.filter((action) => action.role !== 'navigation').length;
|
|
4703
|
-
const availableSummaryRows = pageActionBudget(Math.min(2, actionCount));
|
|
4704
|
-
const pageRows = Math.max(1, availableSummaryRows - Number(summaryLines.length > availableSummaryRows));
|
|
4705
|
-
const offset = Math.min(batchTargetOffset, Math.max(0, summaryLines.length - pageRows));
|
|
4706
|
-
const visible = summaryLines.slice(offset, offset + pageRows);
|
|
4707
|
-
const paginated = summaryLines.length > pageRows;
|
|
4708
|
-
if (paginated) {
|
|
4709
|
-
referenceDetailPagination = { total: summaryLines.length, pageRows };
|
|
4710
|
-
context = `${offset + 1}–${offset + visible.length} / ${summaryLines.length}`;
|
|
4711
|
-
help = '←→ 翻页 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4712
|
-
}
|
|
4713
|
-
currentActionViewportLineBudget = pageActionBudget(visible.length + Number(paginated));
|
|
4714
|
-
const nodeActionViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
4715
|
-
content = _jsxs(Box, { flexDirection: "column", children: [visible.map((line, index) => _jsx(Text, { wrap: "truncate-end", children: line }, `${offset + index}`)), paginated ? _jsx(Text, { color: tuiTheme.colors.muted, children: offset + visible.length < summaryLines.length
|
|
4716
|
-
? `↓ 后面还有 ${summaryLines.length - offset - visible.length} 行` : '已到正文末尾' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: nodeActionViewport.offset, limit: nodeActionViewport.limit })] });
|
|
4717
|
-
}
|
|
4609
|
+
const references = selectedRow?.edge?.availablePlacementReferences ?? [];
|
|
4610
|
+
breadcrumb = `${root.displayName} / 依赖关系 / ${node?.displayName ?? route.nodePluginId} / 引用`;
|
|
4611
|
+
context = '完整引用';
|
|
4612
|
+
title = `当前可用依赖位置 ${references.length} 个`;
|
|
4613
|
+
description = references.length > 0
|
|
4614
|
+
? '这些位置在本次检查中已验证;其他项目未检查。'
|
|
4615
|
+
: '本次没有可验证的位置引用;声明关系不表示当前可用。';
|
|
4616
|
+
readingText = selectedRow
|
|
4617
|
+
? dependencyEvidenceLines(selectedRow, node).join('\n')
|
|
4618
|
+
: `pluginId · ${route.nodePluginId}\n目录版本 · ${node?.catalogVersion ?? '未确认'}\nrelationship · unknown\n连续路径 · 未验证`;
|
|
4619
|
+
content = _jsx(Box, { flexDirection: "column", children: readingPage(readingText, size.columns - 2, pageActionBudget(2)) });
|
|
4620
|
+
help = '←→/PgUp/PgDn 翻页 · y 复制全文 · Esc 返回';
|
|
4718
4621
|
}
|
|
4719
4622
|
else if (route.name === 'installTargets' && plugin) {
|
|
4720
4623
|
breadcrumb = `${plugin.displayName} / 添加 / 目标平台`;
|
|
@@ -5123,7 +5026,7 @@ export function ConsoleAppV4(props) {
|
|
|
5123
5026
|
return [[`update:${pluginWriteTaskTargetKey(candidate)}`, {
|
|
5124
5027
|
label: `更新并恢复 ${targetLabel}`,
|
|
5125
5028
|
description: '改用当前候选版本建立新任务;显示操作汇总前仍会重新检查状态',
|
|
5126
|
-
status: '
|
|
5029
|
+
status: 'info',
|
|
5127
5030
|
run: () => props.controller.beginNestedPluginWriteTask({
|
|
5128
5031
|
name: 'pluginWriteTask',
|
|
5129
5032
|
pluginId: target.pluginId,
|
|
@@ -5245,7 +5148,7 @@ export function ConsoleAppV4(props) {
|
|
|
5245
5148
|
...activationSources.map((sourceId) => ({
|
|
5246
5149
|
label: `先更新来源目录 ${sourceId}`,
|
|
5247
5150
|
description: '先单独确认目录变更;启用完成后重新准备插件更新',
|
|
5248
|
-
status: '
|
|
5151
|
+
status: 'info',
|
|
5249
5152
|
run: () => beginReview({ kind: 'catalogGeneration', action: 'update', sourceId }),
|
|
5250
5153
|
})),
|
|
5251
5154
|
...recoveryActions,
|
|
@@ -5274,8 +5177,8 @@ export function ConsoleAppV4(props) {
|
|
|
5274
5177
|
if (candidates.length > taskTargetLimit) {
|
|
5275
5178
|
referenceDetailPagination = { total: candidates.length, pageRows: taskTargetLimit };
|
|
5276
5179
|
}
|
|
5277
|
-
const
|
|
5278
|
-
const visibleTaskCandidates = candidates.slice(
|
|
5180
|
+
const taskPage = pagedItems(candidates.length, taskTargetLimit);
|
|
5181
|
+
const visibleTaskCandidates = candidates.slice(taskPage.offset, taskPage.offset + taskTargetLimit);
|
|
5279
5182
|
const targetRows = visibleTaskCandidates.map((candidate) => {
|
|
5280
5183
|
const target = candidate.target;
|
|
5281
5184
|
const version = target.expectedUpdate
|
|
@@ -5286,43 +5189,41 @@ export function ConsoleAppV4(props) {
|
|
|
5286
5189
|
? pluginWriteTaskMethodLabel(candidate.recommendedMethod)
|
|
5287
5190
|
: pluginWriteTaskBlockLabel(candidate.blockReason), meta: candidate.eligible ? version : '需要决定', status: candidate.eligible ? 'success' : 'warning', compact: policy.mode === 'compact' }, `${target.pluginId}:${target.platform}:${target.scope}`);
|
|
5288
5191
|
});
|
|
5289
|
-
const taskViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(Math.min(candidates.length, policy.mode === 'compact' ? 3 : 6) +
|
|
5290
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u5904\u7406\u8303\u56F4", meta: `${executable.length} 个可执行 · ${blockedCandidates.length} 个需决定`, compact: policy.mode === 'compact', children: [
|
|
5291
|
-
: _jsx(StaticRow, { title: "\u5F53\u524D\u6CA1\u6709\u53EF\u6267\u884C\u76EE\u6807", status: "muted", compact: true }),
|
|
5292
|
-
? _jsx(
|
|
5192
|
+
const taskViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(Math.min(candidates.length, policy.mode === 'compact' ? 3 : 6) + 3));
|
|
5193
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u5904\u7406\u8303\u56F4", meta: `${executable.length} 个可执行 · ${blockedCandidates.length} 个需决定`, compact: policy.mode === 'compact', children: [targetRows.length > 0 ? targetRows
|
|
5194
|
+
: _jsx(StaticRow, { title: "\u5F53\u524D\u6CA1\u6709\u53EF\u6267\u884C\u76EE\u6807", status: "muted", compact: true }), taskPage.pageCount > 1
|
|
5195
|
+
? _jsx(Pager, { page: taskPage.page, pageCount: taskPage.pageCount, columns: size.columns - 6 }) : null] }), blockedCandidates.length > 0 ? _jsx(Callout, { status: "warning", title: `${blockedCandidates.length} 个位置需要先处理或决定`, description: executable.length > 0
|
|
5293
5196
|
? '可以先处理可执行项,或进入下方对应入口处理受阻位置。'
|
|
5294
5197
|
: '请进入下方对应入口处理;返回后会重新读取当前证据。', compact: policy.mode === 'compact' }) : null, missingTargetKeys.length > 0 ? _jsx(Callout, { status: "warning", title: `${missingTargetKeys.length} 个本次安装位置已失效或无需处理`, description: "\u4E0D\u4F1A\u628A\u8FD9\u4E9B\u4F4D\u7F6E\u5F53\u4F5C\u5DF2\u5B8C\u6210\uFF0C\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u7F29\u5C0F\u672C\u6B21\u8303\u56F4\u3002\u8BF7\u8FD4\u56DE\u8C03\u6574\u9009\u62E9\u540E\u91CD\u65B0\u5F00\u59CB\u3002", compact: policy.mode === 'compact' }) : null, addedTargetKeys.length > 0 ? _jsx(Callout, { status: "warning", title: `${addedTargetKeys.length} 个新位置需要重新确认`, description: "\u4E0D\u4F1A\u628A\u5237\u65B0\u540E\u65B0\u589E\u7684\u4F4D\u7F6E\u81EA\u52A8\u52A0\u5165\u672C\u6B21\u64CD\u4F5C\u3002\u8BF7\u8FD4\u56DE\u8C03\u6574\u9009\u62E9\u540E\u91CD\u65B0\u5F00\u59CB\u3002", compact: policy.mode === 'compact' }) : null, versionEvidenceChanged ? _jsx(Callout, { status: "warning", title: "\u63D2\u4EF6\u76EE\u6807\u7248\u672C\u5DF2\u53D8\u5316", description: "\u4E0D\u4F1A\u6CBF\u7528\u6765\u6E90\u542F\u7528\u524D\u7684\u7248\u672C\u8BC1\u636E\uFF0C\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u6267\u884C\u65B0\u7248\u672C\u3002", compact: policy.mode === 'compact' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: taskViewport.offset, limit: taskViewport.limit })] });
|
|
5295
5198
|
}
|
|
5296
|
-
else if (route.name === 'pluginDetail' &&
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
const
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5199
|
+
else if (route.name === 'pluginDetail' && detailSubject && !plugin) {
|
|
5200
|
+
breadcrumb = `插件 / ${detailSubject.displayName}`;
|
|
5201
|
+
title = '';
|
|
5202
|
+
description = undefined;
|
|
5203
|
+
context = '插件详情';
|
|
5204
|
+
const issues = props.controller.listUserIssues().filter((entry) => issueMatchesTarget(entry, detailSubject.pluginId));
|
|
5205
|
+
const root = props.controller.getProjectInventoryRoot(detailSubject.pluginId);
|
|
5206
|
+
actions = [
|
|
5207
|
+
...(issues.length > 0 ? [{ actionId: 'exceptional', label: '查看处理建议',
|
|
5208
|
+
status: 'warning',
|
|
5209
|
+
run: () => go(issues.length === 1
|
|
5210
|
+
? { name: 'issueDetail', issueId: issues[0].issueId } : { name: 'issueList' }) }] : []),
|
|
5211
|
+
...(detailSubject.savedConfigScopes.length > 0 ? [{ actionId: 'settings',
|
|
5212
|
+
label: '查看已保存设置', run: () => props.controller.beginSavedConfigRecovery(detailSubject.pluginId) }] : []),
|
|
5213
|
+
...(detailSubject.relations.hasKnownRelations ? [{ actionId: 'dependencies',
|
|
5214
|
+
label: '依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: detailSubject.pluginId }) }] : []),
|
|
5215
|
+
...(props.controller.getSourceOverview(detailSubject.sourceId).source ? [{ actionId: 'source',
|
|
5216
|
+
label: '查看插件来源', run: () => go({ name: 'sourceDetail', sourceId: detailSubject.sourceId }) }] : []),
|
|
5217
|
+
...(root && root.state !== 'ready' && issues.length === 0 ? [{ actionId: 'project-recheck',
|
|
5218
|
+
label: '重新检查项目', run: async () => { await props.controller.refreshProjectInventory(); } }] : []),
|
|
5219
|
+
];
|
|
5220
|
+
content = detailCardContent(detailSubject);
|
|
5309
5221
|
}
|
|
5310
|
-
else if (route.name === 'pluginDetail' && plugin) {
|
|
5222
|
+
else if (route.name === 'pluginDetail' && plugin && detailSubject) {
|
|
5311
5223
|
breadcrumb = `插件 / ${plugin.displayName}`;
|
|
5312
5224
|
context = `${plugin.sourceLabel}${plugin.version ? ` · ${plugin.version}` : ''}`;
|
|
5313
5225
|
title = plugin.displayName;
|
|
5314
5226
|
description = '';
|
|
5315
|
-
const updateDescription = pluginUpdateDescription(plugin);
|
|
5316
|
-
const installedVersions = [...new Set((plugin.update?.targets ?? [])
|
|
5317
|
-
.flatMap((target) => target.installedVersion ? [target.installedVersion] : []))];
|
|
5318
|
-
const versionSummary = installedVersions.length > 1
|
|
5319
|
-
? `各平台版本不同:${installedVersions.join('、')}`
|
|
5320
|
-
: installedVersions.length === 1
|
|
5321
|
-
? plugin.version && plugin.version !== installedVersions[0]
|
|
5322
|
-
? `${installedVersions[0]} → ${plugin.version}`
|
|
5323
|
-
: `已安装 ${installedVersions[0]}`
|
|
5324
|
-
: plugin.version ?? '待确认';
|
|
5325
|
-
context = `${plugin.sourceLabel} · ${versionSummary}`;
|
|
5326
5227
|
const updateTaskCandidates = plugin.configOnly ? []
|
|
5327
5228
|
: props.controller.listPluginWriteTaskCandidates('update')
|
|
5328
5229
|
.filter((candidate) => candidate.target.pluginId === plugin.pluginId);
|
|
@@ -5336,7 +5237,7 @@ export function ConsoleAppV4(props) {
|
|
|
5336
5237
|
? `更新 ${plugin.displayName}` : '准备插件更新',
|
|
5337
5238
|
description: updateTaskCandidates.some((candidate) => candidate.blockReason === 'catalogActivationRequired')
|
|
5338
5239
|
? '需要先单独确认来源目录变更' : '使用精确版本和现有安装位置',
|
|
5339
|
-
status: '
|
|
5240
|
+
status: 'info',
|
|
5340
5241
|
run: () => go({
|
|
5341
5242
|
name: 'pluginWriteTask', pluginId: plugin.pluginId, goal: 'update',
|
|
5342
5243
|
targetKeys: updateTaskCandidates.map(pluginWriteTaskTargetKey),
|
|
@@ -5380,7 +5281,7 @@ export function ConsoleAppV4(props) {
|
|
|
5380
5281
|
? plugin.capabilityUnsupported && !plugin.sourceUnavailable ? {
|
|
5381
5282
|
label: '需要升级 AW CLI',
|
|
5382
5283
|
description: '查看缺少的功能和安全恢复方向',
|
|
5383
|
-
status: '
|
|
5284
|
+
status: 'info',
|
|
5384
5285
|
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
5385
5286
|
} : {
|
|
5386
5287
|
label: '恢复插件来源',
|
|
@@ -5415,7 +5316,7 @@ export function ConsoleAppV4(props) {
|
|
|
5415
5316
|
? { name: 'installTargets', pluginId: plugin.pluginId }
|
|
5416
5317
|
: { name: 'installTargets', pluginId: plugin.pluginId, view: 'blocked' });
|
|
5417
5318
|
};
|
|
5418
|
-
const installAction = !plugin.
|
|
5319
|
+
const installAction = !plugin.catalogEntryMissing ? {
|
|
5419
5320
|
actionId: 'install',
|
|
5420
5321
|
label: installed ? '安装到其他位置' : '安装',
|
|
5421
5322
|
description: plugin.sourceUnavailable
|
|
@@ -5431,7 +5332,8 @@ export function ConsoleAppV4(props) {
|
|
|
5431
5332
|
: { run: beginInstall }),
|
|
5432
5333
|
} : undefined;
|
|
5433
5334
|
const contentOverview = props.controller.getPluginContentOverview(plugin.pluginId);
|
|
5434
|
-
const contentAction = !plugin.
|
|
5335
|
+
const contentAction = !plugin.catalogEntryMissing
|
|
5336
|
+
&& detailSubject.definitionAvailable ? {
|
|
5435
5337
|
actionId: 'content',
|
|
5436
5338
|
label: '内容详情',
|
|
5437
5339
|
description: contentOverview
|
|
@@ -5440,7 +5342,8 @@ export function ConsoleAppV4(props) {
|
|
|
5440
5342
|
run: () => go({ name: 'pluginContent', pluginId: plugin.pluginId }),
|
|
5441
5343
|
} : undefined;
|
|
5442
5344
|
const dependencyOverviewForDetail = dependencyOverview(plugin);
|
|
5443
|
-
const dependencyAction = !plugin.
|
|
5345
|
+
const dependencyAction = !plugin.catalogEntryMissing
|
|
5346
|
+
&& detailSubject.relations.hasKnownRelations ? {
|
|
5444
5347
|
actionId: 'dependencies',
|
|
5445
5348
|
label: '依赖关系',
|
|
5446
5349
|
description: dependencyOverviewForDetail
|
|
@@ -5469,6 +5372,23 @@ export function ConsoleAppV4(props) {
|
|
|
5469
5372
|
go({ name: 'uninstallTargets', pluginId: plugin.pluginId });
|
|
5470
5373
|
},
|
|
5471
5374
|
} : undefined;
|
|
5375
|
+
const cleanupAction = plugin.configOnly
|
|
5376
|
+
|| plugin.removedPluginCleanupRequired ? {
|
|
5377
|
+
actionId: 'cleanup',
|
|
5378
|
+
label: plugin.removedPluginCleanupRequired ? '清理已删除插件' : '检查并清理插件残留',
|
|
5379
|
+
description: plugin.removedPluginCleanupRequired
|
|
5380
|
+
? '预览两个平台、用户与当前项目中的残留和保存设置,再确认实际清理范围'
|
|
5381
|
+
: '先核验当前来源和现场,再显示可清理内容与实际范围',
|
|
5382
|
+
status: 'error',
|
|
5383
|
+
run: () => {
|
|
5384
|
+
const target = props.controller.listPluginWriteBatchTargets('cleanup', [plugin.pluginId])[0];
|
|
5385
|
+
beginReview({
|
|
5386
|
+
kind: 'pluginWrite', action: 'cleanup', pluginId: plugin.pluginId,
|
|
5387
|
+
platform: target?.platform ?? 'codex',
|
|
5388
|
+
scope: target?.scope ?? plugin.savedConfigScopes[0] ?? 'user',
|
|
5389
|
+
});
|
|
5390
|
+
},
|
|
5391
|
+
} : undefined;
|
|
5472
5392
|
actions = dedupeActions([
|
|
5473
5393
|
...(exceptionalAction ? [{ ...exceptionalAction, actionId: 'exceptional' }] : []),
|
|
5474
5394
|
...(restoreAction ? [{ ...restoreAction, actionId: 'restore' }] : []),
|
|
@@ -5478,6 +5398,7 @@ export function ConsoleAppV4(props) {
|
|
|
5478
5398
|
...(contentAction ? [contentAction] : []),
|
|
5479
5399
|
...(dependencyAction ? [dependencyAction] : []),
|
|
5480
5400
|
...(uninstallAction ? [uninstallAction] : []),
|
|
5401
|
+
...(cleanupAction ? [cleanupAction] : []),
|
|
5481
5402
|
{
|
|
5482
5403
|
actionId: 'more',
|
|
5483
5404
|
label: '更多操作',
|
|
@@ -5485,92 +5406,64 @@ export function ConsoleAppV4(props) {
|
|
|
5485
5406
|
run: () => go({ name: 'pluginMore', pluginId: plugin.pluginId }),
|
|
5486
5407
|
},
|
|
5487
5408
|
]);
|
|
5488
|
-
const dependencyDetailText = dependencyOverviewForDetail
|
|
5489
|
-
&& dependencyOverviewForDetail.summary.totalCount > 0
|
|
5490
|
-
? `需要 ${dependencyOverviewForDetail.summary.totalCount} 个插件;当前可用关系按当前范围检查,未显示的关系尚未检查通过。`
|
|
5491
|
-
: undefined;
|
|
5492
|
-
const detailPlatforms = [...new Set([
|
|
5493
|
-
...plugin.installedPlacements.map((placement) => placement.platform), ...plugin.supportedPlatforms,
|
|
5494
|
-
])];
|
|
5495
|
-
const detailPositions = detailPlatforms.flatMap((platform) => {
|
|
5496
|
-
const placements = plugin.installedPlacements.filter((placement) => placement.platform === platform && placement.state !== 'notInstalled');
|
|
5497
|
-
return placements.length > 0
|
|
5498
|
-
? placements.map((placement) => `${platformLabel(platform)}:${pluginPlacementConclusions(placement).map((entry) => entry.text).join(' · ')}`)
|
|
5499
|
-
: [`${platformLabel(platform)} · ${pluginPlatformUnknown(plugin, platform) ? '未知 · 尚未确认' : '尚未安装'}`];
|
|
5500
|
-
});
|
|
5501
|
-
const platformSummary = detailPositions.length > 0 ? detailPositions.join(';') : '当前没有可用平台';
|
|
5502
5409
|
context = '插件详情';
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
`版本 · ${versionSummary}`,
|
|
5506
|
-
`来源 · ${plugin.sourceLabel}`,
|
|
5507
|
-
...(props.controller.getProjectInventoryRoot(plugin.pluginId)?.targets.map((target) => `${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '项目声明'} · 期望 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'}`) ?? []),
|
|
5508
|
-
...projectDependencyLines(props.controller.getProjectInventoryRoot(plugin.pluginId)),
|
|
5509
|
-
...(detailPositions.length > 0 ? detailPositions : [platformSummary]),
|
|
5510
|
-
...(plugin.portableSkills ? ['[Skills 可选]'] : []),
|
|
5511
|
-
...(plugin.configOnly ? ['[已保存设置]'] : []),
|
|
5512
|
-
...(updateDescription ? [`有可用更新 · ${updateDescription}`] : []),
|
|
5513
|
-
...(dependencyDetailText ? [dependencyDetailText] : []),
|
|
5514
|
-
];
|
|
5515
|
-
readingText = [plugin.displayName, ...summary].join('\n');
|
|
5516
|
-
const summaryLines = summary.flatMap((line) => wrapConsoleText(line, size.columns - 2));
|
|
5410
|
+
title = '';
|
|
5411
|
+
description = undefined;
|
|
5517
5412
|
const completion = snapshot.completionNotice?.pluginId === plugin.pluginId
|
|
5518
5413
|
? snapshot.completionNotice : undefined;
|
|
5519
5414
|
const completionLines = completion
|
|
5520
5415
|
? wrapConsoleText(`${completion.title} · ${completion.description}`, size.columns - 2) : [];
|
|
5521
|
-
|
|
5522
|
-
const summaryPageRows = Math.max(1, availableSummaryRows - Number(summaryLines.length > availableSummaryRows));
|
|
5523
|
-
const summaryOffset = Math.min(batchTargetOffset, Math.max(0, summaryLines.length - summaryPageRows));
|
|
5524
|
-
const visibleSummary = summaryLines.slice(summaryOffset, summaryOffset + summaryPageRows);
|
|
5525
|
-
const summaryPaginated = summaryLines.length > summaryPageRows;
|
|
5526
|
-
if (summaryPaginated) {
|
|
5527
|
-
referenceDetailPagination = { total: summaryLines.length, pageRows: summaryPageRows };
|
|
5528
|
-
context = `${summaryOffset + 1}–${summaryOffset + visibleSummary.length} / ${summaryLines.length}`;
|
|
5529
|
-
help = '←→ 翻页 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
5530
|
-
}
|
|
5531
|
-
currentActionViewportLineBudget = pageActionBudget(visibleSummary.length
|
|
5532
|
-
+ Number(summaryPaginated) + completionLines.length);
|
|
5533
|
-
const detailViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
5534
|
-
content = _jsxs(Box, { flexDirection: "column", children: [completionLines.map((line, index) => _jsx(Text, { color: tuiTheme.statusColors.success, children: line }, `completion:${index}`)), visibleSummary.map((line, index) => _jsx(Text, { wrap: "truncate-end", children: line }, `${summaryOffset + index}`)), summaryPaginated ? _jsx(Text, { color: tuiTheme.colors.muted, children: summaryOffset + visibleSummary.length < summaryLines.length
|
|
5535
|
-
? `↓ 后面还有 ${summaryLines.length - summaryOffset - visibleSummary.length} 行`
|
|
5536
|
-
: '已到正文末尾' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: detailViewport.offset, limit: detailViewport.limit })] });
|
|
5416
|
+
content = detailCardContent(detailSubject, completionLines);
|
|
5537
5417
|
}
|
|
5538
5418
|
else if (route.name === 'pluginMore' && plugin) {
|
|
5539
5419
|
breadcrumb = `插件 / ${plugin.displayName} / 更多操作`;
|
|
5540
5420
|
context = plugin.sourceLabel;
|
|
5541
5421
|
title = '更多操作';
|
|
5542
5422
|
description = '选择平台可使用同步、重新安装、移除和受限清理。';
|
|
5423
|
+
const detail = props.controller.getPluginDetailSubject(plugin.pluginId);
|
|
5424
|
+
const platforms = plugin.installedPlacements.filter((placement) => placement.state !== 'notInstalled');
|
|
5425
|
+
const entryFacts = platforms.map((placement) => detail?.placements
|
|
5426
|
+
.filter((entry) => entry.platform === placement.platform) ?? []);
|
|
5543
5427
|
actions = [
|
|
5544
|
-
...
|
|
5545
|
-
.filter((placement) => placement.state !== 'notInstalled')
|
|
5428
|
+
...platforms
|
|
5546
5429
|
.map((placement) => {
|
|
5547
|
-
const projected = placementStatus(placement);
|
|
5548
5430
|
return {
|
|
5431
|
+
actionId: `platform:${placement.platform}`,
|
|
5549
5432
|
label: `${platformLabel(placement.platform)} 平台维护`,
|
|
5550
|
-
description: '同步、重新安装和受限清理',
|
|
5551
|
-
meta: projected.value,
|
|
5552
|
-
status: projected.status,
|
|
5553
5433
|
run: () => go({ name: 'platform', pluginId: plugin.pluginId, platform: placement.platform }),
|
|
5554
5434
|
};
|
|
5555
5435
|
}),
|
|
5556
5436
|
{ label: '查看插件来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) },
|
|
5557
5437
|
{ label: '诊断与技术详情', description: '查看插件身份、组件状态和安全错误代码', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
5558
5438
|
];
|
|
5559
|
-
const
|
|
5560
|
-
|
|
5439
|
+
const cardWidth = size.columns - 2;
|
|
5440
|
+
const moreViewport = rowViewport({ rowHeights: actions.map((_, index) => index < platforms.length
|
|
5441
|
+
? 2 + (entryFacts[index].length ? entryFacts[index].reduce((height, fact) => height
|
|
5442
|
+
+ entityPlacementRowHeight({ ...fact, shared: fact.sharedWithCurrentProject }, cardWidth), 0) : 1)
|
|
5443
|
+
: 1), focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(0) });
|
|
5444
|
+
content = _jsx(ContentViewport, { children: actions.slice(moreViewport.offset, moreViewport.offset + moreViewport.limit)
|
|
5445
|
+
.map((action, visibleIndex) => {
|
|
5446
|
+
const index = moreViewport.offset + visibleIndex;
|
|
5447
|
+
return index < platforms.length ? _jsx(ObjectCardFrame, { width: cardWidth, title: action.label, focused: focus === index, children: entryFacts[index].length ? entryFacts[index].map((fact) => _jsx(EntityPlacementRow, { placement: { ...fact, shared: fact.sharedWithCurrentProject }, width: cardWidth }, fact.scope)) : _jsx(Text, { color: tuiTheme.colors.muted, children: "\u4F4D\u7F6E\u72B6\u6001\u672A\u786E\u8BA4" }) }, action.actionId) : _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: index, limit: 1 }, index);
|
|
5448
|
+
}) });
|
|
5561
5449
|
}
|
|
5562
5450
|
else if (route.name === 'platform' && plugin) {
|
|
5563
|
-
const
|
|
5451
|
+
const aggregatePlacement = props.controller.getPlatformPlacement(plugin.pluginId, route.platform);
|
|
5452
|
+
const placement = aggregatePlacement?.state === 'installed' && platformSelectedScope
|
|
5453
|
+
? { ...aggregatePlacement,
|
|
5454
|
+
...(aggregatePlacement.scopedPlacements?.find((entry) => entry.scope === platformSelectedScope) ?? {}) }
|
|
5455
|
+
: aggregatePlacement;
|
|
5564
5456
|
const platformMenu = dynamicMenu;
|
|
5565
|
-
const typedIssue =
|
|
5457
|
+
const typedIssue = platformSelectedScope
|
|
5566
5458
|
? props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId
|
|
5567
5459
|
&& candidate.platform === route.platform
|
|
5568
|
-
&& candidate.scope ===
|
|
5460
|
+
&& candidate.scope === platformSelectedScope)
|
|
5569
5461
|
: undefined;
|
|
5570
5462
|
const collisionIssue = placement?.state === 'blocked'
|
|
5571
5463
|
|| placement?.state === 'installed' && placement.blockingReason === 'artifactCollision'
|
|
5572
5464
|
? props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId
|
|
5573
5465
|
&& candidate.platform === route.platform
|
|
5466
|
+
&& (!platformSelectedScope || candidate.scope === platformSelectedScope)
|
|
5574
5467
|
&& candidate.category === 'artifactCollision')
|
|
5575
5468
|
: undefined;
|
|
5576
5469
|
const capabilityUnavailable = plugin.capabilityUnsupported && !plugin.sourceUnavailable;
|
|
@@ -5582,14 +5475,16 @@ export function ConsoleAppV4(props) {
|
|
|
5582
5475
|
return {
|
|
5583
5476
|
label: '升级 AW CLI',
|
|
5584
5477
|
description: '升级到支持此插件的 AW CLI 版本后重试',
|
|
5585
|
-
status: '
|
|
5586
|
-
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId
|
|
5478
|
+
status: 'info',
|
|
5479
|
+
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId,
|
|
5480
|
+
platform: route.platform, scope: platformSelectedScope }),
|
|
5587
5481
|
};
|
|
5588
5482
|
if (action.id === 'capabilityTechnical')
|
|
5589
5483
|
return {
|
|
5590
5484
|
label: '查看功能支持详情',
|
|
5591
5485
|
description: '查看缺少的功能和安全恢复方向',
|
|
5592
|
-
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId
|
|
5486
|
+
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId,
|
|
5487
|
+
platform: route.platform, scope: platformSelectedScope }),
|
|
5593
5488
|
};
|
|
5594
5489
|
if (action.id === 'sourceProblem')
|
|
5595
5490
|
return { label: '查看来源问题', description: `${plugin.sourceId} 当前不可用`, run: () => go({ name: 'issueDetail', issueId: `source:${plugin.sourceId}:unavailable` }) };
|
|
@@ -5609,11 +5504,11 @@ export function ConsoleAppV4(props) {
|
|
|
5609
5504
|
if (action.id === 'addScope')
|
|
5610
5505
|
return {
|
|
5611
5506
|
label: `在${scopeLabel(action.scope)}中启用`,
|
|
5612
|
-
description:
|
|
5507
|
+
description: `补充安装目标:${platformLabel(action.platform)} · ${scopeLabel(action.scope)};在操作汇总中确认。`,
|
|
5613
5508
|
run: () => beginSecondScope(plugin, action.platform, action.scope),
|
|
5614
5509
|
};
|
|
5615
5510
|
if (action.id === 'about')
|
|
5616
|
-
return { label: '查看支持信息', description: '插件定义与平台支持', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) };
|
|
5511
|
+
return { label: '查看支持信息', description: '插件定义与平台支持', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId, platform: route.platform, scope: platformSelectedScope }) };
|
|
5617
5512
|
if (action.id === 'source')
|
|
5618
5513
|
return { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) };
|
|
5619
5514
|
if (action.id === 'outputCollision')
|
|
@@ -5623,7 +5518,8 @@ export function ConsoleAppV4(props) {
|
|
|
5623
5518
|
status: 'error',
|
|
5624
5519
|
run: () => collisionIssue
|
|
5625
5520
|
? go({ name: 'issueDetail', issueId: collisionIssue.issueId })
|
|
5626
|
-
: go({ name: 'pluginTechnical', pluginId: plugin.pluginId
|
|
5521
|
+
: go({ name: 'pluginTechnical', pluginId: plugin.pluginId,
|
|
5522
|
+
platform: route.platform, scope: platformSelectedScope }),
|
|
5627
5523
|
};
|
|
5628
5524
|
if (action.id === 'scopeConflict')
|
|
5629
5525
|
return { label: '查看位置冲突问题', run: () => go({ name: 'issueDetail', issueId: `scope:${plugin.pluginId}:${route.platform}` }) };
|
|
@@ -5637,6 +5533,7 @@ export function ConsoleAppV4(props) {
|
|
|
5637
5533
|
if (action.id === 'refreshAndSync')
|
|
5638
5534
|
return {
|
|
5639
5535
|
label: `更新到 ${action.availableVersion}`,
|
|
5536
|
+
status: 'info',
|
|
5640
5537
|
description: '使用当前启用目录中的已确认版本',
|
|
5641
5538
|
run: () => beginReview({
|
|
5642
5539
|
kind: 'pluginWrite', action: 'sync', pluginId: plugin.pluginId,
|
|
@@ -5652,6 +5549,7 @@ export function ConsoleAppV4(props) {
|
|
|
5652
5549
|
if (action.id === 'sync')
|
|
5653
5550
|
return {
|
|
5654
5551
|
label: `同步${scopeLabel(action.scope)}的使用位置`,
|
|
5552
|
+
status: 'info',
|
|
5655
5553
|
description: placement?.state === 'installed'
|
|
5656
5554
|
? (placement.scopedPlacements?.find((entry) => entry.scope === action.scope)?.recovery
|
|
5657
5555
|
?? placement.recovery)?.find((entry) => entry.command === 'sync')?.description
|
|
@@ -5662,7 +5560,7 @@ export function ConsoleAppV4(props) {
|
|
|
5662
5560
|
: undefined,
|
|
5663
5561
|
};
|
|
5664
5562
|
if (action.id === 'reinstall')
|
|
5665
|
-
return { label: `重新安装${scopeLabel(action.scope)}位置`, description: '保留现有设置并重新生成受管内容', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: action.platform, scope: action.scope }) };
|
|
5563
|
+
return { label: `重新安装${scopeLabel(action.scope)}位置`, status: 'info', description: '保留现有设置并重新生成受管内容', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: action.platform, scope: action.scope }) };
|
|
5666
5564
|
if (action.id === 'remove')
|
|
5667
5565
|
return { label: `从${scopeLabel(action.scope)}移除`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, action.platform, action.scope) };
|
|
5668
5566
|
if (action.id === 'cleanup')
|
|
@@ -5670,7 +5568,7 @@ export function ConsoleAppV4(props) {
|
|
|
5670
5568
|
if (action.id === 'technical')
|
|
5671
5569
|
return typedIssue
|
|
5672
5570
|
? { label: '查看对应问题', description: typedIssue.explanation, run: () => go({ name: 'issueDetail', issueId: typedIssue.issueId }) }
|
|
5673
|
-
: { label: placement?.state === 'installed' && placement.health === 'error' ? '查看安全技术详情' : placement?.state === 'installed' && placement.health === 'incomplete' ? '查看影响' : '查看安装详情', description: '组件与诊断信息', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) };
|
|
5571
|
+
: { label: placement?.state === 'installed' && placement.health === 'error' ? '查看安全技术详情' : placement?.state === 'installed' && placement.health === 'incomplete' ? '查看影响' : '查看安装详情', description: '组件与诊断信息', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId, platform: route.platform, scope: platformSelectedScope }) };
|
|
5674
5572
|
return { role: 'navigation', label: '返回插件', run: back };
|
|
5675
5573
|
};
|
|
5676
5574
|
const offlineCleanupAction = typedIssue?.actions.find((action) => action.id === 'cleanup');
|
|
@@ -5678,6 +5576,7 @@ export function ConsoleAppV4(props) {
|
|
|
5678
5576
|
&& placement?.state === 'installed'
|
|
5679
5577
|
&& placement.blockingReason === undefined
|
|
5680
5578
|
&& (placement.actualState === 'orphaned' || placement.actualState === 'blocked')
|
|
5579
|
+
&& platformSelectedScope === placement.scope
|
|
5681
5580
|
&& offlineCleanupAction !== undefined
|
|
5682
5581
|
&& !platformMenu.some((action) => action.id === 'cleanup')
|
|
5683
5582
|
&& !platformMenu.some((action) => action.id === 'resolveScopeConflict')
|
|
@@ -5695,6 +5594,7 @@ export function ConsoleAppV4(props) {
|
|
|
5695
5594
|
const restrictedForceCleanup = placement?.state === 'installed'
|
|
5696
5595
|
&& placement.blockingReason === undefined
|
|
5697
5596
|
&& (placement.actualState === 'orphaned' || placement.actualState === 'blocked')
|
|
5597
|
+
&& platformSelectedScope === placement.scope
|
|
5698
5598
|
&& !platformMenu.some((action) => action.id === 'resolveScopeConflict')
|
|
5699
5599
|
? {
|
|
5700
5600
|
label: '检查受限清理',
|
|
@@ -5704,102 +5604,84 @@ export function ConsoleAppV4(props) {
|
|
|
5704
5604
|
}
|
|
5705
5605
|
: undefined;
|
|
5706
5606
|
const platformActions = () => [
|
|
5707
|
-
...platformMenu.
|
|
5708
|
-
|
|
5709
|
-
|
|
5607
|
+
...platformMenu.filter((action) => action.id === 'addScope' || !('scope' in action) || action.scope === platformSelectedScope)
|
|
5608
|
+
.filter((action) => platformSelectedScope !== undefined
|
|
5609
|
+
|| !('scope' in action))
|
|
5610
|
+
.filter((action) => platformSelectedScope !== undefined
|
|
5611
|
+
|| platformFacts.length === 0
|
|
5612
|
+
|| action.id !== 'technical' && action.id !== 'outputCollision')
|
|
5613
|
+
.filter((action) => {
|
|
5614
|
+
const selectedFact = platformFacts.find((entry) => entry.scope === platformSelectedScope);
|
|
5615
|
+
return selectedFact?.observation !== 'unknown'
|
|
5616
|
+
|| !['refreshAndSync', 'sync', 'reinstall', 'remove', 'cleanup'].includes(action.id);
|
|
5617
|
+
}).map((action) => ({ ...platformAction(action),
|
|
5618
|
+
actionId: pluginMenuActionId(action) })),
|
|
5619
|
+
...(offlineCleanup ? [{ ...offlineCleanup,
|
|
5620
|
+
actionId: `offlineCleanup:${route.platform}:${platformSelectedScope}` }] : []),
|
|
5621
|
+
...(restrictedForceCleanup ? [{ ...restrictedForceCleanup,
|
|
5622
|
+
actionId: `forceCleanup:${route.platform}:${platformSelectedScope}` }] : []),
|
|
5710
5623
|
];
|
|
5711
5624
|
breadcrumb = `${plugin.displayName} / ${platformLabel(route.platform)}`;
|
|
5712
|
-
const
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
}
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
stage: 'read-platform-state',
|
|
5773
|
-
context: [
|
|
5774
|
-
{ key: 'pluginId', value: plugin.pluginId },
|
|
5775
|
-
{ key: 'platform', value: route.platform },
|
|
5776
|
-
{ key: 'scope', value: placement.scope },
|
|
5777
|
-
],
|
|
5778
|
-
},
|
|
5779
|
-
};
|
|
5780
|
-
title = `${platformLabel(route.platform)} 暂时不可管理`;
|
|
5781
|
-
description = '';
|
|
5782
|
-
actions = platformActions();
|
|
5783
|
-
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' })] });
|
|
5784
|
-
}
|
|
5785
|
-
else if (placement.health === 'incomplete') {
|
|
5786
|
-
const issue = props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId
|
|
5787
|
-
&& candidate.platform === route.platform
|
|
5788
|
-
&& candidate.scope === placement.scope);
|
|
5789
|
-
title = issue?.title ?? `${platformLabel(route.platform)} 状态待同步`;
|
|
5790
|
-
description = issue?.explanation ?? '现场状态与期望状态不一致,需要重新检查并同步。';
|
|
5791
|
-
actions = platformActions();
|
|
5792
|
-
const actualTitle = placement.actualState === 'needsSync' ? '需要同步'
|
|
5793
|
-
: placement.actualState === 'blocked' ? '当前有安全阻断' : '需要处理';
|
|
5794
|
-
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' })] });
|
|
5795
|
-
}
|
|
5796
|
-
else {
|
|
5797
|
-
title = `已安装到${scopeLabel(placement.scope)}`;
|
|
5798
|
-
const installedVersion = placement.revision.installedVersion;
|
|
5799
|
-
description = `当前检查正常${installedVersion ? `;已安装版本 ${installedVersion}` : ''}。`;
|
|
5800
|
-
actions = platformActions();
|
|
5801
|
-
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' })] });
|
|
5802
|
-
}
|
|
5625
|
+
const selectedFact = platformFacts.find((entry) => entry.scope === platformSelectedScope);
|
|
5626
|
+
const selectedIssue = platformSelectedScope ? props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId && candidate.platform === route.platform
|
|
5627
|
+
&& candidate.scope === platformSelectedScope) : undefined;
|
|
5628
|
+
const factActions = platformFacts.map((entry) => ({
|
|
5629
|
+
actionId: `placement:${entry.platform}:${entry.scope}`,
|
|
5630
|
+
label: `${platformLabel(entry.platform)} · ${scopeLabel(entry.scope)}`,
|
|
5631
|
+
selected: entry.scope === platformSelectedScope,
|
|
5632
|
+
run: () => {
|
|
5633
|
+
props.controller.updatePageContext({
|
|
5634
|
+
platformSelectionInitialized: true,
|
|
5635
|
+
platformSelectedScope: entry.scope,
|
|
5636
|
+
});
|
|
5637
|
+
dynamicMenuFocus.current = undefined;
|
|
5638
|
+
setActionError(undefined);
|
|
5639
|
+
},
|
|
5640
|
+
}));
|
|
5641
|
+
actions = [...factActions, ...platformActions()];
|
|
5642
|
+
dynamicActionIds = actions.map((action) => action.actionId);
|
|
5643
|
+
dynamicActionSignature = dynamicActionIds.join('\0');
|
|
5644
|
+
const previousDynamicFocus = dynamicMenuFocus.current;
|
|
5645
|
+
if (previousDynamicFocus?.routeKey === routeKey
|
|
5646
|
+
&& previousDynamicFocus.actionIds !== dynamicActionSignature) {
|
|
5647
|
+
const retained = previousDynamicFocus.focusedActionId
|
|
5648
|
+
? dynamicActionIds.indexOf(previousDynamicFocus.focusedActionId) : -1;
|
|
5649
|
+
const selectedCard = platformSelectedScope
|
|
5650
|
+
? dynamicActionIds.indexOf(`placement:${route.platform}:${platformSelectedScope}`) : -1;
|
|
5651
|
+
effectiveFocus = retained >= 0 ? retained : Math.max(0, selectedCard);
|
|
5652
|
+
}
|
|
5653
|
+
const cardWidth = size.columns - 2;
|
|
5654
|
+
const rowHeights = actions.map((action, index) => index < platformFacts.length
|
|
5655
|
+
? entityPlacementRowHeight({ ...platformFacts[index], shared: platformFacts[index].sharedWithCurrentProject }, cardWidth) + 2
|
|
5656
|
+
: action.role === 'navigation' ? 0 : 1);
|
|
5657
|
+
title = `${platformLabel(route.platform)} 维护`;
|
|
5658
|
+
description = selectedFact
|
|
5659
|
+
? `${scopeLabel(selectedFact.scope)} · ${selectedFact.observation === 'unknown'
|
|
5660
|
+
? '位置状态未确认;普通写操作不可用。'
|
|
5661
|
+
: selectedIssue?.explanation ?? (placement?.state === 'installed'
|
|
5662
|
+
&& placement.blockingReason === 'artifactCollision'
|
|
5663
|
+
? '输出位置存在冲突内容;不会覆盖或删除占用内容。'
|
|
5664
|
+
: placement?.state === 'installed' && placement.sharedNative?.reason === 'outdated'
|
|
5665
|
+
? '已安装版本与当前 Catalog 版本不一致。'
|
|
5666
|
+
: '仅显示此位置获许可的动作。')}`
|
|
5667
|
+
: platformFacts.length > 0 ? '先按 Enter 选择一个位置,再执行该位置的操作。'
|
|
5668
|
+
: plugin.aggregateStatus === 'unavailable'
|
|
5669
|
+
? capabilityUnavailable ? '当前 AW CLI 不支持此插件所需功能。'
|
|
5670
|
+
: sourceIssue?.explanation ?? '来源当前不可用;请检查来源。'
|
|
5671
|
+
: placement?.state === 'blocked' ? '输出位置已有冲突内容;不会覆盖或删除。'
|
|
5672
|
+
: placement?.state === 'scopeConflict' ? '安装位置冲突;请先查看问题。'
|
|
5673
|
+
: '当前没有已确认的安装位置。';
|
|
5674
|
+
const viewport = rowViewport({ rowHeights, focus: effectiveFocus,
|
|
5675
|
+
requestedOffset: scrollOffset, rowBudget: pageActionBudget(1) });
|
|
5676
|
+
help = '↑↓ 移动焦点 · Enter 选择位置或动作 · Esc 返回';
|
|
5677
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: "\u4F7F\u7528\u4F4D\u7F6E", meta: `${platformFacts.length} 个`, compact: true }), _jsx(ContentViewport, { children: actions.slice(viewport.offset, viewport.offset + viewport.limit)
|
|
5678
|
+
.map((action, visibleIndex) => {
|
|
5679
|
+
const index = viewport.offset + visibleIndex;
|
|
5680
|
+
if (index >= platformFacts.length)
|
|
5681
|
+
return _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: true, offset: index, limit: 1 }, action.actionId ?? index);
|
|
5682
|
+
const fact = platformFacts[index];
|
|
5683
|
+
return _jsx(ObjectCardFrame, { width: cardWidth, focused: effectiveFocus === index, title: `${scopeLabel(fact.scope)} ${fact.scope === platformSelectedScope ? '[选定]' : '[选择]'}`, children: _jsx(EntityPlacementRow, { placement: { ...fact, shared: fact.sharedWithCurrentProject }, width: cardWidth }) }, action.actionId);
|
|
5684
|
+
}) })] });
|
|
5803
5685
|
}
|
|
5804
5686
|
else if (route.name === 'configLayer' && plugin) {
|
|
5805
5687
|
breadcrumb = `${plugin.displayName} / 设置`;
|
|
@@ -5869,7 +5751,7 @@ export function ConsoleAppV4(props) {
|
|
|
5869
5751
|
description = undefined;
|
|
5870
5752
|
if (configActionError)
|
|
5871
5753
|
description = configActionError;
|
|
5872
|
-
help = '
|
|
5754
|
+
help = 'Esc 返回';
|
|
5873
5755
|
if (!currentConfigPage) {
|
|
5874
5756
|
content = _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u8BBE\u7F6E", children: _jsx(ProgressRow, { title: "\u8BFB\u53D6\u8BBE\u7F6E\u5B57\u6BB5", state: "active" }) });
|
|
5875
5757
|
}
|
|
@@ -5905,6 +5787,8 @@ export function ConsoleAppV4(props) {
|
|
|
5905
5787
|
return {
|
|
5906
5788
|
value: pending ?? effectiveFieldDisplay(field),
|
|
5907
5789
|
source: field.effectiveSourceLabel,
|
|
5790
|
+
description: field.description?.split('\n').map(safeConfigText).join('\n'),
|
|
5791
|
+
issue: field.invalidOverrideIssue?.split('\n').map(safeConfigText).join('\n'),
|
|
5908
5792
|
invalid: Boolean(field.invalidOverrideIssue),
|
|
5909
5793
|
...(targets.length ? { pending: covered
|
|
5910
5794
|
? '待保存到个人;个人修改不会影响本项目'
|
|
@@ -5973,17 +5857,25 @@ export function ConsoleAppV4(props) {
|
|
|
5973
5857
|
} }, ...operationActions];
|
|
5974
5858
|
}
|
|
5975
5859
|
const effectiveConfigFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
5976
|
-
const focusedConfigField =
|
|
5977
|
-
|
|
5978
|
-
:
|
|
5860
|
+
const focusedConfigField = activeGroup?.fields.find((field) => field.key === actions[effectiveConfigFocus]?.configKey);
|
|
5861
|
+
const configRowBudget = pageActionBudget(Number(visibleConfigTabs.length > 1)
|
|
5862
|
+
+ (configDraftIssues.length > 0 ? 3 : 0));
|
|
5863
|
+
const focusedTile = actions[effectiveConfigFocus]?.configTile;
|
|
5864
|
+
if (focusedTile && route.view !== 'options' && route.view !== 'values') {
|
|
5865
|
+
const key = `${routeKey}:${focusedConfigField?.key}`;
|
|
5866
|
+
const pageCount = Math.max(1, Math.ceil((configTileHeight({ width: size.columns - 2,
|
|
5867
|
+
...focusedTile, focused: true }) - 2) / Math.max(1, configRowBudget - 3)));
|
|
5868
|
+
configCardNavigation = { key, pageCount,
|
|
5869
|
+
page: Math.min(pageCount, configCardPage?.key === key ? configCardPage.page : 1) };
|
|
5870
|
+
}
|
|
5979
5871
|
if (!readonly) {
|
|
5980
5872
|
const canUnsetFocused = focusedConfigField
|
|
5981
5873
|
? ['project', 'user'].some((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
5982
5874
|
.some((field) => field.key === focusedConfigField.key && configFieldCanUnset(field)))
|
|
5983
5875
|
: false;
|
|
5984
|
-
help =
|
|
5985
|
-
? '
|
|
5986
|
-
|
|
5876
|
+
help = `↑↓ 选择 · Enter ${focusedConfigField ? '修改' : '选择'}${!route.view && visibleConfigTabs.length > 1
|
|
5877
|
+
? ' · ←→ 分组' : ''}${(configCardNavigation?.pageCount ?? 1) > 1 ? ' · PgUp/PgDn 卡片全文' : ''}`
|
|
5878
|
+
+ `${configDraftCount > 0 ? ' · s 查看汇总' : ''}${canUnsetFocused ? ' · u 恢复继承' : ''} · Esc 返回`;
|
|
5987
5879
|
}
|
|
5988
5880
|
const totalFields = currentConfigPage.groups.reduce((count, group) => count + group.fields.length, 0);
|
|
5989
5881
|
const countLabel = `${totalFields} 项${currentConfigPage.presets.length > 0 ? ` · ${currentConfigPage.presets.length} 个方案` : ''}`;
|
|
@@ -5992,19 +5884,15 @@ export function ConsoleAppV4(props) {
|
|
|
5992
5884
|
: route.view === 'values' ? (() => {
|
|
5993
5885
|
title = '完整设置值与来源';
|
|
5994
5886
|
description = '显示各保存位置的完整安全值;敏感值不回显。';
|
|
5995
|
-
const lines = ['effective', 'project', 'user'].flatMap((scope) => ((scope === 'effective' ? currentConfigPage : configLayerPages[scope])?.groups.flatMap((group) => group.fields) ?? []).flatMap((field) => wrapConsoleText(`${field.title} · ${configLayerLabel({ scope })}:${scope === 'effective'
|
|
5996
|
-
? field.invalidOverrideIssue ? '需修正' : fieldValueText(field, field.draftValue ?? field.effectiveValue, true, field.effectiveSourceLabel !== '未设置')
|
|
5997
|
-
: fieldValueText(field, field.draftValue ?? field.value, true, field.configured)}`, size.columns - 4)));
|
|
5998
5887
|
readingText = ['effective', 'project', 'user'].flatMap((scope) => ((scope === 'effective' ? currentConfigPage : configLayerPages[scope])?.groups.flatMap((group) => group.fields) ?? []).map((field) => `${field.title} · ${configLayerLabel({ scope })}:${scope === 'effective'
|
|
5999
5888
|
? field.invalidOverrideIssue ? '需修正' : fieldValueText(field, field.draftValue ?? field.effectiveValue, true, field.effectiveSourceLabel !== '未设置')
|
|
6000
5889
|
: fieldValueText(field, field.draftValue ?? field.value, true, field.configured)}`)).join('\n');
|
|
6001
|
-
const pageRows = Math.max(1, pageActionBudget(4));
|
|
6002
|
-
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
6003
|
-
referenceDetailPagination = { total: lines.length, pageRows };
|
|
6004
5890
|
actions = [{ label: '返回设置', run: back }];
|
|
6005
5891
|
help = '←→ 或 PgUp/PgDn 阅读 · Esc 返回';
|
|
6006
|
-
return _jsxs(Box, { flexDirection: "column", children: [
|
|
6007
|
-
})() : _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab
|
|
5892
|
+
return _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, Math.max(1, pageActionBudget(4) - 1)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
5893
|
+
})() : _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab && visibleConfigTabs.length > 1 ? _jsx(QueryHeader, { width: size.columns - 2, tabs: { label: '分组', items: visibleConfigTabs.map((tab) => ({
|
|
5894
|
+
id: tab.id, label: tab.label, count: tab.count,
|
|
5895
|
+
})), currentId: activeTab.id } }) : null, _jsx(SequenceRows, { actions: actions, itemCount: tabActions.length, focus: effectiveConfigFocus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: configRowBudget, configPage: configCardNavigation?.page, compact: policy.mode === 'compact' })] });
|
|
6008
5896
|
}
|
|
6009
5897
|
}
|
|
6010
5898
|
else if (route.name === 'configSave' && plugin) {
|
|
@@ -6018,22 +5906,16 @@ export function ConsoleAppV4(props) {
|
|
|
6018
5906
|
title: `${scopeLabel(group.scope)} · ${change.title}`,
|
|
6019
5907
|
})));
|
|
6020
5908
|
if (route.view === 'changes') {
|
|
6021
|
-
const lines = changeDetailLines(changes, size.columns - 4);
|
|
6022
5909
|
readingText = changeDetailText(changes);
|
|
6023
|
-
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5)));
|
|
6024
|
-
const detailOffset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
6025
|
-
const visibleLines = lines.slice(detailOffset, detailOffset + pageRows);
|
|
6026
|
-
referenceDetailPagination = { total: lines.length, pageRows };
|
|
5910
|
+
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5) - 1));
|
|
6027
5911
|
breadcrumb = `${plugin.displayName} / 设置 / 完整变化`;
|
|
6028
|
-
context = `${Math.min(detailOffset + 1, lines.length)}–${Math.min(detailOffset + visibleLines.length, lines.length)} / ${lines.length}`;
|
|
6029
5912
|
title = '完整设置变化';
|
|
6030
5913
|
description = '逐项显示修改前和修改后的完整值。';
|
|
6031
5914
|
help = '←→ 或 PgUp/PgDn 阅读 · ↑↓ 移动 · Enter 选择 · Esc 返回保存汇总';
|
|
6032
5915
|
actions = [
|
|
6033
5916
|
{ label: '返回保存汇总', run: back },
|
|
6034
5917
|
];
|
|
6035
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
6036
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${lines.length - detailOffset - visibleLines.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
5918
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, pageRows), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
6037
5919
|
}
|
|
6038
5920
|
else {
|
|
6039
5921
|
const visibleChanges = changes.slice(0, 2);
|
|
@@ -6408,7 +6290,24 @@ export function ConsoleAppV4(props) {
|
|
|
6408
6290
|
description = '单个来源失败不会阻止其他来源继续工作。';
|
|
6409
6291
|
context = `${props.controller.listSources().length} 个来源`;
|
|
6410
6292
|
help = '↑↓ 移动 · Enter 打开 · Esc 返回';
|
|
6411
|
-
|
|
6293
|
+
const allSources = props.controller.listSources();
|
|
6294
|
+
const sourceKinds = new Set(allSources.map((source) => props.controller.getConfiguredSource(source.id)?.type
|
|
6295
|
+
?? props.controller.getSourceOverview(source.id).detail?.type));
|
|
6296
|
+
const hasTypeTabs = sourceKinds.has('git') && sourceKinds.has('path');
|
|
6297
|
+
const sourceType = hasTypeTabs ? snapshot.router.current.context.sourceTypeTab ?? 'all' : 'all';
|
|
6298
|
+
const visibleSources = matchingSources(props.controller, query, sourceType);
|
|
6299
|
+
moveSourceType = hasTypeTabs ? (direction) => {
|
|
6300
|
+
const tabs = ['all', 'git', 'path'];
|
|
6301
|
+
const index = tabs.indexOf(sourceType);
|
|
6302
|
+
const next = tabs[(index + direction + tabs.length) % tabs.length];
|
|
6303
|
+
const nextSources = matchingSources(props.controller, query, next);
|
|
6304
|
+
const focusedId = actions[focus]?.actionId;
|
|
6305
|
+
const retained = nextSources.findIndex((source) => `source:${source.id}` === focusedId);
|
|
6306
|
+
props.controller.updatePageContext({ sourceTypeTab: next });
|
|
6307
|
+
setFocus(retained >= 0 ? retained : Math.min(focus, Math.max(0, nextSources.length - 1)));
|
|
6308
|
+
setScrollOffset(0);
|
|
6309
|
+
} : undefined;
|
|
6310
|
+
actions = visibleSources.map((source) => {
|
|
6412
6311
|
const overview = props.controller.getSourceOverview(source.id);
|
|
6413
6312
|
const generation = props.controller.getCatalogGeneration(source.id);
|
|
6414
6313
|
const freshness = props.controller.getCatalogFreshness(source.id);
|
|
@@ -6435,6 +6334,7 @@ export function ConsoleAppV4(props) {
|
|
|
6435
6334
|
const available = generation?.state === 'ready' || health === 'ready';
|
|
6436
6335
|
const unavailable = health === 'unavailable' || health === 'error' || health === 'recovery';
|
|
6437
6336
|
return {
|
|
6337
|
+
actionId: `source:${source.id}`,
|
|
6438
6338
|
label: source.id,
|
|
6439
6339
|
description: sourceLocationSummary(overview.detail?.type ?? 'git', overview.detail?.locationSummary),
|
|
6440
6340
|
sourceLabel: policy,
|
|
@@ -6459,7 +6359,13 @@ export function ConsoleAppV4(props) {
|
|
|
6459
6359
|
help = '↑↓ 移动 · Enter 打开 · Esc 返回';
|
|
6460
6360
|
const sourceCount = actions.length;
|
|
6461
6361
|
actions = [...actions, ...bottomActions];
|
|
6462
|
-
content =
|
|
6362
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: size.columns - 2, tabs: hasTypeTabs ? { label: '类型', currentId: sourceType, items: [
|
|
6363
|
+
{ id: 'all', label: '全部', count: matchingSources(props.controller, query, 'all').length },
|
|
6364
|
+
{ id: 'git', label: 'Git', count: matchingSources(props.controller, query, 'git').length },
|
|
6365
|
+
{ id: 'path', label: '本地', count: matchingSources(props.controller, query, 'path').length },
|
|
6366
|
+
] } : undefined, search: { value: query, placeholder: '名称或地址', input: searchFocus
|
|
6367
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: querySearchWidth(size.columns - 2) }) : undefined } }), sourceCount === 0 && allSources.length > 0
|
|
6368
|
+
? _jsx(Text, { color: tuiTheme.roles.secondary, children: "\u5F53\u524D\u6761\u4EF6\u65E0\u5339\u914D\uFF1B\u641C\u7D22\u548C\u7C7B\u578B\u4FDD\u6301\u539F\u6837\u3002" }) : null, _jsx(SequenceRows, { actions: actions, itemCount: sourceCount, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(1 + Number(hasTypeTabs) + Number(sourceCount === 0 && allSources.length > 0)), compact: policy.mode === 'compact', cards: true, inactive: searchFocus })] });
|
|
6463
6369
|
}
|
|
6464
6370
|
else if (route.name === 'catalogStoreOverview') {
|
|
6465
6371
|
breadcrumb = '插件来源 / 本地存储';
|
|
@@ -6498,7 +6404,16 @@ export function ConsoleAppV4(props) {
|
|
|
6498
6404
|
{ actionId: 'catalog-store-rules-technical', label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
6499
6405
|
{ role: 'navigation', actionId: 'catalog-store-rules-back', label: '返回存储概览', run: back },
|
|
6500
6406
|
];
|
|
6501
|
-
|
|
6407
|
+
if (showTechnical)
|
|
6408
|
+
readingText = [
|
|
6409
|
+
'锁 · AW_HOME 写锁内重新检查',
|
|
6410
|
+
'正式目录版本 · 非 Active 项保留 30 天',
|
|
6411
|
+
'staging、replacement、delete claim · 保留 24 小时',
|
|
6412
|
+
'quarantine · 保留 24 小时;对应正式目录版本必须完整健康',
|
|
6413
|
+
'保留节点 · foreign、符号链接、unsafe、损坏或未知项目',
|
|
6414
|
+
].join('\n');
|
|
6415
|
+
content = _jsxs(Box, { flexDirection: "column", children: [!showTechnical ? _jsxs(_Fragment, { children: [_jsx(SectionCard, { title: "\u59CB\u7EC8\u4FDD\u7559 \u00B7 \u5F53\u524D\u4F7F\u7528\u7684\u76EE\u5F55\u4E0D\u4F1A\u5220\u9664", children: _jsx(StaticRow, { title: "\u4E0D\u5C5E\u4E8E AW\u3001\u65E0\u6CD5\u786E\u8BA4\u5B89\u5168\u6216\u4ECD\u5728\u4F7F\u7528\u7684\u5185\u5BB9\u4F1A\u4FDD\u7559", status: "muted", compact: true }) }), _jsxs(SectionCard, { title: "\u6EE1\u8DB3\u65F6\u95F4\u548C\u5B8C\u6574\u9A8C\u8BC1\u540E\u53EF\u56DE\u6536", children: [_jsx(StaticRow, { title: "\u65E7\u7248\u672C\u6EE1\u8DB3\u4FDD\u7559\u671F\u9650\u540E\u624D\u6E05\u7406", compact: true }), _jsx(StaticRow, { title: "\u672A\u5B8C\u6210\u7684\u4E0B\u8F7D\u6216\u66FF\u6362\u5185\u5BB9\u81F3\u5C11\u4FDD\u7559 24 \u5C0F\u65F6", compact: true }), _jsx(StaticRow, { title: "\u9694\u79BB\u5185\u5BB9\u81F3\u5C11\u4FDD\u7559 24 \u5C0F\u65F6", compact: true }), _jsx(StaticRow, { title: "\u5BF9\u5E94\u6B63\u5F0F\u7248\u672C\u5B8C\u6574\u53EF\u7528\u540E\u624D\u6E05\u7406", compact: true })] })] }) : null, readingText !== undefined
|
|
6416
|
+
? readingPage(readingText, size.columns - 2, Math.max(1, pageActionBudget(5) - 1)) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
6502
6417
|
}
|
|
6503
6418
|
else if (route.name === 'sourceDetail') {
|
|
6504
6419
|
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
@@ -6595,6 +6510,7 @@ export function ConsoleAppV4(props) {
|
|
|
6595
6510
|
: freshness?.state === 'updateAvailable' || freshness?.state === 'localChangesAvailable'
|
|
6596
6511
|
? [{
|
|
6597
6512
|
actionId: 'source-update',
|
|
6513
|
+
status: 'info',
|
|
6598
6514
|
label: freshness.state === 'localChangesAvailable'
|
|
6599
6515
|
? '应用本地内容变化' : `更新到 ${candidateVersion ?? '待检查版本'}`,
|
|
6600
6516
|
description: '进入确认;确认前不会切换当前版本',
|
|
@@ -6720,26 +6636,36 @@ export function ConsoleAppV4(props) {
|
|
|
6720
6636
|
: generationState === 'configMismatch' ? '来源设置与当前目录不一致'
|
|
6721
6637
|
: generationState === 'stateInvalid' ? '来源状态需要恢复'
|
|
6722
6638
|
: '来源尚未启用';
|
|
6723
|
-
const
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6639
|
+
const sourceFacts = {
|
|
6640
|
+
sourceId: route.sourceId, primary: detail?.primary ?? false,
|
|
6641
|
+
address: sourceLocationSummary(detail?.type ?? 'git', detail?.locationSummary),
|
|
6642
|
+
transport, versionPolicy, currentVersion: activeVersion, candidateVersion,
|
|
6643
|
+
pluginCount: sourceCount,
|
|
6644
|
+
state: { label: sourceStateLabel, status: generationState === 'ready' ? 'success' : 'warning' },
|
|
6645
|
+
check: { label: freshnessLabel, time: relativeCheckTime(freshness?.checkedAt) },
|
|
6646
|
+
};
|
|
6647
|
+
title = '';
|
|
6648
|
+
context = '来源详情';
|
|
6649
|
+
if (generationState === 'ready' && freshness?.state !== 'checkFailed'
|
|
6650
|
+
&& freshness?.state !== 'incompatible')
|
|
6651
|
+
description = undefined;
|
|
6652
|
+
actions.unshift({ actionId: 'source-information', label: '来源信息', run: () => setFocus(1) });
|
|
6734
6653
|
const focusedSourceDescription = actions[focus]?.disabled ?? actions[focus]?.description;
|
|
6735
6654
|
const sourceDescriptionLines = focusedSourceDescription
|
|
6736
6655
|
? wrapConsoleText(focusedSourceDescription, size.columns - 2) : [];
|
|
6737
|
-
const sourceActionDescriptionLines = sourceDescriptionLines
|
|
6738
|
-
|
|
6739
|
-
const
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6656
|
+
const sourceActionDescriptionLines = sourceDescriptionLines;
|
|
6657
|
+
const cardWidth = size.columns - 2;
|
|
6658
|
+
const sourceLayout = sourceDetailCardLayout(sourceFacts, cardWidth);
|
|
6659
|
+
const lineLimit = Math.max(1, pageActionBudget(sourceActionDescriptionLines.length + 2) - 4);
|
|
6660
|
+
const lineOffset = Math.min(batchTargetOffset, Math.max(0, sourceLayout.factRows - lineLimit));
|
|
6661
|
+
entityCardNavigation = { itemCount: 1, factRows: [sourceLayout.factRows], lineLimit };
|
|
6662
|
+
const sourceCardHeight = entityCardWindowHeight(sourceLayout, lineOffset, lineLimit);
|
|
6663
|
+
currentActionViewportLineBudget = Math.max(1, pageActionBudget(sourceCardHeight + sourceActionDescriptionLines.length));
|
|
6664
|
+
const detailMenuViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
6665
|
+
help = focus === 0
|
|
6666
|
+
? '↑↓ 浏览信息 · PgUp/PgDn 翻信息 · a 选择动作 · Enter 选择动作 · Esc 返回'
|
|
6667
|
+
: '↑↓ 选择动作 · a 查看信息 · Enter 选择 · Esc 返回';
|
|
6668
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SourceDetailCard, { facts: sourceFacts, width: cardWidth, focused: focus === 0, lineOffset: lineOffset, lineLimit: lineLimit }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: Math.max(1, detailMenuViewport.offset), limit: Math.max(0, detailMenuViewport.limit - Number(detailMenuViewport.offset === 0)) }), sourceActionDescriptionLines.map((line, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: line }, index))] });
|
|
6743
6669
|
}
|
|
6744
6670
|
else if (route.name === 'sourceMore') {
|
|
6745
6671
|
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
@@ -6830,16 +6756,13 @@ export function ConsoleAppV4(props) {
|
|
|
6830
6756
|
const diagnostic = resolveSourceTechnicalDiagnostic(route.sourceId);
|
|
6831
6757
|
readingText = diagnosticDetailText(diagnostic);
|
|
6832
6758
|
breadcrumb = `插件来源 / ${route.sourceId} / 技术详情`;
|
|
6833
|
-
const lines = diagnosticDetailLines(diagnostic, size.columns - 6);
|
|
6834
|
-
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - sourceTechnicalPageRows));
|
|
6835
|
-
context = `${offset + 1}–${Math.min(lines.length, offset + sourceTechnicalPageRows)} / ${lines.length} 行`;
|
|
6836
6759
|
title = '来源技术诊断';
|
|
6837
6760
|
description = '';
|
|
6838
6761
|
help = '←→ 或 PgUp/PgDn 翻页 · Esc 返回';
|
|
6839
6762
|
actions = [
|
|
6840
6763
|
{ label: '返回来源详情', run: back },
|
|
6841
6764
|
];
|
|
6842
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
6765
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, Math.max(1, pageActionBudget(4) - 1)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
6843
6766
|
}
|
|
6844
6767
|
else if (route.name === 'sourceAdd' && sourceDraft?.owner === route.owner) {
|
|
6845
6768
|
breadcrumb = sourceDraft.mode === 'add' ? '插件来源 / 添加' : `插件来源 / ${sourceDraft.sourceId} / 编辑`;
|
|
@@ -6976,22 +6899,16 @@ export function ConsoleAppV4(props) {
|
|
|
6976
6899
|
else if (route.name === 'sourceSave' && sourceDraft?.owner === route.owner) {
|
|
6977
6900
|
const changes = sourceDraftChanges(sourceDraft);
|
|
6978
6901
|
if (route.view === 'changes') {
|
|
6979
|
-
const lines = changeDetailLines(changes, size.columns - 4);
|
|
6980
6902
|
readingText = changeDetailText(changes);
|
|
6981
|
-
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5)));
|
|
6982
|
-
const detailOffset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
6983
|
-
const visibleLines = lines.slice(detailOffset, detailOffset + pageRows);
|
|
6984
|
-
referenceDetailPagination = { total: lines.length, pageRows };
|
|
6903
|
+
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5) - 1));
|
|
6985
6904
|
breadcrumb = `插件来源 / ${sourceDraft.sourceId} / 完整变化`;
|
|
6986
|
-
context = `${Math.min(detailOffset + 1, lines.length)}–${Math.min(detailOffset + visibleLines.length, lines.length)} / ${lines.length}`;
|
|
6987
6905
|
title = '完整来源变化';
|
|
6988
6906
|
description = '逐项显示修改前和修改后的完整值。';
|
|
6989
6907
|
help = '←→ 或 PgUp/PgDn 阅读 · ↑↓ 移动 · Enter 选择 · Esc 返回修改汇总';
|
|
6990
6908
|
actions = [
|
|
6991
6909
|
{ label: '返回修改汇总', run: back },
|
|
6992
6910
|
];
|
|
6993
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
6994
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${lines.length - detailOffset - visibleLines.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
6911
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, pageRows), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
6995
6912
|
}
|
|
6996
6913
|
else {
|
|
6997
6914
|
const savedSource = props.controller.getConfiguredSource(sourceDraft.sourceId);
|
|
@@ -7046,16 +6963,14 @@ export function ConsoleAppV4(props) {
|
|
|
7046
6963
|
const references = sourceReferenceSummary?.dependencies ?? [];
|
|
7047
6964
|
if (sourceDependenciesExpanded)
|
|
7048
6965
|
readingText = references.map((dependency) => `${dependency.category} · ${dependency.pluginId ?? dependency.sourceId ?? '身份未知'} · ${dependency.platform ? platformLabel(dependency.platform) : ''}${dependency.scope ? ` · ${scopeLabel(dependency.scope)}` : ''}`).join('\n');
|
|
7049
|
-
const
|
|
7050
|
-
const visibleReferences = references.slice(
|
|
7051
|
-
const hiddenReferences = Math.max(0, references.length - referenceOffset - visibleReferences.length);
|
|
6966
|
+
const referencePage = pagedItems(references.length, referenceLimit);
|
|
6967
|
+
const visibleReferences = references.slice(referencePage.offset, referencePage.offset + referenceLimit);
|
|
7052
6968
|
if (sourceDependenciesExpanded && references.length > referenceLimit) {
|
|
7053
6969
|
referenceDetailPagination = { total: references.length, pageRows: referenceLimit };
|
|
7054
6970
|
help = '←→ 查看全部引用 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7055
6971
|
}
|
|
7056
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "error", title: sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整' : '发现活动引用' }), _jsxs(SectionCard, { title: sourceDependenciesExpanded
|
|
7057
|
-
|
|
7058
|
-
: '引用汇总', children: [!sourceDependenciesExpanded ? _jsxs(_Fragment, { 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, '个') })] }) : 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' })] });
|
|
6972
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "error", title: sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整' : '发现活动引用' }), _jsxs(SectionCard, { title: sourceDependenciesExpanded ? '引用内容' : '引用汇总', children: [!sourceDependenciesExpanded ? _jsxs(_Fragment, { 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, '个') })] }) : 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 ?? referencePage.offset + index}`)) : null, sourceDependenciesExpanded && referencePage.pageCount > 1
|
|
6973
|
+
? _jsx(Pager, { page: referencePage.page, pageCount: referencePage.pageCount, columns: size.columns - 6 }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
7059
6974
|
}
|
|
7060
6975
|
else if (route.name === 'issueList') {
|
|
7061
6976
|
const issues = props.controller.listUserIssues();
|
|
@@ -7293,14 +7208,12 @@ export function ConsoleAppV4(props) {
|
|
|
7293
7208
|
context = '安全详情';
|
|
7294
7209
|
title = detail ? '安全技术详情' : '操作记录已不可用';
|
|
7295
7210
|
description = detail
|
|
7296
|
-
? '
|
|
7211
|
+
? '可翻页查看所有目标、阶段、代码和已保留的安全原因。'
|
|
7297
7212
|
: '这项会话记录已经不再可用。';
|
|
7298
7213
|
const preview = detail ? props.controller.previewOperationReport(route.recordId) : undefined;
|
|
7299
|
-
const detailLineLimit =
|
|
7214
|
+
const detailLineLimit = Math.max(1, pageActionBudget(policy.mode === 'compact' ? 1 : 5) - 1);
|
|
7300
7215
|
if (detail) {
|
|
7301
7216
|
readingText = operationDiagnosticText(detail);
|
|
7302
|
-
const total = operationDiagnosticLineCount(detail, size.columns);
|
|
7303
|
-
context = `${Math.min(total, scrollOffset + 1)}–${Math.min(total, scrollOffset + detailLineLimit)} / ${total} 行`;
|
|
7304
7217
|
}
|
|
7305
7218
|
actions = detail ? [
|
|
7306
7219
|
preview
|
|
@@ -7309,7 +7222,7 @@ export function ConsoleAppV4(props) {
|
|
|
7309
7222
|
{ role: 'navigation', label: '返回操作记录', run: back },
|
|
7310
7223
|
] : [{ role: 'navigation', label: '返回操作记录', run: back }];
|
|
7311
7224
|
const diagnosticActionViewport = actionViewport(actions, policy.mode === 'compact', focus, 0, policy.mode === 'compact' ? 2 : 3);
|
|
7312
|
-
content = detail ? _jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
7225
|
+
content = detail && readingText !== undefined ? _jsxs(Box, { flexDirection: "column", children: [policy.mode === 'compact' ? null : _jsx(ResultCard, { status: operationHistoryState(detail.summary).status, title: detail.summary.message, meta: operationHistoryState(detail.summary).label, compact: false }), _jsx(ContentViewport, { children: readingPage(readingText, size.columns - 2, detailLineLimit, 'scroll') }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: diagnosticActionViewport.offset, limit: diagnosticActionViewport.limit })] }) : _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' })] });
|
|
7313
7226
|
}
|
|
7314
7227
|
else if (route.name === 'operationReportPreview') {
|
|
7315
7228
|
const preview = props.controller.previewOperationReport(route.recordId);
|
|
@@ -7327,9 +7240,9 @@ export function ConsoleAppV4(props) {
|
|
|
7327
7240
|
}
|
|
7328
7241
|
else if (preview) {
|
|
7329
7242
|
readingText = preview.content;
|
|
7330
|
-
const
|
|
7331
|
-
const
|
|
7332
|
-
|
|
7243
|
+
const reportFeedbackVisible = Boolean(reportSaveResult || reportSaveError);
|
|
7244
|
+
const reportLineLimit = Math.max(1, pageActionBudget((policy.mode === 'compact' ? 2 : 8)
|
|
7245
|
+
+ (reportFeedbackVisible ? 4 : 0)) - 1);
|
|
7333
7246
|
actions = [
|
|
7334
7247
|
{ label: '选择本地保存位置', description: '保存前输入明确的文件路径', run: () => {
|
|
7335
7248
|
setReportSaveMode(true);
|
|
@@ -7346,11 +7259,10 @@ export function ConsoleAppV4(props) {
|
|
|
7346
7259
|
} },
|
|
7347
7260
|
{ role: 'navigation', label: '返回技术详情', run: back },
|
|
7348
7261
|
];
|
|
7349
|
-
const reportFeedbackVisible = Boolean(reportSaveResult || reportSaveError);
|
|
7350
7262
|
const compactReportActions = policy.mode === 'compact' || reportFeedbackVisible;
|
|
7351
7263
|
const reportActionLimit = policy.mode === 'compact' || reportFeedbackVisible ? 2 : 3;
|
|
7352
7264
|
const reportActionOffset = Math.min(Math.max(0, focus - 1), Math.max(0, actions.length - reportActionLimit));
|
|
7353
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
7265
|
+
content = _jsxs(Box, { flexDirection: "column", children: [policy.mode === 'compact' ? null : _jsx(ResultCard, { status: preview.omitted ? 'warning' : 'info', title: "\u62A5\u544A\u9884\u89C8", description: preview.omitted ? '报告只包含已保留的安全内容。' : '报告只包含本页安全内容。', meta: preview.fileName, compact: false }), _jsx(ContentViewport, { children: readingPage(readingText, size.columns - 2, reportLineLimit, 'scroll') }), 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: compactReportActions, offset: reportActionOffset, limit: reportActionLimit })] });
|
|
7354
7266
|
}
|
|
7355
7267
|
else {
|
|
7356
7268
|
actions = [{ role: 'navigation', label: '返回技术详情', run: back }];
|
|
@@ -7648,13 +7560,6 @@ export function ConsoleAppV4(props) {
|
|
|
7648
7560
|
} : group).filter((group) => group.changes.length > 0);
|
|
7649
7561
|
const headingLines = compressedReview ? 0 : visibleSpecs.filter((group) => group.label).length;
|
|
7650
7562
|
const staticRowBudget = Math.max(visibleSpecs.length, responsiveBudget(7, 12) - headingLines);
|
|
7651
|
-
const reviewPaginationTotal = showTechnical
|
|
7652
|
-
? Math.max(review.changes.length, technicalDetails.length)
|
|
7653
|
-
: userReviewChanges.length;
|
|
7654
|
-
const reviewPageRows = Math.max(1, responsiveBudget(6, 10) - actions.length - 2);
|
|
7655
|
-
if (reviewPaginationTotal > reviewPageRows) {
|
|
7656
|
-
referenceDetailPagination = { total: reviewPaginationTotal, pageRows: 1 };
|
|
7657
|
-
}
|
|
7658
7563
|
const reviewActionViewport = actionViewport(actions, true, focus, scrollOffset, responsiveBudget(2, 6));
|
|
7659
7564
|
const allocations = new Map(visibleSpecs.map((group) => [group.key, 1]));
|
|
7660
7565
|
const targetGroup = visibleSpecs.find((group) => group.key === 'portableTarget');
|
|
@@ -7692,8 +7597,11 @@ export function ConsoleAppV4(props) {
|
|
|
7692
7597
|
const batchChangeLimit = portableReview
|
|
7693
7598
|
? responsiveBudget(3, 5)
|
|
7694
7599
|
: policy.mode === 'compact' ? 1 : Math.max(1, responsiveBudget(6, 10) - reviewActionViewport.limit);
|
|
7695
|
-
const
|
|
7696
|
-
|
|
7600
|
+
const batchChangePage = paginateWrappedRows({
|
|
7601
|
+
lines: userReviewChanges.map((_, index) => String(index)), rows: batchChangeLimit,
|
|
7602
|
+
page: batchTargetOffset + 1,
|
|
7603
|
+
});
|
|
7604
|
+
const batchChanges = userReviewChanges.slice(batchChangePage.offset, batchChangePage.offset + batchChangeLimit);
|
|
7697
7605
|
const prioritizedReviewChanges = portableReview ? [
|
|
7698
7606
|
...userReviewChanges.filter((change) => change.kind === 'warning'),
|
|
7699
7607
|
...userReviewChanges.filter((change) => change.kind !== 'warning'
|
|
@@ -7704,21 +7612,19 @@ export function ConsoleAppV4(props) {
|
|
|
7704
7612
|
...userReviewChanges.filter((change) => change.kind === 'warning'),
|
|
7705
7613
|
...userReviewChanges.filter((change) => change.kind !== 'warning'),
|
|
7706
7614
|
];
|
|
7707
|
-
const pagedReviewChanges = prioritizedReviewChanges.slice(
|
|
7615
|
+
const pagedReviewChanges = prioritizedReviewChanges.slice(batchChangePage.offset, batchChangePage.offset + batchChangeLimit);
|
|
7708
7616
|
const changeGroups = portableReview || batchReview || userReviewChanges.length > staticRowBudget
|
|
7709
|
-
?
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7617
|
+
? _jsx(Box, { flexDirection: "column", children: (batchReview ? batchChanges : pagedReviewChanges).map((change, index) => _jsx(ReviewChangeRow, { title: `${[
|
|
7618
|
+
portableReview && change.group ? groupTitle[change.group] : undefined,
|
|
7619
|
+
change.sourceId,
|
|
7620
|
+
change.relation === 'requested' ? '根插件'
|
|
7621
|
+
: change.relation === 'direct' ? '直接依赖'
|
|
7622
|
+
: change.relation === 'transitive' ? '间接依赖' : undefined,
|
|
7623
|
+
].filter(Boolean).join(' · ')}${portableReview && change.group || change.sourceId || change.relation ? ' · ' : ''}${reviewSymbol(change)} ${change.title}`, description: (portableReview && change.kind !== 'warning') || exactProjectSyncReview
|
|
7624
|
+
? change.description : undefined, meta: portableReview || exactProjectSyncReview ? undefined : change.description, status: changeStatus(change), compact: !portableReview && !exactProjectSyncReview }, `${change.batchGroupId ?? ''}:${change.pluginId ?? change.title}:${index}`)) })
|
|
7717
7625
|
: standardChangeGroups;
|
|
7718
7626
|
const technicalDetailLimit = responsiveBudget(1, 1);
|
|
7719
|
-
const
|
|
7720
|
-
const hiddenTechnicalDetails = Math.max(0, technicalDetails.length - batchTargetOffset - visibleTechnicalDetails.length);
|
|
7721
|
-
const technicalCardRows = 3 + visibleTechnicalDetails.length;
|
|
7627
|
+
const technicalCardRows = 3 + Math.min(technicalDetails.length, technicalDetailLimit);
|
|
7722
7628
|
const technicalChangeBudget = Math.max(1, Math.floor((size.rows - 8 - 3 - (review.equivalentCommand ? 1 : 0)
|
|
7723
7629
|
- reviewActionViewport.limit - technicalCardRows) / 2));
|
|
7724
7630
|
const prioritizedTechnicalChanges = [
|
|
@@ -7731,8 +7637,27 @@ export function ConsoleAppV4(props) {
|
|
|
7731
7637
|
&& change.group !== 'portableTarget'
|
|
7732
7638
|
&& (change.group === undefined || !technicalGroups.has(change.group))),
|
|
7733
7639
|
];
|
|
7734
|
-
const
|
|
7735
|
-
const
|
|
7640
|
+
const technicalChangePageCount = Math.ceil(prioritizedTechnicalChanges.length / technicalChangeBudget);
|
|
7641
|
+
const technicalDetailPageCount = Math.ceil(technicalDetails.length / technicalDetailLimit);
|
|
7642
|
+
const reviewPageCount = showTechnical
|
|
7643
|
+
? Math.max(1, technicalChangePageCount + technicalDetailPageCount)
|
|
7644
|
+
: portableReview || batchReview || userReviewChanges.length > staticRowBudget
|
|
7645
|
+
? batchChangePage.pageCount : 1;
|
|
7646
|
+
const reviewCurrentPage = Math.min(reviewPageCount, batchTargetOffset + 1);
|
|
7647
|
+
const technicalChangePage = paginateWrappedRows({
|
|
7648
|
+
lines: prioritizedTechnicalChanges.map((_, index) => String(index)),
|
|
7649
|
+
rows: technicalChangeBudget, page: reviewCurrentPage,
|
|
7650
|
+
});
|
|
7651
|
+
const technicalDetailPage = paginateWrappedRows({
|
|
7652
|
+
lines: technicalDetails, rows: technicalDetailLimit,
|
|
7653
|
+
page: Math.max(1, reviewCurrentPage - technicalChangePageCount),
|
|
7654
|
+
});
|
|
7655
|
+
const visibleTechnicalDetails = reviewCurrentPage > technicalChangePageCount
|
|
7656
|
+
? technicalDetailPage.lines : [];
|
|
7657
|
+
const hiddenTechnicalDetails = Math.max(0, technicalDetails.length - technicalDetailPage.offset - visibleTechnicalDetails.length);
|
|
7658
|
+
const visibleTechnicalChanges = reviewCurrentPage <= technicalChangePageCount
|
|
7659
|
+
? prioritizedTechnicalChanges.slice(technicalChangePage.offset, technicalChangePage.offset + technicalChangeBudget) : [];
|
|
7660
|
+
const hiddenTechnicalChanges = Math.max(0, review.changes.length - technicalChangePage.offset - visibleTechnicalChanges.length);
|
|
7736
7661
|
const technicalChanges = visibleTechnicalChanges.map((change, index) => {
|
|
7737
7662
|
const category = change.group ? groupTitle[change.group] : undefined;
|
|
7738
7663
|
return _jsx(ReviewChangeRow, { title: category
|
|
@@ -7764,11 +7689,15 @@ export function ConsoleAppV4(props) {
|
|
|
7764
7689
|
const widenedDependencyCount = new Set(installDependencyTargets
|
|
7765
7690
|
.filter((target) => target.affectsOtherProjects && target.action !== 'keep')
|
|
7766
7691
|
.map((target) => target.pluginId)).size;
|
|
7767
|
-
if (
|
|
7768
|
-
|
|
7692
|
+
if (reviewPageCount > 1) {
|
|
7693
|
+
readerNavigation = {
|
|
7694
|
+
page: reviewCurrentPage,
|
|
7695
|
+
pageCount: reviewPageCount,
|
|
7696
|
+
move: (direction) => setBatchTargetOffset(Math.min(reviewPageCount, Math.max(1, reviewCurrentPage + direction)) - 1),
|
|
7697
|
+
};
|
|
7769
7698
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7770
7699
|
}
|
|
7771
|
-
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: reviewRootSummary ? `本次插件 · ${reviewRootSummary}` : review.subject.name, meta: reviewRootSummary ? `版本 ${reviewVersionSummary} · 范围 ${reviewRangeSummary || '未知'}` : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups,
|
|
7700
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: reviewRootSummary ? `本次插件 · ${reviewRootSummary}` : review.subject.name, meta: reviewRootSummary ? `版本 ${reviewVersionSummary} · 范围 ${reviewRangeSummary || '未知'}` : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, showTechnical && visibleTechnicalDetails.length > 0 ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: catalogReferenceDetailOffset >= 0 && technicalDetailPage.offset + index >= catalogReferenceDetailOffset ? undefined : index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, `${technicalDetailPage.offset + index}:${detail}`)) }) : null, reviewPageCount > 1 ? _jsx(Pager, { page: reviewCurrentPage, pageCount: reviewPageCount, columns: size.columns - 6 }) : null, portableReview ? null : _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" })] }) });
|
|
7772
7701
|
if (pendingReviewIntent?.kind === 'configSaveBatch' || sourceDraft && (pendingReviewIntent?.kind === 'sourceMutation' || pendingReviewIntent?.kind === 'catalogGeneration') && pendingReviewIntent.sourceId === sourceDraft.sourceId) {
|
|
7773
7702
|
const changesText = pendingReviewIntent.kind === 'configSaveBatch' ? pendingReviewIntent.groups.map((group) => `${configLayerLabel({ scope: group.scope })}\n${(group.changes ?? []).flatMap((change) => [
|
|
7774
7703
|
`${change.title} · ${change.before ?? '未设置'} → ${change.after}${change.children?.length ? ` · ${change.children.length} 个子属性变化` : ''}`,
|
|
@@ -7782,11 +7711,8 @@ export function ConsoleAppV4(props) {
|
|
|
7782
7711
|
: `${changesText}\n\n${readingText}`;
|
|
7783
7712
|
if (pendingReviewIntent.kind === 'configSaveBatch')
|
|
7784
7713
|
description = pendingReviewIntent.apply ? '保存并生效;每层独立提交,应用结果单独报告。' : '只保存;每层独立提交,稍后应用。';
|
|
7785
|
-
const
|
|
7786
|
-
|
|
7787
|
-
referenceDetailPagination = { total: lines.length, pageRows };
|
|
7788
|
-
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
7789
|
-
content = _jsxs(Box, { flexDirection: "column", children: [lines.slice(offset, offset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsxs(Text, { color: tuiTheme.roles.secondary, children: [offset + 1, "\u2013", Math.min(lines.length, offset + pageRows), " / ", lines.length] }), _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true })] });
|
|
7714
|
+
const pageRows = Math.max(1, pageActionBudget(actions.length + 1) - 1);
|
|
7715
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, pageRows), _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true })] });
|
|
7790
7716
|
}
|
|
7791
7717
|
}
|
|
7792
7718
|
else if (route.name === 'progress') {
|
|
@@ -7942,7 +7868,7 @@ export function ConsoleAppV4(props) {
|
|
|
7942
7868
|
return step === 2 ? '正在安装' : step === 3 ? '正在验证' : step === 1 ? '正在生成' : '正在检查';
|
|
7943
7869
|
};
|
|
7944
7870
|
const compactPortableProgress = policy.mode === 'compact' && showTechnical;
|
|
7945
|
-
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('、') : '
|
|
7871
|
+
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('、') : '自动选择可用 Agent'}`, 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: `${progressAction}所选插件和所需插件`, 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: storeOperation ? '复核精确清理目标' : sourceOperation ? '检查来源操作输入' : configOperation ? '验证设置值' : '检查兼容性', state: !currentPhase ? 'pending' : preflightActive ? 'active' : 'complete' }), _jsx(ProgressRow, { title: progress.current?.phase || (storeOperation ? '安全删除已确认项目' : 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) => {
|
|
7946
7872
|
const state = progressLabel(entry);
|
|
7947
7873
|
const relation = entry.relation === 'requested' ? '你选择的插件' : entry.relation === 'direct' ? '直接依赖' : '间接依赖';
|
|
7948
7874
|
const title = props.controller.getPluginOverview(entry.pluginId)?.displayName
|
|
@@ -8511,8 +8437,8 @@ export function ConsoleAppV4(props) {
|
|
|
8511
8437
|
const resultPaginationTotal = result.operationKind === 'portableInstall'
|
|
8512
8438
|
? prioritizedPortablePairs.length
|
|
8513
8439
|
: baseResultPaginationTotal;
|
|
8514
|
-
const
|
|
8515
|
-
const portableSkillOffset =
|
|
8440
|
+
const portableSkillPage = pagedItems(prioritizedPortablePairs.length, portableSkillLimit);
|
|
8441
|
+
const portableSkillOffset = portableSkillPage.offset;
|
|
8516
8442
|
const visiblePortablePairs = prioritizedPortablePairs.slice(portableSkillOffset, portableSkillOffset + portableSkillLimit);
|
|
8517
8443
|
const portableResultRange = prioritizedPortablePairs.length > 0
|
|
8518
8444
|
? `第 ${portableSkillOffset + 1}–${portableSkillOffset + visiblePortablePairs.length} 项,共 ${prioritizedPortablePairs.length} 项`
|
|
@@ -8523,25 +8449,6 @@ export function ConsoleAppV4(props) {
|
|
|
8523
8449
|
const selectedPortablePair = portableResultDetailOpen
|
|
8524
8450
|
? prioritizedPortablePairs.find((pair) => portablePairKey(pair) === portableResultDetailPairKey.current)
|
|
8525
8451
|
: visiblePortablePairs[0];
|
|
8526
|
-
const wrapPortableField = (label, value) => {
|
|
8527
|
-
const chunks = wrapConsoleText(value, Math.max(12, size.columns - stringWidth(label) - 14));
|
|
8528
|
-
return chunks.map((line, index) => `${label} ${index + 1}/${chunks.length} · ${line}`);
|
|
8529
|
-
};
|
|
8530
|
-
const portableResultDetailLines = selectedPortablePair ? [
|
|
8531
|
-
...wrapPortableField('Skill', selectedPortablePair.skill),
|
|
8532
|
-
...wrapPortableField('Agent', selectedPortablePair.agent ?? '无法确认'),
|
|
8533
|
-
`状态 · ${portablePairStateLabel(selectedPortablePair)}`,
|
|
8534
|
-
...(portableResultScope ? [`范围 · ${scopeLabel(portableResultScope)}`] : []),
|
|
8535
|
-
...(portableReceipt?.recovery
|
|
8536
|
-
? wrapPortableField('安全恢复', portableReceipt.recovery) : []),
|
|
8537
|
-
...(portableReceipt?.failureCode
|
|
8538
|
-
? wrapPortableField('failure code', portableReceipt.failureCode) : []),
|
|
8539
|
-
...(portableReceipt ? [`verification missing · ${portableReceipt.missing.length}`] : []),
|
|
8540
|
-
...(portableReceipt ? wrapPortableField('Skills CLI version', portableReceipt.skillsCliVersion) : []),
|
|
8541
|
-
...(portableReceipt
|
|
8542
|
-
? wrapPortableField('materialization digest', portableReceipt.materializationDigest) : []),
|
|
8543
|
-
...(resultSummary?.elapsedMs !== undefined ? [`elapsed · ${resultSummary.elapsedMs} ms`] : []),
|
|
8544
|
-
] : ['当前结果已变化。请返回结果摘要。'];
|
|
8545
8452
|
readingText = portableResultDetailOpen ? selectedPortablePair ? [
|
|
8546
8453
|
`Skill · ${selectedPortablePair.skill}`,
|
|
8547
8454
|
`Agent · ${selectedPortablePair.agent ?? '无法确认'}`,
|
|
@@ -8565,8 +8472,8 @@ export function ConsoleAppV4(props) {
|
|
|
8565
8472
|
...(resultSummary?.batch?.rootResults ?? []).map((root) => `${root.pluginId} · ${root.state}`),
|
|
8566
8473
|
].join('\n');
|
|
8567
8474
|
const portableResultDetailPageRows = responsiveBudget(5, 10);
|
|
8568
|
-
const
|
|
8569
|
-
|
|
8475
|
+
const portableDetailPage = readerPage({ text: readingText, columns: size.columns - 2,
|
|
8476
|
+
rows: portableResultDetailPageRows, page: portableResultDetailOffset + 1 });
|
|
8570
8477
|
if (portableOperation && portableResultDetailOpen) {
|
|
8571
8478
|
title = '当前结果完整字段';
|
|
8572
8479
|
description = '按行显示当前 Skill、Agent、状态和技术证据。';
|
|
@@ -8579,10 +8486,6 @@ export function ConsoleAppV4(props) {
|
|
|
8579
8486
|
setScrollOffset(0);
|
|
8580
8487
|
} },
|
|
8581
8488
|
];
|
|
8582
|
-
portableResultDetailPagination = {
|
|
8583
|
-
total: portableResultDetailLines.length,
|
|
8584
|
-
pageRows: portableResultDetailPageRows,
|
|
8585
|
-
};
|
|
8586
8489
|
}
|
|
8587
8490
|
else if (portableOperation) {
|
|
8588
8491
|
const detailAction = selectedPortablePair ? {
|
|
@@ -8602,38 +8505,29 @@ export function ConsoleAppV4(props) {
|
|
|
8602
8505
|
...(detailAction ? [detailAction] : []),
|
|
8603
8506
|
...actions.slice(1),
|
|
8604
8507
|
];
|
|
8605
|
-
if (resultPaginationTotal > resultPageRows) {
|
|
8606
|
-
referenceDetailPagination = { total: resultPaginationTotal, pageRows: resultPageRows };
|
|
8607
|
-
}
|
|
8608
|
-
}
|
|
8609
|
-
else if (resultPaginationTotal > 1) {
|
|
8610
|
-
referenceDetailPagination = { total: resultPaginationTotal, pageRows: resultPageRows };
|
|
8611
8508
|
}
|
|
8612
8509
|
const portableTechnicalFieldCount = portableReceipt
|
|
8613
8510
|
? 3 + (portableReceipt.failureCode ? 1 : 0)
|
|
8614
8511
|
+ (policy.mode === 'compact' ? 0 : resultSummary?.elapsedMs !== undefined ? 1 : 0)
|
|
8615
8512
|
: 1;
|
|
8616
8513
|
const portableFixedRows = (portableRecheckBusy ? 1 : 0) + (showTechnical
|
|
8617
|
-
? 1 + portableSkillLimit + 1
|
|
8618
|
-
+ (portableResultAgentMode === 'automatic' ? 1 : 0)
|
|
8514
|
+
? 1 + portableSkillLimit + (portableResultScope && policy.mode !== 'compact' ? 1 : 0)
|
|
8515
|
+
+ (portableResultAgentMode === 'automatic' && policy.mode !== 'compact' ? 1 : 0)
|
|
8619
8516
|
+ (portableReceipt?.recovery ? 1 : 0)
|
|
8620
|
-
+ 1 + portableTechnicalFieldCount
|
|
8517
|
+
+ (policy.mode === 'compact' ? 0 : 1) + portableTechnicalFieldCount
|
|
8621
8518
|
: 3 + portableSkillLimit + 1
|
|
8622
8519
|
+ (portableResultAgentMode === 'automatic' ? 1 : 0)
|
|
8623
8520
|
+ (portableReceipt?.recovery ? 1 : 0));
|
|
8624
8521
|
currentActionViewportLineBudget = pageActionBudget(portableOperation
|
|
8625
8522
|
? portableResultDetailOpen
|
|
8626
|
-
?
|
|
8627
|
-
+ (safePortableResultDetailOffset > 0 ? 1 : 0)
|
|
8628
|
-
+ (safePortableResultDetailOffset + visiblePortableResultDetailLines.length
|
|
8629
|
-
< portableResultDetailLines.length ? 1 : 0)
|
|
8523
|
+
? portableDetailPage.lines.length + 1
|
|
8630
8524
|
: portableFixedRows
|
|
8631
8525
|
: resultPaginationTotal > 1 ? 7 : 8);
|
|
8632
8526
|
const resultActionViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
8633
8527
|
const resultTargetLimit = Math.max(1, prefixRowLimit(prioritizedTargets.map(() => policy.mode === 'compact' ? 1 : 2), pageActionBudget() - 3 - resultActionViewport.limit - resultExtraRows));
|
|
8634
|
-
const
|
|
8528
|
+
const resultTargetPage = pagedItems(prioritizedTargets.length, resultTargetLimit);
|
|
8529
|
+
const resultTargetOffset = resultTargetPage.offset;
|
|
8635
8530
|
const visibleResultTargets = prioritizedTargets.slice(resultTargetOffset, resultTargetOffset + resultTargetLimit);
|
|
8636
|
-
const hiddenResultTargets = Math.max(0, resultTargets.length - resultTargetOffset - visibleResultTargets.length);
|
|
8637
8531
|
const portableTechnicalSummary = showTechnical;
|
|
8638
8532
|
const portableRows = visiblePortablePairs.map((pair) => {
|
|
8639
8533
|
const status = blocked || pair.state !== 'verified' ? 'warning' : 'success';
|
|
@@ -8752,7 +8646,15 @@ export function ConsoleAppV4(props) {
|
|
|
8752
8646
|
const pluginResultRows = taskOperation ? taskSelectedPluginRows : batchPluginResultRows;
|
|
8753
8647
|
const locationResultRows = taskOperation ? taskLocationRows : batchLocationResultRows;
|
|
8754
8648
|
const combinedResultRows = [...pluginResultRows, ...locationResultRows];
|
|
8755
|
-
const
|
|
8649
|
+
const actualResultRows = taskOperation || batchOperation ? combinedResultRows.length
|
|
8650
|
+
: portableOperation ? prioritizedPortablePairs.length : prioritizedTargets.length;
|
|
8651
|
+
const actualResultPageRows = taskOperation || batchOperation ? taskResultLimit
|
|
8652
|
+
: portableOperation ? portableSkillLimit : resultTargetLimit;
|
|
8653
|
+
if (!portableResultDetailOpen && actualResultRows > actualResultPageRows) {
|
|
8654
|
+
referenceDetailPagination = { total: actualResultRows, pageRows: actualResultPageRows };
|
|
8655
|
+
}
|
|
8656
|
+
const taskResultPage = pagedItems(combinedResultRows.length, taskResultLimit);
|
|
8657
|
+
const taskResultOffset = taskResultPage.offset;
|
|
8756
8658
|
const visiblePluginResultRows = taskResultOffset < pluginResultRows.length
|
|
8757
8659
|
? pluginResultRows.slice(taskResultOffset, taskResultOffset + taskResultLimit) : [];
|
|
8758
8660
|
const visibleLocationStart = Math.max(0, taskResultOffset - pluginResultRows.length);
|
|
@@ -8762,19 +8664,17 @@ export function ConsoleAppV4(props) {
|
|
|
8762
8664
|
|| prioritizedPortablePairs.length > portableSkillLimit) {
|
|
8763
8665
|
help = '←→ 查看全部项目 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
8764
8666
|
}
|
|
8765
|
-
content = taskOperation || batchOperation ? _jsxs(Box, { flexDirection: "column", children: [
|
|
8766
|
-
? _jsx(
|
|
8667
|
+
content = taskOperation || batchOperation ? _jsxs(Box, { flexDirection: "column", children: [visiblePluginResultRows.length > 0 ? _jsx(ResultCard, { status: resultStatus, title: "\u63D2\u4EF6\u7ED3\u679C", meta: `${pluginResultRows.length} 个插件`, children: visiblePluginResultRows }) : null, visibleLocationResultRows.length > 0 ? _jsx(ResultCard, { status: resultStatus, title: "\u4F7F\u7528\u4F4D\u7F6E\u4E0E\u6240\u9700\u63D2\u4EF6", meta: `${locationResultRows.length} 项`, children: visibleLocationResultRows }) : null, taskResultPage.pageCount > 1
|
|
8668
|
+
? _jsx(Pager, { page: taskResultPage.page, pageCount: taskResultPage.pageCount, columns: size.columns - 2 }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] }) : portableOperation ? _jsxs(Box, { flexDirection: "column", children: [portableRecheckBusy
|
|
8767
8669
|
? _jsx(ProgressRow, { title: "\u6B63\u5728\u91CD\u65B0\u68C0\u67E5\u7ED3\u679C", description: "\u53EA\u8BFB\u53D6 Skills CLI \u5F53\u524D\u72B6\u6001\uFF0C\u4E0D\u6267\u884C\u5B89\u88C5", state: "active", compact: policy.mode === 'compact' })
|
|
8768
|
-
: null, portableResultDetailOpen ? _jsxs(_Fragment, { children: [
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
? _jsx(StaticRow, { title:
|
|
8772
|
-
|
|
8773
|
-
: null] }) : portableTechnicalSummary ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: blocked ? '安装未开始或结果未确认' : partial ? '部分 Skills 未确认' : '通用 Skills 安装结果', meta: portableResultRange, status: resultStatus, compact: true }), portableRows, portableResultScope ? _jsx(StaticRow, { title: "\u8303\u56F4", meta: scopeLabel(portableResultScope), compact: true }) : null, portableResultAgentMode === 'automatic'
|
|
8774
|
-
? _jsx(StaticRow, { title: "Agent", meta: "Skills CLI \u9ED8\u8BA4\u9009\u62E9", compact: true }) : null, portableReceipt?.recovery ? _jsx(StaticRow, { title: `安全恢复:${portableReceipt.recovery}`, status: "warning", compact: true }) : null, _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5", status: "muted", compact: true }), portableTechnicalRows] }) : _jsx(_Fragment, { children: _jsxs(ResultCard, { status: resultStatus, title: blocked ? '安装未开始或结果未确认' : partial ? '部分 Skills 未确认' : '通用 Skills 安装结果', meta: portableResultRange, children: [portableRows, portableResultScope ? _jsx(StaticRow, { title: "\u8303\u56F4", meta: scopeLabel(portableResultScope), compact: true }) : null, portableResultAgentMode === 'automatic'
|
|
8775
|
-
? _jsx(StaticRow, { title: "Agent", meta: "Skills CLI \u9ED8\u8BA4\u9009\u62E9", compact: true }) : null, portableReceipt?.recovery
|
|
8670
|
+
: null, portableResultDetailOpen ? _jsx(_Fragment, { children: readingPage(readingText, size.columns - 2, portableResultDetailPageRows, 'portable') }) : portableTechnicalSummary ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: blocked ? '安装未开始或结果未确认' : partial ? '部分 Skills 未确认' : '通用 Skills 安装结果', meta: portableResultScope && policy.mode === 'compact'
|
|
8671
|
+
? `${portableResultRange ?? ''} · ${scopeLabel(portableResultScope)}` : portableResultRange, status: resultStatus, compact: true }), portableRows, portableResultScope && policy.mode !== 'compact'
|
|
8672
|
+
? _jsx(StaticRow, { title: "\u8303\u56F4", meta: scopeLabel(portableResultScope), compact: true }) : null, portableResultAgentMode === 'automatic' && policy.mode !== 'compact'
|
|
8673
|
+
? _jsx(StaticRow, { title: "Agent", meta: "\u81EA\u52A8\u9009\u62E9\u53EF\u7528 Agent", compact: true }) : null, portableReceipt?.recovery ? _jsx(StaticRow, { title: `安全恢复:${portableReceipt.recovery}`, status: "warning", compact: true }) : null, policy.mode === 'compact' ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5", status: "muted", compact: true }), portableTechnicalRows] }) : _jsx(_Fragment, { children: _jsxs(ResultCard, { status: resultStatus, title: blocked ? '安装未开始或结果未确认' : partial ? '部分 Skills 未确认' : '通用 Skills 安装结果', meta: portableResultRange, children: [portableRows, portableResultScope ? _jsx(StaticRow, { title: "\u8303\u56F4", meta: scopeLabel(portableResultScope), compact: true }) : null, portableResultAgentMode === 'automatic'
|
|
8674
|
+
? _jsx(StaticRow, { title: "Agent", meta: "\u81EA\u52A8\u9009\u62E9\u53EF\u7528 Agent", compact: true }) : null, portableReceipt?.recovery
|
|
8776
8675
|
? _jsx(StaticRow, { title: `安全恢复:${portableReceipt.recovery}`, status: "warning", compact: true })
|
|
8777
|
-
: null] }) }),
|
|
8676
|
+
: null] }) }), !portableResultDetailOpen && portableSkillPage.pageCount > 1
|
|
8677
|
+
? _jsx(Pager, { page: portableSkillPage.page, pageCount: portableSkillPage.pageCount, columns: size.columns - 2 }) : 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 ?? '状态已变化,请重新确认', meta: resultTargets.length > 0 ? `${resultTargets.length} 个目标` : undefined, children: [resetDispositionText ? _jsx(StaticRow, { title: resetDispositionText, status: "success", compact: true }) : null, configGroupRows, 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}`); }), resultTargetPage.pageCount > 1 ? _jsx(Pager, { page: resultTargetPage.page, pageCount: resultTargetPage.pageCount, columns: size.columns - 6 }) : 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: "\u5DF2\u4FDD\u5B58\u7684\u8BBE\u7F6E\u5C42\u4E0D\u4F1A\u91CD\u5199\u3002\u5931\u8D25\u7684\u4FDD\u5B58\u5C42\u4FDD\u7559\u8349\u7A3F\uFF1B\u5E94\u7528\u5931\u8D25\u65F6\u53EA\u91CD\u8BD5\u5E94\u7528\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 })] });
|
|
8778
8678
|
}
|
|
8779
8679
|
else if (route.name === 'pluginContent' && plugin) {
|
|
8780
8680
|
const overview = props.controller.getPluginContentOverview(plugin.pluginId);
|
|
@@ -8782,46 +8682,104 @@ export function ConsoleAppV4(props) {
|
|
|
8782
8682
|
const contentAvailable = overview?.evidence === 'catalogBundle';
|
|
8783
8683
|
breadcrumb = `${plugin.displayName} / 内容详情`;
|
|
8784
8684
|
context = contentState === 'loading' ? '正在读取'
|
|
8785
|
-
: contentAvailable ?
|
|
8685
|
+
: contentAvailable ? undefined : '内容不可用';
|
|
8786
8686
|
title = '内容详情';
|
|
8787
|
-
description =
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
? '这里显示已验证目录包声明的组成,不读取或展示完整操作指令。'
|
|
8791
|
-
: '当前没有可安全读取的目录内容。';
|
|
8792
|
-
const contentNavigationActions = [
|
|
8793
|
-
{ label: '查看技术详情', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
8794
|
-
];
|
|
8795
|
-
const contentTypeLabels = {
|
|
8796
|
-
skill: 'Skill', agent: 'Agent', role: '角色', instruction: '指令', permission: '权限',
|
|
8797
|
-
hook: 'Hook', command: '命令', mcpServer: 'MCP Server', app: 'App',
|
|
8798
|
-
};
|
|
8687
|
+
description = undefined;
|
|
8688
|
+
const reader = contentReader?.pluginId === plugin.pluginId ? contentReader : undefined;
|
|
8689
|
+
const activeType = snapshot.router.current.context.contentTypeTab ?? 'all';
|
|
8799
8690
|
const installedVersions = [...new Set((plugin.update?.targets ?? [])
|
|
8800
8691
|
.flatMap((target) => target.installedVersion ? [target.installedVersion] : []))];
|
|
8801
8692
|
const versionDiffers = contentAvailable
|
|
8802
8693
|
&& installedVersions.some((version) => version !== overview.pluginVersion);
|
|
8803
|
-
const
|
|
8694
|
+
const allItems = overview?.items ?? [];
|
|
8695
|
+
const visibleTypes = [...new Set(allItems.map((item) => item.type))];
|
|
8696
|
+
const typeTabs = ['all', ...visibleTypes];
|
|
8697
|
+
const selectedType = activeType === 'all' || visibleTypes.includes(activeType) ? activeType : 'all';
|
|
8698
|
+
const searchedItems = allItems.filter((item) => contentMatches(query, item));
|
|
8699
|
+
const items = searchedItems.filter((item) => selectedType === 'all' || item.type === selectedType);
|
|
8700
|
+
moveContentType = (direction) => {
|
|
8701
|
+
const next = typeTabs[(typeTabs.indexOf(selectedType) + direction + typeTabs.length) % typeTabs.length];
|
|
8702
|
+
const selectedId = items[focus]?.id;
|
|
8703
|
+
const nextItems = searchedItems.filter((item) => next === 'all' || item.type === next);
|
|
8704
|
+
const retained = nextItems.findIndex((item) => item.id === selectedId);
|
|
8705
|
+
props.controller.updatePageContext({ contentTypeTab: next });
|
|
8706
|
+
setFocus(retained >= 0 ? retained : 0);
|
|
8707
|
+
setScrollOffset(0);
|
|
8708
|
+
setBatchTargetOffset(0);
|
|
8709
|
+
};
|
|
8804
8710
|
const itemActions = items.map((item) => ({
|
|
8805
8711
|
actionId: `content:${item.id}`,
|
|
8806
8712
|
label: item.name,
|
|
8807
|
-
|
|
8808
|
-
|
|
8713
|
+
...(item.issue ? { status: 'warning' } : {}),
|
|
8714
|
+
run: () => {
|
|
8715
|
+
if (!contentItemHasReader(item, size.columns - 2))
|
|
8716
|
+
return;
|
|
8717
|
+
setContentReader({ pluginId: plugin.pluginId, itemId: item.id,
|
|
8718
|
+
returnLineOffset: batchTargetOffset });
|
|
8719
|
+
setBatchTargetOffset(0);
|
|
8720
|
+
},
|
|
8809
8721
|
}));
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8722
|
+
bottomActions = [
|
|
8723
|
+
...(overview?.coverageIssue ? [{ actionId: 'content-coverage-issue',
|
|
8724
|
+
label: '查看清单读取问题', status: 'warning',
|
|
8725
|
+
run: () => {
|
|
8726
|
+
setContentReader({ pluginId: plugin.pluginId,
|
|
8727
|
+
returnLineOffset: batchTargetOffset });
|
|
8728
|
+
setBatchTargetOffset(0);
|
|
8729
|
+
} }] : []),
|
|
8730
|
+
{ actionId: 'content-technical', label: '查看技术详情',
|
|
8731
|
+
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
8732
|
+
];
|
|
8733
|
+
actions = [...itemActions, ...bottomActions];
|
|
8734
|
+
if (reader) {
|
|
8735
|
+
const item = allItems.find((candidate) => candidate.id === reader.itemId);
|
|
8736
|
+
readingText = item ? [
|
|
8737
|
+
`名称 ${item.name}`, `类型 ${contentTypeLabels[item.type]}`,
|
|
8738
|
+
`适用工具 ${item.tools.length ? item.tools.join('、') : '未确认'}`,
|
|
8739
|
+
...(item.descriptionState !== 'issue' && item.description ? [`简介 ${item.description}`] : []),
|
|
8740
|
+
...(item.issue ? [`读取问题 ${item.issue.code}`, item.issue.message] : []),
|
|
8741
|
+
].join('\n') : overview?.coverageIssue
|
|
8742
|
+
? [`清单覆盖 ${overview.coverage ?? '未确认'}`,
|
|
8743
|
+
`读取问题 ${overview.coverageIssue.code}`, overview.coverageIssue.message].join('\n') : '';
|
|
8744
|
+
title = item ? item.name : '清单读取问题';
|
|
8745
|
+
description = undefined;
|
|
8746
|
+
help = '←→ 或 PgUp/PgDn 阅读 · y 复制全文 · Esc 返回清单';
|
|
8747
|
+
content = readingPage(readingText, size.columns - 2, pageActionBudget(1));
|
|
8748
|
+
}
|
|
8749
|
+
else {
|
|
8750
|
+
const cardWidth = size.columns - 2;
|
|
8751
|
+
const lineLimit = 3;
|
|
8752
|
+
const layouts = items.map((item) => contentItemCardLayout(item, cardWidth));
|
|
8753
|
+
entityCardNavigation = { itemCount: items.length, factRows: layouts.map((card) => card.factRows), lineLimit };
|
|
8754
|
+
const coverageState = overview?.coverage ?? (contentAvailable ? 'complete' : 'unavailable');
|
|
8755
|
+
const showCoverage = contentAvailable && coverageState === 'partial';
|
|
8756
|
+
const showUnavailable = contentState !== 'loading' && coverageState === 'unavailable';
|
|
8757
|
+
const emptyHint = contentAvailable && coverageState !== 'unavailable' && items.length === 0
|
|
8758
|
+
&& (query || selectedType !== 'all');
|
|
8759
|
+
const viewport = rowViewport({
|
|
8760
|
+
rowHeights: [...layouts.map((card, index) => contentItemCardWindowHeight(card, index === focus ? batchTargetOffset : 0, lineLimit)), ...bottomActions.map(() => 1)],
|
|
8761
|
+
focus, requestedOffset: scrollOffset,
|
|
8762
|
+
rowBudget: pageActionBudget(1 + Number(visibleTypes.length > 0) + 1
|
|
8763
|
+
+ Number(showCoverage) + Number(showUnavailable && contentAvailable) + Number(emptyHint)),
|
|
8764
|
+
});
|
|
8765
|
+
help = `↑↓ 浏览内容 · ←→ 切换类型 · / 搜索 · a 下方操作${items[focus]
|
|
8766
|
+
&& contentItemHasReader(items[focus], cardWidth) ? ' · Enter 阅读' : ''} · Esc 返回`;
|
|
8767
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(QueryHeader, { width: cardWidth, tabs: visibleTypes.length > 0 ? { label: '类型', items: typeTabs.map((type) => ({ id: type,
|
|
8768
|
+
label: type === 'all' ? '全部' : contentTypeLabels[type],
|
|
8769
|
+
count: type === 'all' ? searchedItems.length
|
|
8770
|
+
: searchedItems.filter((item) => item.type === type).length })), currentId: selectedType } : undefined, search: { value: query, placeholder: '名称或可信简介', input: searchFocus
|
|
8771
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: querySearchWidth(size.columns - 2) }) : undefined } }), contentAvailable ? _jsxs(Box, { width: cardWidth, justifyContent: "space-between", height: 1, overflow: "hidden", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5185\u5BB9\u7248\u672C ", _jsx(Text, { color: tuiTheme.colors.text, children: overview.pluginVersion })] }), _jsxs(Text, { color: versionDiffers ? tuiTheme.roles.change : tuiTheme.colors.muted, wrap: "truncate-end", children: ["\u5B89\u88C5 ", installedVersions.length ? installedVersions.join('、') : '未确认'] })] }) : contentState === 'loading'
|
|
8772
|
+
? _jsx(StaticRow, { title: "\u6B63\u5728\u8BFB\u53D6\u5185\u5BB9", status: "muted", compact: true })
|
|
8773
|
+
: _jsx(StaticRow, { title: "\u5185\u5BB9\u6682\u4E0D\u53EF\u7528", status: "warning", compact: true }), showCoverage ? _jsx(StaticRow, { title: "\u6E05\u5355\u8986\u76D6\u4E0D\u5B8C\u6574\uFF1B\u6570\u91CF\u4EC5\u542B\u5DF2\u786E\u8BA4\u6761\u76EE", status: "warning", compact: true }) : null, showUnavailable && contentAvailable
|
|
8774
|
+
? _jsx(StaticRow, { title: "\u6E05\u5355\u65E0\u6CD5\u786E\u8BA4", status: "warning", compact: true }) : null, emptyHint ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5F53\u524D\u6761\u4EF6\u65E0\u5339\u914D\uFF1B\u67E5\u8BE2\u4FDD\u6301\u539F\u6837\u3002" }) : null, contentAvailable && allItems.length === 0 && coverageState === 'complete'
|
|
8775
|
+
? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u5185\u5BB9\u6761\u76EE\u3002" }) : null, _jsx(ContentViewport, { children: actions.slice(viewport.offset, viewport.offset + viewport.limit)
|
|
8776
|
+
.map((action, visibleIndex) => {
|
|
8777
|
+
const index = viewport.offset + visibleIndex;
|
|
8778
|
+
if (index >= items.length)
|
|
8779
|
+
return _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : focus, kind: "action", compact: true, offset: index, limit: 1 }, action.actionId);
|
|
8780
|
+
return _jsx(ContentItemCard, { item: items[index], width: cardWidth, focused: !searchFocus && focus === index, lineOffset: focus === index ? batchTargetOffset : 0, lineLimit: lineLimit }, action.actionId);
|
|
8781
|
+
}) })] });
|
|
8782
|
+
}
|
|
8825
8783
|
}
|
|
8826
8784
|
else if (route.name === 'pluginTechnical' && plugin) {
|
|
8827
8785
|
breadcrumb = `${plugin.displayName} / 技术详情`;
|
|
@@ -8831,7 +8789,9 @@ export function ConsoleAppV4(props) {
|
|
|
8831
8789
|
? '当前 AW CLI 不支持此插件所需 capability。'
|
|
8832
8790
|
: '默认页面不会展示这些内部信息。';
|
|
8833
8791
|
actions = [{ role: 'navigation', label: '返回插件', run: back }];
|
|
8834
|
-
const prioritizedComponents =
|
|
8792
|
+
const prioritizedComponents = plugin.technicalComponents.filter((component) => !route.platform || component.platform === route.platform
|
|
8793
|
+
&& (!route.scope || component.scope === route.scope || component.scope === undefined))
|
|
8794
|
+
.sort((left, right) => {
|
|
8835
8795
|
const priority = (state) => state === 'error' ? 0
|
|
8836
8796
|
: state === 'partial' ? 1 : state === 'absent' ? 2 : 3;
|
|
8837
8797
|
return priority(left.state) - priority(right.state);
|
|
@@ -8839,6 +8799,8 @@ export function ConsoleAppV4(props) {
|
|
|
8839
8799
|
const technicalFields = [
|
|
8840
8800
|
`canonical pluginId · ${plugin.pluginId}`,
|
|
8841
8801
|
`来源 · ${plugin.sourceId}`,
|
|
8802
|
+
...(route.platform ? [`目标平台 · ${platformLabel(route.platform)}`] : []),
|
|
8803
|
+
...(route.scope ? [`目标位置 · ${scopeLabel(route.scope)}`] : []),
|
|
8842
8804
|
...prioritizedComponents.map((component) => `组件 · ${component.state === 'absent' ? '缺失' : component.state}${component.errorCode ? ` · ${component.errorCode}` : ''} · ${component.key} · ${platformLabel(component.platform)} · ${component.shared ? '平台共享' : scopeLabel(component.scope)}`),
|
|
8843
8805
|
...(plugin.capabilityUnsupported ? [
|
|
8844
8806
|
'failure code · awCapabilityUnsupported',
|
|
@@ -8846,19 +8808,23 @@ export function ConsoleAppV4(props) {
|
|
|
8846
8808
|
`缺少 capability · ${plugin.missingCapabilities?.join('、') ?? 'Status 未提供具体 capability'}`,
|
|
8847
8809
|
`恢复方向 · ${plugin.capabilityRecovery ?? '升级 AW CLI 到支持所需 capability 的版本后重试。'}`,
|
|
8848
8810
|
] : []),
|
|
8849
|
-
...plugin.installedPlacements.
|
|
8811
|
+
...plugin.installedPlacements.filter((placement) => !route.platform || placement.platform === route.platform).flatMap((placement) => {
|
|
8812
|
+
if (placement.state === 'installed' && route.scope) {
|
|
8813
|
+
const scoped = placement.scopedPlacements?.find((entry) => entry.scope === route.scope);
|
|
8814
|
+
if (placement.scope !== route.scope && !scoped)
|
|
8815
|
+
return [];
|
|
8816
|
+
const target = scoped ? { ...placement, ...scoped, scopedPlacements: undefined } : placement;
|
|
8817
|
+
return [`${platformLabel(placement.platform)} · ${scopeLabel(route.scope)} target · ${placementStatus(target).value}`];
|
|
8818
|
+
}
|
|
8819
|
+
return [`${platformLabel(placement.platform)} target · ${placementStatus(placement).value}`];
|
|
8820
|
+
}),
|
|
8821
|
+
...(route.platform && route.scope ? (props.controller.getPluginDetailSubject(plugin.pluginId)?.placements
|
|
8822
|
+
.filter((entry) => entry.platform === route.platform && entry.scope === route.scope)
|
|
8823
|
+
.map((entry) => `观察 · ${entry.observation === 'unknown' ? '未确认' : '已检查'} · 安装版本 ${entry.installedVersion ?? '未确认'}`)
|
|
8824
|
+
?? []) : []),
|
|
8850
8825
|
];
|
|
8851
8826
|
readingText = technicalFields.join('\n');
|
|
8852
|
-
|
|
8853
|
-
const technicalPageRows = Math.max(1, pageActionBudget(7));
|
|
8854
|
-
const technicalOffset = Math.min(batchTargetOffset, Math.max(0, technicalLines.length - 1));
|
|
8855
|
-
const visibleTechnicalLines = technicalLines.slice(technicalOffset, technicalOffset + technicalPageRows);
|
|
8856
|
-
if (technicalLines.length > technicalPageRows) {
|
|
8857
|
-
referenceDetailPagination = { total: technicalLines.length, pageRows: technicalPageRows };
|
|
8858
|
-
help = '←→ 或 PgUp/PgDn 阅读技术字段 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
8859
|
-
}
|
|
8860
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [technicalOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${technicalOffset} 行`, status: "muted", compact: true }) : null, visibleTechnicalLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${technicalOffset + index}:${line}`)), technicalOffset + visibleTechnicalLines.length < technicalLines.length
|
|
8861
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${technicalLines.length - technicalOffset - visibleTechnicalLines.length} 行`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
8827
|
+
content = _jsxs(Box, { flexDirection: "column", children: [readingPage(readingText, size.columns - 2, Math.max(1, pageActionBudget(4) - 1)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
8862
8828
|
}
|
|
8863
8829
|
else {
|
|
8864
8830
|
const missingPlugin = definition.entity === 'plugin' && plugin === undefined;
|
|
@@ -8941,16 +8907,54 @@ export function ConsoleAppV4(props) {
|
|
|
8941
8907
|
}];
|
|
8942
8908
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' });
|
|
8943
8909
|
}
|
|
8910
|
+
if (snapshot.phase === 'ready' && !busy && !characterInput) {
|
|
8911
|
+
const hints = [];
|
|
8912
|
+
if (route.name === 'pluginList')
|
|
8913
|
+
hints.push('/ 搜索', 'f 筛选', '←→ 来源', 'm 多选');
|
|
8914
|
+
if (route.name === 'projectStatus' && !showTechnical)
|
|
8915
|
+
hints.push('/ 搜索', '←→ 分区', 'f 筛选');
|
|
8916
|
+
if (route.name === 'sourceList') {
|
|
8917
|
+
hints.push('/ 搜索');
|
|
8918
|
+
if (moveSourceType)
|
|
8919
|
+
hints.push('←→ 类型');
|
|
8920
|
+
}
|
|
8921
|
+
if (route.name === 'batchSelection' && batchDraft?.stage === 'select') {
|
|
8922
|
+
hints.push('/ 搜索', '←→ 来源', 'f 筛选');
|
|
8923
|
+
if (bottomActions.some((action) => action.actionId === 'batch-selection-continue' && !action.disabled))
|
|
8924
|
+
hints.push('c 继续');
|
|
8925
|
+
}
|
|
8926
|
+
if (route.name === 'portableSkillSelect' && !portableToolsOpen)
|
|
8927
|
+
hints.push('/ 搜索', 'c 继续');
|
|
8928
|
+
if (bottomActions.some((action) => action.actionId === 'project-recheck' && !action.disabled))
|
|
8929
|
+
hints.push('r 检查项目');
|
|
8930
|
+
if (bottomActions.some((action) => action.actionId === 'project-sync-actionable' && !action.disabled))
|
|
8931
|
+
hints.push('c 同步声明');
|
|
8932
|
+
if (bottomActions.some((action) => action.actionId === 'source-check-all' && !action.disabled))
|
|
8933
|
+
hints.push('r 检查全部');
|
|
8934
|
+
if (route.name === 'sourceDetail' || route.name === 'sourceMore')
|
|
8935
|
+
hints.push('r 检查来源');
|
|
8936
|
+
if (bottomActions.length > 0 && actions.length > bottomActions.length
|
|
8937
|
+
&& bottomActions.some((action) => actions.includes(action)))
|
|
8938
|
+
hints.push('a 下方操作');
|
|
8939
|
+
if (entityCardNavigation && focus < entityCardNavigation.itemCount
|
|
8940
|
+
&& (entityCardNavigation.factRows[focus] ?? 0) > entityCardNavigation.lineLimit)
|
|
8941
|
+
hints.push('PgUp/PgDn 翻信息');
|
|
8942
|
+
for (const hint of hints) {
|
|
8943
|
+
const key = hint.slice(0, hint.indexOf(' '));
|
|
8944
|
+
if (!help.split(' · ').some((part) => part.startsWith(`${key} `)))
|
|
8945
|
+
help += ` · ${hint}`;
|
|
8946
|
+
}
|
|
8947
|
+
}
|
|
8944
8948
|
if (canOpenHelp && supportsTechnicalDetails) {
|
|
8945
|
-
const technicalHelp = route.name === 'progress' ? 'd 技术进度' : 'd 技术详情';
|
|
8949
|
+
const technicalHelp = showTechnical ? 'd 返回摘要' : route.name === 'progress' ? 'd 技术进度' : 'd 技术详情';
|
|
8946
8950
|
const remainingHelp = help.split(' · ')
|
|
8947
|
-
.filter((item) => item !== '? 帮助' && item
|
|
8951
|
+
.filter((item) => item !== '? 帮助' && !item.startsWith('d ')).join(' · ');
|
|
8948
8952
|
help = `${technicalHelp} · ? 帮助 · ${remainingHelp}`;
|
|
8949
8953
|
}
|
|
8950
8954
|
else if (canOpenHelp && !help.includes('? 帮助')) {
|
|
8951
8955
|
help = `? 帮助 · ${help}`;
|
|
8952
8956
|
}
|
|
8953
|
-
if (props.onSessionNavigation && !
|
|
8957
|
+
if (props.onSessionNavigation && !characterInput) {
|
|
8954
8958
|
help = busy
|
|
8955
8959
|
? `${props.pendingSessionNavigation === 'reload' ? '已安排完成后重载'
|
|
8956
8960
|
: props.pendingSessionNavigation === 'home' ? '已安排完成后回首页'
|
|
@@ -8958,12 +8962,10 @@ export function ConsoleAppV4(props) {
|
|
|
8958
8962
|
: `h 首页 · H 重载 · ${help}`;
|
|
8959
8963
|
}
|
|
8960
8964
|
const helpParts = help.split(' · ');
|
|
8961
|
-
const isRoutineKey = (part) => /^(↑|Enter|Space|Esc|Ctrl-C
|
|
8965
|
+
const isRoutineKey = (part) => /^(↑|Enter|Space|Esc|Ctrl-C|←→|PgUp)/u.test(part);
|
|
8962
8966
|
help = [...helpParts.filter(isRoutineKey), ...helpParts.filter((part) => !isRoutineKey(part))].join(' · ');
|
|
8963
8967
|
if (searchFocus)
|
|
8964
8968
|
help = '←→ 光标 · ↑↓ 移动结果 · Enter/Esc 完成输入';
|
|
8965
|
-
else if (bottomActions.length > 0 && actions.length > 8)
|
|
8966
|
-
help += ' · a 下方操作';
|
|
8967
8969
|
useEffect(() => {
|
|
8968
8970
|
if ((bottomActions.length > 0 || route.name === 'issueList' || route.name === 'operationHistory')
|
|
8969
8971
|
&& focus >= actions.length && actions.length > 0)
|
|
@@ -8971,10 +8973,31 @@ export function ConsoleAppV4(props) {
|
|
|
8971
8973
|
else if (actions[focus]?.role === 'navigation')
|
|
8972
8974
|
setFocus(moveFocus(actions, focus, 1));
|
|
8973
8975
|
}, [routeKey, focus, actions.length, actions.map((action) => action.role ?? '').join(',')]);
|
|
8974
|
-
const listCandidates = route.name === 'pluginList'
|
|
8976
|
+
const listCandidates = route.name === 'pluginList'
|
|
8975
8977
|
|| route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8976
8978
|
? actions.filter((action) => action.pluginId !== undefined) : [];
|
|
8977
8979
|
const listCandidateSignature = listCandidates.map((action) => action.pluginId).join('\0');
|
|
8980
|
+
const detailActionSignature = route.name === 'pluginDetail' || route.name === 'sourceDetail'
|
|
8981
|
+
? actions.map((action) => action.actionId ?? action.label).join('\0') : undefined;
|
|
8982
|
+
useEffect(() => {
|
|
8983
|
+
if (detailActionSignature === undefined) {
|
|
8984
|
+
detailActionAnchor.current = undefined;
|
|
8985
|
+
return;
|
|
8986
|
+
}
|
|
8987
|
+
const previous = detailActionAnchor.current;
|
|
8988
|
+
if (previous?.routeKey === routeKey && previous.signature !== detailActionSignature
|
|
8989
|
+
&& previous.actionId) {
|
|
8990
|
+
const index = actions.findIndex((action) => action.actionId === previous.actionId);
|
|
8991
|
+
if (index >= 0) {
|
|
8992
|
+
detailActionAnchor.current = { routeKey, signature: detailActionSignature,
|
|
8993
|
+
actionId: previous.actionId };
|
|
8994
|
+
setFocus(index);
|
|
8995
|
+
return;
|
|
8996
|
+
}
|
|
8997
|
+
}
|
|
8998
|
+
detailActionAnchor.current = { routeKey, signature: detailActionSignature,
|
|
8999
|
+
actionId: actions[focus]?.actionId };
|
|
9000
|
+
}, [routeKey, detailActionSignature, focus]);
|
|
8978
9001
|
useEffect(() => {
|
|
8979
9002
|
const pluginId = listCandidates[focus]?.pluginId;
|
|
8980
9003
|
if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
@@ -8986,7 +9009,7 @@ export function ConsoleAppV4(props) {
|
|
|
8986
9009
|
}, [snapshot.router.currentFrameId, route.name, batchDraft?.stage,
|
|
8987
9010
|
batchDraft?.showSelectionTools, listCandidateSignature, focus, scrollOffset]);
|
|
8988
9011
|
useEffect(() => {
|
|
8989
|
-
if (route.name !== 'pluginList' &&
|
|
9012
|
+
if (route.name !== 'pluginList' && !(route.name === 'batchSelection' && batchDraft?.stage === 'select')) {
|
|
8990
9013
|
listFocusAnchor.current = undefined;
|
|
8991
9014
|
return;
|
|
8992
9015
|
}
|
|
@@ -9055,14 +9078,13 @@ export function ConsoleAppV4(props) {
|
|
|
9055
9078
|
}
|
|
9056
9079
|
if (effectiveFocus !== focus) {
|
|
9057
9080
|
setFocus(effectiveFocus);
|
|
9058
|
-
setScrollOffset(0);
|
|
9059
9081
|
}
|
|
9060
9082
|
dynamicMenuFocus.current = {
|
|
9061
9083
|
routeKey,
|
|
9062
9084
|
actionIds: dynamicActionSignature,
|
|
9063
9085
|
focusedActionId: dynamicActionIds[effectiveFocus],
|
|
9064
9086
|
};
|
|
9065
|
-
}, [dynamicActionSignature, routeKey]);
|
|
9087
|
+
}, [dynamicActionSignature, routeKey, focus]);
|
|
9066
9088
|
const runAction = (action) => {
|
|
9067
9089
|
if (!action || action.disabled || action.role === 'navigation')
|
|
9068
9090
|
return;
|
|
@@ -9177,6 +9199,12 @@ export function ConsoleAppV4(props) {
|
|
|
9177
9199
|
}
|
|
9178
9200
|
return;
|
|
9179
9201
|
}
|
|
9202
|
+
if (overlay === 'help' && (key.leftArrow || key.rightArrow || key.pageUp || key.pageDown)) {
|
|
9203
|
+
const current = readerPage({ text: helpTextForPage(help, busy), columns: size.columns - 6,
|
|
9204
|
+
rows: helpBodyRows, page: helpPage });
|
|
9205
|
+
setHelpPage(Math.min(current.pageCount, Math.max(1, current.page + (key.leftArrow || key.pageUp ? -1 : 1))));
|
|
9206
|
+
return;
|
|
9207
|
+
}
|
|
9180
9208
|
if (busy) {
|
|
9181
9209
|
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: false });
|
|
9182
9210
|
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
@@ -9194,9 +9222,19 @@ export function ConsoleAppV4(props) {
|
|
|
9194
9222
|
});
|
|
9195
9223
|
}
|
|
9196
9224
|
else if (overlay === 'help') {
|
|
9197
|
-
if (key.escape ||
|
|
9225
|
+
if (key.escape || input.toLowerCase() === 'b' || input === '?') {
|
|
9198
9226
|
setOverlay(undefined);
|
|
9199
9227
|
}
|
|
9228
|
+
else if (key.upArrow || key.downArrow) {
|
|
9229
|
+
setOverlayFocus((value) => moveIndex(props.onSessionNavigation ? 3 : 1, value, key.upArrow ? -1 : 1));
|
|
9230
|
+
}
|
|
9231
|
+
else if (key.return) {
|
|
9232
|
+
setOverlay(undefined);
|
|
9233
|
+
if (props.onSessionNavigation && overlayFocus === 1)
|
|
9234
|
+
requestSessionNavigation('home');
|
|
9235
|
+
else if (props.onSessionNavigation && overlayFocus === 2)
|
|
9236
|
+
requestSessionNavigation('reload');
|
|
9237
|
+
}
|
|
9200
9238
|
else if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
9201
9239
|
setExitAfterBusy(true);
|
|
9202
9240
|
}
|
|
@@ -9216,6 +9254,7 @@ export function ConsoleAppV4(props) {
|
|
|
9216
9254
|
}
|
|
9217
9255
|
else if (canOpenHelp && input === '?') {
|
|
9218
9256
|
setOverlayFocus(0);
|
|
9257
|
+
setHelpPage(1);
|
|
9219
9258
|
setOverlay('help');
|
|
9220
9259
|
}
|
|
9221
9260
|
else if (!tooSmall && supportsTechnicalDetails && input.toLowerCase() === 'd') {
|
|
@@ -9326,8 +9365,7 @@ export function ConsoleAppV4(props) {
|
|
|
9326
9365
|
const warningLimit = confirmationPagination.pageRows;
|
|
9327
9366
|
setOperationConfirmation((current) => current ? {
|
|
9328
9367
|
...current,
|
|
9329
|
-
warningOffset:
|
|
9330
|
-
+ (key.pageUp || key.leftArrow ? -warningLimit : warningLimit))),
|
|
9368
|
+
warningOffset: pagedItems(warningCount, warningLimit, current.warningOffset + (key.pageUp || key.leftArrow ? -warningLimit : warningLimit)).offset,
|
|
9331
9369
|
} : current);
|
|
9332
9370
|
}
|
|
9333
9371
|
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
@@ -9516,12 +9554,113 @@ export function ConsoleAppV4(props) {
|
|
|
9516
9554
|
});
|
|
9517
9555
|
return;
|
|
9518
9556
|
}
|
|
9519
|
-
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9557
|
+
if (readerNavigation && (key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)) {
|
|
9558
|
+
readerNavigation.move(key.pageUp || key.leftArrow ? -1 : 1);
|
|
9559
|
+
return;
|
|
9560
|
+
}
|
|
9561
|
+
if (configCardNavigation && (key.pageUp || key.pageDown)) {
|
|
9562
|
+
setConfigCardPage({ key: configCardNavigation.key,
|
|
9563
|
+
page: Math.min(configCardNavigation.pageCount, Math.max(1, configCardNavigation.page + (key.pageUp ? -1 : 1))) });
|
|
9564
|
+
return;
|
|
9565
|
+
}
|
|
9566
|
+
if (route.name === 'platform' && (key.upArrow || key.downArrow
|
|
9567
|
+
|| input.toLowerCase() === 'j' || input.toLowerCase() === 'k')) {
|
|
9568
|
+
const next = moveFocus(actions, effectiveFocus, key.upArrow || input.toLowerCase() === 'k' ? -1 : 1);
|
|
9569
|
+
const cardWidth = size.columns - 2;
|
|
9570
|
+
const viewport = rowViewport({
|
|
9571
|
+
rowHeights: actions.map((action, index) => index < platformFacts.length
|
|
9572
|
+
? entityPlacementRowHeight({ ...platformFacts[index],
|
|
9573
|
+
shared: platformFacts[index].sharedWithCurrentProject }, cardWidth) + 1
|
|
9574
|
+
: action.role === 'navigation' ? 0 : 1),
|
|
9575
|
+
focus: next, requestedOffset: scrollOffset, rowBudget: pageActionBudget(3),
|
|
9576
|
+
});
|
|
9577
|
+
dynamicMenuFocus.current = dynamicActionIds && dynamicActionSignature
|
|
9578
|
+
? { routeKey, actionIds: dynamicActionSignature,
|
|
9579
|
+
focusedActionId: actions[next]?.actionId } : undefined;
|
|
9580
|
+
setFocus(next);
|
|
9581
|
+
setScrollOffset(viewport.offset);
|
|
9582
|
+
return;
|
|
9583
|
+
}
|
|
9584
|
+
if (graphLayout && graphProjection) {
|
|
9585
|
+
const portable = route.name === 'portableDependencies';
|
|
9586
|
+
const graphPluginId = route.name === 'portableDependencies'
|
|
9587
|
+
|| route.name === 'pluginDependencies' ? route.pluginId : '';
|
|
9588
|
+
if (graphEvidenceOpen || graphPortableSelectionOpen) {
|
|
9589
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
9590
|
+
setGraphEvidenceOpen(false);
|
|
9591
|
+
setGraphPortableSelectionOpen(false);
|
|
9592
|
+
return;
|
|
9593
|
+
}
|
|
9594
|
+
if (input !== 'h' && input !== 'H' && input !== '?'
|
|
9595
|
+
&& input.toLowerCase() !== 'q' && !(key.ctrl && input.toLowerCase() === 'c'))
|
|
9596
|
+
return;
|
|
9597
|
+
}
|
|
9598
|
+
const row = graphLayout.rows[focus] ?? graphLayout.rows[graphLayout.focusIndex];
|
|
9599
|
+
if (input.toLowerCase() === 'r' && row?.node.edge) {
|
|
9600
|
+
setGraphEvidenceOpen(true);
|
|
9601
|
+
setBatchTargetOffset(0);
|
|
9602
|
+
return;
|
|
9603
|
+
}
|
|
9604
|
+
if (portable && input.toLowerCase() === 'p') {
|
|
9605
|
+
setGraphPortableSelectionOpen(true);
|
|
9606
|
+
setBatchTargetOffset(0);
|
|
9607
|
+
return;
|
|
9608
|
+
}
|
|
9609
|
+
if (key.upArrow || key.downArrow || input.toLowerCase() === 'j'
|
|
9610
|
+
|| input.toLowerCase() === 'k') {
|
|
9611
|
+
const next = moveIndex(graphLayout.rows.length, focus, key.upArrow || input.toLowerCase() === 'k' ? -1 : 1);
|
|
9612
|
+
const nextLayout = projectDependencyGraphView(graphProjection, {
|
|
9613
|
+
columns: size.columns - 2, rowBudget: graphRowBudget,
|
|
9614
|
+
focusRowId: graphLayout.rows[next]?.id,
|
|
9615
|
+
scrollAnchorRowId: graphLayout.rows[scrollOffset]?.id,
|
|
9616
|
+
});
|
|
9617
|
+
setFocus(nextLayout.focusIndex);
|
|
9618
|
+
setScrollOffset(nextLayout.viewport.offset);
|
|
9619
|
+
persistUnifiedDependencyGraphContext(graphPluginId, nextLayout, expandedDependencyPluginIds ?? new Set([
|
|
9620
|
+
...graphProjection.dependencies.defaultExpandedPluginIds,
|
|
9621
|
+
...graphProjection.dependents.defaultExpandedPluginIds,
|
|
9622
|
+
]), nextLayout.focusIndex, nextLayout.viewport.offset, portable);
|
|
9623
|
+
return;
|
|
9624
|
+
}
|
|
9625
|
+
if ((key.leftArrow || key.rightArrow) && row) {
|
|
9626
|
+
const nextExpanded = new Set(expandedDependencyPluginIds ?? [
|
|
9627
|
+
...graphProjection.dependencies.defaultExpandedPluginIds,
|
|
9628
|
+
...graphProjection.dependents.defaultExpandedPluginIds,
|
|
9629
|
+
]);
|
|
9630
|
+
if (key.rightArrow && row.node.canExpand)
|
|
9631
|
+
nextExpanded.add(row.node.pluginId);
|
|
9632
|
+
if (key.leftArrow && row.node.canExpand)
|
|
9633
|
+
nextExpanded.delete(row.node.pluginId);
|
|
9634
|
+
const nextProjection = buildUnifiedDependencyGraphProjection({
|
|
9635
|
+
overviews: props.controller.listPluginDependencyOverviews(),
|
|
9636
|
+
rootPluginId: graphPluginId, expandedPluginIds: nextExpanded,
|
|
9637
|
+
});
|
|
9638
|
+
const nextLayout = projectDependencyGraphView(nextProjection, {
|
|
9639
|
+
columns: size.columns - 2, rowBudget: graphRowBudget,
|
|
9640
|
+
focusRowId: row.id,
|
|
9641
|
+
scrollAnchorRowId: graphLayout.rows[scrollOffset]?.id,
|
|
9642
|
+
});
|
|
9643
|
+
setExpandedDependencyPluginIds(nextExpanded);
|
|
9644
|
+
setFocus(nextLayout.focusIndex);
|
|
9645
|
+
setScrollOffset(nextLayout.viewport.offset);
|
|
9646
|
+
persistUnifiedDependencyGraphContext(graphPluginId, nextLayout, nextExpanded, nextLayout.focusIndex, nextLayout.viewport.offset, portable);
|
|
9647
|
+
return;
|
|
9648
|
+
}
|
|
9649
|
+
if (key.return) {
|
|
9650
|
+
runAction(actions[focus]);
|
|
9651
|
+
return;
|
|
9652
|
+
}
|
|
9653
|
+
}
|
|
9654
|
+
if (route.name === 'pluginContent' && contentReader
|
|
9655
|
+
&& !key.escape && input !== 'h' && input !== 'H' && input !== '?'
|
|
9656
|
+
&& input.toLowerCase() !== 'q' && !(key.ctrl && input.toLowerCase() === 'c'))
|
|
9657
|
+
return;
|
|
9658
|
+
if (entityCardNavigation && focus < entityCardNavigation.itemCount
|
|
9659
|
+
&& (key.pageUp || key.pageDown)) {
|
|
9660
|
+
const maxOffset = Math.max(0, (entityCardNavigation.factRows[focus] ?? 0) - entityCardNavigation.lineLimit);
|
|
9661
|
+
const step = entityCardNavigation.lineLimit;
|
|
9662
|
+
setBatchTargetOffset((value) => Math.min(maxOffset, Math.max(0, value + (key.pageUp || key.leftArrow
|
|
9663
|
+
? -step : step))));
|
|
9525
9664
|
return;
|
|
9526
9665
|
}
|
|
9527
9666
|
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
@@ -9539,27 +9678,13 @@ export function ConsoleAppV4(props) {
|
|
|
9539
9678
|
|| route.name === 'configFields' && route.view === 'values'
|
|
9540
9679
|
|| route.name === 'sourceSave' && route.view === 'changes'
|
|
9541
9680
|
|| route.name === 'review'
|
|
9681
|
+
|| route.name === 'pluginWriteTask'
|
|
9542
9682
|
|| route.name === 'result')
|
|
9543
9683
|
&& referenceDetailPagination) {
|
|
9544
9684
|
const { total, pageRows } = referenceDetailPagination;
|
|
9545
9685
|
moveBatchTargetPage(total, pageRows, key.pageUp || key.leftArrow ? -1 : 1);
|
|
9546
9686
|
return;
|
|
9547
9687
|
}
|
|
9548
|
-
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9549
|
-
&& route.name === 'pluginWriteTask') {
|
|
9550
|
-
const total = props.controller.listPluginWriteTaskCandidates(route.goal)
|
|
9551
|
-
.filter((candidate) => candidate.target.pluginId === route.pluginId
|
|
9552
|
-
&& (!route.targetKeys || route.targetKeys.includes(pluginWriteTaskTargetKey(candidate)))).length;
|
|
9553
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp || key.leftArrow ? -1 : 1))));
|
|
9554
|
-
return;
|
|
9555
|
-
}
|
|
9556
|
-
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9557
|
-
&& route.name === 'sourceTechnical') {
|
|
9558
|
-
const total = diagnosticDetailLines(resolveSourceTechnicalDiagnostic(route.sourceId), size.columns - 6).length;
|
|
9559
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - sourceTechnicalPageRows), Math.max(0, value + (key.pageUp || key.leftArrow
|
|
9560
|
-
? -sourceTechnicalPageRows : sourceTechnicalPageRows))));
|
|
9561
|
-
return;
|
|
9562
|
-
}
|
|
9563
9688
|
if ((key.pageUp || key.pageDown) && route.name === 'sourceRemoveBlocked'
|
|
9564
9689
|
&& sourceDependenciesExpanded) {
|
|
9565
9690
|
const total = sourceReferenceSummary?.dependencies.length ?? 0;
|
|
@@ -9579,19 +9704,6 @@ export function ConsoleAppV4(props) {
|
|
|
9579
9704
|
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
9580
9705
|
return;
|
|
9581
9706
|
}
|
|
9582
|
-
if ((route.name === 'operationDiagnostic' || route.name === 'operationReportPreview')
|
|
9583
|
-
&& (key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)) {
|
|
9584
|
-
const detail = route.name === 'operationDiagnostic'
|
|
9585
|
-
? props.controller.getOperationDiagnostic(route.recordId) : undefined;
|
|
9586
|
-
const preview = route.name === 'operationReportPreview'
|
|
9587
|
-
? props.controller.previewOperationReport(route.recordId) : undefined;
|
|
9588
|
-
const total = detail ? operationDiagnosticLineCount(detail, size.columns)
|
|
9589
|
-
: preview ? operationReportLineCount(preview, size.columns) : 0;
|
|
9590
|
-
const pageRows = route.name === 'operationReportPreview'
|
|
9591
|
-
? operationReportPageRows : operationDetailPageRows;
|
|
9592
|
-
setScrollOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp || key.leftArrow ? -pageRows : pageRows))));
|
|
9593
|
-
return;
|
|
9594
|
-
}
|
|
9595
9707
|
if (route.name === 'portableAgentInput') {
|
|
9596
9708
|
if (key.escape) {
|
|
9597
9709
|
back();
|
|
@@ -9869,7 +9981,10 @@ export function ConsoleAppV4(props) {
|
|
|
9869
9981
|
setInputError(undefined);
|
|
9870
9982
|
return;
|
|
9871
9983
|
}
|
|
9872
|
-
if (searchFocus && (route.name === 'pluginList' || route.name === 'projectStatus'
|
|
9984
|
+
if (searchFocus && (route.name === 'pluginList' || route.name === 'projectStatus'
|
|
9985
|
+
|| route.name === 'pluginContent'
|
|
9986
|
+
|| route.name === 'batchSelection' || route.name === 'sourceList'
|
|
9987
|
+
|| route.name === 'portableSkillSelect')) {
|
|
9873
9988
|
const currentQuery = route.name !== 'batchSelection' ? query : batchDraft?.query ?? '';
|
|
9874
9989
|
const parts = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(currentQuery)]
|
|
9875
9990
|
.map((part) => part.segment);
|
|
@@ -9881,7 +9996,10 @@ export function ConsoleAppV4(props) {
|
|
|
9881
9996
|
if (key.upArrow || key.downArrow) {
|
|
9882
9997
|
setSearchFocus(false);
|
|
9883
9998
|
const candidateCount = route.name === 'batchSelection' && batchDraft
|
|
9884
|
-
? batchCandidates(batchDraft).length
|
|
9999
|
+
? batchCandidates(batchDraft).length
|
|
10000
|
+
: route.name === 'projectStatus' || route.name === 'pluginContent'
|
|
10001
|
+
|| route.name === 'portableSkillSelect'
|
|
10002
|
+
|| route.name === 'sourceList' ? actions.length - bottomActions.length : pluginRows.length;
|
|
9885
10003
|
setFocus((value) => moveIndex(candidateCount, Math.min(value, Math.max(0, candidateCount - 1)), key.upArrow ? -1 : 1));
|
|
9886
10004
|
return;
|
|
9887
10005
|
}
|
|
@@ -9906,15 +10024,56 @@ export function ConsoleAppV4(props) {
|
|
|
9906
10024
|
}
|
|
9907
10025
|
const nextQuery = parts.join('');
|
|
9908
10026
|
setSearchCursor(nextCursor);
|
|
10027
|
+
const focusedId = actions[focus]?.actionId;
|
|
10028
|
+
let retained = -1;
|
|
10029
|
+
if (focusedId) {
|
|
10030
|
+
if (route.name === 'pluginList') {
|
|
10031
|
+
const ids = props.controller.listPlugins({ mode: route.mode,
|
|
10032
|
+
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
10033
|
+
...(nextQuery ? { query: nextQuery } : {}), installFilter }).map((item) => `plugin:${item.pluginId}`);
|
|
10034
|
+
retained = ids.indexOf(focusedId);
|
|
10035
|
+
}
|
|
10036
|
+
else if (route.name === 'projectStatus') {
|
|
10037
|
+
const section = snapshot.router.current.context.projectSection ?? 'declared';
|
|
10038
|
+
const filter = snapshot.router.current.context.projectStatusFilter ?? 'all';
|
|
10039
|
+
retained = props.controller.listProjectInventoryRoots(section).filter((root) => queryMatches(nextQuery, root.displayName, root.pluginId, root.sourceId)
|
|
10040
|
+
&& (filter === 'all' || root.state === filter))
|
|
10041
|
+
.findIndex((root) => `project-root:${root.pluginId}` === focusedId);
|
|
10042
|
+
}
|
|
10043
|
+
else if (route.name === 'pluginContent') {
|
|
10044
|
+
const type = snapshot.router.current.context.contentTypeTab ?? 'all';
|
|
10045
|
+
retained = (props.controller.getPluginContentOverview(route.pluginId)?.items ?? [])
|
|
10046
|
+
.filter((item) => contentMatches(nextQuery, item)
|
|
10047
|
+
&& (type === 'all' || item.type === type))
|
|
10048
|
+
.findIndex((item) => `content:${item.id}` === focusedId);
|
|
10049
|
+
}
|
|
10050
|
+
else if (route.name === 'sourceList') {
|
|
10051
|
+
retained = matchingSources(props.controller, nextQuery, snapshot.router.current.context.sourceTypeTab ?? 'all')
|
|
10052
|
+
.findIndex((source) => `source:${source.id}` === focusedId);
|
|
10053
|
+
}
|
|
10054
|
+
else if (route.name === 'portableSkillSelect') {
|
|
10055
|
+
retained = (snapshot.portableDraft?.candidates ?? []).filter((candidate) => (portableSelectionView === 'all' || candidate.selected)
|
|
10056
|
+
&& queryMatches(nextQuery, candidate.name, candidate.shortDescription ?? candidate.description.split(/[。!?\n]/u)[0]))
|
|
10057
|
+
.findIndex((candidate) => `skill:${candidate.providerPluginId}:${candidate.name}` === focusedId);
|
|
10058
|
+
}
|
|
10059
|
+
else if (batchDraft) {
|
|
10060
|
+
retained = batchCandidates({ ...batchDraft, query: nextQuery, showSelected: false })
|
|
10061
|
+
.findIndex((candidate) => candidate.pluginId === actions[focus]?.pluginId);
|
|
10062
|
+
}
|
|
10063
|
+
}
|
|
9909
10064
|
if (route.name !== 'batchSelection') {
|
|
9910
10065
|
setQuery(nextQuery);
|
|
9911
10066
|
props.controller.updatePageContext({ query: nextQuery });
|
|
10067
|
+
if (route.name === 'projectStatus') {
|
|
10068
|
+
dynamicMenuFocus.current = undefined;
|
|
10069
|
+
actionJumpAnchor.current = undefined;
|
|
10070
|
+
}
|
|
9912
10071
|
}
|
|
9913
10072
|
else {
|
|
9914
10073
|
setBatchDraft((draft) => draft ? { ...draft, query: nextQuery, showSelected: false } : draft);
|
|
9915
10074
|
}
|
|
9916
|
-
setFocus(0);
|
|
9917
|
-
setScrollOffset(0);
|
|
10075
|
+
setFocus(retained >= 0 ? retained : 0);
|
|
10076
|
+
setScrollOffset(retained >= 0 ? Math.min(scrollOffset, retained) : 0);
|
|
9918
10077
|
return;
|
|
9919
10078
|
}
|
|
9920
10079
|
// Paste and composed text are data, never multiple background commands.
|
|
@@ -9922,8 +10081,11 @@ export function ConsoleAppV4(props) {
|
|
|
9922
10081
|
return;
|
|
9923
10082
|
if (key.tab)
|
|
9924
10083
|
return;
|
|
9925
|
-
if (route.name === 'projectStatus' &&
|
|
9926
|
-
|
|
10084
|
+
if ((route.name === 'projectStatus' && !showTechnical
|
|
10085
|
+
|| route.name === 'pluginContent' && !contentReader
|
|
10086
|
+
|| route.name === 'sourceList' || route.name === 'portableSkillSelect' && !portableToolsOpen) && input === '/') {
|
|
10087
|
+
setSearchCursor([...new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
10088
|
+
.segment(query)].length);
|
|
9927
10089
|
setSearchFocus(true);
|
|
9928
10090
|
return;
|
|
9929
10091
|
}
|
|
@@ -9935,6 +10097,12 @@ export function ConsoleAppV4(props) {
|
|
|
9935
10097
|
runAction(bottomActions.find((action) => action.actionId === 'plugin-list-multi'));
|
|
9936
10098
|
return;
|
|
9937
10099
|
}
|
|
10100
|
+
if (input.toLowerCase() === 'a' && route.name === 'sourceDetail'
|
|
10101
|
+
&& entityCardNavigation) {
|
|
10102
|
+
setFocus(focus === 0 ? Math.min(1, actions.length - 1) : 0);
|
|
10103
|
+
setScrollOffset(0);
|
|
10104
|
+
return;
|
|
10105
|
+
}
|
|
9938
10106
|
if (input.toLowerCase() === 'a' && bottomActions.length > 0) {
|
|
9939
10107
|
const firstAction = actions.findIndex((action) => bottomActions.includes(action));
|
|
9940
10108
|
if (firstAction >= 0) {
|
|
@@ -9942,11 +10110,20 @@ export function ConsoleAppV4(props) {
|
|
|
9942
10110
|
if (anchor?.routeKey === routeKey && focus >= firstAction) {
|
|
9943
10111
|
const entityIndex = anchor.entityId
|
|
9944
10112
|
? actions.findIndex((action) => action.pluginId === anchor.entityId) : -1;
|
|
9945
|
-
|
|
10113
|
+
const next = entityIndex >= 0 ? entityIndex : Math.min(anchor.index, Math.max(0, firstAction - 1));
|
|
10114
|
+
if (route.name === 'projectStatus' && dynamicActionIds && dynamicActionSignature) {
|
|
10115
|
+
dynamicMenuFocus.current = { routeKey, actionIds: dynamicActionSignature,
|
|
10116
|
+
focusedActionId: dynamicActionIds[next] };
|
|
10117
|
+
}
|
|
10118
|
+
setFocus(next);
|
|
9946
10119
|
actionJumpAnchor.current = undefined;
|
|
9947
10120
|
}
|
|
9948
10121
|
else {
|
|
9949
10122
|
actionJumpAnchor.current = { routeKey, index: focus, entityId: actions[focus]?.pluginId };
|
|
10123
|
+
if (route.name === 'projectStatus' && dynamicActionIds && dynamicActionSignature) {
|
|
10124
|
+
dynamicMenuFocus.current = { routeKey, actionIds: dynamicActionSignature,
|
|
10125
|
+
focusedActionId: dynamicActionIds[firstAction] };
|
|
10126
|
+
}
|
|
9950
10127
|
setFocus(firstAction);
|
|
9951
10128
|
}
|
|
9952
10129
|
setScrollOffset(0);
|
|
@@ -9959,37 +10136,7 @@ export function ConsoleAppV4(props) {
|
|
|
9959
10136
|
|| route.name === 'configObjectPropertyEditor')) {
|
|
9960
10137
|
return;
|
|
9961
10138
|
}
|
|
9962
|
-
|
|
9963
|
-
&& (key.leftArrow || key.rightArrow)) {
|
|
9964
|
-
const unified = buildUnifiedDependencyGraphProjection({
|
|
9965
|
-
overviews: props.controller.listPluginDependencyOverviews(),
|
|
9966
|
-
rootPluginId: route.pluginId,
|
|
9967
|
-
...(expandedDependencyPluginIds ? { expandedPluginIds: expandedDependencyPluginIds } : {}),
|
|
9968
|
-
});
|
|
9969
|
-
const row = unified.rows[focus];
|
|
9970
|
-
if (!row)
|
|
9971
|
-
return;
|
|
9972
|
-
const next = new Set(expandedDependencyPluginIds ?? [
|
|
9973
|
-
...unified.dependencies.defaultExpandedPluginIds,
|
|
9974
|
-
...unified.dependents.defaultExpandedPluginIds,
|
|
9975
|
-
]);
|
|
9976
|
-
if (key.rightArrow && row.canExpand)
|
|
9977
|
-
next.add(row.pluginId);
|
|
9978
|
-
if (key.leftArrow && row.canExpand)
|
|
9979
|
-
next.delete(row.pluginId);
|
|
9980
|
-
const nextUnified = buildUnifiedDependencyGraphProjection({
|
|
9981
|
-
overviews: props.controller.listPluginDependencyOverviews(),
|
|
9982
|
-
rootPluginId: route.pluginId,
|
|
9983
|
-
expandedPluginIds: next,
|
|
9984
|
-
});
|
|
9985
|
-
const nextFocus = Math.max(0, nextUnified.rows.findIndex((candidate) => candidate.id === row.id && candidate.branch === row.branch));
|
|
9986
|
-
const nextScroll = Math.min(scrollOffset, nextFocus);
|
|
9987
|
-
setExpandedDependencyPluginIds(next);
|
|
9988
|
-
setFocus(nextFocus);
|
|
9989
|
-
setScrollOffset(nextScroll);
|
|
9990
|
-
return;
|
|
9991
|
-
}
|
|
9992
|
-
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: isCharacterInputMode(route, searchFocus) });
|
|
10139
|
+
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput });
|
|
9993
10140
|
const globalAction = resolveTuiGlobalKeyAction({ input, key }, { mode: 'menu', allowBack: true });
|
|
9994
10141
|
if (sessionNavigation) {
|
|
9995
10142
|
requestSessionNavigation(sessionNavigation);
|
|
@@ -10006,6 +10153,7 @@ export function ConsoleAppV4(props) {
|
|
|
10006
10153
|
}
|
|
10007
10154
|
else if (canOpenHelp && globalAction === 'help') {
|
|
10008
10155
|
setOverlayFocus(0);
|
|
10156
|
+
setHelpPage(1);
|
|
10009
10157
|
setOverlay('help');
|
|
10010
10158
|
}
|
|
10011
10159
|
else if (globalAction === 'exit') {
|
|
@@ -10014,6 +10162,10 @@ export function ConsoleAppV4(props) {
|
|
|
10014
10162
|
else if (input.toLowerCase() === 'd' && supportsTechnicalDetails) {
|
|
10015
10163
|
toggleTechnical();
|
|
10016
10164
|
}
|
|
10165
|
+
else if (globalAction === 'back' && route.name === 'pluginContent' && contentReader) {
|
|
10166
|
+
setContentReader(undefined);
|
|
10167
|
+
setBatchTargetOffset(contentReader.returnLineOffset);
|
|
10168
|
+
}
|
|
10017
10169
|
else if (globalAction === 'back' && route.name === 'result' && portableResultDetailOpen) {
|
|
10018
10170
|
setPortableResultDetailOpen(false);
|
|
10019
10171
|
setPortableResultDetailOffset(0);
|
|
@@ -10114,9 +10266,7 @@ export function ConsoleAppV4(props) {
|
|
|
10114
10266
|
setScrollOffset(0);
|
|
10115
10267
|
}
|
|
10116
10268
|
else if (route.name === 'projectStatus' && input.toLowerCase() === 'f' && !showTechnical) {
|
|
10117
|
-
|
|
10118
|
-
setFocus(0);
|
|
10119
|
-
setScrollOffset(0);
|
|
10269
|
+
toggleProjectStatusFilter?.();
|
|
10120
10270
|
}
|
|
10121
10271
|
else if (route.name === 'projectStatus' && input.toLowerCase() === 'r') {
|
|
10122
10272
|
runAction(bottomActions.find((action) => action.actionId === 'project-recheck'));
|
|
@@ -10193,7 +10343,7 @@ export function ConsoleAppV4(props) {
|
|
|
10193
10343
|
removeDraft(field.key);
|
|
10194
10344
|
}
|
|
10195
10345
|
}
|
|
10196
|
-
else if (route.name === 'configFields' && visibleConfigTabs.length > 1
|
|
10346
|
+
else if (route.name === 'configFields' && !route.view && visibleConfigTabs.length > 1
|
|
10197
10347
|
&& (key.leftArrow || key.rightArrow)) {
|
|
10198
10348
|
const current = Math.max(0, visibleConfigTabs.findIndex((tab) => tab.id === activeConfigTabId));
|
|
10199
10349
|
const direction = key.leftArrow ? -1 : 1;
|
|
@@ -10202,6 +10352,17 @@ export function ConsoleAppV4(props) {
|
|
|
10202
10352
|
setFocus(0);
|
|
10203
10353
|
setScrollOffset(0);
|
|
10204
10354
|
}
|
|
10355
|
+
else if (route.name === 'projectStatus' && !showTechnical
|
|
10356
|
+
&& (key.leftArrow || key.rightArrow)) {
|
|
10357
|
+
moveProjectSection?.(key.leftArrow ? -1 : 1);
|
|
10358
|
+
}
|
|
10359
|
+
else if (route.name === 'pluginContent' && !contentReader
|
|
10360
|
+
&& (key.leftArrow || key.rightArrow)) {
|
|
10361
|
+
moveContentType?.(key.leftArrow ? -1 : 1);
|
|
10362
|
+
}
|
|
10363
|
+
else if (route.name === 'sourceList' && (key.leftArrow || key.rightArrow)) {
|
|
10364
|
+
moveSourceType?.(key.leftArrow ? -1 : 1);
|
|
10365
|
+
}
|
|
10205
10366
|
else if ((input.toLowerCase() === 'r' || input.toLowerCase() === 'c')
|
|
10206
10367
|
&& (route.name === 'sourceDetail' || route.name === 'sourceMore')) {
|
|
10207
10368
|
void props.controller.recheckCatalogSource(route.sourceId);
|
|
@@ -10223,7 +10384,18 @@ export function ConsoleAppV4(props) {
|
|
|
10223
10384
|
if (candidate)
|
|
10224
10385
|
props.controller.togglePortableSkill(candidate.name);
|
|
10225
10386
|
}
|
|
10387
|
+
else if (entityCardNavigation && focus < entityCardNavigation.itemCount
|
|
10388
|
+
&& (key.upArrow || input.toLowerCase() === 'k') && batchTargetOffset > 0) {
|
|
10389
|
+
setBatchTargetOffset((value) => value - 1);
|
|
10390
|
+
}
|
|
10391
|
+
else if (entityCardNavigation && focus < entityCardNavigation.itemCount
|
|
10392
|
+
&& (key.downArrow || input.toLowerCase() === 'j')
|
|
10393
|
+
&& batchTargetOffset < Math.max(0, (entityCardNavigation.factRows[focus] ?? 0) - entityCardNavigation.lineLimit)) {
|
|
10394
|
+
setBatchTargetOffset((value) => value + 1);
|
|
10395
|
+
}
|
|
10226
10396
|
else if (key.upArrow || input.toLowerCase() === 'k') {
|
|
10397
|
+
if (entityCardNavigation)
|
|
10398
|
+
setBatchTargetOffset(0);
|
|
10227
10399
|
if (route.name === 'pluginDependencyNode' && route.view === 'references' && !showTechnical) {
|
|
10228
10400
|
setBatchTargetOffset(0);
|
|
10229
10401
|
}
|
|
@@ -10240,23 +10412,14 @@ export function ConsoleAppV4(props) {
|
|
|
10240
10412
|
};
|
|
10241
10413
|
}
|
|
10242
10414
|
const viewport = actionViewport(actions, policy.mode === 'compact', next, scrollOffset, currentActionViewportLineBudget);
|
|
10243
|
-
if (route.name === 'pluginDependencies' && route.view !== 'removeBlocked') {
|
|
10244
|
-
const projection = buildUnifiedDependencyGraphProjection({
|
|
10245
|
-
overviews: props.controller.listPluginDependencyOverviews(),
|
|
10246
|
-
rootPluginId: route.pluginId,
|
|
10247
|
-
...(expandedDependencyPluginIds ? { expandedPluginIds: expandedDependencyPluginIds } : {}),
|
|
10248
|
-
});
|
|
10249
|
-
persistUnifiedDependencyGraphContext(route.pluginId, projection, expandedDependencyPluginIds ?? new Set([
|
|
10250
|
-
...projection.dependencies.defaultExpandedPluginIds,
|
|
10251
|
-
...projection.dependents.defaultExpandedPluginIds,
|
|
10252
|
-
]), next, viewport.offset);
|
|
10253
|
-
}
|
|
10254
10415
|
if (bottomActions.length === 0)
|
|
10255
10416
|
setScrollOffset(viewport.offset);
|
|
10256
10417
|
return next;
|
|
10257
10418
|
});
|
|
10258
10419
|
}
|
|
10259
10420
|
else if (key.downArrow || input.toLowerCase() === 'j') {
|
|
10421
|
+
if (entityCardNavigation)
|
|
10422
|
+
setBatchTargetOffset(0);
|
|
10260
10423
|
if (route.name === 'pluginDependencyNode' && route.view === 'references' && !showTechnical) {
|
|
10261
10424
|
setBatchTargetOffset(0);
|
|
10262
10425
|
}
|
|
@@ -10273,17 +10436,6 @@ export function ConsoleAppV4(props) {
|
|
|
10273
10436
|
};
|
|
10274
10437
|
}
|
|
10275
10438
|
const viewport = actionViewport(actions, policy.mode === 'compact', next, scrollOffset, currentActionViewportLineBudget);
|
|
10276
|
-
if (route.name === 'pluginDependencies' && route.view !== 'removeBlocked') {
|
|
10277
|
-
const projection = buildUnifiedDependencyGraphProjection({
|
|
10278
|
-
overviews: props.controller.listPluginDependencyOverviews(),
|
|
10279
|
-
rootPluginId: route.pluginId,
|
|
10280
|
-
...(expandedDependencyPluginIds ? { expandedPluginIds: expandedDependencyPluginIds } : {}),
|
|
10281
|
-
});
|
|
10282
|
-
persistUnifiedDependencyGraphContext(route.pluginId, projection, expandedDependencyPluginIds ?? new Set([
|
|
10283
|
-
...projection.dependencies.defaultExpandedPluginIds,
|
|
10284
|
-
...projection.dependents.defaultExpandedPluginIds,
|
|
10285
|
-
]), next, viewport.offset);
|
|
10286
|
-
}
|
|
10287
10439
|
if (bottomActions.length === 0)
|
|
10288
10440
|
setScrollOffset(viewport.offset);
|
|
10289
10441
|
return next;
|
|
@@ -10297,21 +10449,26 @@ export function ConsoleAppV4(props) {
|
|
|
10297
10449
|
return (_jsx(ScreenFrame, { title: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", breadcrumb: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", context: `${size.columns} × ${size.rows}`, help: "q \u9000\u51FA", size: size, children: _jsx(Callout, { status: "warning", title: "\u2194 \u8BF7\u5C06\u7EC8\u7AEF\u8C03\u6574\u5230\u81F3\u5C11 60 \u00D7 18", description: "\u8C03\u6574\u540E\u4F1A\u8FD4\u56DE\u539F\u9875\u9762\uFF0C\u4E0D\u4F1A\u4E22\u5931\u5F53\u524D\u9009\u62E9\u3002" }) }));
|
|
10298
10450
|
}
|
|
10299
10451
|
if (readingText !== undefined && route.name !== 'progress') {
|
|
10300
|
-
const paginated = referenceDetailPagination !== undefined
|
|
10301
|
-
|| portableResultDetailPagination !== undefined
|
|
10452
|
+
const paginated = readerNavigation !== undefined || referenceDetailPagination !== undefined
|
|
10302
10453
|
|| route.name === 'sourceTechnical'
|
|
10303
10454
|
|| route.name === 'operationDiagnostic'
|
|
10304
10455
|
|| route.name === 'operationReportPreview'
|
|
10305
10456
|
|| route.name === 'portableDependencies';
|
|
10306
|
-
help =
|
|
10307
|
-
? ' ·
|
|
10308
|
-
|
|
10457
|
+
help = graphEvidenceOpen || graphPortableSelectionOpen
|
|
10458
|
+
? '←→/PgUp/PgDn 翻页 · [y] 复制全文 · Esc 返回关系图'
|
|
10459
|
+
: route.name === 'pluginContent' && contentReader
|
|
10460
|
+
? `${paginated ? '←→/PgUp/PgDn 翻页 · ' : ''}[y] 复制全文 · Esc 返回清单`
|
|
10461
|
+
: `${paginated ? '←→ 翻页 · ' : ''}[y] 复制全文 · Esc 返回${supportsTechnicalDetails
|
|
10462
|
+
? showTechnical ? ' · d 返回摘要' : ' · d 技术详情' : ''}${actions.length > 0
|
|
10463
|
+
? ` · ${size.columns < 80 ? '↑↓/Enter 菜单' : '↑↓ 移动 · Enter 选择'}` : ''}`;
|
|
10464
|
+
if (canOpenHelp)
|
|
10465
|
+
help += ' · ? 帮助';
|
|
10309
10466
|
if (copyFeedback?.routeKey === routeKey)
|
|
10310
10467
|
description = copyFeedback.message;
|
|
10311
10468
|
}
|
|
10312
10469
|
if (actionError)
|
|
10313
10470
|
description = actionError;
|
|
10314
|
-
const base = (_jsx(ScreenFrame, { title: projectReminderOpen ? '' : title, description: projectReminderOpen ? undefined : description, breadcrumb: breadcrumb, context: context, help: portableEmptySelectionOpen ? 'Enter 返回选择 · Esc 返回' : help, size: size, children: projectReminderOpen || sourceTransportOverlay ? null : content }));
|
|
10471
|
+
const base = (_jsx(ScreenFrame, { title: projectReminderOpen ? '' : title, description: projectReminderOpen ? undefined : description, breadcrumb: breadcrumb, context: context, headerHint: !projectReminderOpen && route.name === 'pluginList' ? { key: 'm', label: '多选' } : undefined, help: portableEmptySelectionOpen ? 'Enter 返回选择 · Esc 返回' : help, size: size, children: projectReminderOpen || sourceTransportOverlay ? null : content }));
|
|
10315
10472
|
if (portableEmptySelectionOpen) {
|
|
10316
10473
|
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
10317
10474
|
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 7, marginLeft: Math.max(2, Math.floor((size.columns - modalWidth) / 2)), width: modalWidth, flexDirection: "column", backgroundColor: tuiTheme.colors.panelBackground, children: _jsx(ReviewCard, { title: "\u8FD8\u6CA1\u6709\u9009\u62E9 Skill", description: "\u7528 Space \u52FE\u9009\uFF1B\u4E5F\u53EF\u4EE5\u6253\u5F00\u9009\u62E9\u5DE5\u5177\u3002", children: _jsx(SemanticRows, { actions: [{ label: '返回选择' }], focus: 0, kind: "action", compact: true }) }) })] });
|
|
@@ -10367,9 +10524,10 @@ export function ConsoleAppV4(props) {
|
|
|
10367
10524
|
? modalWarningLines
|
|
10368
10525
|
: confirmationRiskLines(Math.max(12, size.columns - 8));
|
|
10369
10526
|
const warningLimit = Math.max(1, policy.mode === 'compact' ? 1 : 7);
|
|
10370
|
-
const
|
|
10527
|
+
const warningPage = paginateWrappedRows({ lines: warningLines, rows: warningLimit,
|
|
10528
|
+
page: Math.floor(operationConfirmation.warningOffset / warningLimit) + 1 });
|
|
10371
10529
|
const visibleWarnings = shortConfirmation
|
|
10372
|
-
? warningLines :
|
|
10530
|
+
? warningLines : warningPage.lines;
|
|
10373
10531
|
const warningPaginated = !shortConfirmation && warningLines.length > warningLimit;
|
|
10374
10532
|
confirmationText = portableUserConfirmation
|
|
10375
10533
|
? '! 本次在所有项目范围安装选定 Skills。\n具体对象和范围以刚才的汇总为准。'
|
|
@@ -10389,10 +10547,10 @@ export function ConsoleAppV4(props) {
|
|
|
10389
10547
|
},
|
|
10390
10548
|
];
|
|
10391
10549
|
const confirmationCard = (_jsxs(ReviewCard, { title: portableUserConfirmation ? '确认本次影响' : '请确认本次风险', description: `${copyFeedback?.routeKey === `${routeKey}:confirmation`
|
|
10392
|
-
? `${copyFeedback.message} · ` : ''}确认只对当前操作汇总有效;[y] 复制全文。`, children: [
|
|
10393
|
-
? visibleWarnings.map((warning, index) => _jsx(StaticRow, { title: warning, status: warning.startsWith('! ') ? 'warning' : 'info', compact: true }, `${
|
|
10394
|
-
: _jsx(StaticRow, { title: "! \u6B64\u64CD\u4F5C\u9700\u8981\u660E\u786E\u6388\u6743", status: "warning", compact: true }),
|
|
10395
|
-
? _jsx(
|
|
10550
|
+
? `${copyFeedback.message} · ` : ''}确认只对当前操作汇总有效;[y] 复制全文。`, children: [visibleWarnings.length > 0
|
|
10551
|
+
? visibleWarnings.map((warning, index) => _jsx(StaticRow, { title: warning, status: warning.startsWith('! ') ? 'warning' : 'info', compact: true }, `${warningPage.offset + index}:${warning}`))
|
|
10552
|
+
: _jsx(StaticRow, { title: "! \u6B64\u64CD\u4F5C\u9700\u8981\u660E\u786E\u6388\u6743", status: "warning", compact: true }), warningPaginated
|
|
10553
|
+
? _jsx(Pager, { page: warningPage.page, pageCount: warningPage.pageCount, columns: modalWidth - 6 }) : null, _jsx(SemanticRows, { actions: confirmationActions, focus: overlayFocus, kind: "action", compact: true })] }));
|
|
10396
10554
|
if (shortConfirmation) {
|
|
10397
10555
|
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: modalMarginLeft, width: modalWidth, flexDirection: "column", children: confirmationCard })] });
|
|
10398
10556
|
}
|
|
@@ -10418,13 +10576,13 @@ export function ConsoleAppV4(props) {
|
|
|
10418
10576
|
}
|
|
10419
10577
|
if (!overlay)
|
|
10420
10578
|
return base;
|
|
10421
|
-
const
|
|
10579
|
+
const helpText = helpTextForPage(help, busy);
|
|
10422
10580
|
const overlayActions = overlay === 'help'
|
|
10423
10581
|
? [
|
|
10424
10582
|
{ label: '关闭' },
|
|
10425
10583
|
...(props.onSessionNavigation ? [
|
|
10426
|
-
{ label: '返回首页' },
|
|
10427
|
-
{ label: '返回首页并重新加载' },
|
|
10584
|
+
{ label: busy ? '操作成功后返回首页' : '返回首页' },
|
|
10585
|
+
{ label: busy ? '操作成功后重载' : '返回首页并重新加载' },
|
|
10428
10586
|
] : []),
|
|
10429
10587
|
]
|
|
10430
10588
|
: [
|
|
@@ -10434,11 +10592,11 @@ export function ConsoleAppV4(props) {
|
|
|
10434
10592
|
return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: overlay === 'help' ? null : base }), _jsx(Box, { position: "absolute", marginTop: overlay === 'help' ? 0 : 5, marginLeft: overlay === 'help' ? 1 : Math.max(2, Math.floor(size.columns / 5)), width: overlay === 'help' ? size.columns - 2 : Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", children: _jsxs(ReviewCard, { title: overlay === 'help' ? '当前页快捷键'
|
|
10435
10593
|
: overlay === 'discard' || overlay === 'sessionNavigation' ? '放弃未保存修改?'
|
|
10436
10594
|
: '退出控制台?', children: [overlay === 'help'
|
|
10437
|
-
? _jsx(
|
|
10595
|
+
? _jsx(Reader, { text: helpText, columns: size.columns - 6, rows: helpBodyRows, page: helpPage })
|
|
10438
10596
|
: _jsx(StaticRow, { title: overlay === 'discard'
|
|
10439
10597
|
? discardForExit ? '放弃后将退出控制台。' : '放弃后将返回上一页。'
|
|
10440
10598
|
: overlay === 'sessionNavigation'
|
|
10441
10599
|
? `放弃后将${discardForSessionNavigation === 'reload' ? '重新读取本地状态' : '返回首页'}。`
|
|
10442
|
-
: '进行中的写操作会先完成。', status: "muted" }), _jsx(SemanticRows, { actions: overlayActions, focus: overlayFocus, kind: "action", compact: true })] }) })] }));
|
|
10600
|
+
: '进行中的写操作会先完成。', status: "muted" }), _jsx(SemanticRows, { actions: overlayActions, focus: overlayFocus, kind: "action", compact: true }), overlay === 'help' ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u2190\u2192 \u7FFB\u5E2E\u52A9 \u00B7 \u2191\u2193 \u9009\u62E9\u64CD\u4F5C \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u5173\u95ED" }) : null] }) })] }));
|
|
10443
10601
|
}
|
|
10444
10602
|
//# sourceMappingURL=console-app-v4.js.map
|