@lesliechan721/aw 0.1.0-beta.18 → 0.1.0-beta.19
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-planner.d.ts +3 -3
- package/dist/src/commands/status-v6.d.ts +81 -31
- package/dist/src/commands/status-v6.js +494 -183
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/wizard.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/tui/catalog-sources/projection.d.ts +2 -2
- package/dist/src/tui/catalog-sources/screens.js +17 -10
- package/dist/src/tui/catalog-sources/screens.js.map +1 -1
- package/dist/src/tui/console-app-v4.js +1291 -435
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +3 -3
- package/dist/src/tui/controller-runtime.js +85 -27
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +17 -7
- package/dist/src/tui/controller.js +265 -24
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph.d.ts +3 -0
- package/dist/src/tui/dependency-graph.js +37 -1
- package/dist/src/tui/dependency-graph.js.map +1 -1
- package/dist/src/tui/diagnostic-projection.d.ts +1 -0
- package/dist/src/tui/diagnostic-projection.js +51 -4
- package/dist/src/tui/diagnostic-projection.js.map +1 -1
- package/dist/src/tui/model.d.ts +22 -12
- package/dist/src/tui/model.js +251 -86
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-history.d.ts +1 -0
- package/dist/src/tui/operation-history.js +5 -0
- package/dist/src/tui/operation-history.js.map +1 -1
- package/dist/src/tui/operation-review-planner.js +67 -2
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/plugin-write-task.d.ts +2 -0
- package/dist/src/tui/plugin-write-task.js.map +1 -1
- package/dist/src/tui/router.d.ts +6 -0
- package/dist/src/tui/router.js +56 -12
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +7 -0
- package/dist/src/utils/plugin-config-write.js +3 -3
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +2 -2
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/package.json +3 -3
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text, useInput, useWindowSize } from 'ink';
|
|
3
3
|
import { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import stringWidth from 'string-width';
|
|
5
6
|
import { validatePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
6
7
|
import { CATALOG_BETA_REF, CATALOG_LATEST_REF, OFFICIAL_CATALOG_SOURCE_SSH_URL, OFFICIAL_CATALOG_SOURCE_URL, } from '../constants.js';
|
|
7
8
|
import { CATALOG_SOURCE_ID_PATTERN } from '../utils/catalog-sources.js';
|
|
@@ -40,18 +41,6 @@ function sourceOperationBlockLabel(reason) {
|
|
|
40
41
|
case 'writeProgress': return '正在执行的写入操作';
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
function catalogGenerationStateLabel(state) {
|
|
44
|
-
switch (state) {
|
|
45
|
-
case 'ready': return '可用';
|
|
46
|
-
case 'activationRequired': return '需要启用';
|
|
47
|
-
case 'repairRequired': return '需要修复';
|
|
48
|
-
case 'configMismatch': return '来源设置已变化';
|
|
49
|
-
case 'stateInvalid': return '版本状态需要恢复';
|
|
50
|
-
case 'incompatible': return '当前 AW 不兼容';
|
|
51
|
-
case 'unavailable': return '不可用';
|
|
52
|
-
case undefined: return '尚未取得状态';
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
44
|
function productResultMessage(message) {
|
|
56
45
|
if (message === '后续:当前项目 sync')
|
|
57
46
|
return '后续:同步当前项目位置';
|
|
@@ -68,6 +57,36 @@ function pluginMenuActionId(action) {
|
|
|
68
57
|
? `${action.id}:${action.pluginId}:${action.platform}:${action.scope}`
|
|
69
58
|
: action.id;
|
|
70
59
|
}
|
|
60
|
+
function batchTargetKey(target) {
|
|
61
|
+
return [target.pluginId, target.platform, target.scope].join('\0');
|
|
62
|
+
}
|
|
63
|
+
function batchPluginVersionSummary(plugin) {
|
|
64
|
+
const updates = plugin.update.targets.filter((target) => target.availableVersion !== undefined);
|
|
65
|
+
if (updates.length === 0)
|
|
66
|
+
return plugin.version ? `版本 ${plugin.version}` : '版本待确认';
|
|
67
|
+
const changes = [...new Set(updates.map((target) => `${target.installedVersion ?? '待确认'} → ${target.availableVersion}`))];
|
|
68
|
+
return changes.length === 1
|
|
69
|
+
? changes[0]
|
|
70
|
+
: `${updates.length} 个位置版本不同(Enter 查看)`;
|
|
71
|
+
}
|
|
72
|
+
function pluginPlacementConclusion(placement) {
|
|
73
|
+
if (placement.state === 'notInstalled')
|
|
74
|
+
return { text: '未添加', status: 'muted' };
|
|
75
|
+
if (placement.state === 'blocked')
|
|
76
|
+
return { text: '无法添加:输出位置有冲突', status: 'error' };
|
|
77
|
+
if (placement.state === 'scopeConflict')
|
|
78
|
+
return { text: '使用位置冲突,需要选择一个位置', status: 'warning' };
|
|
79
|
+
const location = placement.desiredScopes?.map(scopeLabel).join('、') || scopeLabel(placement.scope);
|
|
80
|
+
if (placement.blockingReason === 'artifactCollision' || placement.actualState === 'blocked') {
|
|
81
|
+
return { text: `${location}暂时不可用`, status: 'error' };
|
|
82
|
+
}
|
|
83
|
+
if (placement.actualState === 'needsSync' || placement.health === 'incomplete') {
|
|
84
|
+
return { text: `${location}需要同步`, status: 'warning' };
|
|
85
|
+
}
|
|
86
|
+
if (placement.health === 'error')
|
|
87
|
+
return { text: `${location}状态未知`, status: 'warning' };
|
|
88
|
+
return { text: `${location}可用`, status: 'success' };
|
|
89
|
+
}
|
|
71
90
|
function isCharacterInputMode(route, searchMode) {
|
|
72
91
|
if (routeDefinition(route).keymap !== 'menuOrTextInput')
|
|
73
92
|
return false;
|
|
@@ -246,12 +265,26 @@ function pluginPlacementSummary(plugin) {
|
|
|
246
265
|
: placement.actualState === 'blocked' ? '已阻断'
|
|
247
266
|
: placement.health === 'ready' ? '可用'
|
|
248
267
|
: placement.health === 'incomplete' ? '需要处理' : '不可用';
|
|
249
|
-
const desired = placement.desiredScopes?.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
268
|
+
const desired = placement.desiredScopes?.map(scopeLabel).join('+') || '未保存';
|
|
269
|
+
const actual = placement.actualScopes?.map(scopeLabel).join('+') || '未启用';
|
|
270
|
+
return placement.desiredScopes?.length
|
|
271
|
+
? `${label} · 已保存 ${desired} · 当前 ${actual} · ${health}`
|
|
272
|
+
: `${label} · 当前 ${actual} · ${health}`;
|
|
253
273
|
}).join(' | ') || '没有可用平台';
|
|
254
274
|
}
|
|
275
|
+
function issueMatchesTarget(issue, pluginId, platform, scope) {
|
|
276
|
+
if (!platform && !scope && issue.pluginId === pluginId)
|
|
277
|
+
return true;
|
|
278
|
+
const matches = [
|
|
279
|
+
issue.rootEntity,
|
|
280
|
+
...(issue.affectedTargets ?? []),
|
|
281
|
+
...(issue.pluginId && issue.platform && issue.scope
|
|
282
|
+
? [{ pluginId: issue.pluginId, platform: issue.platform, scope: issue.scope }]
|
|
283
|
+
: []),
|
|
284
|
+
].filter((target) => target !== undefined && target.pluginId === pluginId);
|
|
285
|
+
return matches.some((target) => (!platform || target.platform === platform)
|
|
286
|
+
&& (!scope || target.scope === scope));
|
|
287
|
+
}
|
|
255
288
|
function issueGroupLabel(group) {
|
|
256
289
|
return {
|
|
257
290
|
sourceRemoved: '来源已移除',
|
|
@@ -290,6 +323,57 @@ function moveIndex(count, current, direction) {
|
|
|
290
323
|
return 0;
|
|
291
324
|
return (current + direction + count) % count;
|
|
292
325
|
}
|
|
326
|
+
function dedupeActions(actions) {
|
|
327
|
+
const seen = new Set();
|
|
328
|
+
return actions.filter((action) => {
|
|
329
|
+
const id = action.actionId ?? action.label;
|
|
330
|
+
if (seen.has(id))
|
|
331
|
+
return false;
|
|
332
|
+
seen.add(id);
|
|
333
|
+
return true;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function wrapConsoleText(value, columns) {
|
|
337
|
+
const width = Math.max(1, columns);
|
|
338
|
+
const graphemes = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(value)]
|
|
339
|
+
.map((part) => part.segment);
|
|
340
|
+
const lines = [];
|
|
341
|
+
let line = '';
|
|
342
|
+
let lineWidth = 0;
|
|
343
|
+
for (const grapheme of graphemes) {
|
|
344
|
+
const graphemeWidth = stringWidth(grapheme);
|
|
345
|
+
if (line && lineWidth + graphemeWidth > width) {
|
|
346
|
+
lines.push(line);
|
|
347
|
+
line = '';
|
|
348
|
+
lineWidth = 0;
|
|
349
|
+
}
|
|
350
|
+
line += grapheme;
|
|
351
|
+
lineWidth += graphemeWidth;
|
|
352
|
+
}
|
|
353
|
+
if (line || lines.length === 0)
|
|
354
|
+
lines.push(line);
|
|
355
|
+
return lines;
|
|
356
|
+
}
|
|
357
|
+
function operationRiskWarnings(review) {
|
|
358
|
+
return [...new Set([
|
|
359
|
+
...(review.sharedImpact.warning ? [review.sharedImpact.warning] : []),
|
|
360
|
+
...review.changes.filter((change) => change.kind === 'warning' || change.kind === 'remove')
|
|
361
|
+
.map((change) => `${change.title}${change.description ? `:${change.description}` : ''}`),
|
|
362
|
+
...review.changes.filter((change) => change.group === 'preserved')
|
|
363
|
+
.map((change) => `保留:${change.title}${change.description ? `:${change.description}` : ''}`),
|
|
364
|
+
])];
|
|
365
|
+
}
|
|
366
|
+
function operationRiskLines(review, columns) {
|
|
367
|
+
const summaries = [
|
|
368
|
+
`操作:${review.title} · ${review.subject.name}`,
|
|
369
|
+
...(review.confirmationDetails ?? []),
|
|
370
|
+
...operationRiskWarnings(review).map((warning) => `! ${warning}`),
|
|
371
|
+
];
|
|
372
|
+
return summaries.flatMap((summary) => {
|
|
373
|
+
const lines = wrapConsoleText(summary, Math.max(1, columns - 2));
|
|
374
|
+
return lines.map((line, index) => index === 0 ? line : ` ${line}`);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
293
377
|
function actionViewport(actions, compact, focus, requestedOffset, lineBudget) {
|
|
294
378
|
return rowViewport({
|
|
295
379
|
rowHeights: actions.map((action) => compact || (!action.description && !action.disabled) ? 1 : 2),
|
|
@@ -734,8 +818,15 @@ function placementStatus(placement) {
|
|
|
734
818
|
if (placement.blockingReason === 'artifactCollision') {
|
|
735
819
|
return { value: '输出冲突', status: 'error', description: '只允许查看或安全移除受管内容' };
|
|
736
820
|
}
|
|
821
|
+
const current = placement.actualScopes?.map(scopeLabel).join('+') || '未启用';
|
|
822
|
+
const state = placement.actualState === 'needsSync' ? '需要同步'
|
|
823
|
+
: placement.actualState === 'blocked' ? '已阻断'
|
|
824
|
+
: placement.health === 'ready' ? '可用'
|
|
825
|
+
: placement.health === 'incomplete' ? '需要处理' : '不可用';
|
|
737
826
|
return {
|
|
738
|
-
value:
|
|
827
|
+
value: placement.desiredScopes?.length
|
|
828
|
+
? `已保存 ${placement.desiredScopes.map(scopeLabel).join('+')} · 当前 ${current} · ${state}`
|
|
829
|
+
: `当前 ${current} · ${state}`,
|
|
739
830
|
status: placement.actualState === 'blocked' ? 'error' : placement.health === 'ready' ? 'success' : placement.health === 'incomplete' ? 'warning' : 'error',
|
|
740
831
|
description: placement.actualState === 'needsSync' ? '已保存的使用设置与当前安装不同;可安全同步'
|
|
741
832
|
: placement.actualState === 'blocked' ? '当前有安全阻断;查看恢复路径'
|
|
@@ -761,11 +852,8 @@ function dependencyPlacementLabel(item) {
|
|
|
761
852
|
if (entry.state !== 'installed')
|
|
762
853
|
return '';
|
|
763
854
|
const desired = entry.desiredScopes?.map(scopeLabel).join('+') || scopeLabel(entry.scope);
|
|
764
|
-
const actualScopes = [
|
|
765
|
-
|
|
766
|
-
...(entry.activation?.project ? ['当前项目'] : []),
|
|
767
|
-
];
|
|
768
|
-
const actual = actualScopes.join('+') || scopeLabel(entry.scope);
|
|
855
|
+
const actualScopes = entry.actualScopes?.map(scopeLabel) ?? [];
|
|
856
|
+
const actual = actualScopes.join('+') || '未启用';
|
|
769
857
|
const state = entry.actualState === 'needsSync' ? '需同步'
|
|
770
858
|
: entry.actualState === 'blocked' ? '已阻断'
|
|
771
859
|
: entry.actualState === 'ready' ? '可用' : aggregate.value;
|
|
@@ -792,6 +880,12 @@ function dependencyRelationDescription(item) {
|
|
|
792
880
|
const requiredBy = item.requiredBy.map((entry) => entry.displayName).join('、');
|
|
793
881
|
return `${item.relation === 'direct' ? '直接依赖' : '间接依赖'}${requiredBy ? ` · ${requiredBy} 需要它` : ''}`;
|
|
794
882
|
}
|
|
883
|
+
function dependencyReferenceUsageLabel(reference, consumerName) {
|
|
884
|
+
const location = reference.consumer.scope === 'user'
|
|
885
|
+
? '所有项目'
|
|
886
|
+
: reference.consumer.project === 'current' ? '当前项目' : '其他项目';
|
|
887
|
+
return `${consumerName}在${location}的${platformLabel(reference.consumer.platform)}中使用它`;
|
|
888
|
+
}
|
|
795
889
|
function dependencySummaryText(summary) {
|
|
796
890
|
return `${summary.directCount} 个直接 · ${summary.totalCount} 个总计`;
|
|
797
891
|
}
|
|
@@ -836,6 +930,11 @@ function operationTargetStateLabel(state) {
|
|
|
836
930
|
function reviewSymbol(change) {
|
|
837
931
|
return { add: '+', change: '~', remove: '−', keep: '✓', warning: '!' }[change.kind];
|
|
838
932
|
}
|
|
933
|
+
function isTechnicalReviewGroup(group) {
|
|
934
|
+
return group === 'projectIntent' || group === 'userIntent' || group === 'sharedCodexNative'
|
|
935
|
+
|| group === 'userActivation' || group === 'projectActivation'
|
|
936
|
+
|| group === 'awExtensions' || group === 'runtime';
|
|
937
|
+
}
|
|
839
938
|
function currentPlugin(controller, route) {
|
|
840
939
|
return 'pluginId' in route ? controller.getPluginOverview(route.pluginId) : undefined;
|
|
841
940
|
}
|
|
@@ -862,6 +961,8 @@ export function ConsoleAppV4(props) {
|
|
|
862
961
|
const [snapshot, setSnapshot] = useState(props.controller.snapshot);
|
|
863
962
|
const [overlay, setOverlay] = useState();
|
|
864
963
|
const [overlayFocus, setOverlayFocus] = useState(0);
|
|
964
|
+
const [operationConfirmation, setOperationConfirmation] = useState();
|
|
965
|
+
const [sourceTransportOverlay, setSourceTransportOverlay] = useState();
|
|
865
966
|
const [discardForExit, setDiscardForExit] = useState(false);
|
|
866
967
|
const [discardForSessionNavigation, setDiscardForSessionNavigation] = useState();
|
|
867
968
|
const [discardDraftConfirmation, setDiscardDraftConfirmation] = useState();
|
|
@@ -884,6 +985,7 @@ export function ConsoleAppV4(props) {
|
|
|
884
985
|
const [configLayerPages, setConfigLayerPages] = useState({});
|
|
885
986
|
const [configSaveImpact, setConfigSaveImpact] = useState();
|
|
886
987
|
const [configSaveImpactError, setConfigSaveImpactError] = useState(false);
|
|
988
|
+
const [configSaveImpactAttempt, setConfigSaveImpactAttempt] = useState(0);
|
|
887
989
|
const [draftUpdates, setDraftUpdates] = useState({});
|
|
888
990
|
const [draftRemoves, setDraftRemoves] = useState([]);
|
|
889
991
|
const [objectDrafts, setObjectDrafts] = useState({});
|
|
@@ -929,7 +1031,6 @@ export function ConsoleAppV4(props) {
|
|
|
929
1031
|
const [draftOwner, setDraftOwner] = useState();
|
|
930
1032
|
const [pendingPresetId, setPendingPresetId] = useState();
|
|
931
1033
|
const [pendingLayerFieldKey, setPendingLayerFieldKey] = useState();
|
|
932
|
-
const [confirmedSharedReviewToken, setConfirmedSharedReviewToken] = useState();
|
|
933
1034
|
const dynamicMenuFocus = useRef(undefined);
|
|
934
1035
|
const dependencyGraphContexts = useRef(new Map());
|
|
935
1036
|
const externalDraftRevision = useRef({ config: 0, source: 0, batch: 0, catalog: 0 });
|
|
@@ -1429,25 +1530,15 @@ export function ConsoleAppV4(props) {
|
|
|
1429
1530
|
count = snapshot.operationHistorySummaries.length + 1;
|
|
1430
1531
|
}
|
|
1431
1532
|
else if (route.name === 'pluginList') {
|
|
1432
|
-
const
|
|
1433
|
-
mode: route.mode,
|
|
1434
|
-
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
1435
|
-
...(query ? { query } : {}),
|
|
1436
|
-
installFilter,
|
|
1437
|
-
}).length;
|
|
1438
|
-
const listed = new Set(props.controller.listPlugins({
|
|
1533
|
+
const items = props.controller.listPlugins({
|
|
1439
1534
|
mode: route.mode,
|
|
1440
1535
|
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
1441
1536
|
...(query ? { query } : {}),
|
|
1442
1537
|
installFilter,
|
|
1443
|
-
})
|
|
1444
|
-
const
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
+ Number(route.mode === 'installed' && props.controller.listCurrentProjectIntentRoots().length > 0)
|
|
1448
|
-
+ Number(route.mode === 'installed' && props.controller.listPluginWriteTaskCandidates('restore')
|
|
1449
|
-
.some((candidate) => candidate.target.scope === 'project')) : 0;
|
|
1450
|
-
count = itemCount + menuCount || 1;
|
|
1538
|
+
});
|
|
1539
|
+
const sourceUnavailable = route.sourceId !== undefined
|
|
1540
|
+
&& props.controller.getSourceOverview(route.sourceId).health?.state === 'unavailable';
|
|
1541
|
+
count = pluginListRowIds(route.mode, items, query, sourceUnavailable).length || 1;
|
|
1451
1542
|
}
|
|
1452
1543
|
if (count === undefined)
|
|
1453
1544
|
return;
|
|
@@ -1463,6 +1554,61 @@ export function ConsoleAppV4(props) {
|
|
|
1463
1554
|
route.name === 'pluginList' ? route.sourceId : '',
|
|
1464
1555
|
snapshot.viewModel,
|
|
1465
1556
|
]);
|
|
1557
|
+
useEffect(() => {
|
|
1558
|
+
if (route.name !== 'pluginWriteTask' || route.goal !== 'update')
|
|
1559
|
+
return;
|
|
1560
|
+
if (snapshot.router.current.context.autoPreparedUpdate)
|
|
1561
|
+
return;
|
|
1562
|
+
const targetKeys = route.targetKeys;
|
|
1563
|
+
const versionEvidence = route.versionEvidence;
|
|
1564
|
+
if (!targetKeys || targetKeys.length === 0 || !versionEvidence)
|
|
1565
|
+
return;
|
|
1566
|
+
const allCandidates = props.controller.listPluginWriteTaskCandidates('update');
|
|
1567
|
+
const retainedTargetKeys = new Set(targetKeys);
|
|
1568
|
+
const frozenPluginIds = new Set(route.recommendedPluginIds
|
|
1569
|
+
?? targetKeys.map((targetKey) => targetKey.split('\0')[0]).filter(Boolean));
|
|
1570
|
+
const currentFrozenCandidates = allCandidates.filter((candidate) => frozenPluginIds.has(candidate.target.pluginId));
|
|
1571
|
+
if (currentFrozenCandidates.length !== targetKeys.length
|
|
1572
|
+
|| currentFrozenCandidates.some((candidate) => !retainedTargetKeys.has(pluginWriteTaskTargetKey(candidate))))
|
|
1573
|
+
return;
|
|
1574
|
+
const currentByKey = new Map(allCandidates.map((candidate) => [
|
|
1575
|
+
pluginWriteTaskTargetKey(candidate), candidate,
|
|
1576
|
+
]));
|
|
1577
|
+
const candidates = targetKeys.flatMap((targetKey) => {
|
|
1578
|
+
const candidate = currentByKey.get(targetKey);
|
|
1579
|
+
return candidate ? [candidate] : [];
|
|
1580
|
+
});
|
|
1581
|
+
const representedPluginIds = new Set(candidates.map((candidate) => candidate.target.pluginId));
|
|
1582
|
+
if (candidates.length !== targetKeys.length
|
|
1583
|
+
|| candidates.some((candidate) => !candidate.eligible
|
|
1584
|
+
|| !frozenPluginIds.has(candidate.target.pluginId))
|
|
1585
|
+
|| [...frozenPluginIds].some((pluginId) => !representedPluginIds.has(pluginId)))
|
|
1586
|
+
return;
|
|
1587
|
+
const currentVersionEvidence = Object.assign({}, ...[...frozenPluginIds].map((pluginId) => {
|
|
1588
|
+
const candidatePlugin = props.controller.getPluginOverview(pluginId);
|
|
1589
|
+
return candidatePlugin ? pluginWriteTaskVersionEvidence(candidatePlugin, candidates.filter((candidate) => candidate.target.pluginId === pluginId)) : {};
|
|
1590
|
+
}));
|
|
1591
|
+
if (Object.keys(currentVersionEvidence).length !== targetKeys.length
|
|
1592
|
+
|| Object.entries(currentVersionEvidence).some(([key, version]) => versionEvidence[key] !== version)
|
|
1593
|
+
|| Object.keys(versionEvidence).some((key) => currentVersionEvidence[key] === undefined))
|
|
1594
|
+
return;
|
|
1595
|
+
props.controller.updatePageContext({ autoPreparedUpdate: true });
|
|
1596
|
+
beginReview({
|
|
1597
|
+
kind: 'pluginWriteTask',
|
|
1598
|
+
goal: 'update',
|
|
1599
|
+
targets: candidates.map((candidate) => candidate.target),
|
|
1600
|
+
executionPolicy: 'all',
|
|
1601
|
+
}, props.controller.pluginWriteTaskReturnRoute(route));
|
|
1602
|
+
}, [
|
|
1603
|
+
props.controller,
|
|
1604
|
+
route.name,
|
|
1605
|
+
route.name === 'pluginWriteTask' ? route.goal : '',
|
|
1606
|
+
route.name === 'pluginWriteTask' ? route.targetKeys?.join('\0') ?? '' : '',
|
|
1607
|
+
route.name === 'pluginWriteTask' ? route.recommendedPluginIds?.join('\0') ?? '' : '',
|
|
1608
|
+
route.name === 'pluginWriteTask' ? JSON.stringify(route.versionEvidence ?? {}) : '',
|
|
1609
|
+
snapshot.router.current.context.autoPreparedUpdate,
|
|
1610
|
+
snapshot.viewModel,
|
|
1611
|
+
]);
|
|
1466
1612
|
useEffect(() => {
|
|
1467
1613
|
if (route.name !== 'operationReportPreview')
|
|
1468
1614
|
return;
|
|
@@ -1654,6 +1800,7 @@ export function ConsoleAppV4(props) {
|
|
|
1654
1800
|
|| Boolean(route.name === 'issueDetail' && issue
|
|
1655
1801
|
&& issue.diagnostic.technical)
|
|
1656
1802
|
|| Boolean(route.name === 'pluginDependencies' && plugin)
|
|
1803
|
+
|| Boolean(route.name === 'pluginDependencyNode' && plugin)
|
|
1657
1804
|
|| portableVariantCandidates.length > 0
|
|
1658
1805
|
|| Boolean(route.name === 'portableDependencies' && plugin
|
|
1659
1806
|
&& props.controller.getPluginDependencyOverview(plugin.pluginId)?.dependencies.length)
|
|
@@ -1675,6 +1822,7 @@ export function ConsoleAppV4(props) {
|
|
|
1675
1822
|
let help = '↑↓ 移动 · Enter 选择 · ? 帮助 · q 退出';
|
|
1676
1823
|
let content;
|
|
1677
1824
|
let actions = [];
|
|
1825
|
+
let referenceDetailPagination;
|
|
1678
1826
|
let pluginRows = [];
|
|
1679
1827
|
let visibleConfigTabs = [];
|
|
1680
1828
|
let activeConfigTabId;
|
|
@@ -1713,16 +1861,27 @@ export function ConsoleAppV4(props) {
|
|
|
1713
1861
|
const base = policy.mode === 'compact' ? compactRows : standardRows;
|
|
1714
1862
|
return base + Math.max(0, size.rows - baseline);
|
|
1715
1863
|
};
|
|
1716
|
-
const
|
|
1717
|
-
if (items.length === 0 ||
|
|
1718
|
-
return
|
|
1864
|
+
const pluginListRowIds = (mode, items, queryValue = query, sourceUnavailable = false) => {
|
|
1865
|
+
if (items.length === 0 || sourceUnavailable)
|
|
1866
|
+
return [];
|
|
1867
|
+
if (queryValue)
|
|
1868
|
+
return [...items.map((item) => item.pluginId), '@menu:batch'];
|
|
1719
1869
|
const listed = new Set(items.map((item) => item.pluginId));
|
|
1720
|
-
return
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1870
|
+
return [
|
|
1871
|
+
...(!queryValue && mode === 'installed'
|
|
1872
|
+
&& props.controller.listPluginWriteTaskCandidates('update')
|
|
1873
|
+
.some((candidate) => candidate.eligible && listed.has(candidate.target.pluginId))
|
|
1874
|
+
? ['@menu:update'] : []),
|
|
1875
|
+
...(!queryValue && mode === 'installed'
|
|
1876
|
+
&& props.controller.listCurrentProjectIntentRoots().length > 0
|
|
1877
|
+
? ['@menu:restore-project'] : []),
|
|
1878
|
+
...(!queryValue && mode === 'installed'
|
|
1879
|
+
&& props.controller.listPluginWriteTaskCandidates('restore')
|
|
1880
|
+
.some((candidate) => candidate.target.scope === 'project')
|
|
1881
|
+
? ['@menu:repair-project'] : []),
|
|
1882
|
+
'@menu:batch',
|
|
1883
|
+
...items.map((item) => item.pluginId),
|
|
1884
|
+
];
|
|
1726
1885
|
};
|
|
1727
1886
|
const go = (next) => {
|
|
1728
1887
|
props.controller.updatePageContext({
|
|
@@ -1758,13 +1917,13 @@ export function ConsoleAppV4(props) {
|
|
|
1758
1917
|
...(restored.query ? { query: restored.query } : {}),
|
|
1759
1918
|
installFilter: restored.installFilter ?? 'all',
|
|
1760
1919
|
});
|
|
1761
|
-
|
|
1762
|
-
const
|
|
1763
|
-
?
|
|
1764
|
-
|
|
1765
|
-
restoredFocus =
|
|
1766
|
-
?
|
|
1767
|
-
: Math.min(restoredFocus, Math.max(0,
|
|
1920
|
+
const rowIds = pluginListRowIds(restoredFrame.route.mode, items, restored.query ?? '');
|
|
1921
|
+
const restoredEntityIndex = restored.focusedEntityId
|
|
1922
|
+
? rowIds.indexOf(restored.focusedEntityId) : -1;
|
|
1923
|
+
itemCount = rowIds.length;
|
|
1924
|
+
restoredFocus = restoredEntityIndex >= 0
|
|
1925
|
+
? restoredEntityIndex
|
|
1926
|
+
: Math.min(restoredFocus, Math.max(0, rowIds.length - 1));
|
|
1768
1927
|
}
|
|
1769
1928
|
else if (restoredFrame.route.name === 'sourceList') {
|
|
1770
1929
|
const sources = props.controller.listSources();
|
|
@@ -1802,6 +1961,20 @@ export function ConsoleAppV4(props) {
|
|
|
1802
1961
|
setShowTechnical(false);
|
|
1803
1962
|
setActionError(undefined);
|
|
1804
1963
|
};
|
|
1964
|
+
const moveBatchStage = (stage) => {
|
|
1965
|
+
const restored = batchDraft?.stageView?.[stage];
|
|
1966
|
+
setBatchDraft((current) => current ? {
|
|
1967
|
+
...current,
|
|
1968
|
+
stage,
|
|
1969
|
+
stageView: {
|
|
1970
|
+
...current.stageView,
|
|
1971
|
+
[current.stage]: { focusIndex: focus, scrollOffset },
|
|
1972
|
+
},
|
|
1973
|
+
} : current);
|
|
1974
|
+
setFocus(restored?.focusIndex ?? 0);
|
|
1975
|
+
setScrollOffset(restored?.scrollOffset ?? 0);
|
|
1976
|
+
setActionError(undefined);
|
|
1977
|
+
};
|
|
1805
1978
|
const back = () => {
|
|
1806
1979
|
if (route.name === 'sourceEdit'
|
|
1807
1980
|
&& sourceDraft
|
|
@@ -1929,7 +2102,9 @@ export function ConsoleAppV4(props) {
|
|
|
1929
2102
|
};
|
|
1930
2103
|
const batchCandidates = (draft) => {
|
|
1931
2104
|
const mode = draft.mode;
|
|
1932
|
-
const
|
|
2105
|
+
const frozenPluginIds = draft.frozenPluginIds ? new Set(draft.frozenPluginIds) : undefined;
|
|
2106
|
+
const all = props.controller.listPlugins({ mode })
|
|
2107
|
+
.filter((candidate) => !frozenPluginIds || frozenPluginIds.has(candidate.pluginId));
|
|
1933
2108
|
if (draft.showSelected) {
|
|
1934
2109
|
const byId = new Map(all.map((candidate) => [candidate.pluginId, candidate]));
|
|
1935
2110
|
return draft.selectedPluginIds.map((pluginId) => ({
|
|
@@ -1942,17 +2117,24 @@ export function ConsoleAppV4(props) {
|
|
|
1942
2117
|
...(draft.sourceId ? { sourceId: draft.sourceId } : {}),
|
|
1943
2118
|
...(draft.query ? { query: draft.query } : {}),
|
|
1944
2119
|
installFilter: draft.installFilter,
|
|
1945
|
-
}).
|
|
2120
|
+
}).filter((plugin) => !frozenPluginIds || frozenPluginIds.has(plugin.pluginId))
|
|
2121
|
+
.map((plugin) => ({ pluginId: plugin.pluginId, plugin }));
|
|
1946
2122
|
};
|
|
1947
2123
|
const batchCandidateBlockReason = (draft, candidate) => !candidate.plugin
|
|
1948
2124
|
? '插件已不在当前状态中,可取消选择'
|
|
1949
|
-
: candidate.plugin.configOnly
|
|
2125
|
+
: draft.mode === 'browse' && candidate.plugin.configOnly
|
|
1950
2126
|
? '来源中没有可信插件定义'
|
|
1951
|
-
: draft.mode === 'browse' &&
|
|
1952
|
-
? '
|
|
1953
|
-
:
|
|
2127
|
+
: draft.mode === 'browse' && candidate.plugin.sourceUnavailable
|
|
2128
|
+
? '插件来源不可用'
|
|
2129
|
+
: draft.mode === 'browse' && candidate.plugin.capabilityUnsupported
|
|
2130
|
+
? '当前 AW CLI 不支持插件所需功能'
|
|
2131
|
+
: draft.mode === 'browse' && !props.controller.pluginHasInstallableTarget(candidate.plugin.pluginId)
|
|
2132
|
+
? '没有可信且兼容的缺失位置'
|
|
2133
|
+
: undefined;
|
|
1954
2134
|
const invalidBatchSelection = (draft) => {
|
|
2135
|
+
const frozenPluginIds = draft.frozenPluginIds ? new Set(draft.frozenPluginIds) : undefined;
|
|
1955
2136
|
const byId = new Map(props.controller.listPlugins({ mode: draft.mode })
|
|
2137
|
+
.filter((plugin) => !frozenPluginIds || frozenPluginIds.has(plugin.pluginId))
|
|
1956
2138
|
.map((plugin) => [plugin.pluginId, plugin]));
|
|
1957
2139
|
return draft.selectedPluginIds.filter((pluginId) => batchCandidateBlockReason(draft, {
|
|
1958
2140
|
pluginId,
|
|
@@ -2017,10 +2199,68 @@ export function ConsoleAppV4(props) {
|
|
|
2017
2199
|
? { versionEvidence: Object.assign({}, ...[...selected].map((pluginId) => {
|
|
2018
2200
|
const candidatePlugin = props.controller.getPluginOverview(pluginId);
|
|
2019
2201
|
return candidatePlugin ? pluginWriteTaskVersionEvidence(candidatePlugin, candidates.filter((candidate) => candidate.target.pluginId === pluginId)) : {};
|
|
2020
|
-
}))
|
|
2202
|
+
})),
|
|
2203
|
+
...(batchDraft?.frozenPluginIds
|
|
2204
|
+
? { recommendedPluginIds: batchDraft.selectedPluginIds.slice() }
|
|
2205
|
+
: {}) }
|
|
2021
2206
|
: {}),
|
|
2022
2207
|
});
|
|
2023
2208
|
};
|
|
2209
|
+
const openRecommendedUpdateTask = (pluginIds) => {
|
|
2210
|
+
props.controller.updatePageContext({
|
|
2211
|
+
focusIndex: focus,
|
|
2212
|
+
scrollOffset,
|
|
2213
|
+
query,
|
|
2214
|
+
installFilter,
|
|
2215
|
+
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
2216
|
+
focusedEntityId: route.name === 'pluginList' ? pluginRows[focus]?.pluginId : undefined,
|
|
2217
|
+
});
|
|
2218
|
+
props.controller.beginPluginWriteBatch();
|
|
2219
|
+
const flowId = props.controller.snapshot.router.currentFlowId;
|
|
2220
|
+
if (!flowId)
|
|
2221
|
+
throw new Error('推荐更新流程缺少 flowId。');
|
|
2222
|
+
const frozenPluginIds = [...new Set(pluginIds)];
|
|
2223
|
+
setBatchDraft({
|
|
2224
|
+
flowId,
|
|
2225
|
+
recommendedGoal: 'update',
|
|
2226
|
+
mode: 'installed',
|
|
2227
|
+
stage: 'select',
|
|
2228
|
+
selection: 'selected',
|
|
2229
|
+
selectedPluginIds: frozenPluginIds.slice(),
|
|
2230
|
+
frozenPluginIds,
|
|
2231
|
+
platform: 'both',
|
|
2232
|
+
scope: 'project',
|
|
2233
|
+
...(route.name === 'pluginList' && route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
2234
|
+
query,
|
|
2235
|
+
installFilter,
|
|
2236
|
+
showSelected: false,
|
|
2237
|
+
});
|
|
2238
|
+
setDirty(true);
|
|
2239
|
+
const selected = new Set(frozenPluginIds);
|
|
2240
|
+
const candidates = props.controller.listPluginWriteTaskCandidates('update')
|
|
2241
|
+
.filter((candidate) => selected.has(candidate.target.pluginId));
|
|
2242
|
+
const first = candidates[0];
|
|
2243
|
+
if (!first) {
|
|
2244
|
+
setActionError('推荐插件没有经过版本证据确认的更新。');
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
const versionEvidence = Object.assign({}, ...frozenPluginIds.map((pluginId) => {
|
|
2248
|
+
const candidatePlugin = props.controller.getPluginOverview(pluginId);
|
|
2249
|
+
return candidatePlugin ? pluginWriteTaskVersionEvidence(candidatePlugin, candidates.filter((candidate) => candidate.target.pluginId === pluginId)) : {};
|
|
2250
|
+
}));
|
|
2251
|
+
props.controller.push({
|
|
2252
|
+
name: 'pluginWriteTask',
|
|
2253
|
+
pluginId: first.target.pluginId,
|
|
2254
|
+
goal: 'update',
|
|
2255
|
+
targetKeys: candidates.map(pluginWriteTaskTargetKey),
|
|
2256
|
+
versionEvidence,
|
|
2257
|
+
recommendedPluginIds: frozenPluginIds,
|
|
2258
|
+
batchOrigin: { flowId, owner: 'batch-write' },
|
|
2259
|
+
});
|
|
2260
|
+
setFocus(0);
|
|
2261
|
+
setScrollOffset(0);
|
|
2262
|
+
setSearchFocus(false);
|
|
2263
|
+
};
|
|
2024
2264
|
const continueInstallAfterTargets = async (candidate, targets) => {
|
|
2025
2265
|
if (props.controller.snapshot.installPlacementDraft?.pluginId !== candidate.pluginId) {
|
|
2026
2266
|
props.controller.beginInstallFlow(candidate.pluginId, scope);
|
|
@@ -2145,12 +2385,29 @@ export function ConsoleAppV4(props) {
|
|
|
2145
2385
|
};
|
|
2146
2386
|
const issueWriteIntent = (selectedIssue, action) => {
|
|
2147
2387
|
const pluginId = action.pluginId ?? selectedIssue.pluginId;
|
|
2148
|
-
if (!pluginId
|
|
2388
|
+
if (!pluginId)
|
|
2149
2389
|
return undefined;
|
|
2150
2390
|
const selectedScope = action.id === 'keepUserScope'
|
|
2151
2391
|
? 'project'
|
|
2152
2392
|
: action.id === 'keepProjectScope' ? 'user' : action.scope ?? selectedIssue.scope;
|
|
2153
2393
|
const selectedPlatform = action.platform ?? selectedIssue.platform;
|
|
2394
|
+
const dependencyIssue = selectedIssue.category === 'dependencyMissing'
|
|
2395
|
+
|| selectedIssue.category === 'dependencyNotReady';
|
|
2396
|
+
const dependencyTargetAction = action.id === 'installDependency'
|
|
2397
|
+
|| action.id === 'reinstallDependency';
|
|
2398
|
+
const sourceTargetCleanup = selectedIssue.entity.kind === 'source'
|
|
2399
|
+
&& action.id === 'cleanup'
|
|
2400
|
+
&& action.pluginId !== undefined
|
|
2401
|
+
&& action.platform !== undefined
|
|
2402
|
+
&& action.scope !== undefined;
|
|
2403
|
+
const targetSpecificOrigin = (dependencyIssue && !dependencyTargetAction)
|
|
2404
|
+
|| sourceTargetCleanup;
|
|
2405
|
+
const consumerPluginId = targetSpecificOrigin
|
|
2406
|
+
? pluginId : selectedIssue.pluginId;
|
|
2407
|
+
const consumerPlatform = targetSpecificOrigin
|
|
2408
|
+
? selectedPlatform : selectedIssue.platform;
|
|
2409
|
+
const consumerScope = targetSpecificOrigin
|
|
2410
|
+
? selectedScope : selectedIssue.scope;
|
|
2154
2411
|
const writeAction = action.writeAction ?? (action.id === 'sync'
|
|
2155
2412
|
? 'sync'
|
|
2156
2413
|
: action.id === 'refreshAndSync'
|
|
@@ -2180,16 +2437,20 @@ export function ConsoleAppV4(props) {
|
|
|
2180
2437
|
issueKey: userIssueKey(selectedIssue),
|
|
2181
2438
|
category: selectedIssue.category,
|
|
2182
2439
|
actionId: action.id,
|
|
2183
|
-
...(
|
|
2440
|
+
...(targetSpecificOrigin
|
|
2184
2441
|
? {
|
|
2185
|
-
consumerPluginId
|
|
2186
|
-
consumerSourceIdentity:
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2442
|
+
consumerPluginId,
|
|
2443
|
+
consumerSourceIdentity: consumerPluginId
|
|
2444
|
+
? props.controller.getPluginSourceIdentity(consumerPluginId)
|
|
2445
|
+
: selectedIssue.sourceIdentity,
|
|
2446
|
+
consumerPlatform,
|
|
2447
|
+
consumerScope,
|
|
2448
|
+
...(dependencyIssue ? {
|
|
2449
|
+
dependencyPluginId: selectedIssue.dependencyPluginId,
|
|
2450
|
+
dependencyPlatform: selectedIssue.dependencyPlatform,
|
|
2451
|
+
dependencyScope: selectedIssue.dependencyScope,
|
|
2452
|
+
dependencyProject: selectedIssue.dependencyProject,
|
|
2453
|
+
} : {}),
|
|
2193
2454
|
}
|
|
2194
2455
|
: {}),
|
|
2195
2456
|
},
|
|
@@ -2264,7 +2525,7 @@ export function ConsoleAppV4(props) {
|
|
|
2264
2525
|
},
|
|
2265
2526
|
...(draft.type === 'git' ? [
|
|
2266
2527
|
{ title: '版本引用', before: ref(current), after: draft.refPolicy === 'cliChannel' ? '跟随 AW CLI' : draft.ref, changed: ref(current) !== (draft.refPolicy === 'cliChannel' ? '跟随 AW CLI' : draft.ref) },
|
|
2267
|
-
{ title: 'Catalog 子目录', before: current?.type === 'git' ? current.subdir ?? '
|
|
2528
|
+
{ title: 'Catalog 子目录', before: current?.type === 'git' ? current.subdir ?? '仓库根目录' : undefined, after: draft.subdir || '仓库根目录', changed: current?.type !== 'git' || (current.subdir ?? '') !== draft.subdir },
|
|
2268
2529
|
] : []),
|
|
2269
2530
|
{ title: '自动检查更新', before: current ? current.autoCheck !== false ? '开启' : '关闭' : undefined, after: draft.autoCheck ? '开启' : '关闭', changed: !current || (current.autoCheck !== false) !== draft.autoCheck },
|
|
2270
2531
|
];
|
|
@@ -2737,7 +2998,17 @@ export function ConsoleAppV4(props) {
|
|
|
2737
2998
|
route.name === 'configSave' ? `${route.pluginId}:${route.layer.scope}` : '',
|
|
2738
2999
|
draftRemoves,
|
|
2739
3000
|
draftUpdates,
|
|
3001
|
+
configSaveImpactAttempt,
|
|
2740
3002
|
]);
|
|
3003
|
+
useEffect(() => {
|
|
3004
|
+
if (!operationConfirmation)
|
|
3005
|
+
return;
|
|
3006
|
+
if (route.name !== 'review'
|
|
3007
|
+
|| snapshot.activeReview?.reviewToken !== operationConfirmation.reviewToken) {
|
|
3008
|
+
setOperationConfirmation(undefined);
|
|
3009
|
+
setOverlayFocus(0);
|
|
3010
|
+
}
|
|
3011
|
+
}, [operationConfirmation, route.name, snapshot.activeReview?.reviewToken]);
|
|
2741
3012
|
if (snapshot.phase === 'error' && !isCatalogSourcesBootstrapFlow(snapshot)
|
|
2742
3013
|
&& !isOperationHistoryRoute(route)) {
|
|
2743
3014
|
const sourcesConfigInvalid = snapshot.loadError?.code === 'catalogSourcesConfigInvalid';
|
|
@@ -2781,9 +3052,7 @@ export function ConsoleAppV4(props) {
|
|
|
2781
3052
|
run: () => go({ name: 'sourceList' }),
|
|
2782
3053
|
},
|
|
2783
3054
|
];
|
|
2784
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusCard, { status: home.issueCount > 0 || home.unavailableSourceCount > 0 ? 'warning' : 'success', title: "\u5F53\u524D\u72B6\u6001", children: [
|
|
2785
|
-
? _jsx(StaticRow, { title: "\u6700\u8FD1\u6210\u529F\u8BFB\u53D6\u72B6\u6001", meta: new Date(snapshot.statusReadAtMs).toLocaleString('zh-CN', { hour12: false }), compact: true })
|
|
2786
|
-
: null, _jsx(ResultRow, { title: `${home.installedPlugins} 个插件可用`, status: "success" }), _jsx(ResultRow, { title: `${home.issueCount} 项需要处理`, status: home.issueCount > 0 ? 'warning' : 'muted' }), home.unavailableSourceCount > 0 ? _jsx(ResultRow, { title: `${home.unavailableSourceCount} 个来源不可用`, status: "error" }) : null, home.catalogCheckingCount > 0 ? _jsx(ResultRow, { title: `${home.catalogCheckingCount} 个插件来源正在检查`, status: "muted" }) : null, home.catalogUpdateCount > 0 ? _jsx(ResultRow, { title: `${home.catalogUpdateCount} 个插件来源有更新`, status: "warning" }) : null, home.pluginUpdateCount > 0 ? _jsx(ResultRow, { title: `${home.pluginUpdateCount} 个插件可更新${home.directPluginUpdateCount < home.pluginUpdateCount ? `;${home.directPluginUpdateCount} 个可直接确认` : ''}`, status: "warning" }) : null, home.catalogRecoveryCount > 0 ? _jsx(ResultRow, { title: `${home.catalogRecoveryCount} 个插件来源需要检查或恢复`, status: "warning" }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "task", compact: policy.mode === 'compact' })] });
|
|
3055
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusCard, { status: home.issueCount > 0 || home.unavailableSourceCount > 0 ? 'warning' : 'success', title: "\u5F53\u524D\u72B6\u6001", children: [_jsx(ResultRow, { title: `已添加 ${home.installedPlugins} 个插件`, status: "muted" }), _jsx(ResultRow, { title: `${home.issueCount} 项需要处理`, status: home.issueCount > 0 ? 'warning' : 'muted' }), home.unavailableSourceCount > 0 ? _jsx(ResultRow, { title: `${home.unavailableSourceCount} 个来源不可用`, status: "error" }) : null, home.catalogCheckingCount > 0 ? _jsx(ResultRow, { title: `${home.catalogCheckingCount} 个插件来源正在检查`, status: "muted" }) : null, home.catalogUpdateCount > 0 ? _jsx(ResultRow, { title: `${home.catalogUpdateCount} 个插件来源有更新`, status: "warning" }) : null, home.pluginUpdateCount > 0 ? _jsx(ResultRow, { title: `${home.pluginUpdateCount} 个插件可更新`, status: "warning" }) : null, home.catalogRecoveryCount > 0 ? _jsx(ResultRow, { title: `${home.catalogRecoveryCount} 个插件来源需要检查或恢复`, status: "warning" }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "task", compact: policy.mode === 'compact' })] });
|
|
2787
3056
|
}
|
|
2788
3057
|
else if (route.name === 'pluginList') {
|
|
2789
3058
|
const sourceId = route.sourceId ?? 'all';
|
|
@@ -2825,7 +3094,7 @@ export function ConsoleAppV4(props) {
|
|
|
2825
3094
|
const projectRestoreCandidates = props.controller.listPluginWriteTaskCandidates('restore')
|
|
2826
3095
|
.filter((candidate) => candidate.target.scope === 'project');
|
|
2827
3096
|
const projectIntentRoots = props.controller.listCurrentProjectIntentRoots();
|
|
2828
|
-
const menuRows =
|
|
3097
|
+
const menuRows = [
|
|
2829
3098
|
...(route.mode === 'installed' && recommendedUpdatePluginIds.length > 0 ? [{
|
|
2830
3099
|
pluginId: '@menu:update',
|
|
2831
3100
|
name: `更新 ${recommendedUpdatePluginIds.length} 个插件…`,
|
|
@@ -2834,7 +3103,7 @@ export function ConsoleAppV4(props) {
|
|
|
2834
3103
|
placementSummary: '',
|
|
2835
3104
|
statusLabel: '推荐',
|
|
2836
3105
|
status: 'warning',
|
|
2837
|
-
menuAction: () =>
|
|
3106
|
+
menuAction: () => openRecommendedUpdateTask(recommendedUpdatePluginIds),
|
|
2838
3107
|
}] : []),
|
|
2839
3108
|
...(route.mode === 'installed' && projectIntentRoots.length > 0 ? [{
|
|
2840
3109
|
pluginId: '@menu:restore-project',
|
|
@@ -2859,7 +3128,9 @@ export function ConsoleAppV4(props) {
|
|
|
2859
3128
|
}] : []),
|
|
2860
3129
|
{
|
|
2861
3130
|
pluginId: '@menu:batch',
|
|
2862
|
-
name:
|
|
3131
|
+
name: query
|
|
3132
|
+
? '选择多个当前结果…'
|
|
3133
|
+
: route.mode === 'browse' ? '选择多个插件添加…' : '选择多个…',
|
|
2863
3134
|
sourceLabel: '',
|
|
2864
3135
|
description: route.mode === 'browse'
|
|
2865
3136
|
? '先选择插件,再选择安装位置和平台'
|
|
@@ -2870,18 +3141,23 @@ export function ConsoleAppV4(props) {
|
|
|
2870
3141
|
menuAction: () => openBatchSelection(route.mode === 'browse' ? 'install' : undefined),
|
|
2871
3142
|
},
|
|
2872
3143
|
];
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
: item.
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
3144
|
+
const pluginRowsById = new Map([
|
|
3145
|
+
...menuRows.map((row) => [row.pluginId, row]),
|
|
3146
|
+
...items.map((item) => [item.pluginId, {
|
|
3147
|
+
pluginId: item.pluginId,
|
|
3148
|
+
name: item.displayName,
|
|
3149
|
+
sourceLabel: item.sourceLabel,
|
|
3150
|
+
description: item.configOnly
|
|
3151
|
+
? '来源当前没有这个插件;可以查看已保存设置或定点重置设置层。'
|
|
3152
|
+
: item.description,
|
|
3153
|
+
...(item.primaryAttention ? { attention: item.primaryAttention.message } : {}),
|
|
3154
|
+
placementSummary: pluginPlacementSummary(item),
|
|
3155
|
+
statusLabel: statusText(item),
|
|
3156
|
+
status: pluginRowStatus(item),
|
|
3157
|
+
}]),
|
|
3158
|
+
]);
|
|
3159
|
+
pluginRows = pluginListRowIds(route.mode, items, query, sourceError)
|
|
3160
|
+
.flatMap((rowId) => pluginRowsById.get(rowId) ?? []);
|
|
2885
3161
|
if (route.mode === 'installed' && installFilter === 'updateAvailable'
|
|
2886
3162
|
&& items.length === 0 && !sourceError) {
|
|
2887
3163
|
title = '没有可更新插件';
|
|
@@ -2895,6 +3171,32 @@ export function ConsoleAppV4(props) {
|
|
|
2895
3171
|
},
|
|
2896
3172
|
}];
|
|
2897
3173
|
}
|
|
3174
|
+
else if (query && items.length === 0 && !sourceError) {
|
|
3175
|
+
title = '搜索没有匹配插件';
|
|
3176
|
+
description = `“${query}”在当前来源和状态筛选中没有匹配项。`;
|
|
3177
|
+
actions = [{
|
|
3178
|
+
label: '清除搜索',
|
|
3179
|
+
description: '保留当前来源和状态筛选',
|
|
3180
|
+
run: () => {
|
|
3181
|
+
setQuery('');
|
|
3182
|
+
props.controller.updatePageContext({ query: '' });
|
|
3183
|
+
},
|
|
3184
|
+
}];
|
|
3185
|
+
}
|
|
3186
|
+
else if (sourceId !== 'all' && items.length === 0 && !sourceError) {
|
|
3187
|
+
title = route.mode === 'installed' ? '当前来源没有已添加插件' : '当前来源没有可显示插件';
|
|
3188
|
+
description = `来源 ${sourceId} 在当前状态筛选中没有匹配插件。`;
|
|
3189
|
+
actions = [{
|
|
3190
|
+
label: '查看全部来源',
|
|
3191
|
+
description: '保留当前状态筛选',
|
|
3192
|
+
run: () => {
|
|
3193
|
+
props.controller.replace({ name: 'pluginList', mode: route.mode });
|
|
3194
|
+
props.controller.updatePageContext({ sourceTabId: 'all' });
|
|
3195
|
+
setFocus(0);
|
|
3196
|
+
setScrollOffset(0);
|
|
3197
|
+
},
|
|
3198
|
+
}];
|
|
3199
|
+
}
|
|
2898
3200
|
else if (route.mode === 'installed' && items.length === 0 && !sourceError) {
|
|
2899
3201
|
title = '还没有添加插件';
|
|
2900
3202
|
description = `从已配置的 ${props.controller.listSources().length} 个来源中浏览可用能力。`;
|
|
@@ -2902,11 +3204,14 @@ export function ConsoleAppV4(props) {
|
|
|
2902
3204
|
}
|
|
2903
3205
|
else if (route.mode === 'browse' && items.length === 0 && !sourceError) {
|
|
2904
3206
|
actions = [{
|
|
2905
|
-
label: '
|
|
3207
|
+
label: installFilter === 'uninstalled' ? '查看全部插件' : '没有可显示插件',
|
|
2906
3208
|
description: installFilter === 'uninstalled'
|
|
2907
|
-
? '
|
|
2908
|
-
: '
|
|
2909
|
-
|
|
3209
|
+
? '当前没有整个插件都未安装的结果;保留部分已添加插件并查看其缺失位置'
|
|
3210
|
+
: '当前所有来源都没有可浏览的插件',
|
|
3211
|
+
...(installFilter === 'uninstalled' ? { run: () => {
|
|
3212
|
+
setInstallFilter('all');
|
|
3213
|
+
props.controller.updatePageContext({ installFilter: 'all' });
|
|
3214
|
+
} } : { disabled: '没有可打开的插件' }),
|
|
2910
3215
|
}];
|
|
2911
3216
|
}
|
|
2912
3217
|
const pluginListStaticRows = 3;
|
|
@@ -2929,7 +3234,7 @@ export function ConsoleAppV4(props) {
|
|
|
2929
3234
|
? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u72B6\u6001 ", _jsx(Text, { color: installFilter === 'all' ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: installFilter === 'all' ? '[全部]' : '全部' }), " ", _jsxs(Text, { color: installFilter === 'uninstalled' ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: [installFilter === 'uninstalled' ? '[未安装]' : '未安装', " ", uninstalledCount] }), " f \u5207\u6362"] })
|
|
2930
3235
|
: _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u72B6\u6001 ", _jsx(Text, { color: installFilter === 'all' ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: installFilter === 'all' ? '[全部]' : '全部' }), " ", _jsxs(Text, { color: installFilter === 'updateAvailable' ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: [installFilter === 'updateAvailable' ? '[可更新]' : '可更新', " ", updateCount] }), " f \u5207\u6362"] }), sourceError
|
|
2931
3236
|
? _jsx(Callout, { status: "error", title: `${sourceId} 暂时无法提供插件`, description: "\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })
|
|
2932
|
-
: route.mode === 'installed' && items.length === 0
|
|
3237
|
+
: route.mode === 'installed' && items.length === 0 && !query && sourceId === 'all'
|
|
2933
3238
|
? installFilter === 'updateAvailable'
|
|
2934
3239
|
? _jsx(Callout, { title: "\u6CA1\u6709\u53EF\u66F4\u65B0\u63D2\u4EF6", description: "\u5F53\u524D\u7B5B\u9009\u8303\u56F4\u5185\u6CA1\u6709\u7ECF\u8FC7\u7248\u672C\u8BC1\u636E\u786E\u8BA4\u7684\u66F4\u65B0\u3002" })
|
|
2935
3240
|
: _jsx(Callout, { title: "\u5F00\u59CB\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u63D2\u4EF6", description: "\u63D2\u4EF6\u53EF\u4EE5\u5B89\u88C5\u5230 Codex\u3001Claude Code \u6216\u4E24\u8005\u3002" })
|
|
@@ -2959,11 +3264,17 @@ export function ConsoleAppV4(props) {
|
|
|
2959
3264
|
const candidateActions = candidates.map((candidate) => {
|
|
2960
3265
|
const blockReason = batchCandidateBlockReason(batchDraft, candidate);
|
|
2961
3266
|
const isSelected = selected.has(candidate.pluginId);
|
|
3267
|
+
const plugin = candidate.plugin;
|
|
2962
3268
|
return {
|
|
2963
|
-
label:
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
3269
|
+
label: plugin
|
|
3270
|
+
? `${plugin.displayName} · ${plugin.sourceLabel}`
|
|
3271
|
+
: `${candidate.pluginId}(已不可用,可取消选择)`,
|
|
3272
|
+
description: plugin
|
|
3273
|
+
? `${plugin.description} · ${blockReason
|
|
3274
|
+
? `${isSelected ? '可取消选择' : '不可选择'}:${blockReason}`
|
|
3275
|
+
: statusText(plugin)}`
|
|
3276
|
+
: `${isSelected ? '可取消选择' : '不可选择'}:${blockReason}`,
|
|
3277
|
+
meta: plugin ? batchPluginVersionSummary(plugin) : '版本待确认',
|
|
2967
3278
|
selected: isSelected,
|
|
2968
3279
|
pluginId: candidate.pluginId,
|
|
2969
3280
|
canSelectNew: blockReason === undefined,
|
|
@@ -2986,27 +3297,47 @@ export function ConsoleAppV4(props) {
|
|
|
2986
3297
|
setActionError(`${invalid.length} 个已选插件已不可用。请先在“查看全部已选”中取消选择。`);
|
|
2987
3298
|
return;
|
|
2988
3299
|
}
|
|
3300
|
+
if (batchDraft.recommendedGoal) {
|
|
3301
|
+
openSelectedPluginTask(batchDraft.recommendedGoal, batchDraft.selectedPluginIds);
|
|
3302
|
+
return;
|
|
3303
|
+
}
|
|
2989
3304
|
if (batchDraft.mode === 'browse') {
|
|
2990
3305
|
setBatchDraft((current) => current ? { ...current, action: 'install' } : current);
|
|
2991
3306
|
props.controller.push({ name: 'batchPlacement' });
|
|
3307
|
+
setFocus(0);
|
|
3308
|
+
setScrollOffset(0);
|
|
2992
3309
|
}
|
|
2993
3310
|
else {
|
|
2994
|
-
|
|
3311
|
+
moveBatchStage('task');
|
|
2995
3312
|
}
|
|
2996
|
-
setFocus(0);
|
|
2997
|
-
setScrollOffset(0);
|
|
2998
3313
|
};
|
|
2999
3314
|
const continueAction = {
|
|
3000
3315
|
label: `查看确认(${batchDraft.selectedPluginIds.length})`,
|
|
3001
|
-
description: batchDraft.selectedPluginIds.length > 0
|
|
3316
|
+
description: batchDraft.selectedPluginIds.length > 0
|
|
3317
|
+
? batchDraft.recommendedGoal ? '重新读取更新证据并建立 Review' : '继续选择任务和确认范围'
|
|
3318
|
+
: '先选择至少一个插件',
|
|
3319
|
+
disabled: batchDraft.selectedPluginIds.length === 0 ? '先选择至少一个插件' : undefined,
|
|
3002
3320
|
run: continueSelection,
|
|
3003
3321
|
};
|
|
3004
|
-
const
|
|
3322
|
+
const selectedSetAction = {
|
|
3323
|
+
label: batchDraft.showSelected ? '返回当前结果' : `查看全部已选(${batchDraft.selectedPluginIds.length})`,
|
|
3324
|
+
description: hiddenSelected > 0 ? `${hiddenSelected} 个选择在其他筛选中` : '包括其他筛选中的选择',
|
|
3325
|
+
run: () => {
|
|
3326
|
+
setBatchDraft((current) => current ? {
|
|
3327
|
+
...current,
|
|
3328
|
+
showSelected: !current.showSelected,
|
|
3329
|
+
showSelectionTools: false,
|
|
3330
|
+
} : current);
|
|
3331
|
+
setFocus(0);
|
|
3332
|
+
setScrollOffset(0);
|
|
3333
|
+
},
|
|
3334
|
+
};
|
|
3335
|
+
const utilityActions = batchDraft.showSelectionTools ? [
|
|
3005
3336
|
{
|
|
3006
|
-
label:
|
|
3007
|
-
description:
|
|
3337
|
+
label: '返回插件选择',
|
|
3338
|
+
description: '保留当前已选插件',
|
|
3008
3339
|
run: () => {
|
|
3009
|
-
setBatchDraft((current) => current ? { ...current,
|
|
3340
|
+
setBatchDraft((current) => current ? { ...current, showSelectionTools: false } : current);
|
|
3010
3341
|
setFocus(0);
|
|
3011
3342
|
setScrollOffset(0);
|
|
3012
3343
|
},
|
|
@@ -3031,20 +3362,40 @@ export function ConsoleAppV4(props) {
|
|
|
3031
3362
|
label: '清空全部已选',
|
|
3032
3363
|
run: () => setBatchDraft((current) => current ? { ...current, selectedPluginIds: [] } : current),
|
|
3033
3364
|
},
|
|
3365
|
+
] : [
|
|
3366
|
+
selectedSetAction,
|
|
3367
|
+
{
|
|
3368
|
+
label: '选择工具…',
|
|
3369
|
+
description: '全选、取消当前结果或清空全部选择',
|
|
3370
|
+
run: () => {
|
|
3371
|
+
setBatchDraft((current) => current ? { ...current, showSelectionTools: true } : current);
|
|
3372
|
+
setFocus(candidateActions.length + 1);
|
|
3373
|
+
setScrollOffset(Math.max(0, candidateActions.length));
|
|
3374
|
+
},
|
|
3375
|
+
},
|
|
3034
3376
|
];
|
|
3035
3377
|
actions = [continueAction, ...candidateActions, ...utilityActions];
|
|
3036
3378
|
const batchFocus = Math.min(focus, actions.length - 1);
|
|
3037
3379
|
context = `已选 ${batchDraft.selectedPluginIds.length} 个 · 显示 ${candidates.length} 个${hiddenSelected > 0 ? ` · 隐藏 ${hiddenSelected} 个` : ''}`;
|
|
3038
3380
|
help = '↑↓ 移动 · Space 选择 · Enter 查看/操作 · c 查看确认 · ←→ 来源 · / 搜索 · Esc 放弃';
|
|
3039
|
-
const
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3381
|
+
const viewport = rowViewport({
|
|
3382
|
+
rowHeights: [
|
|
3383
|
+
...candidateActions.map(() => 2),
|
|
3384
|
+
...utilityActions.map((action) => policy.mode === 'compact'
|
|
3385
|
+
? 1 : action.description || action.disabled ? 2 : 1),
|
|
3386
|
+
],
|
|
3387
|
+
focus: Math.max(0, batchFocus - 1),
|
|
3388
|
+
requestedOffset: Math.max(0, scrollOffset - 1),
|
|
3389
|
+
rowBudget: Math.max(1, pageActionBudget(4)),
|
|
3390
|
+
});
|
|
3391
|
+
const candidateOffset = Math.min(viewport.offset, candidateActions.length);
|
|
3392
|
+
const candidateLimit = Math.max(0, Math.min(candidateActions.length, viewport.offset + viewport.limit) - candidateOffset);
|
|
3393
|
+
const utilityOffset = Math.max(0, viewport.offset - candidateActions.length);
|
|
3394
|
+
const utilityLimit = Math.max(0, Math.min(utilityActions.length, viewport.offset + viewport.limit - candidateActions.length)
|
|
3395
|
+
- utilityOffset);
|
|
3043
3396
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SourceTabs, { controller: props.controller, current: batchDraft.showSelected ? 'all' : batchDraft.sourceId ?? 'all', compact: policy.mode === 'compact', columns: size.columns - 2, mode: batchDraft.mode, query: batchDraft.showSelected ? undefined : batchDraft.query || undefined, installFilter: batchDraft.showSelected ? 'all' : batchDraft.installFilter }), _jsxs(Text, { color: searchFocus ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["\u641C\u7D22 / ", searchFocus
|
|
3044
3397
|
? _jsx(InputDisplay, { value: batchDraft.query, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3045
|
-
: _jsxs(_Fragment, { children: [batchDraft.query || '搜索名称、用途或来源…', batchDraft.query ? ' / 编辑' : ''] })] }), _jsx(SemanticRows, { actions: [continueAction], focus: batchFocus, kind: "action", compact: true }),
|
|
3046
|
-
? _jsx(StaticRow, { title: `用途 · ${focusedCandidate.description}`, status: "info", compact: true })
|
|
3047
|
-
: null, _jsx(SemanticRows, { actions: scrollActions, focus: batchFocus - 1, kind: "toggle", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit })] });
|
|
3398
|
+
: _jsxs(_Fragment, { children: [batchDraft.query || '搜索名称、用途或来源…', batchDraft.query ? ' / 编辑' : ''] })] }), _jsx(SemanticRows, { actions: [continueAction], focus: batchFocus, kind: "action", compact: true }), candidateLimit > 0 ? _jsx(SemanticRows, { actions: candidateActions, focus: batchFocus - 1, kind: "toggle", compact: false, offset: candidateOffset, limit: candidateLimit }) : null, utilityLimit > 0 ? _jsx(SemanticRows, { actions: utilityActions, focus: batchFocus - 1 - candidateActions.length, kind: "action", compact: policy.mode === 'compact', offset: utilityOffset, limit: utilityLimit }) : null] });
|
|
3048
3399
|
}
|
|
3049
3400
|
else {
|
|
3050
3401
|
const selectedIds = batchDraft.selectedPluginIds;
|
|
@@ -3063,6 +3414,13 @@ export function ConsoleAppV4(props) {
|
|
|
3063
3414
|
setActionError('所选插件没有可用的已有安装位置。');
|
|
3064
3415
|
return;
|
|
3065
3416
|
}
|
|
3417
|
+
setBatchDraft((current) => current ? {
|
|
3418
|
+
...current,
|
|
3419
|
+
action,
|
|
3420
|
+
maintenanceTargets: exactTargets,
|
|
3421
|
+
selectedMaintenanceTargetKeys: exactTargets.map(batchTargetKey),
|
|
3422
|
+
showPlacementTargets: false,
|
|
3423
|
+
} : current);
|
|
3066
3424
|
beginReview({
|
|
3067
3425
|
kind: 'pluginWriteBatch', action,
|
|
3068
3426
|
selection: { kind: 'targets', targets: exactTargets },
|
|
@@ -3079,11 +3437,7 @@ export function ConsoleAppV4(props) {
|
|
|
3079
3437
|
{ label: '清理遗留内容…', description: `${props.controller.listPluginWriteBatchTargets('cleanup', selectedIds).length} 个有受管证据的位置;保留 foreign 和 shared 内容`, run: () => startExistingBatch('cleanup') },
|
|
3080
3438
|
{
|
|
3081
3439
|
label: '返回任务选择',
|
|
3082
|
-
run: () =>
|
|
3083
|
-
setBatchDraft((current) => current ? { ...current, stage: 'task' } : current);
|
|
3084
|
-
setFocus(0);
|
|
3085
|
-
setScrollOffset(0);
|
|
3086
|
-
},
|
|
3440
|
+
run: () => moveBatchStage('task'),
|
|
3087
3441
|
},
|
|
3088
3442
|
];
|
|
3089
3443
|
}
|
|
@@ -3105,17 +3459,13 @@ export function ConsoleAppV4(props) {
|
|
|
3105
3459
|
status: 'warning',
|
|
3106
3460
|
run: () => openSelectedPluginTask('restore', selectedIds),
|
|
3107
3461
|
}] : []),
|
|
3108
|
-
{ label: '移除所选插件…', description: `${uninstallTargets.length}
|
|
3462
|
+
{ label: '移除所选插件…', description: `${uninstallTargets.length} 个现有位置;可在下一页删减范围;保留设置`, status: 'error', run: () => startExistingBatch('uninstall') },
|
|
3109
3463
|
{
|
|
3110
3464
|
label: '其他维护方式…',
|
|
3111
3465
|
description: '同步、重新安装和普通清理',
|
|
3112
|
-
run: () =>
|
|
3113
|
-
setBatchDraft((current) => current ? { ...current, stage: 'maintenance' } : current);
|
|
3114
|
-
setFocus(0);
|
|
3115
|
-
setScrollOffset(0);
|
|
3116
|
-
},
|
|
3466
|
+
run: () => moveBatchStage('maintenance'),
|
|
3117
3467
|
},
|
|
3118
|
-
{ label: '返回调整选择', run: () =>
|
|
3468
|
+
{ label: '返回调整选择', run: () => moveBatchStage('select') },
|
|
3119
3469
|
];
|
|
3120
3470
|
}
|
|
3121
3471
|
context = `${selectedIds.length} 个插件 · ${syncTargets.length} 个已保存或现场目标`;
|
|
@@ -3126,66 +3476,196 @@ export function ConsoleAppV4(props) {
|
|
|
3126
3476
|
}
|
|
3127
3477
|
else if (route.name === 'batchPlacement') {
|
|
3128
3478
|
breadcrumb = '我的插件 / 批量操作 / 位置';
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3479
|
+
const maintenanceTargets = batchDraft?.maintenanceTargets ?? [];
|
|
3480
|
+
const maintenanceSelection = new Set(batchDraft?.selectedMaintenanceTargetKeys ?? []);
|
|
3481
|
+
const maintenanceMode = (batchDraft?.maintenanceGoal !== undefined
|
|
3482
|
+
|| batchDraft?.action !== undefined && batchDraft.action !== 'install')
|
|
3483
|
+
&& maintenanceTargets.length > 0;
|
|
3484
|
+
const maintenanceLabel = batchDraft?.maintenanceGoal === 'update' ? '更新'
|
|
3485
|
+
: batchDraft?.maintenanceGoal === 'restore' ? '恢复'
|
|
3486
|
+
: batchDraft?.action ? batchActionLabel(batchDraft.action) : '处理';
|
|
3487
|
+
title = maintenanceMode ? `确认批量${maintenanceLabel}范围`
|
|
3488
|
+
: batchDraft?.selection === 'currentProjectIntentRoots'
|
|
3489
|
+
? '同步当前项目全部保存目标'
|
|
3490
|
+
: `设置批量${batchDraft?.action ? batchActionLabel(batchDraft.action) : ''}位置`;
|
|
3491
|
+
description = maintenanceMode
|
|
3492
|
+
? '默认沿用进入此页时的现有位置。可以取消位置,也可以恢复本次集合中的位置。'
|
|
3493
|
+
: '分别选择使用范围和工具。确认页会重新读取状态并冻结实际计划。';
|
|
3494
|
+
let installPlacementTargets = [];
|
|
3495
|
+
let maintenanceTargetCount = 0;
|
|
3496
|
+
if (!batchDraft?.action && !batchDraft?.maintenanceGoal) {
|
|
3134
3497
|
actions = [{ label: '返回批量选择', run: back }];
|
|
3135
3498
|
}
|
|
3136
|
-
else {
|
|
3137
|
-
const
|
|
3499
|
+
else if (maintenanceMode) {
|
|
3500
|
+
const currentTargets = batchDraft.maintenanceGoal
|
|
3501
|
+
? props.controller.listPluginWriteTaskCandidates(batchDraft.maintenanceGoal)
|
|
3502
|
+
.filter((candidate) => batchDraft.selectedPluginIds.includes(candidate.target.pluginId))
|
|
3503
|
+
.map((candidate) => candidate.target)
|
|
3504
|
+
: props.controller.listPluginWriteBatchTargets(batchDraft.action, batchDraft.selectedPluginIds);
|
|
3505
|
+
const frozenKeys = maintenanceTargets.map(batchTargetKey);
|
|
3506
|
+
const currentKeys = currentTargets.map(batchTargetKey);
|
|
3507
|
+
const targetEvidenceChanged = frozenKeys.length !== currentKeys.length
|
|
3508
|
+
|| frozenKeys.some((key, index) => key !== currentKeys[index]);
|
|
3509
|
+
const selectedTargets = maintenanceTargets.filter((target) => maintenanceSelection.has(batchTargetKey(target)));
|
|
3510
|
+
const targetActions = maintenanceTargets.map((target) => ({
|
|
3511
|
+
label: `${props.controller.getPluginOverview(target.pluginId)?.displayName ?? target.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
3512
|
+
description: '本次处理这个现有位置',
|
|
3513
|
+
selected: maintenanceSelection.has(batchTargetKey(target)),
|
|
3514
|
+
run: () => setBatchDraft((current) => {
|
|
3515
|
+
if (!current)
|
|
3516
|
+
return current;
|
|
3517
|
+
const selectedKeys = new Set(current.selectedMaintenanceTargetKeys ?? []);
|
|
3518
|
+
const key = batchTargetKey(target);
|
|
3519
|
+
if (selectedKeys.has(key))
|
|
3520
|
+
selectedKeys.delete(key);
|
|
3521
|
+
else
|
|
3522
|
+
selectedKeys.add(key);
|
|
3523
|
+
return { ...current, selectedMaintenanceTargetKeys: [...selectedKeys] };
|
|
3524
|
+
}),
|
|
3525
|
+
}));
|
|
3526
|
+
maintenanceTargetCount = targetActions.length;
|
|
3138
3527
|
actions = [
|
|
3528
|
+
...targetActions,
|
|
3139
3529
|
{
|
|
3140
|
-
label:
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
run: () =>
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
run: () => {
|
|
3159
|
-
if (invalidBatchSelection(batchDraft).length > 0) {
|
|
3160
|
-
setActionError('已选插件状态已变化。请返回选择页检查并取消失效项。');
|
|
3161
|
-
return;
|
|
3162
|
-
}
|
|
3163
|
-
const intent = {
|
|
3164
|
-
kind: 'pluginWriteBatch',
|
|
3165
|
-
action: batchDraft.action,
|
|
3166
|
-
selection: batchDraft.selection === 'selected'
|
|
3167
|
-
? {
|
|
3168
|
-
kind: 'selected',
|
|
3169
|
-
pluginIds: [...new Set(batchDraft.selectedPluginIds)]
|
|
3170
|
-
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
3171
|
-
}
|
|
3172
|
-
: { kind: 'currentProjectIntentRoots' },
|
|
3173
|
-
platform: batchDraft.platform,
|
|
3174
|
-
scope: batchDraft.scope,
|
|
3175
|
-
};
|
|
3176
|
-
beginReview(intent);
|
|
3177
|
-
setDirty(false);
|
|
3178
|
-
setFocus(0);
|
|
3179
|
-
setScrollOffset(0);
|
|
3180
|
-
},
|
|
3530
|
+
label: `查看批量${maintenanceLabel}确认`,
|
|
3531
|
+
description: targetEvidenceChanged
|
|
3532
|
+
? '安装位置已变化,请返回任务页重新选择'
|
|
3533
|
+
: `${selectedTargets.length} 个现有位置将处理`,
|
|
3534
|
+
disabled: targetEvidenceChanged ? '安装位置已变化,请重新确认本次范围'
|
|
3535
|
+
: selectedTargets.length === 0 ? '请至少保留一个处理位置' : undefined,
|
|
3536
|
+
run: () => beginReview(batchDraft.maintenanceGoal ? {
|
|
3537
|
+
kind: 'pluginWriteTask',
|
|
3538
|
+
goal: batchDraft.maintenanceGoal,
|
|
3539
|
+
targets: selectedTargets,
|
|
3540
|
+
executionPolicy: 'all',
|
|
3541
|
+
} : {
|
|
3542
|
+
kind: 'pluginWriteBatch',
|
|
3543
|
+
action: batchDraft.action,
|
|
3544
|
+
selection: { kind: 'targets', targets: selectedTargets },
|
|
3545
|
+
platform: selectedTargets[0].platform,
|
|
3546
|
+
scope: selectedTargets[0].scope,
|
|
3547
|
+
}),
|
|
3181
3548
|
},
|
|
3549
|
+
{ label: '返回任务选择', run: back },
|
|
3182
3550
|
];
|
|
3183
3551
|
}
|
|
3552
|
+
else {
|
|
3553
|
+
const projectIntent = batchDraft.selection === 'currentProjectIntentRoots';
|
|
3554
|
+
installPlacementTargets = batchDraft.action === 'install'
|
|
3555
|
+
&& batchDraft.selection === 'selected'
|
|
3556
|
+
? props.controller.listPluginInstallBatchTargets(batchDraft.selectedPluginIds, batchDraft.platform, batchDraft.scope)
|
|
3557
|
+
: [];
|
|
3558
|
+
const addTargets = installPlacementTargets.filter((target) => target.state === 'add');
|
|
3559
|
+
const blockedTargets = installPlacementTargets.filter((target) => target.state === 'blocked');
|
|
3560
|
+
const existingTargets = installPlacementTargets.filter((target) => target.state === 'existing');
|
|
3561
|
+
if (batchDraft.showPlacementTargets) {
|
|
3562
|
+
actions = [
|
|
3563
|
+
...installPlacementTargets.map((target) => ({
|
|
3564
|
+
label: `${target.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
3565
|
+
description: target.state === 'add' ? '可添加'
|
|
3566
|
+
: target.state === 'existing' ? '已有,不重复添加' : `受阻:${target.reason}`,
|
|
3567
|
+
status: target.state === 'add' ? 'success'
|
|
3568
|
+
: target.state === 'blocked' ? 'warning' : 'muted',
|
|
3569
|
+
})),
|
|
3570
|
+
{
|
|
3571
|
+
label: '返回位置选择',
|
|
3572
|
+
run: () => {
|
|
3573
|
+
setBatchDraft((current) => current ? { ...current, showPlacementTargets: false } : current);
|
|
3574
|
+
setFocus(5);
|
|
3575
|
+
setScrollOffset(0);
|
|
3576
|
+
},
|
|
3577
|
+
},
|
|
3578
|
+
];
|
|
3579
|
+
}
|
|
3580
|
+
else
|
|
3581
|
+
actions = [
|
|
3582
|
+
{
|
|
3583
|
+
label: '当前项目', selected: batchDraft.scope === 'project',
|
|
3584
|
+
run: () => setBatchDraft((current) => current ? { ...current, scope: 'project' } : current),
|
|
3585
|
+
},
|
|
3586
|
+
{
|
|
3587
|
+
label: '所有项目', selected: batchDraft.scope === 'user',
|
|
3588
|
+
disabled: projectIntent ? '项目全部同步固定使用当前项目' : undefined,
|
|
3589
|
+
run: () => setBatchDraft((current) => current ? { ...current, scope: 'user' } : current),
|
|
3590
|
+
},
|
|
3591
|
+
...['codex', 'claude', 'both'].map((platform) => ({
|
|
3592
|
+
label: platform === 'both' ? 'Codex 和 Claude Code' : platformLabel(platform),
|
|
3593
|
+
selected: batchDraft.platform === platform,
|
|
3594
|
+
run: () => setBatchDraft((current) => current ? { ...current, platform } : current),
|
|
3595
|
+
})),
|
|
3596
|
+
...(installPlacementTargets.length > 0 ? [{
|
|
3597
|
+
label: '查看全部位置和受阻原因…',
|
|
3598
|
+
description: `${addTargets.length} 个可添加 · ${existingTargets.length} 个已有 · ${blockedTargets.length} 个受阻`,
|
|
3599
|
+
run: () => {
|
|
3600
|
+
setBatchDraft((current) => current ? { ...current, showPlacementTargets: true } : current);
|
|
3601
|
+
setFocus(0);
|
|
3602
|
+
setScrollOffset(0);
|
|
3603
|
+
},
|
|
3604
|
+
}] : []),
|
|
3605
|
+
{
|
|
3606
|
+
label: batchDraft.action === 'install' && blockedTargets.length > 0
|
|
3607
|
+
? `仅添加可执行的 ${addTargets.length} 个位置`
|
|
3608
|
+
: `查看批量${batchActionLabel(batchDraft.action)}确认`,
|
|
3609
|
+
description: batchDraft.action === 'install'
|
|
3610
|
+
? `${addTargets.length} 个可添加 · ${existingTargets.length} 个已有 · ${blockedTargets.length} 个受阻`
|
|
3611
|
+
: `${batchDraft.selection === 'selected' ? `${batchDraft.selectedPluginIds.length} 个所选插件` : '当前项目全部保存目标'} · ${scopeLabel(batchDraft.scope)}`,
|
|
3612
|
+
disabled: batchDraft.selection === 'selected' && batchDraft.selectedPluginIds.length === 0
|
|
3613
|
+
? '请至少选择一个插件'
|
|
3614
|
+
: batchDraft.action === 'install' && addTargets.length === 0
|
|
3615
|
+
? '所选工具和位置没有可添加目标'
|
|
3616
|
+
: undefined,
|
|
3617
|
+
run: () => {
|
|
3618
|
+
if (invalidBatchSelection(batchDraft).length > 0) {
|
|
3619
|
+
setActionError('已选插件状态已变化。请返回选择页检查并取消失效项。');
|
|
3620
|
+
return;
|
|
3621
|
+
}
|
|
3622
|
+
const intent = {
|
|
3623
|
+
kind: 'pluginWriteBatch',
|
|
3624
|
+
action: batchDraft.action,
|
|
3625
|
+
selection: batchDraft.action === 'install'
|
|
3626
|
+
? { kind: 'targets', targets: addTargets.map(({ pluginId, platform, scope }) => ({
|
|
3627
|
+
pluginId, platform, scope,
|
|
3628
|
+
})) }
|
|
3629
|
+
: batchDraft.selection === 'selected'
|
|
3630
|
+
? {
|
|
3631
|
+
kind: 'selected',
|
|
3632
|
+
pluginIds: [...new Set(batchDraft.selectedPluginIds)]
|
|
3633
|
+
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
3634
|
+
}
|
|
3635
|
+
: { kind: 'currentProjectIntentRoots' },
|
|
3636
|
+
platform: addTargets[0]?.platform ?? batchDraft.platform,
|
|
3637
|
+
scope: addTargets[0]?.scope ?? batchDraft.scope,
|
|
3638
|
+
};
|
|
3639
|
+
beginReview(intent);
|
|
3640
|
+
setDirty(false);
|
|
3641
|
+
setFocus(0);
|
|
3642
|
+
setScrollOffset(0);
|
|
3643
|
+
},
|
|
3644
|
+
},
|
|
3645
|
+
];
|
|
3646
|
+
}
|
|
3184
3647
|
context = batchDraft?.selection === 'selected'
|
|
3185
|
-
?
|
|
3648
|
+
? maintenanceMode
|
|
3649
|
+
? `${maintenanceSelection.size}/${maintenanceTargets.length} 个位置`
|
|
3650
|
+
: `${batchDraft.selectedPluginIds.length} 个插件`
|
|
3186
3651
|
: '保存的项目目标';
|
|
3187
|
-
help =
|
|
3188
|
-
|
|
3652
|
+
help = batchDraft?.showPlacementTargets
|
|
3653
|
+
? '↑↓ 查看全部位置 · Esc 返回位置选择'
|
|
3654
|
+
: '↑↓ 移动 · Enter 选择 · Esc 返回修改';
|
|
3655
|
+
const placementDetail = Boolean(batchDraft?.showPlacementTargets);
|
|
3656
|
+
const viewport = actionViewport(actions, placementDetail ? false : policy.mode === 'compact', focus, scrollOffset, pageActionBudget(1));
|
|
3657
|
+
if (maintenanceMode) {
|
|
3658
|
+
const targetOffset = Math.min(viewport.offset, maintenanceTargetCount);
|
|
3659
|
+
const targetLimit = Math.max(0, Math.min(maintenanceTargetCount, viewport.offset + viewport.limit) - targetOffset);
|
|
3660
|
+
const actionOffset = Math.max(0, viewport.offset - maintenanceTargetCount);
|
|
3661
|
+
const actionLimit = Math.max(0, Math.min(actions.length - maintenanceTargetCount, viewport.offset + viewport.limit - maintenanceTargetCount) - actionOffset);
|
|
3662
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u5904\u7406\u8303\u56F4", meta: `${maintenanceSelection.size} 个位置`, compact: true, children: targetLimit > 0 ? _jsx(SemanticRows, { actions: actions.slice(0, maintenanceTargetCount), focus: focus, kind: "toggle", compact: true, offset: targetOffset, limit: targetLimit }) : null }), actionLimit > 0 ? _jsx(SemanticRows, { actions: actions.slice(maintenanceTargetCount), focus: focus - maintenanceTargetCount, kind: "action", compact: policy.mode === 'compact', offset: actionOffset, limit: actionLimit }) : null] });
|
|
3663
|
+
}
|
|
3664
|
+
else if (!placementDetail && batchDraft?.action === 'install') {
|
|
3665
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions.slice(5), focus: focus - 5, kind: "action", compact: policy.mode === 'compact' }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5728\u54EA\u91CC\u4F7F\u7528" }), _jsx(SemanticRows, { actions: actions.slice(0, 2), focus: focus, kind: "choice", compact: true }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u7528\u4E8E\u54EA\u4E2A\u5DE5\u5177" }), _jsx(SemanticRows, { actions: actions.slice(2, 5), focus: focus - 2, kind: "choice", compact: true })] });
|
|
3666
|
+
}
|
|
3667
|
+
else
|
|
3668
|
+
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: placementDetail ? 'action' : 'choice', compact: placementDetail ? false : policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit });
|
|
3189
3669
|
}
|
|
3190
3670
|
else if (route.name === 'pluginPreview' && plugin) {
|
|
3191
3671
|
breadcrumb = `添加插件 / ${plugin.displayName}`;
|
|
@@ -3277,70 +3757,16 @@ export function ConsoleAppV4(props) {
|
|
|
3277
3757
|
run: () => go({ name: 'pluginMore', pluginId: plugin.pluginId }),
|
|
3278
3758
|
},
|
|
3279
3759
|
];
|
|
3280
|
-
const previewViewport = actionViewport(actions, true, effectiveFocus, scrollOffset, responsiveBudget(4,
|
|
3281
|
-
const
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
}),
|
|
3291
|
-
}));
|
|
3292
|
-
const previewContentLines = size.rows - 6;
|
|
3293
|
-
const previewActionLines = previewViewport.limit ?? actions.length;
|
|
3294
|
-
const previewFixedStaticLines = 7 + (plugin.portableSkills && policy.mode !== 'compact' ? 5 : 0);
|
|
3295
|
-
let dependencyLineBudget = Math.max(dependencyGroups.filter((group) => group.dependencies.length > 0).length, previewContentLines - previewActionLines - previewFixedStaticLines);
|
|
3296
|
-
const visibleByRelation = new Map();
|
|
3297
|
-
for (const group of dependencyGroups) {
|
|
3298
|
-
if (group.dependencies.length === 0)
|
|
3299
|
-
continue;
|
|
3300
|
-
const firstItemLines = policy.mode === 'compact' || group.dependencies.length > 1 ? 1 : 2;
|
|
3301
|
-
const hiddenSummaryLines = group.dependencies.length > 1 ? 1 : 0;
|
|
3302
|
-
if (dependencyLineBudget >= firstItemLines + hiddenSummaryLines) {
|
|
3303
|
-
visibleByRelation.set(group.relation, 1);
|
|
3304
|
-
dependencyLineBudget -= firstItemLines + hiddenSummaryLines;
|
|
3305
|
-
}
|
|
3306
|
-
else {
|
|
3307
|
-
visibleByRelation.set(group.relation, 0);
|
|
3308
|
-
dependencyLineBudget -= 1;
|
|
3309
|
-
}
|
|
3310
|
-
}
|
|
3311
|
-
for (const group of dependencyGroups) {
|
|
3312
|
-
let visibleCount = visibleByRelation.get(group.relation) ?? 0;
|
|
3313
|
-
while (dependencyLineBudget > 0 && visibleCount < group.dependencies.length) {
|
|
3314
|
-
visibleCount += 1;
|
|
3315
|
-
dependencyLineBudget -= 1;
|
|
3316
|
-
}
|
|
3317
|
-
visibleByRelation.set(group.relation, visibleCount);
|
|
3318
|
-
}
|
|
3319
|
-
const summaryRows = (relation) => {
|
|
3320
|
-
const dependencies = dependencyGroups.find((group) => group.relation === relation).dependencies;
|
|
3321
|
-
const visibleCount = policy.mode === 'compact' ? Math.min(1, dependencies.length)
|
|
3322
|
-
: visibleByRelation.get(relation) ?? 0;
|
|
3323
|
-
const visible = dependencies.slice(0, visibleCount);
|
|
3324
|
-
const compactRows = policy.mode === 'compact' || dependencies.length > visible.length;
|
|
3325
|
-
return [
|
|
3326
|
-
...visible.map((entry) => {
|
|
3327
|
-
const state = dependencyStatus(entry);
|
|
3328
|
-
return _jsx(StaticRow, { title: entry.displayName, description: dependencyRelationDescription(entry), meta: state.value, status: state.status, compact: compactRows }, entry.pluginId);
|
|
3329
|
-
}),
|
|
3330
|
-
...(visible.length === 0 && dependencies.length > 0
|
|
3331
|
-
? [_jsx(StaticRow, { title: `${dependencies.length} 个${relation === 'direct' ? '直接' : '间接'}依赖`, status: "muted", compact: true }, `${relation}:count`)]
|
|
3332
|
-
: dependencies.length > visible.length
|
|
3333
|
-
? [_jsx(StaticRow, { title: `另有 ${dependencies.length - visible.length} 个${relation === 'direct' ? '直接' : '间接'}依赖`, status: "muted", compact: true }, `${relation}:more`)]
|
|
3334
|
-
: []),
|
|
3335
|
-
];
|
|
3336
|
-
};
|
|
3337
|
-
const compactDependencyRows = overview?.dependencies.slice(0, 2) ?? [];
|
|
3338
|
-
content = hasDependencies
|
|
3339
|
-
? _jsxs(Box, { flexDirection: "column", children: [plugin.portableSkills && policy.mode !== 'compact' ? _jsx(SectionCard, { title: "\u53EF\u7528\u65B9\u5F0F", children: _jsx(StaticRow, { title: "\u901A\u7528 Skills", meta: `可安装 · ${plugin.dependencySummary.totalCount} 个插件依赖`, description: "\u9009\u62E9 Skill \u548C Agent \u540E\u4EA4\u7ED9 Skills CLI \u5B89\u88C5" }) }) : null, _jsxs(SectionCard, { title: "\u5B89\u88C5\u5185\u5BB9", children: [policy.mode !== 'compact' ? _jsx(StaticRow, { title: plugin.displayName, description: "\u4F60\u9009\u62E9\u7684\u63D2\u4EF6", status: "info" }) : null, plugin.portableSkills && policy.mode === 'compact' ? _jsx(StaticRow, { title: "\u901A\u7528 Skills", meta: `可安装 · ${plugin.dependencySummary.totalCount} 个插件依赖`, compact: true }) : null, policy.mode === 'compact' ? compactDependencyRows.map((entry) => {
|
|
3340
|
-
const state = dependencyStatus(entry);
|
|
3341
|
-
return _jsx(StaticRow, { title: entry.displayName, meta: `${entry.relation === 'direct' ? '直接' : '间接'} · ${state.value}`, status: state.status, compact: true }, entry.pluginId);
|
|
3342
|
-
}) : _jsxs(_Fragment, { children: [summaryRows('direct'), summaryRows('transitive')] }), policy.mode === 'compact' && (overview?.dependencies.length ?? 0) > compactDependencyRows.length ? _jsx(StaticRow, { title: `另有 ${(overview?.dependencies.length ?? 0) - compactDependencyRows.length} 个依赖`, status: "muted", compact: true }) : null, policy.mode !== 'compact' ? _jsx(StaticRow, { title: `${plugin.dependencySummary.totalCount + 1} 个插件将作为一个安装计划检查`, description: "\u4F9D\u8D56\u4ECE\u540C\u4E00\u4E2A\u6765\u6E90\u5FEB\u7167\u89E3\u6790\u3002" }) : null] }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact' || previewViewport.limit !== undefined, offset: previewViewport.offset, limit: previewViewport.limit })] })
|
|
3343
|
-
: _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u53EF\u7528\u76EE\u6807\u5E73\u53F0", children: [_jsx(StaticRow, { title: plugin.supportedPlatforms.map(platformLabel).join(' · ') || '暂无可用目标平台' }), plugin.portableSkills ? _jsx(StaticRow, { title: "\u901A\u7528 Skills", meta: "\u53EF\u5B89\u88C5", description: "\u9009\u62E9 Skill \u548C Agent \u540E\u4EA4\u7ED9 Skills CLI \u5B89\u88C5", compact: policy.mode === 'compact' }) : null] }), _jsx(SectionCard, { title: "\u4F7F\u7528\u8BF4\u660E", children: _jsx(StaticRow, { title: "\u5728 Agent \u4F1A\u8BDD\u4E2D\u63D0\u4F9B\u80FD\u529B", status: "success", description: "\u5B89\u88C5\u4E0D\u4F1A\u81EA\u52A8\u4FEE\u6539\u9879\u76EE\u6E90\u7801\u3002" }) }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact', offset: previewViewport.offset, limit: previewViewport.limit })] });
|
|
3760
|
+
const previewViewport = actionViewport(actions, true, effectiveFocus, scrollOffset, responsiveBudget(4, 3));
|
|
3761
|
+
const dependencySummary = hasDependencies && plugin.dependencySummary
|
|
3762
|
+
? `自动使用 ${plugin.dependencySummary.totalCount} 个所需插件;${plugin.dependencySummary.readyCount} 个可复用。`
|
|
3763
|
+
: '不需要其他插件。';
|
|
3764
|
+
const confirmationIssue = overview?.blockers[0]?.explanation
|
|
3765
|
+
?? (plugin.capabilityUnsupported ? '当前 AW CLI 缺少此插件所需功能。'
|
|
3766
|
+
: plugin.sourceUnavailable ? '插件来源当前不可用。' : undefined);
|
|
3767
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u652F\u6301", children: _jsx(StaticRow, { title: plugin.supportedPlatforms.map(platformLabel).join(' · ') || '暂无可用目标平台', meta: plugin.portableSkills ? '也可安装通用 Skills' : undefined, status: plugin.supportedPlatforms.length > 0 || plugin.portableSkills ? 'success' : 'warning', compact: true }) }), _jsxs(SectionCard, { title: "\u6DFB\u52A0\u5185\u5BB9", children: [_jsx(StaticRow, { title: dependencySummary, status: confirmationIssue ? 'warning' : 'info', compact: true }), confirmationIssue
|
|
3768
|
+
? _jsx(StaticRow, { title: "\u786E\u8BA4\u7684\u95EE\u9898", description: confirmationIssue, status: "warning", compact: true })
|
|
3769
|
+
: null] }), _jsx(SemanticRows, { actions: actions, focus: effectiveFocus, kind: "action", compact: policy.mode === 'compact', offset: previewViewport.offset, limit: previewViewport.limit })] });
|
|
3344
3770
|
}
|
|
3345
3771
|
else if (route.name === 'portableSkillSelect' && plugin && snapshot.portableDraft) {
|
|
3346
3772
|
const draft = snapshot.portableDraft;
|
|
@@ -3512,8 +3938,8 @@ export function ConsoleAppV4(props) {
|
|
|
3512
3938
|
context = direction === 'dependencies' ? '它需要的' : '谁在使用它';
|
|
3513
3939
|
title = hasInstalledPlacement(plugin) ? '依赖与被使用情况' : '安装所需插件';
|
|
3514
3940
|
description = direction === 'dependencies'
|
|
3515
|
-
? '
|
|
3516
|
-
: '
|
|
3941
|
+
? '每一层表示上面的插件需要下面的插件。'
|
|
3942
|
+
: '每一层表示下面的插件正在使用上面的插件;只显示已确认的引用。';
|
|
3517
3943
|
const rowActions = visibleRows.map((row) => {
|
|
3518
3944
|
const state = row.depth === 0
|
|
3519
3945
|
? { value: statusText(plugin), status: plugin.aggregateStatus === 'needsAttention'
|
|
@@ -3524,8 +3950,8 @@ export function ConsoleAppV4(props) {
|
|
|
3524
3950
|
const compactBranch = policy.mode === 'compact' && row.depth > 2 ? '… └─ ' : branch;
|
|
3525
3951
|
return {
|
|
3526
3952
|
label: row.depth === 0
|
|
3527
|
-
?
|
|
3528
|
-
: `${compactBranch}${
|
|
3953
|
+
? `${row.label}(当前插件)`
|
|
3954
|
+
: `${compactBranch}${row.label}${row.sharedWithRowId ? '(共用,见上方)' : ''}`,
|
|
3529
3955
|
description: row.hiddenDescendantCount > 0
|
|
3530
3956
|
? `另有 ${row.hiddenDescendantCount} 个${direction === 'dependencies' ? '所需插件' : '实际使用者'}`
|
|
3531
3957
|
: row.depth === 0 ? '当前插件' : undefined,
|
|
@@ -3538,6 +3964,7 @@ export function ConsoleAppV4(props) {
|
|
|
3538
3964
|
pluginId: plugin.pluginId,
|
|
3539
3965
|
nodePluginId: row.pluginId,
|
|
3540
3966
|
direction,
|
|
3967
|
+
nodePathPluginIds: row.pathPluginIds,
|
|
3541
3968
|
});
|
|
3542
3969
|
},
|
|
3543
3970
|
};
|
|
@@ -3546,9 +3973,9 @@ export function ConsoleAppV4(props) {
|
|
|
3546
3973
|
? projection.scopeSummary.unknownConsumerPluginIds.map((pluginId) => {
|
|
3547
3974
|
const declared = overview?.dependents.find((entry) => entry.pluginId === pluginId);
|
|
3548
3975
|
return {
|
|
3549
|
-
label:
|
|
3550
|
-
description: '
|
|
3551
|
-
meta: declared?.aggregateStatus === 'notInstalled' ? '未安装' : '未验证',
|
|
3976
|
+
label: declared?.displayName ?? pluginId,
|
|
3977
|
+
description: '仅有依赖声明,尚未确认实际使用',
|
|
3978
|
+
meta: declared?.aggregateStatus === 'notInstalled' ? '仅声明 · 未安装' : '仅声明 · 未验证',
|
|
3552
3979
|
status: 'muted',
|
|
3553
3980
|
run: () => {
|
|
3554
3981
|
persistDependencyGraphContext(route, projection, expandedDependencyPluginIds ?? new Set(projection.defaultExpandedPluginIds), focus, scrollOffset);
|
|
@@ -3582,47 +4009,133 @@ export function ConsoleAppV4(props) {
|
|
|
3582
4009
|
const scopeText = projection.scopeSummary.references.length > 0
|
|
3583
4010
|
? projection.scopeSummary.references.map((entry) => `${platformLabel(entry.platform)} · ${scopeLabel(entry.scope)}`).join(';')
|
|
3584
4011
|
: '当前加载范围;其他项目引用未检查';
|
|
3585
|
-
const relationViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, showTechnical ? responsiveBudget(3, 6) : responsiveBudget(
|
|
4012
|
+
const relationViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, showTechnical ? responsiveBudget(3, 6) : responsiveBudget(6, 10));
|
|
4013
|
+
const focusedGraphRow = focus < rowActions.length ? visibleRows[focus] : undefined;
|
|
3586
4014
|
help = '↑↓ 选择 · → 展开 · ← 收起 · Enter 节点详情 · Esc 返回';
|
|
3587
4015
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: `当前检查范围 · ${scopeText}`, status: "muted", compact: true }), projection.status.state === 'invalid'
|
|
3588
4016
|
? _jsx(Callout, { status: "error", title: "\u4F9D\u8D56\u5173\u7CFB\u65E0\u6548", description: projection.status.message }) : null, unverifiedConsumerActions.length > 0
|
|
3589
4017
|
? _jsx(StaticRow, { title: `未验证声明者 · ${unverifiedConsumerActions.length} 个`, description: "\u4EE5\u4E0B\u58F0\u660E\u8005\u4E0D\u8BA1\u5165\u5B9E\u9645\u4F7F\u7528\u8005", status: "muted", compact: true })
|
|
3590
|
-
: null,
|
|
4018
|
+
: null, focusedGraphRow ? _jsx(StaticRow, { title: "\u7528\u9014", description: props.controller
|
|
4019
|
+
.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} 个依赖 · ${projection.scopeSummary.actualConsumerPluginIds.length} 个实际使用者`, meta: plugin.pluginId, compact: true }), _jsx(StaticRow, { title: `未验证声明者 · ${projection.scopeSummary.unknownConsumerPluginIds.length} 个`, compact: true })] }) : null] });
|
|
3591
4020
|
}
|
|
3592
4021
|
}
|
|
3593
4022
|
else if (route.name === 'pluginDependencyNode' && plugin) {
|
|
3594
4023
|
const node = props.controller.getPluginOverview(route.nodePluginId);
|
|
3595
|
-
const
|
|
4024
|
+
const nodePathPluginIds = route.nodePathPluginIds ?? [plugin.pluginId, route.nodePluginId];
|
|
4025
|
+
const nodeProjection = buildDependencyGraphProjection({
|
|
4026
|
+
overviews: props.controller.listPluginDependencyOverviews(),
|
|
4027
|
+
rootPluginId: plugin.pluginId,
|
|
4028
|
+
direction: route.direction,
|
|
4029
|
+
expandedPluginIds: nodePathPluginIds,
|
|
4030
|
+
focusPluginId: route.nodePluginId,
|
|
4031
|
+
});
|
|
4032
|
+
const selectedRow = nodeProjection.rows.find((row) => row.pathPluginIds.join('\0') === nodePathPluginIds.join('\0'))
|
|
4033
|
+
?? nodeProjection.rows.find((row) => row.pluginId === route.nodePluginId);
|
|
3596
4034
|
const item = route.nodePluginId === plugin.pluginId ? undefined
|
|
3597
|
-
: route.direction === 'dependencies'
|
|
3598
|
-
?
|
|
3599
|
-
:
|
|
3600
|
-
|
|
3601
|
-
|| (item?.placementReferences?.length ?? 0) > 0;
|
|
4035
|
+
: (route.direction === 'dependencies'
|
|
4036
|
+
? props.controller.getPluginDependencyOverview(plugin.pluginId)?.dependencies
|
|
4037
|
+
: props.controller.getPluginDependencyOverview(plugin.pluginId)?.dependents)
|
|
4038
|
+
?.find((entry) => entry.pluginId === route.nodePluginId);
|
|
3602
4039
|
const state = item ? dependencyStatus(item) : undefined;
|
|
4040
|
+
const relationship = selectedRow?.relationship
|
|
4041
|
+
?? ((item?.placementReferences?.length ?? 0) > 0 ? 'unknown'
|
|
4042
|
+
: item ? 'declared' : undefined);
|
|
3603
4043
|
breadcrumb = `${plugin.displayName} / 依赖关系 / ${node?.displayName ?? route.nodePluginId}`;
|
|
3604
4044
|
context = route.direction === 'dependencies' ? '它需要的' : '谁在使用它';
|
|
3605
4045
|
title = node?.displayName ?? route.nodePluginId;
|
|
3606
|
-
|
|
4046
|
+
const pathPluginIds = selectedRow?.pathPluginIds ?? nodePathPluginIds;
|
|
4047
|
+
const intermediateNames = pathPluginIds.slice(1, -1).reverse().map((pluginId) => props.controller.getPluginOverview(pluginId)?.displayName ?? pluginId);
|
|
4048
|
+
const declaringPluginId = pathPluginIds.at(-2) ?? plugin.pluginId;
|
|
4049
|
+
const declaringPluginName = props.controller.getPluginOverview(declaringPluginId)?.displayName
|
|
4050
|
+
?? declaringPluginId;
|
|
4051
|
+
description = relationship === 'root'
|
|
3607
4052
|
? '这是当前关系图的根插件。'
|
|
3608
|
-
:
|
|
3609
|
-
?
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
4053
|
+
: relationship === 'directVerified'
|
|
4054
|
+
? route.direction === 'dependencies'
|
|
4055
|
+
? `${plugin.displayName} 的已安装位置直接使用这个插件。`
|
|
4056
|
+
: `${node?.displayName ?? route.nodePluginId} 的已安装位置直接使用当前插件。`
|
|
4057
|
+
: relationship === 'indirectVerified'
|
|
4058
|
+
? route.direction === 'dependencies'
|
|
4059
|
+
? `${plugin.displayName} 通过 ${intermediateNames.join('、')} 间接使用 ${node?.displayName ?? route.nodePluginId}。`
|
|
4060
|
+
: `${node?.displayName ?? route.nodePluginId} 通过 ${intermediateNames.join('、')} 间接使用 ${plugin.displayName}。`
|
|
4061
|
+
: relationship === 'unknown'
|
|
4062
|
+
? '引用位置不能组成连续路径,不能确认这项间接使用。'
|
|
4063
|
+
: relationship === 'declared'
|
|
4064
|
+
? route.direction === 'dependencies'
|
|
4065
|
+
? `${declaringPluginName} 声明需要 ${node?.displayName ?? route.nodePluginId},但当前没有实际引用证据。`
|
|
4066
|
+
: `${node?.displayName ?? route.nodePluginId} 声明依赖当前插件,但当前没有实际引用证据。`
|
|
4067
|
+
: '当前数据不足,不能判断这项依赖关系。';
|
|
4068
|
+
const references = selectedRow?.edge?.placementReferences ?? item?.placementReferences ?? [];
|
|
4069
|
+
const referenceActions = references.map((reference) => {
|
|
4070
|
+
const consumerName = props.controller.getPluginOverview(reference.consumer.pluginId)?.displayName
|
|
4071
|
+
?? reference.consumer.pluginId.split('/').at(-1);
|
|
4072
|
+
return {
|
|
4073
|
+
label: dependencyReferenceUsageLabel(reference, consumerName),
|
|
4074
|
+
meta: '已确认使用',
|
|
4075
|
+
};
|
|
4076
|
+
});
|
|
4077
|
+
if (route.view === 'references') {
|
|
4078
|
+
breadcrumb = `${plugin.displayName} / 依赖关系 / ${node?.displayName ?? route.nodePluginId} / 使用位置`;
|
|
4079
|
+
context = showTechnical ? '技术详情' : '使用位置';
|
|
4080
|
+
title = `已检查的使用位置 ${references.length} 个`;
|
|
4081
|
+
description = showTechnical
|
|
4082
|
+
? '显示当前选择的完整引用字段。'
|
|
4083
|
+
: '这些位置有连续、已确认的实际引用。';
|
|
4084
|
+
actions = [...referenceActions, { label: '返回节点详情', run: back }];
|
|
4085
|
+
const referenceViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(3, 6));
|
|
4086
|
+
const selectedReference = references[Math.min(focus, references.length - 1)];
|
|
4087
|
+
const referenceIdLines = (label, pluginId) => {
|
|
4088
|
+
const prefix = `${label} · `;
|
|
4089
|
+
return wrapConsoleText(pluginId, Math.max(1, size.columns - 10 - stringWidth(prefix)))
|
|
4090
|
+
.map((line) => `${prefix}${line}`);
|
|
4091
|
+
};
|
|
4092
|
+
const referenceDetailLines = selectedReference ? [
|
|
4093
|
+
...referenceIdLines('consumer', selectedReference.consumer.pluginId),
|
|
4094
|
+
`consumer · platform=${platformLabel(selectedReference.consumer.platform)} · scope=${selectedReference.consumer.scope}`,
|
|
4095
|
+
`consumer · project=${selectedReference.consumer.project ?? 'none'}`,
|
|
4096
|
+
...referenceIdLines('dependency', selectedReference.dependency.pluginId),
|
|
4097
|
+
`dependency · platform=${platformLabel(selectedReference.dependency.platform)} · scope=${selectedReference.dependency.scope}`,
|
|
4098
|
+
`dependency · project=${selectedReference.dependency.project ?? 'none'}`,
|
|
4099
|
+
] : [];
|
|
4100
|
+
const referenceDetailPageRows = referenceDetailLines.length <= 6
|
|
4101
|
+
? 6
|
|
4102
|
+
: responsiveBudget(4, 8);
|
|
4103
|
+
const referenceDetailOffset = Math.min(batchTargetOffset, Math.max(0, referenceDetailLines.length - 1));
|
|
4104
|
+
const visibleReferenceDetailLines = referenceDetailLines.slice(referenceDetailOffset, referenceDetailOffset + referenceDetailPageRows);
|
|
4105
|
+
referenceDetailPagination = showTechnical ? {
|
|
4106
|
+
total: referenceDetailLines.length,
|
|
4107
|
+
pageRows: referenceDetailPageRows,
|
|
4108
|
+
} : undefined;
|
|
4109
|
+
help = showTechnical
|
|
4110
|
+
? 'PgUp/PgDn 查看当前位置详情 · ↑↓ 查看全部位置 · d 隐藏技术详情 · Esc 返回节点详情'
|
|
4111
|
+
: '↑↓ 查看全部位置 · d 技术详情 · Enter 返回节点详情 · Esc 返回节点详情';
|
|
4112
|
+
content = _jsxs(Box, { flexDirection: "column", children: [showTechnical ? null
|
|
4113
|
+
: _jsx(StaticRow, { title: "\u8BC1\u636E\u8FB9\u754C", description: "\u8FD9\u91CC\u53EA\u663E\u793A\u5DF2\u786E\u8BA4\u7684\u5B9E\u9645\u5F15\u7528\uFF1B\u63D2\u4EF6\u72B6\u6001\u4E0D\u4F5C\u4E3A\u5F15\u7528", status: "muted", compact: true }), showTechnical && referenceDetailOffset > 0
|
|
4114
|
+
? _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
|
|
4115
|
+
? _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 })] });
|
|
4116
|
+
}
|
|
4117
|
+
else {
|
|
4118
|
+
actions = [
|
|
4119
|
+
...(references.length > 0 ? [{
|
|
4120
|
+
label: `查看全部位置(${references.length})`,
|
|
4121
|
+
description: '查看每个已检查位置及证据状态',
|
|
4122
|
+
run: () => go({ ...route, view: 'references' }),
|
|
4123
|
+
}] : []),
|
|
4124
|
+
...(route.nodePluginId !== plugin.pluginId ? [{
|
|
4125
|
+
label: '以此节点查看关系',
|
|
4126
|
+
description: '打开局部子树;面包屑保留来路',
|
|
4127
|
+
run: () => go({
|
|
4128
|
+
name: 'pluginDependencies',
|
|
4129
|
+
pluginId: route.nodePluginId,
|
|
4130
|
+
direction: route.direction,
|
|
4131
|
+
}),
|
|
4132
|
+
}] : []),
|
|
4133
|
+
{ label: '返回关系图', run: back },
|
|
4134
|
+
];
|
|
4135
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u8282\u70B9\u72B6\u6001", children: [_jsx(StaticRow, { title: node?.displayName ?? route.nodePluginId, meta: state?.value ?? (node ? statusText(node) : '未检查'), status: state?.status ?? 'muted', compact: true }), _jsx(StaticRow, { title: "\u7528\u9014", description: node?.description ?? '当前目录没有用途说明', compact: true }), _jsx(StaticRow, { title: "\u4E3A\u4F55\u9700\u8981", description: description, compact: true }), _jsx(StaticRow, { title: "\u5F71\u54CD", description: state?.value ?? (node ? statusText(node) : '尚未检查当前状态'), status: state?.status ?? 'muted', compact: true }), references.length > 0
|
|
4136
|
+
? _jsx(StaticRow, { title: `已检查的使用位置 ${references.length} 个`, description: "\u9009\u62E9\u201C\u67E5\u770B\u5168\u90E8\u4F4D\u7F6E\u201D\u53EF\u9010\u9879\u67E5\u770B", compact: true })
|
|
4137
|
+
: null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' }), showTechnical ? _jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: "pluginId", meta: route.nodePluginId, compact: true }), _jsx(StaticRow, { title: "relationship", meta: relationship ?? 'unknown', compact: true })] }) : null] });
|
|
4138
|
+
}
|
|
3626
4139
|
}
|
|
3627
4140
|
else if (route.name === 'installPlacement' && plugin) {
|
|
3628
4141
|
breadcrumb = `${plugin.displayName} / 添加`;
|
|
@@ -3812,6 +4325,14 @@ export function ConsoleAppV4(props) {
|
|
|
3812
4325
|
const allTaskCandidates = props.controller.listPluginWriteTaskCandidates(route.goal);
|
|
3813
4326
|
const currentTargetKeys = new Set(allTaskCandidates.map(pluginWriteTaskTargetKey));
|
|
3814
4327
|
const missingTargetKeys = route.targetKeys?.filter((targetKey) => !currentTargetKeys.has(targetKey)) ?? [];
|
|
4328
|
+
const recommendedPluginIds = route.recommendedPluginIds
|
|
4329
|
+
? new Set(route.recommendedPluginIds) : undefined;
|
|
4330
|
+
const addedTargetKeys = retainedTargetKeys && recommendedPluginIds
|
|
4331
|
+
? allTaskCandidates.filter((candidate) => recommendedPluginIds.has(candidate.target.pluginId)
|
|
4332
|
+
&& !retainedTargetKeys.has(pluginWriteTaskTargetKey(candidate)))
|
|
4333
|
+
.map(pluginWriteTaskTargetKey)
|
|
4334
|
+
: [];
|
|
4335
|
+
const targetSetChanged = missingTargetKeys.length > 0 || addedTargetKeys.length > 0;
|
|
3815
4336
|
const candidates = allTaskCandidates
|
|
3816
4337
|
.filter((candidate) => retainedTargetKeys
|
|
3817
4338
|
? retainedTargetKeys.has(pluginWriteTaskTargetKey(candidate))
|
|
@@ -3947,8 +4468,8 @@ export function ConsoleAppV4(props) {
|
|
|
3947
4468
|
})).values()];
|
|
3948
4469
|
const taskLabel = pluginWriteTaskLabel(route.goal);
|
|
3949
4470
|
const startTaskReview = (executionPolicy) => {
|
|
3950
|
-
if (
|
|
3951
|
-
setActionError(
|
|
4471
|
+
if (targetSetChanged) {
|
|
4472
|
+
setActionError('冻结的精确位置已变化。请返回调整后重新开始。');
|
|
3952
4473
|
return;
|
|
3953
4474
|
}
|
|
3954
4475
|
props.controller.startPluginWriteTaskReview({
|
|
@@ -3974,14 +4495,14 @@ export function ConsoleAppV4(props) {
|
|
|
3974
4495
|
? 'PgUp/PgDn 查看全部位置 · ↑↓ 移动 · Enter 选择 · Esc 返回'
|
|
3975
4496
|
: '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
3976
4497
|
actions = [
|
|
3977
|
-
...(
|
|
4498
|
+
...(!targetSetChanged && !versionEvidenceChanged
|
|
3978
4499
|
&& blockedCandidates.length === 0 && executable.length > 0 ? [{
|
|
3979
4500
|
label: `确认${taskLabel} ${executable.length} 个位置…`,
|
|
3980
4501
|
description: '进入统一影响确认;执行前再次检查当前证据',
|
|
3981
4502
|
status: 'success',
|
|
3982
4503
|
run: () => startTaskReview('all'),
|
|
3983
4504
|
}] : []),
|
|
3984
|
-
...(
|
|
4505
|
+
...(!targetSetChanged && !versionEvidenceChanged
|
|
3985
4506
|
&& blockedCandidates.length > 0 && executable.length > 0 ? [{
|
|
3986
4507
|
label: `先处理可执行的 ${executable.length} 个位置…`,
|
|
3987
4508
|
description: `${blockedCandidates.length} 个受阻位置保留,不会无声跳过`,
|
|
@@ -4005,7 +4526,7 @@ export function ConsoleAppV4(props) {
|
|
|
4005
4526
|
setScrollOffset(0);
|
|
4006
4527
|
},
|
|
4007
4528
|
}] : []),
|
|
4008
|
-
{ label: '返回插件详情', run: back },
|
|
4529
|
+
{ label: route.recommendedPluginIds ? '返回调整选择' : '返回插件详情', run: back },
|
|
4009
4530
|
];
|
|
4010
4531
|
const taskTargetLimit = policy.mode === 'compact' ? 3 : 6;
|
|
4011
4532
|
const taskTargetOffset = Math.min(batchTargetOffset, Math.max(0, candidates.length - 1));
|
|
@@ -4025,7 +4546,7 @@ export function ConsoleAppV4(props) {
|
|
|
4025
4546
|
: _jsx(StaticRow, { title: "\u5F53\u524D\u6CA1\u6709\u53EF\u6267\u884C\u76EE\u6807", status: "muted", compact: true }), candidates.length > taskTargetOffset + targetRows.length
|
|
4026
4547
|
? _jsx(StaticRow, { title: `↓ 后面还有 ${candidates.length - taskTargetOffset - targetRows.length} 个位置`, status: "muted", compact: true }) : null] }), blockedCandidates.length > 0 ? _jsx(Callout, { status: "warning", title: `${blockedCandidates.length} 个位置需要先处理或决定`, description: executable.length > 0
|
|
4027
4548
|
? '可以先处理可执行项,或进入下方对应入口处理受阻位置。'
|
|
4028
|
-
: '请进入下方对应入口处理;返回后会重新读取当前证据。', 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, 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 })] });
|
|
4549
|
+
: '请进入下方对应入口处理;返回后会重新读取当前证据。', 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\u51BB\u7ED3\u8303\u56F4\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 })] });
|
|
4029
4550
|
}
|
|
4030
4551
|
else if (route.name === 'pluginDetail' && plugin) {
|
|
4031
4552
|
breadcrumb = `我的插件 / ${plugin.displayName}`;
|
|
@@ -4079,8 +4600,14 @@ export function ConsoleAppV4(props) {
|
|
|
4079
4600
|
setScrollOffset(0);
|
|
4080
4601
|
},
|
|
4081
4602
|
} : undefined;
|
|
4082
|
-
const
|
|
4083
|
-
const
|
|
4603
|
+
const issuesForPlugin = props.controller.listUserIssues().filter((candidate) => issueMatchesTarget(candidate, plugin.pluginId));
|
|
4604
|
+
const configIssue = issuesForPlugin.find((candidate) => candidate.group === 'config');
|
|
4605
|
+
const exceptionalAction = configIssue ? {
|
|
4606
|
+
label: '查看处理建议',
|
|
4607
|
+
description: '当前插件设置问题需要先完成或修正设置',
|
|
4608
|
+
status: 'warning',
|
|
4609
|
+
run: () => go({ name: 'issueDetail', issueId: configIssue.issueId }),
|
|
4610
|
+
} : plugin.aggregateStatus === 'unavailable'
|
|
4084
4611
|
? plugin.capabilityUnsupported && !plugin.sourceUnavailable ? {
|
|
4085
4612
|
label: '需要升级 AW CLI',
|
|
4086
4613
|
description: '查看缺少的功能和安全恢复方向',
|
|
@@ -4096,38 +4623,54 @@ export function ConsoleAppV4(props) {
|
|
|
4096
4623
|
label: '查看处理建议',
|
|
4097
4624
|
description: '当前问题需要先确认原因或处理方式',
|
|
4098
4625
|
status: 'warning',
|
|
4099
|
-
run: () =>
|
|
4100
|
-
? go({ name: 'issueDetail', issueId:
|
|
4101
|
-
:
|
|
4626
|
+
run: () => issuesForPlugin.length === 1
|
|
4627
|
+
? go({ name: 'issueDetail', issueId: issuesForPlugin[0].issueId })
|
|
4628
|
+
: issuesForPlugin.length > 1
|
|
4629
|
+
? go({ name: 'issueList' })
|
|
4630
|
+
: go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
4102
4631
|
} : undefined;
|
|
4103
|
-
const
|
|
4104
|
-
|
|
4632
|
+
const aboutAction = {
|
|
4633
|
+
actionId: 'about',
|
|
4105
4634
|
label: '使用说明',
|
|
4106
4635
|
description: '查看用途、支持平台和来源',
|
|
4107
4636
|
run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }),
|
|
4108
4637
|
};
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4638
|
+
const primary = exceptionalAction ?? restoreAction ?? updateAction
|
|
4639
|
+
?? settingsAction ?? aboutAction;
|
|
4640
|
+
actions = dedupeActions([
|
|
4641
|
+
{ ...primary, actionId: primary === aboutAction ? 'about'
|
|
4642
|
+
: primary === settingsAction ? 'settings'
|
|
4643
|
+
: primary === updateAction ? 'update'
|
|
4644
|
+
: primary === restoreAction ? 'restore' : 'exceptional' },
|
|
4645
|
+
...(settingsAction && primary !== settingsAction
|
|
4646
|
+
? [{ ...settingsAction, actionId: 'settings' }] : [aboutAction]),
|
|
4116
4647
|
{
|
|
4648
|
+
actionId: 'more',
|
|
4117
4649
|
label: '更多操作…',
|
|
4118
|
-
description:
|
|
4650
|
+
description: settingsAction && primary !== settingsAction
|
|
4651
|
+
? '平台维护、依赖、使用说明、通用 Skills、来源和诊断'
|
|
4652
|
+
: '平台维护、依赖、通用 Skills、来源和诊断',
|
|
4119
4653
|
run: () => go({ name: 'pluginMore', pluginId: plugin.pluginId }),
|
|
4120
4654
|
},
|
|
4121
|
-
];
|
|
4655
|
+
]);
|
|
4122
4656
|
const dependencyOverviewForDetail = dependencyOverview(plugin);
|
|
4123
|
-
const
|
|
4124
|
-
|
|
4125
|
-
?
|
|
4126
|
-
:
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4657
|
+
const dependencyDetailText = dependencyOverviewForDetail
|
|
4658
|
+
&& dependencyOverviewForDetail.summary.totalCount > 0
|
|
4659
|
+
? `使用 ${dependencyOverviewForDetail.summary.totalCount} 个所需插件;尚未确认它们在这些位置的实际使用状态。`
|
|
4660
|
+
: undefined;
|
|
4661
|
+
const placementConclusions = plugin.installedPlacements.map((placement) => ({
|
|
4662
|
+
platform: placement.platform,
|
|
4663
|
+
...pluginPlacementConclusion(placement),
|
|
4664
|
+
}));
|
|
4665
|
+
const hasUnavailablePlacement = placementConclusions.some((placement) => placement.status === 'error');
|
|
4666
|
+
const hasUncertainPlacement = placementConclusions.some((placement) => placement.status === 'warning');
|
|
4667
|
+
const confirmedAvailablePlacements = placementConclusions.filter((placement) => placement.status === 'success');
|
|
4668
|
+
const detailViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(Math.min(placementConclusions.length, 2) + 4));
|
|
4669
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: hasUnavailablePlacement ? 'error'
|
|
4670
|
+
: hasUncertainPlacement ? 'warning'
|
|
4671
|
+
: confirmedAvailablePlacements.length > 0 ? 'success' : 'info', title: hasUnavailablePlacement ? '部分使用位置暂时不可用'
|
|
4672
|
+
: hasUncertainPlacement ? '部分使用位置需要处理'
|
|
4673
|
+
: confirmedAvailablePlacements.length > 0 ? '已确认的使用位置可用' : '尚未确认使用位置', children: placementConclusions.map((placement) => _jsx(StaticRow, { title: platformLabel(placement.platform), meta: placement.text, status: placement.status, compact: true }, placement.platform)) }), updateDescription ? _jsx(Callout, { status: "warning", title: "\u6709\u53EF\u7528\u66F4\u65B0", description: updateDescription }) : null, dependencyDetailText ? _jsx(StaticRow, { title: dependencyDetailText, status: "muted", compact: true }) : null, _jsx(SectionCard, { title: "\u63D2\u4EF6\u64CD\u4F5C", children: _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: detailViewport.offset, limit: detailViewport.limit }) })] });
|
|
4131
4674
|
}
|
|
4132
4675
|
else if (route.name === 'pluginMore' && plugin) {
|
|
4133
4676
|
breadcrumb = `我的插件 / ${plugin.displayName} / 更多操作`;
|
|
@@ -4323,17 +4866,18 @@ export function ConsoleAppV4(props) {
|
|
|
4323
4866
|
};
|
|
4324
4867
|
if (action.id === 'sync')
|
|
4325
4868
|
return {
|
|
4326
|
-
label:
|
|
4869
|
+
label: `同步${scopeLabel(action.scope)}的使用位置…`,
|
|
4327
4870
|
description: placement?.state === 'installed'
|
|
4328
|
-
? placement.
|
|
4871
|
+
? (placement.scopedPlacements?.find((entry) => entry.scope === action.scope)?.recovery
|
|
4872
|
+
?? placement.recovery)?.find((entry) => entry.command === 'sync')?.description
|
|
4329
4873
|
?? '进入现有 Review、进度和结果链修复 needsSync 或 drift'
|
|
4330
4874
|
: undefined,
|
|
4331
4875
|
run: () => placement?.state === 'installed'
|
|
4332
|
-
? beginReview({ kind: 'pluginWrite', action: 'sync', pluginId: plugin.pluginId, platform:
|
|
4876
|
+
? beginReview({ kind: 'pluginWrite', action: 'sync', pluginId: plugin.pluginId, platform: action.platform, scope: action.scope })
|
|
4333
4877
|
: undefined,
|
|
4334
4878
|
};
|
|
4335
4879
|
if (action.id === 'reinstall')
|
|
4336
|
-
return { label:
|
|
4880
|
+
return { label: `重新安装${scopeLabel(action.scope)}位置…`, description: '保留现有设置并重新生成受管内容', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: action.platform, scope: action.scope }) };
|
|
4337
4881
|
if (action.id === 'remove')
|
|
4338
4882
|
return { label: `从${scopeLabel(action.scope)}移除…`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, action.platform, action.scope) };
|
|
4339
4883
|
if (action.id === 'cleanup')
|
|
@@ -4380,7 +4924,7 @@ export function ConsoleAppV4(props) {
|
|
|
4380
4924
|
...(restrictedForceCleanup ? [restrictedForceCleanup] : []),
|
|
4381
4925
|
];
|
|
4382
4926
|
breadcrumb = `${plugin.displayName} / ${platformLabel(route.platform)}`;
|
|
4383
|
-
const installedPlacementRows = placement?.state === 'installed' ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u4F4D\u7F6E", meta: placement.desiredScopes?.map(scopeLabel).join('、') ||
|
|
4927
|
+
const installedPlacementRows = placement?.state === 'installed' ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u5DF2\u4FDD\u5B58\u4F4D\u7F6E", meta: placement.desiredScopes?.map(scopeLabel).join('、') || '无', compact: true }), _jsx(StaticRow, { title: "\u5F53\u524D\u4F4D\u7F6E", meta: placement.actualScopes?.map(scopeLabel).join('、') || '未启用', compact: true }), _jsx(StaticRow, { title: "\u72B6\u6001", meta: placement.actualState === 'ready' ? '可用'
|
|
4384
4928
|
: placement.actualState === 'needsSync' ? '需要同步'
|
|
4385
4929
|
: placement.actualState === 'blocked' ? '已阻断'
|
|
4386
4930
|
: placement.health === 'incomplete' ? '需要处理' : '暂时不可用', status: placement.actualState === 'ready' ? 'success' : placement.actualState === 'blocked' ? 'error' : 'warning', compact: true }), placement.health !== 'ready'
|
|
@@ -4697,12 +5241,24 @@ export function ConsoleAppV4(props) {
|
|
|
4697
5241
|
description = route.layer.scope === 'project'
|
|
4698
5242
|
? '修改将保存到当前项目。'
|
|
4699
5243
|
: '修改将保存为个人默认,并影响个人的所有项目;项目设置仍可覆盖。';
|
|
4700
|
-
actions =
|
|
5244
|
+
actions = configSaveImpactError ? [
|
|
5245
|
+
{
|
|
5246
|
+
label: '重新检查影响',
|
|
5247
|
+
description: '重新读取当前安装位置;检查成功前不能保存',
|
|
5248
|
+
run: () => setConfigSaveImpactAttempt((value) => value + 1),
|
|
5249
|
+
},
|
|
5250
|
+
{ label: '返回修改', run: back },
|
|
5251
|
+
] : configSaveImpact ? [
|
|
4701
5252
|
hasConsumers ? {
|
|
4702
5253
|
label: '保存并生效',
|
|
4703
5254
|
description: `保存设置,并重新应用:${targetSummary}`,
|
|
4704
5255
|
status: 'success',
|
|
4705
5256
|
run: () => beginConfigSave(true),
|
|
5257
|
+
} : configSaveImpact.impactState === 'unknown' ? {
|
|
5258
|
+
label: '仅保存设置(应用范围尚未确认)',
|
|
5259
|
+
description: '保存所选设置层;不扩大到未确认的安装位置',
|
|
5260
|
+
status: 'warning',
|
|
5261
|
+
run: () => beginConfigSave(false),
|
|
4706
5262
|
} : {
|
|
4707
5263
|
label: '保存设置',
|
|
4708
5264
|
description: '当前修改没有需要重新应用的已安装目标',
|
|
@@ -4713,6 +5269,10 @@ export function ConsoleAppV4(props) {
|
|
|
4713
5269
|
label: '只保存,稍后应用',
|
|
4714
5270
|
description: '保存所选设置层,不重新应用当前目标',
|
|
4715
5271
|
run: () => beginConfigSave(false),
|
|
5272
|
+
}] : configSaveImpact.impactState === 'unknown' ? [{
|
|
5273
|
+
label: '重新检查影响',
|
|
5274
|
+
description: '再次读取当前安装位置和配置消费者',
|
|
5275
|
+
run: () => setConfigSaveImpactAttempt((value) => value + 1),
|
|
4716
5276
|
}] : []),
|
|
4717
5277
|
{ label: '返回修改', run: back },
|
|
4718
5278
|
] : [{ label: '返回修改', run: back }];
|
|
@@ -4720,9 +5280,11 @@ export function ConsoleAppV4(props) {
|
|
|
4720
5280
|
? _jsx(ProgressRow, { title: "\u5206\u6790\u5B9E\u9645\u914D\u7F6E\u6D88\u8D39\u8005", state: "active" })
|
|
4721
5281
|
: hasConsumers
|
|
4722
5282
|
? affectedTargets.map((target) => _jsx(StaticRow, { title: `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`, meta: "\u9700\u8981\u91CD\u65B0\u5E94\u7528", status: "warning", compact: true }, `${target.platform}:${target.scope}`))
|
|
4723
|
-
: _jsx(StaticRow, { title: configSaveImpactError
|
|
4724
|
-
? '
|
|
4725
|
-
:
|
|
5283
|
+
: _jsx(StaticRow, { title: configSaveImpactError
|
|
5284
|
+
? '影响检查失败;设置尚未保存'
|
|
5285
|
+
: configSaveImpact?.impactState === 'unknown'
|
|
5286
|
+
? '应用范围尚未确认;可以仅保存并稍后重查'
|
|
5287
|
+
: '没有需要重新应用的已安装目标', status: configSaveImpactError || configSaveImpact?.impactState === 'unknown' ? 'warning' : 'success', compact: true }), _jsx(StaticRow, { title: "\u5176\u4ED6\u8BBE\u7F6E\u5C42\u4FDD\u6301\u4E0D\u53D8", status: "success", compact: true })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4726
5288
|
}
|
|
4727
5289
|
else if (route.name === 'configFieldAction' && plugin && activeField) {
|
|
4728
5290
|
const layer = configLayerLabel(route.layer);
|
|
@@ -5251,7 +5813,9 @@ export function ConsoleAppV4(props) {
|
|
|
5251
5813
|
const overview = props.controller.getSourceOverview(source.id);
|
|
5252
5814
|
const generation = props.controller.getCatalogGeneration(source.id);
|
|
5253
5815
|
const freshness = props.controller.getCatalogFreshness(source.id);
|
|
5254
|
-
const health =
|
|
5816
|
+
const health = generation?.state !== undefined && generation.state !== 'ready'
|
|
5817
|
+
? 'unavailable'
|
|
5818
|
+
: overview.health?.state ?? source.state;
|
|
5255
5819
|
const presentation = catalogSourceListPresentation({
|
|
5256
5820
|
freshness,
|
|
5257
5821
|
activeVersion: generation?.active?.catalogVersion,
|
|
@@ -5302,16 +5866,16 @@ export function ConsoleAppV4(props) {
|
|
|
5302
5866
|
: freshness?.state === 'incompatible'
|
|
5303
5867
|
? generationState === 'repairRequired'
|
|
5304
5868
|
? `待检查版本需要升级 AW 或修改来源后才能使用;损坏的当前启用版本仍可按旧版本精确修复。`
|
|
5305
|
-
: activeVersion
|
|
5869
|
+
: activeVersion && generationState === 'ready'
|
|
5306
5870
|
? `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能;当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
5307
5871
|
: `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能;这个来源没有可用目录版本。`
|
|
5308
|
-
: freshness?.state === 'checkFailed' && activeVersion
|
|
5872
|
+
: freshness?.state === 'checkFailed' && activeVersion && generationState === 'ready'
|
|
5309
5873
|
? `更新检查失败,但当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
5310
|
-
: freshness?.state === 'updateAvailable'
|
|
5874
|
+
: freshness?.state === 'updateAvailable' && generationState === 'ready'
|
|
5311
5875
|
? `当前使用 ${activeVersion ?? '未启用'};待检查版本 ${candidateVersion ?? '未知'} 可更新。`
|
|
5312
|
-
: freshness?.state === 'localChangesAvailable'
|
|
5876
|
+
: freshness?.state === 'localChangesAvailable' && generationState === 'ready'
|
|
5313
5877
|
? '本地插件目录内容已变化;确认前仍使用当前启用版本。'
|
|
5314
|
-
: activeVersion ? `当前使用目录版本 ${activeVersion}。`
|
|
5878
|
+
: activeVersion && generationState === 'ready' ? `当前使用目录版本 ${activeVersion}。`
|
|
5315
5879
|
: generationState === 'stateInvalid' ? '目录版本状态无效。确认恢复后可以重新启用这个来源。'
|
|
5316
5880
|
: generationState === 'repairRequired' ? combinedRepairAndMismatch
|
|
5317
5881
|
? '当前目录版本损坏,且来源配置已变化。验证待检查版本后可精确修复同一版本,或明确切换到新版本。'
|
|
@@ -5384,7 +5948,7 @@ export function ConsoleAppV4(props) {
|
|
|
5384
5948
|
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
5385
5949
|
}];
|
|
5386
5950
|
const managementActions = [
|
|
5387
|
-
{ label: '
|
|
5951
|
+
{ label: '来源设置', description: '下载方式、版本、自动检查和高级地址目录', run: () => openSourceEdit(route.sourceId) },
|
|
5388
5952
|
...(generationState === 'incompatible'
|
|
5389
5953
|
|| (freshness?.state === 'incompatible' && generationState !== 'ready') ? [] : [{
|
|
5390
5954
|
label: generationState === 'ready' ? '设为主来源…' : '启用并设为主来源…',
|
|
@@ -5422,18 +5986,43 @@ export function ConsoleAppV4(props) {
|
|
|
5422
5986
|
run: () => props.controller.fullValidateCatalogSource(route.sourceId),
|
|
5423
5987
|
},
|
|
5424
5988
|
];
|
|
5989
|
+
const sourcePrimaryAction = generationAction.length === 0 ? [] : [{
|
|
5990
|
+
...generationAction[0],
|
|
5991
|
+
...(generationState === 'repairRequired' || generationState === 'stateInvalid'
|
|
5992
|
+
|| generationState === 'unavailable'
|
|
5993
|
+
? {
|
|
5994
|
+
label: '恢复来源',
|
|
5995
|
+
description: generationState === 'repairRequired'
|
|
5996
|
+
? '优先取得当前版本并精确修复;不会自动切换版本'
|
|
5997
|
+
: '取得并验证可用目录后再启用',
|
|
5998
|
+
status: 'warning',
|
|
5999
|
+
}
|
|
6000
|
+
: {}),
|
|
6001
|
+
}];
|
|
5425
6002
|
actions = [
|
|
5426
|
-
...
|
|
6003
|
+
...sourcePrimaryAction,
|
|
5427
6004
|
managementActions[0],
|
|
5428
6005
|
{
|
|
5429
|
-
label: '
|
|
5430
|
-
description: '
|
|
6006
|
+
label: '更多操作…',
|
|
6007
|
+
description: '替换候选、主来源、移除、检查和技术详情',
|
|
5431
6008
|
run: () => go({ name: 'sourceMore', sourceId: route.sourceId }),
|
|
5432
6009
|
},
|
|
5433
6010
|
];
|
|
5434
6011
|
const sourceActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 5));
|
|
5435
|
-
const freshnessLabel =
|
|
5436
|
-
|
|
6012
|
+
const freshnessLabel = freshness?.state === 'checkFailed' && generationState !== 'ready'
|
|
6013
|
+
? '检查失败' : catalogSourceFreshnessLabel(freshness);
|
|
6014
|
+
const sourceStateLabel = generationState === 'ready' ? '来源可用'
|
|
6015
|
+
: generationState === 'repairRequired' ? '来源目录损坏,当前无法使用'
|
|
6016
|
+
: generationState === 'incompatible' ? '当前 AW 无法使用这个目录版本'
|
|
6017
|
+
: generationState === 'configMismatch' ? '来源设置与当前目录不一致'
|
|
6018
|
+
: generationState === 'stateInvalid' ? '来源状态需要恢复'
|
|
6019
|
+
: '来源尚未启用';
|
|
6020
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusCard, { title: sourceStateLabel, status: generationState === 'ready' ? 'success'
|
|
6021
|
+
: generationState === 'incompatible' ? 'error' : 'warning', description: activeVersion ? `当前版本 ${activeVersion}` : '当前没有可用目录版本', children: [_jsx(StaticRow, { title: "\u5F71\u54CD", meta: generationState === 'ready'
|
|
6022
|
+
? freshness?.state === 'checkFailed' ? '更新检查失败;当前版本继续可用'
|
|
6023
|
+
: freshness?.state === 'updateAvailable' ? `可选择更新到 ${candidateVersion ?? '待检查版本'}`
|
|
6024
|
+
: '插件浏览和现有使用不受阻止'
|
|
6025
|
+
: '当前不能从这个来源浏览或恢复插件', status: generationState === 'ready' ? 'muted' : 'warning', compact: true }), _jsx(StaticRow, { title: "\u66F4\u65B0\u68C0\u67E5", meta: freshnessLabel, status: freshness?.state === 'checkFailed' ? 'warning' : undefined, compact: true })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: sourceActionViewport.offset, limit: sourceActionViewport.limit })] });
|
|
5437
6026
|
}
|
|
5438
6027
|
else if (route.name === 'sourceMore') {
|
|
5439
6028
|
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
@@ -5618,32 +6207,37 @@ export function ConsoleAppV4(props) {
|
|
|
5618
6207
|
const draft = activeDraft;
|
|
5619
6208
|
breadcrumb = `插件来源 / ${route.sourceId} / 编辑`;
|
|
5620
6209
|
title = draft?.mode === 'add' ? `检查新来源 ${draft.sourceId}` : `编辑 ${route.sourceId}`;
|
|
5621
|
-
description = '
|
|
6210
|
+
description = '在一个草稿中修改来源设置,最后查看修改汇总。';
|
|
5622
6211
|
const locationSummary = draft
|
|
5623
6212
|
? projectCatalogSourceLocation(draft.location)
|
|
5624
6213
|
: detail?.locationSummary ?? '不可用';
|
|
5625
|
-
const
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
})();
|
|
5632
|
-
if (!current || current.type !== 'git' || current.location === location)
|
|
5633
|
-
return;
|
|
5634
|
-
const next = { ...current, location };
|
|
5635
|
-
setSourceDraft(next);
|
|
5636
|
-
setDirty(true);
|
|
5637
|
-
props.controller.replace({ name: 'sourceEdit', sourceId: route.sourceId, owner: next.owner });
|
|
5638
|
-
};
|
|
6214
|
+
const configuredSource = props.controller.getConfiguredSource(route.sourceId);
|
|
6215
|
+
const currentLocation = draft?.location
|
|
6216
|
+
?? (configuredSource?.type === 'git' ? configuredSource.url : undefined);
|
|
6217
|
+
const officialTransport = currentLocation === OFFICIAL_CATALOG_SOURCE_URL ? 'HTTPS'
|
|
6218
|
+
: currentLocation === OFFICIAL_CATALOG_SOURCE_SSH_URL ? 'SSH' : undefined;
|
|
6219
|
+
const catalogSubdir = draft ? draft.subdir : detail?.subdir;
|
|
5639
6220
|
actions = [
|
|
5640
|
-
...(route.sourceId === 'official' &&
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
6221
|
+
...(route.sourceId === 'official' && officialTransport ? [{
|
|
6222
|
+
label: '下载方式',
|
|
6223
|
+
meta: officialTransport,
|
|
6224
|
+
description: officialTransport === 'HTTPS'
|
|
6225
|
+
? '默认方式;无需预先配置 SSH 密钥'
|
|
6226
|
+
: '适合已配置 GitHub SSH 访问的用户',
|
|
6227
|
+
run: () => {
|
|
6228
|
+
const active = draft ?? (configuredSource
|
|
6229
|
+
? sourceDraftFromSource(configuredSource, `source-update:${route.sourceId}:${Date.now()}`)
|
|
6230
|
+
: undefined);
|
|
6231
|
+
if (!active)
|
|
6232
|
+
return;
|
|
6233
|
+
setSourceTransportOverlay({ owner: active.owner });
|
|
6234
|
+
setOverlayFocus(officialTransport === 'HTTPS' ? 0 : 1);
|
|
6235
|
+
},
|
|
6236
|
+
}] : [{ label: draft?.type === 'path' || detail?.type === 'path' ? '本地目录' : 'Git 仓库地址', description: locationSummary, run: () => startDraftField('location') }]),
|
|
5644
6237
|
...(draft?.type === 'git' || !draft && detail?.type === 'git' ? [
|
|
5645
6238
|
{ label: '版本引用', description: draft ? draft.refPolicy === 'explicit' ? `固定:${draft.ref}` : '跟随 AW 版本通道' : detail?.refPolicy === 'explicit' ? `固定:${detail.explicitRef}` : '跟随 AW 版本通道', run: () => startDraftField('refPolicy') },
|
|
5646
|
-
{ label: 'Catalog 子目录', description:
|
|
6239
|
+
{ label: '高级:Catalog 子目录', description: catalogSubdir === '' || catalogSubdir === undefined
|
|
6240
|
+
? '仓库根目录' : catalogSubdir, run: () => startDraftField('subdir') },
|
|
5647
6241
|
] : []),
|
|
5648
6242
|
{ label: '自动检查更新', description: (draft?.autoCheck ?? detail?.autoCheck) ? '已开启' : '已关闭', selected: draft?.autoCheck ?? detail?.autoCheck, run: () => {
|
|
5649
6243
|
if (!draft) {
|
|
@@ -5668,20 +6262,28 @@ export function ConsoleAppV4(props) {
|
|
|
5668
6262
|
go({ name: 'sourceSave', owner: draft.owner });
|
|
5669
6263
|
} },
|
|
5670
6264
|
];
|
|
5671
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
6265
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u5F53\u524D\u8BBE\u7F6E", compact: policy.mode === 'compact', children: _jsx(StaticRow, { title: "\u6765\u6E90\u7C7B\u578B", meta: (draft?.type ?? detail?.type) === 'git' ? 'Git 仓库' : '本地目录', compact: true }) }), _jsx(SemanticRows, { actions: actions, focus: sourceTransportOverlay ? -1 : focus, kind: "section", compact: policy.mode === 'compact' })] });
|
|
5672
6266
|
}
|
|
5673
6267
|
else if (route.name === 'sourceSave' && sourceDraft?.owner === route.owner) {
|
|
5674
6268
|
const changes = sourceDraftChanges(sourceDraft);
|
|
6269
|
+
const savedSource = props.controller.getConfiguredSource(sourceDraft.sourceId);
|
|
6270
|
+
const protocolOnly = changes.length === 1 && changes[0]?.title === 'Git 仓库地址'
|
|
6271
|
+
&& savedSource?.type === 'git'
|
|
6272
|
+
&& (savedSource.url === OFFICIAL_CATALOG_SOURCE_URL
|
|
6273
|
+
|| savedSource.url === OFFICIAL_CATALOG_SOURCE_SSH_URL)
|
|
6274
|
+
&& (sourceDraft.location === OFFICIAL_CATALOG_SOURCE_URL
|
|
6275
|
+
|| sourceDraft.location === OFFICIAL_CATALOG_SOURCE_SSH_URL);
|
|
5675
6276
|
breadcrumb = `插件来源 / ${sourceDraft.sourceId} / 修改汇总`;
|
|
5676
6277
|
context = `${changes.length} 项未保存`;
|
|
5677
6278
|
title = sourceDraft.mode === 'add' ? `保存新来源 ${sourceDraft.sourceId}?` : `保存 ${sourceDraft.sourceId} 的修改?`;
|
|
5678
6279
|
description = '选择是否在保存后验证候选并启用。保存成功与启用成功会分别报告。';
|
|
5679
6280
|
actions = [
|
|
5680
|
-
{ label: '保存并启用', description: '保存来源配置,再验证并切换当前目录版本', status: 'success', run: () => beginSourceReview(sourceDraft, true) },
|
|
5681
|
-
{ label: '只保存', description: '只保存来源配置;当前目录版本保持不变', run: () => beginSourceReview(sourceDraft, false) },
|
|
6281
|
+
...(protocolOnly ? [] : [{ label: '保存并启用', description: '保存来源配置,再验证并切换当前目录版本', status: 'success', run: () => beginSourceReview(sourceDraft, true) }]),
|
|
6282
|
+
{ label: protocolOnly ? '只保存下载方式' : '只保存', description: '只保存来源配置;当前目录版本保持不变', status: protocolOnly ? 'success' : undefined, run: () => beginSourceReview(sourceDraft, false) },
|
|
6283
|
+
...(protocolOnly ? [{ label: '保存并启用…', description: '保存后验证并切换当前目录版本', run: () => beginSourceReview(sourceDraft, true) }] : []),
|
|
5682
6284
|
{ label: '返回修改', run: back },
|
|
5683
6285
|
];
|
|
5684
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u6765\u6E90\u53D8\u5316", compact: policy.mode === 'compact', children: changes.map((change) => _jsx(ReviewChangeRow, { title: `~ ${change.title}`, description: `${change.before ?? '未设置'} → ${change.after}`, compact:
|
|
6286
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u6765\u6E90\u53D8\u5316", compact: policy.mode === 'compact', children: changes.map((change) => _jsx(ReviewChangeRow, { title: `~ ${change.title}`, description: `${change.before ?? '未设置'} → ${change.after}`, compact: policy.mode === 'compact' }, change.title)) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
5685
6287
|
}
|
|
5686
6288
|
else if (route.name === 'scriptTimeout' && pendingReviewIntent) {
|
|
5687
6289
|
breadcrumb = '高级选项 / 安装前检查脚本';
|
|
@@ -5796,12 +6398,12 @@ export function ConsoleAppV4(props) {
|
|
|
5796
6398
|
go({ name: 'pluginDependencies', pluginId: issue.pluginId });
|
|
5797
6399
|
}
|
|
5798
6400
|
else if (entry.id === 'viewDependencyIssue' && entry.pluginId) {
|
|
5799
|
-
const dependencyIssue = props.controller.listUserIssues().find((candidate) => candidate.pluginId
|
|
6401
|
+
const dependencyIssue = props.controller.listUserIssues().find((candidate) => issueMatchesTarget(candidate, entry.pluginId, issue.dependencyPlatform, issue.dependencyScope)
|
|
5800
6402
|
&& candidate.issueId !== issue.issueId);
|
|
5801
6403
|
if (dependencyIssue)
|
|
5802
6404
|
go({ name: 'issueDetail', issueId: dependencyIssue.issueId });
|
|
5803
|
-
else
|
|
5804
|
-
|
|
6405
|
+
else if (!showTechnical)
|
|
6406
|
+
toggleTechnical();
|
|
5805
6407
|
}
|
|
5806
6408
|
else
|
|
5807
6409
|
toggleTechnical();
|
|
@@ -6031,14 +6633,12 @@ export function ConsoleAppV4(props) {
|
|
|
6031
6633
|
const batchReview = review.operationKind === 'pluginWriteBatch';
|
|
6032
6634
|
const taskReview = review.operationKind === 'pluginWriteTask';
|
|
6033
6635
|
const catalogManagementReview = review.operationKind === 'catalogManagement';
|
|
6034
|
-
const sharedImpactConfirmed = !review.requiresConfirmation
|
|
6035
|
-
|| confirmedSharedReviewToken === review.reviewToken;
|
|
6036
6636
|
breadcrumb = `${review.subject.name} / ${review.title}`;
|
|
6037
6637
|
title = `${snapshot.result?.state === 'stale' ? '状态已变化 · ' : ''}${review.operationKind === 'portableInstall' || taskReview
|
|
6038
6638
|
? review.title : `确认${review.title}`} ${review.subject.name}`;
|
|
6039
6639
|
description = snapshot.result?.state === 'stale'
|
|
6040
6640
|
? '当前状态已变化。请检查新的变化后再次确认。'
|
|
6041
|
-
: review.
|
|
6641
|
+
: review.description;
|
|
6042
6642
|
if (taskReview) {
|
|
6043
6643
|
context = '精确目标 · 用户任务';
|
|
6044
6644
|
help = 'PgUp/PgDn 查看全部影响 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
@@ -6048,20 +6648,19 @@ export function ConsoleAppV4(props) {
|
|
|
6048
6648
|
help = 'PgUp/PgDn 查看全部根插件和依赖 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6049
6649
|
}
|
|
6050
6650
|
actions = [
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
{ label: review.primaryLabel, description: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, disabled: sharedImpactConfirmed ? undefined : '请先确认共享影响', run: () => {
|
|
6651
|
+
{ label: review.primaryLabel, description: review.requiresConfirmation
|
|
6652
|
+
? '打开独立风险确认;执行时再次检查当前状态'
|
|
6653
|
+
: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, run: () => {
|
|
6654
|
+
if (review.requiresConfirmation) {
|
|
6655
|
+
setOperationConfirmation({
|
|
6656
|
+
reviewToken: review.reviewToken,
|
|
6657
|
+
returnFocus: focus,
|
|
6658
|
+
returnScrollOffset: scrollOffset,
|
|
6659
|
+
warningOffset: 0,
|
|
6660
|
+
});
|
|
6661
|
+
setOverlayFocus(0);
|
|
6662
|
+
return;
|
|
6663
|
+
}
|
|
6065
6664
|
if (operationBusy.current)
|
|
6066
6665
|
return;
|
|
6067
6666
|
operationBusy.current = true;
|
|
@@ -6072,6 +6671,65 @@ export function ConsoleAppV4(props) {
|
|
|
6072
6671
|
operationBusy.current = false;
|
|
6073
6672
|
});
|
|
6074
6673
|
} },
|
|
6674
|
+
...(taskReview
|
|
6675
|
+
&& pendingReviewIntent?.kind === 'pluginWriteTask'
|
|
6676
|
+
&& pendingReviewIntent.goal === 'update'
|
|
6677
|
+
&& batchDraft?.frozenPluginIds ? [{
|
|
6678
|
+
label: `调整选择(${new Set(pendingReviewIntent.targets
|
|
6679
|
+
.map((target) => target.pluginId)).size})`,
|
|
6680
|
+
description: '只可删减或恢复本次冻结的推荐插件;调整后重新读取证据并建立 Review',
|
|
6681
|
+
run: () => {
|
|
6682
|
+
if (!props.controller.returnToPluginWriteTaskBatchSelection(batchDraft.flowId)) {
|
|
6683
|
+
setActionError('推荐选择已失效。请返回我的插件重新开始。');
|
|
6684
|
+
return;
|
|
6685
|
+
}
|
|
6686
|
+
setBatchDraft((current) => current ? {
|
|
6687
|
+
...current,
|
|
6688
|
+
stage: 'select',
|
|
6689
|
+
showSelected: false,
|
|
6690
|
+
} : current);
|
|
6691
|
+
setFocus(0);
|
|
6692
|
+
setScrollOffset(0);
|
|
6693
|
+
setShowTechnical(false);
|
|
6694
|
+
setActionError(undefined);
|
|
6695
|
+
},
|
|
6696
|
+
}] : []),
|
|
6697
|
+
...(taskReview
|
|
6698
|
+
&& pendingReviewIntent?.kind === 'pluginWriteTask'
|
|
6699
|
+
&& route.name === 'review'
|
|
6700
|
+
&& batchDraft ? [{
|
|
6701
|
+
label: '修改处理范围',
|
|
6702
|
+
description: '只可删减或恢复本次已有位置;修改后重新读取证据',
|
|
6703
|
+
run: () => {
|
|
6704
|
+
const targets = pendingReviewIntent.targets;
|
|
6705
|
+
setBatchDraft((current) => current ? {
|
|
6706
|
+
...current,
|
|
6707
|
+
maintenanceGoal: pendingReviewIntent.goal,
|
|
6708
|
+
maintenanceTargets: current.maintenanceGoal === pendingReviewIntent.goal
|
|
6709
|
+
&& current.maintenanceTargets
|
|
6710
|
+
? current.maintenanceTargets : targets,
|
|
6711
|
+
selectedMaintenanceTargetKeys: targets.map(batchTargetKey),
|
|
6712
|
+
} : current);
|
|
6713
|
+
back();
|
|
6714
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
6715
|
+
setFocus(0);
|
|
6716
|
+
setScrollOffset(0);
|
|
6717
|
+
},
|
|
6718
|
+
}] : []),
|
|
6719
|
+
...(batchReview
|
|
6720
|
+
&& pendingReviewIntent?.kind === 'pluginWriteBatch'
|
|
6721
|
+
&& pendingReviewIntent.selection.kind === 'targets'
|
|
6722
|
+
&& pendingReviewIntent.action !== 'install'
|
|
6723
|
+
&& batchDraft?.maintenanceTargets ? [{
|
|
6724
|
+
label: '修改处理范围',
|
|
6725
|
+
description: '只可删减或恢复本次已有位置;修改后重新读取证据',
|
|
6726
|
+
run: () => {
|
|
6727
|
+
back();
|
|
6728
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
6729
|
+
setFocus(0);
|
|
6730
|
+
setScrollOffset(0);
|
|
6731
|
+
},
|
|
6732
|
+
}] : []),
|
|
6075
6733
|
...(installReviewPluginId
|
|
6076
6734
|
&& (snapshot.installPlacementDraft?.targets.length ?? 0) > 0 ? [{
|
|
6077
6735
|
label: '查看/调整依赖位置',
|
|
@@ -6085,7 +6743,7 @@ export function ConsoleAppV4(props) {
|
|
|
6085
6743
|
setScrollOffset(0);
|
|
6086
6744
|
},
|
|
6087
6745
|
}] : []),
|
|
6088
|
-
...(
|
|
6746
|
+
...((pendingReviewIntent?.kind === 'pluginWrite'
|
|
6089
6747
|
&& (pendingReviewIntent.action === 'install'
|
|
6090
6748
|
|| pendingReviewIntent.action === 'refreshAndSync'
|
|
6091
6749
|
|| pendingReviewIntent.action === 'reinstall'
|
|
@@ -6111,7 +6769,7 @@ export function ConsoleAppV4(props) {
|
|
|
6111
6769
|
: back },
|
|
6112
6770
|
];
|
|
6113
6771
|
const groupedChanges = review.changes.some((change) => change.group);
|
|
6114
|
-
const compressedReview = policy.mode === 'compact'
|
|
6772
|
+
const compressedReview = policy.mode === 'compact';
|
|
6115
6773
|
const groupTitle = {
|
|
6116
6774
|
requested: review.operationKind === 'portableInstall' ? '已选择 Skills · 当前插件' : '你选择的插件',
|
|
6117
6775
|
dependencies: review.operationKind === 'portableInstall' ? '已选择 Skills · 来自依赖插件' : '必需依赖',
|
|
@@ -6149,8 +6807,15 @@ export function ConsoleAppV4(props) {
|
|
|
6149
6807
|
'projectIntent', 'userIntent', 'sharedCodexNative',
|
|
6150
6808
|
'userActivation', 'projectActivation', 'awExtensions', 'runtime',
|
|
6151
6809
|
]);
|
|
6152
|
-
const
|
|
6153
|
-
|
|
6810
|
+
const userReviewChanges = review.changes.filter((change) => change.group === undefined
|
|
6811
|
+
|| !isTechnicalReviewGroup(change.group)
|
|
6812
|
+
|| change.kind === 'warning'
|
|
6813
|
+
|| change.kind === 'remove');
|
|
6814
|
+
const visibleSpecs = groupedSpecs.map((group) => technicalGroups.has(group.key) ? {
|
|
6815
|
+
...group,
|
|
6816
|
+
label: '风险',
|
|
6817
|
+
changes: group.changes.filter((change) => change.kind === 'warning' || change.kind === 'remove'),
|
|
6818
|
+
} : group).filter((group) => group.changes.length > 0);
|
|
6154
6819
|
const headingLines = compressedReview ? 0 : visibleSpecs.filter((group) => group.label).length;
|
|
6155
6820
|
const staticRowBudget = Math.max(visibleSpecs.length, responsiveBudget(7, 12) - headingLines);
|
|
6156
6821
|
const allocations = new Map(visibleSpecs.map((group) => [group.key, 1]));
|
|
@@ -6186,15 +6851,15 @@ export function ConsoleAppV4(props) {
|
|
|
6186
6851
|
})] }, group.key);
|
|
6187
6852
|
});
|
|
6188
6853
|
const batchChangeLimit = Math.max(1, responsiveBudget(6, 10) - actions.length);
|
|
6189
|
-
const batchChanges =
|
|
6854
|
+
const batchChanges = userReviewChanges.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
6190
6855
|
const batchHiddenBefore = batchTargetOffset;
|
|
6191
6856
|
const prioritizedReviewChanges = [
|
|
6192
|
-
...
|
|
6193
|
-
...
|
|
6857
|
+
...userReviewChanges.filter((change) => change.kind === 'warning'),
|
|
6858
|
+
...userReviewChanges.filter((change) => change.kind !== 'warning'),
|
|
6194
6859
|
];
|
|
6195
6860
|
const pagedReviewChanges = prioritizedReviewChanges.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
6196
|
-
const changeGroups = batchReview ||
|
|
6197
|
-
? _jsxs(Box, { flexDirection: "column", children: [batchHiddenBefore > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${batchHiddenBefore} 项`, status: "muted", compact: true }) : null, (batchReview ? batchChanges : pagedReviewChanges).map((change, index) => _jsx(ReviewChangeRow, { title: `${change.sourceId ? `${change.sourceId} · ` : ''}${change.relation === 'requested' ? '根插件' : change.relation === 'direct' ? '直接依赖' : change.relation === 'transitive' ? '间接依赖' : ''} ${reviewSymbol(change)} ${change.title}`, meta: change.description, status: changeStatus(change), compact: true }, `${change.batchGroupId ?? ''}:${change.pluginId ?? change.title}:${index}`)), Math.max(0,
|
|
6861
|
+
const changeGroups = batchReview || userReviewChanges.length > staticRowBudget
|
|
6862
|
+
? _jsxs(Box, { flexDirection: "column", children: [batchHiddenBefore > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${batchHiddenBefore} 项`, status: "muted", compact: true }) : null, (batchReview ? batchChanges : pagedReviewChanges).map((change, index) => _jsx(ReviewChangeRow, { title: `${change.sourceId ? `${change.sourceId} · ` : ''}${change.relation === 'requested' ? '根插件' : change.relation === 'direct' ? '直接依赖' : change.relation === 'transitive' ? '间接依赖' : ''} ${reviewSymbol(change)} ${change.title}`, meta: change.description, status: changeStatus(change), compact: true }, `${change.batchGroupId ?? ''}:${change.pluginId ?? change.title}:${index}`)), Math.max(0, userReviewChanges.length - batchTargetOffset - (batchReview ? batchChanges : pagedReviewChanges).length) > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${Math.max(0, userReviewChanges.length - batchTargetOffset - (batchReview ? batchChanges : pagedReviewChanges).length)} 项`, status: "muted", compact: true }) : null] })
|
|
6198
6863
|
: standardChangeGroups;
|
|
6199
6864
|
const technicalDetails = review.technicalDetails ?? [];
|
|
6200
6865
|
const technicalDetailLimit = responsiveBudget(1, 3);
|
|
@@ -6231,11 +6896,11 @@ export function ConsoleAppV4(props) {
|
|
|
6231
6896
|
const widenedDependencyCount = new Set(installDependencyTargets
|
|
6232
6897
|
.filter((target) => target.affectsOtherProjects && target.action !== 'keep')
|
|
6233
6898
|
.map((target) => target.pluginId)).size;
|
|
6234
|
-
if (
|
|
6899
|
+
if (userReviewChanges.length > staticRowBudget || showTechnical
|
|
6235
6900
|
&& (review.changes.length > technicalChangeBudget || technicalDetails.length > technicalDetailLimit)) {
|
|
6236
6901
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6237
6902
|
}
|
|
6238
|
-
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: review.
|
|
6903
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: review.subject.name, meta: `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, 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: catalogManagementReview ? '! 需要确认本次写入' : '! 需要确认共享影响', meta: !catalogManagementReview && review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: catalogManagementReview ? review.description : review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" }), showTechnical ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, detail)) }) : null] }) });
|
|
6239
6904
|
}
|
|
6240
6905
|
else if (route.name === 'progress') {
|
|
6241
6906
|
const taskOperation = snapshot.activeReview?.operationKind === 'pluginWriteTask';
|
|
@@ -6250,15 +6915,26 @@ export function ConsoleAppV4(props) {
|
|
|
6250
6915
|
breadcrumb = catalogManagementOperation ? '目录管理 / 进度' : taskOperation ? '插件任务 / 进度' : portableOperation ? '通用 Skills / 进度' : issueOperation ? '需要处理 / 进度' : sourceOperation ? '插件来源 / 保存' : configOperation ? '设置 / 进度' : '插件操作 / 进度';
|
|
6251
6916
|
const configApply = snapshot.activeReview?.operationKind === 'configSave'
|
|
6252
6917
|
&& snapshot.activeReview.changes.some((change) => change.group === 'configImpact' && change.kind === 'change');
|
|
6918
|
+
const progressAction = snapshot.activeReview?.taskGoal === 'restore' ? '恢复'
|
|
6919
|
+
: snapshot.activeReview?.taskGoal === 'update' ? '更新'
|
|
6920
|
+
: configApply ? '应用设置'
|
|
6921
|
+
: snapshot.activeReview?.pluginAction === 'install' ? '添加'
|
|
6922
|
+
: snapshot.activeReview?.pluginAction === 'refreshAndSync' ? '更新'
|
|
6923
|
+
: snapshot.activeReview?.pluginAction === 'reinstall' ? '恢复'
|
|
6924
|
+
: snapshot.activeReview?.pluginAction === 'uninstall' ? '移除'
|
|
6925
|
+
: snapshot.activeReview?.pluginAction === 'cleanup' ? '清理'
|
|
6926
|
+
: snapshot.activeReview?.pluginAction === 'sync' ? '同步' : '处理';
|
|
6253
6927
|
const catalogGenerationOperation = snapshot.activeReview?.operationKind === 'catalogGeneration';
|
|
6254
6928
|
const catalogSourcesRecovery = snapshot.activeReview?.operationKind === 'catalogSourcesRecovery';
|
|
6255
|
-
title = catalogManagementOperation
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6929
|
+
title = catalogManagementOperation
|
|
6930
|
+
? `正在${snapshot.activeReview?.title.replace(/^确认/u, '') ?? '完成目录管理任务'}`
|
|
6931
|
+
: taskOperation ? `正在${progressAction}插件` : portableOperation ? '正在安装通用 Skills' : issueOperation ? `正在${progressAction}插件`
|
|
6932
|
+
: catalogGenerationOperation ? '正在验证并启用目录版本'
|
|
6933
|
+
: catalogSourcesRecovery ? '正在恢复插件来源配置'
|
|
6934
|
+
: sourceOperation ? '正在保存来源配置'
|
|
6935
|
+
: configOperation ? configApply ? '正在保存并应用设置' : '正在保存设置'
|
|
6936
|
+
: `正在${progressAction}插件`;
|
|
6937
|
+
description = catalogManagementOperation ? '正在完成已确认的目录管理任务。'
|
|
6262
6938
|
: issueOperation ? '完成后会重新读取状态并检查问题。'
|
|
6263
6939
|
: catalogGenerationOperation ? '正在验证并应用已确认的目录版本操作。'
|
|
6264
6940
|
: catalogSourcesRecovery ? '正在备份损坏配置并恢复官方默认来源。'
|
|
@@ -6289,7 +6965,7 @@ export function ConsoleAppV4(props) {
|
|
|
6289
6965
|
if (entry.state === 'completed')
|
|
6290
6966
|
return { label: '已完成', status: 'success' };
|
|
6291
6967
|
if (entry.state === 'running')
|
|
6292
|
-
return { label:
|
|
6968
|
+
return { label: `正在${progressAction}`, status: 'info' };
|
|
6293
6969
|
if (entry.state === 'failed')
|
|
6294
6970
|
return { label: '失败', status: 'error' };
|
|
6295
6971
|
if (entry.state === 'skipped')
|
|
@@ -6367,13 +7043,13 @@ export function ConsoleAppV4(props) {
|
|
|
6367
7043
|
return step === 2 ? '正在安装' : step === 3 ? '正在验证' : step === 1 ? '正在生成' : '正在检查';
|
|
6368
7044
|
};
|
|
6369
7045
|
const compactPortableProgress = policy.mode === 'compact' && showTechnical;
|
|
6370
|
-
content = catalogManagementOperation ? _jsxs(StatusCard, { status: failedProgress ? 'error' : 'info', title: "
|
|
7046
|
+
content = catalogManagementOperation ? _jsxs(StatusCard, { status: failedProgress ? 'error' : 'info', title: "\u76EE\u5F55\u7BA1\u7406", children: [_jsx(ProgressRow, { title: "\u786E\u8BA4\u8BA1\u5212", meta: "\u5B8C\u6210", state: "complete" }), _jsx(ProgressRow, { title: currentPhase || '准备执行', description: progress.current?.target, meta: `${elapsedSeconds}s`, state: failedProgress ? 'error' : currentPhase ? 'active' : 'pending', compact: policy.mode === 'compact' }), _jsx(ProgressRow, { title: "\u9A8C\u8BC1\u5E76\u8BB0\u5F55\u7ED3\u679C", state: validationActive ? 'active' : 'pending' }), !showTechnical && latestMessage ? _jsx(StaticRow, { title: latestMessage, status: "muted", compact: true }) : null, showTechnical ? technicalMessages.map((message, index) => _jsx(StaticRow, { title: message, compact: true }, `${index}:${message}`)) : null, exitAfterBusy ? _jsx(Callout, { status: "warning", title: "\u64CD\u4F5C\u5B8C\u6210\u540E\u9000\u51FA" }) : null] }) : portableOperation ? _jsxs(StatusCard, { status: failedProgress ? 'error' : 'info', title: "Skills CLI", children: [_jsx(ProgressRow, { title: "\u68C0\u67E5\u63D2\u4EF6\u548C\u4F9D\u8D56", description: `${snapshot.activeReview?.subject.name ?? '当前插件'} · ${portableDraft?.dependencySummary?.directCount ?? 0} 个直接依赖`, meta: portableProgressMeta(0), state: portableProgressState(0), compact: compactPortableProgress }), _jsx(ProgressRow, { title: "\u751F\u6210\u5DF2\u9009\u5019\u9009 Skills", description: `${portableProviderCount} 个来源或依赖插件提供 ${portableSelectedCount} 个 Skills`, meta: portableProgressMeta(1), state: portableProgressState(1), compact: compactPortableProgress }), _jsx(ProgressRow, { title: `安装已选择的 ${portableSelectedCount} 个 Skills`, description: `Agent:${portableDraft?.agentMode === 'explicit' ? portableDraft.requestedAgents.join('、') : 'Skills CLI 默认选择'}`, meta: portableProgressMeta(2), state: portableProgressState(2), compact: compactPortableProgress }), _jsx(ProgressRow, { title: "\u9A8C\u8BC1\u7ED3\u679C", description: "\u9010\u9879\u786E\u8BA4\u6240\u9009 Skill \u7684\u5B89\u88C5\u76EE\u6807", meta: portableProgressMeta(3), state: portableProgressState(3), compact: compactPortableProgress }), showTechnical ? _jsxs(TechnicalDetails, { children: [technicalMessages.map((message, index) => _jsx(StaticRow, { title: message, compact: true }, `${index}:${message}`)), hiddenTechnicalMessages > 0 ? _jsx(StaticRow, { title: `另有 ${hiddenTechnicalMessages} 条技术消息`, status: "muted", compact: true }) : null] }) : null] }) : _jsxs(StatusCard, { status: "info", children: [dependencyProgress ? _jsxs(_Fragment, { children: [_jsx(ProgressRow, { title: "\u68C0\u67E5\u4F9D\u8D56\u5173\u7CFB", state: relationStep }), _jsx(ProgressRow, { title: "\u751F\u6210\u6267\u884C\u8BA1\u5212", state: planStep }), _jsx(ProgressRow, { title: `${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: sourceOperation ? '检查来源操作输入' : configOperation ? '验证设置值' : '检查兼容性', state: !currentPhase ? 'pending' : preflightActive ? 'active' : 'complete' }), _jsx(ProgressRow, { title: progress.current?.phase || (sourceOperation ? '保存来源配置' : configOperation ? '保存设置层并重新应用' : '连接到所选目标平台'), description: progress.current?.target ? `当前:${progress.current.target}` : undefined, meta: `${elapsedSeconds}s`, state: failedProgress ? 'error' : executionActive ? 'active' : validationActive ? 'complete' : 'pending', compact: policy.mode === 'compact' }), _jsx(ProgressRow, { title: "\u9A8C\u8BC1\u7ED3\u679C", state: failedProgress ? 'error' : validationActive ? 'active' : 'pending' })] }), dependencyProgress ? _jsxs(SectionCard, { title: "\u6267\u884C\u76EE\u6807", meta: `${snapshot.progressTargets.length} 个目标`, children: [progressTargetOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${progressTargetOffset} 个目标`, status: "muted", compact: true }) : null, visibleProgressTargets.map((entry) => {
|
|
6371
7047
|
const state = progressLabel(entry);
|
|
6372
7048
|
const relation = entry.relation === 'requested' ? '你选择的插件' : entry.relation === 'direct' ? '直接依赖' : '间接依赖';
|
|
6373
7049
|
const title = props.controller.getPluginOverview(entry.pluginId)?.displayName
|
|
6374
7050
|
?? entry.pluginId.split('/').at(-1);
|
|
6375
7051
|
return _jsx(ResultRow, { title: title, description: relation, meta: state.label, status: state.status ?? 'muted', compact: policy.mode === 'compact' }, `${entry.pluginId}:${entry.platform}:${entry.scope}`);
|
|
6376
|
-
}), hiddenProgressTargets > 0 ? _jsx(StaticRow, { title: `${batchProgress ? '↓ 后面还有' : '另有'} ${hiddenProgressTargets} 个目标`, status: "muted", compact: true }) : null] }) : null, !showTechnical && latestMessage && (!dependencyProgress || dependencyMessageLimit > 0) ? _jsx(StaticRow, { title: latestMessage, status: "muted", compact: true }) : null, showTechnical ? (dependencyProgress
|
|
7052
|
+
}), hiddenProgressTargets > 0 ? _jsx(StaticRow, { title: `${batchProgress ? '↓ 后面还有' : '另有'} ${hiddenProgressTargets} 个目标`, status: "muted", compact: true }) : null] }) : null, !showTechnical && latestMessage && (!dependencyProgress || dependencyMessageLimit > 0) ? _jsx(StaticRow, { title: latestMessage, status: "muted", compact: true }) : null, showTechnical && currentPhase ? _jsx(StaticRow, { title: "\u5F53\u524D\u6280\u672F\u9636\u6BB5", meta: currentPhase, compact: true }) : null, showTechnical ? (dependencyProgress
|
|
6377
7053
|
? dependencyMessageLimit > 0 ? technicalMessages.slice(-dependencyMessageLimit) : []
|
|
6378
7054
|
: technicalMessages).map((message, index) => _jsx(StaticRow, { title: message, compact: true }, `${index}:${message}`)) : null, exitAfterBusy ? _jsx(Callout, { status: "warning", title: "\u64CD\u4F5C\u5B8C\u6210\u540E\u9000\u51FA" }) : null] });
|
|
6379
7055
|
}
|
|
@@ -6494,15 +7170,15 @@ export function ConsoleAppV4(props) {
|
|
|
6494
7170
|
: taskOperation
|
|
6495
7171
|
? blocked ? '任务被安全阻止。已完成和未完成目标按实际结果保留。'
|
|
6496
7172
|
: partial ? `${taskSummary?.retryTargets?.length ?? 0} 个失败目标可重试 · ${taskBlockedCount} 个目标需要决定。`
|
|
6497
|
-
: `${taskSummary?.rootResults.length ?? 0}
|
|
7173
|
+
: `${taskSummary?.rootResults.length ?? 0} 个所选插件已处理;下方显示使用位置。`
|
|
6498
7174
|
: batchOperation
|
|
6499
7175
|
? blocked
|
|
6500
7176
|
? '批量写入被阻止。已完成和未完成目标按实际结果保留。'
|
|
6501
7177
|
: partial
|
|
6502
7178
|
? (resultSummary?.batch?.retryTargets?.length ?? 0) > 0
|
|
6503
|
-
? `${resultSummary?.batch?.failedRequestedPluginIds.length ?? 0}
|
|
7179
|
+
? `${resultSummary?.batch?.failedRequestedPluginIds.length ?? 0} 个所选插件失败,涉及 ${resultSummary?.batch?.retryTargets?.length ?? 0} 个位置。成功目标不会重放。`
|
|
6504
7180
|
: '失败目标无法精确定位。请查看本次记录后重新操作。'
|
|
6505
|
-
: `${resultSummary?.batch?.requestedPluginIds.length ?? 0}
|
|
7181
|
+
: `${resultSummary?.batch?.requestedPluginIds.length ?? 0} 个所选插件已处理;下方显示使用位置和所需插件。`
|
|
6506
7182
|
: portableOperation
|
|
6507
7183
|
? blocked
|
|
6508
7184
|
? result.code === 'operationReviewExpired' || result.code === 'reviewTicketExpired'
|
|
@@ -6898,34 +7574,35 @@ export function ConsoleAppV4(props) {
|
|
|
6898
7574
|
return { label: '未开始', status: 'muted' };
|
|
6899
7575
|
return { label: '待确认', status: 'warning' };
|
|
6900
7576
|
};
|
|
6901
|
-
const
|
|
7577
|
+
const taskSelectedPluginRows = taskRootResults.map((root) => {
|
|
6902
7578
|
const rootState = taskRootState(root.state);
|
|
6903
7579
|
const rootName = props.controller.getPluginOverview(root.pluginId)?.displayName
|
|
6904
7580
|
?? root.pluginId.split('/').at(-1);
|
|
6905
7581
|
const requestedTargets = taskSummary?.requestedTargets.filter((target) => target.pluginId === root.pluginId) ?? [];
|
|
6906
|
-
return
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
7582
|
+
return _jsx(ResultRow, { title: rootName, description: `${requestedTargets.length} 个使用位置`, meta: rootState.label, status: rootState.status, compact: true }, `root:${root.pluginId}`);
|
|
7583
|
+
});
|
|
7584
|
+
const taskLocationRows = taskRootResults.flatMap((root) => {
|
|
7585
|
+
const requestedTargets = taskSummary?.requestedTargets.filter((target) => target.pluginId === root.pluginId) ?? [];
|
|
7586
|
+
return requestedTargets.map((target) => {
|
|
7587
|
+
const key = [target.pluginId, target.platform, target.scope].join('\0');
|
|
7588
|
+
const blockedTarget = blockedTaskTargets.get(key);
|
|
7589
|
+
const targetResult = root.targets.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
|
|
7590
|
+
&& entry.scope === target.scope);
|
|
7591
|
+
const targetState = blockedTarget ? { label: '待确认', status: 'warning' }
|
|
7592
|
+
: targetResult?.state === 'completed' ? { label: '完成', status: 'success' }
|
|
7593
|
+
: targetResult?.state === 'kept' ? { label: '无需更改', status: 'success' }
|
|
7594
|
+
: targetResult?.state === 'failed' ? { label: '失败', status: 'error' }
|
|
7595
|
+
: { label: '未开始', status: 'muted' };
|
|
7596
|
+
return _jsx(ResultRow, { title: `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`, description: blockedTarget
|
|
7597
|
+
? pluginWriteTaskBlockLabel(blockedTarget.reason)
|
|
7598
|
+
: targetResult?.errorCode, meta: targetState.label, status: targetState.status, compact: !blockedTarget }, `target:${key}`);
|
|
7599
|
+
});
|
|
6923
7600
|
});
|
|
6924
7601
|
const batchResultRows = (resultSummary?.batch?.rootResults ?? []).map((root) => {
|
|
6925
7602
|
const rootState = taskRootState(root.state);
|
|
6926
7603
|
const rootName = props.controller.getPluginOverview(root.pluginId)?.displayName
|
|
6927
7604
|
?? root.pluginId.split('/').at(-1);
|
|
6928
|
-
return _jsx(ResultRow, { title: rootName, description: `${root.targets.length}
|
|
7605
|
+
return _jsx(ResultRow, { title: rootName, description: `${root.targets.length} 个使用位置`, meta: rootState.label, status: rootState.status, compact: true }, `batch-root:${root.pluginId}`);
|
|
6929
7606
|
}).concat(prioritizedTargets.map((target) => {
|
|
6930
7607
|
const state = operationTargetStateLabel(target.state);
|
|
6931
7608
|
const relation = target.relation === 'requested' ? '你选择的插件'
|
|
@@ -6934,14 +7611,16 @@ export function ConsoleAppV4(props) {
|
|
|
6934
7611
|
return _jsx(ResultRow, { title: `${resultTargetName(target)} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`, description: relation, meta: state.label, status: state.status ?? 'muted', compact: true }, `batch-target:${target.pluginId ?? ''}:${target.platform}:${target.scope}`);
|
|
6935
7612
|
}));
|
|
6936
7613
|
const taskResultLimit = Math.max(1, pageActionBudget(5) - resultActionViewport.limit);
|
|
6937
|
-
const rootResultRows = taskOperation
|
|
7614
|
+
const rootResultRows = taskOperation
|
|
7615
|
+
? [...taskSelectedPluginRows, ...taskLocationRows]
|
|
7616
|
+
: batchResultRows;
|
|
6938
7617
|
const taskResultOffset = Math.min(batchTargetOffset, Math.max(0, rootResultRows.length - 1));
|
|
6939
7618
|
const visibleTaskResultRows = rootResultRows.slice(taskResultOffset, taskResultOffset + taskResultLimit);
|
|
6940
7619
|
if (taskOperation || batchOperation || resultTargets.length > resultTargetLimit
|
|
6941
7620
|
|| prioritizedPortableSkills.length > portableSkillLimit) {
|
|
6942
7621
|
help = 'PgUp/PgDn 查看全部项目 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6943
7622
|
}
|
|
6944
|
-
content = catalogManagementOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: resultSummary?.message ?? '
|
|
7623
|
+
content = catalogManagementOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: resultSummary?.message ?? '目录管理结果', children: [catalogManagementResult?.catalogVersion ? _jsx(StaticRow, { title: "\u76EE\u5F55\u7248\u672C", meta: catalogManagementResult.catalogVersion, compact: true }) : null, catalogManagementResult?.store ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u5B9E\u9645\u5220\u9664", meta: `${catalogManagementResult.store.deletedEntries} 项 · ${catalogManagementResult.store.deletedBytes} bytes`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u5B9E\u9645\u4FDD\u7559", meta: `${catalogManagementResult.store.retainedEntries} 项`, compact: true })] }) : null, catalogManagementResult?.publication ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u53D1\u5E03\u72B6\u6001", meta: catalogManagementResult.publication.state, status: catalogManagementResult.publication.state === 'uncertain' ? 'warning' : 'success', compact: true }), _jsx(StaticRow, { title: "\u76EE\u6807", meta: `${catalogManagementResult.publication.remote} · ${catalogManagementResult.publication.branch}`, compact: true }), _jsx(StaticRow, { title: "\u8BA1\u5212\u6807\u8BC6", meta: catalogManagementResult.publication.planDigest.slice(0, 15), compact: true })] }) : catalogManagementResult?.planDigest ? _jsx(StaticRow, { title: "\u8BA1\u5212\u5DF2\u51C6\u5907", meta: catalogManagementResult.planDigest.slice(0, 15), status: "success", compact: true }) : 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 })] }) : taskOperation || batchOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: "\u6240\u9009\u63D2\u4EF6\u7ED3\u679C", children: [taskResultOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${taskResultOffset} 项`, status: "muted", compact: true }) : null, visibleTaskResultRows, rootResultRows.length > taskResultOffset + visibleTaskResultRows.length
|
|
6945
7624
|
? _jsx(StaticRow, { title: "\u2193 \u540E\u9762\u8FD8\u6709", meta: `${rootResultRows.length - taskResultOffset - visibleTaskResultRows.length} 项`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] }) : portableOperation ? _jsxs(Box, { flexDirection: "column", children: [portableTechnicalSummary ? _jsxs(_Fragment, { children: [portableSkillOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${portableSkillOffset} 个 Skills`, status: "muted", compact: true }) : null, portableRows, hiddenPortableSkills > 0 ? _jsx(StaticRow, { title: `另有 ${hiddenPortableSkills} 个 Skills`, status: "muted", compact: true }) : null, portableReceipt?.recovery ? _jsx(StaticRow, { title: "\u5B89\u5168\u6062\u590D", meta: portableReceipt.recovery, status: "warning", compact: true }) : null, _jsx(TechnicalDetails, { children: compactTechnicalRows })] }) : _jsx(_Fragment, { children: _jsxs(ResultCard, { status: resultStatus, title: blocked ? '安装未开始或结果未确认' : partial ? '部分 Skills 未确认' : '通用 Skills 安装结果', children: [portableSkillOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${portableSkillOffset} 个 Skills`, status: "muted", compact: true }) : null, portableRows, hiddenPortableSkills > 0 ? _jsx(StaticRow, { title: `另有 ${hiddenPortableSkills} 个 Skills`, status: "muted", compact: true }) : null, portableReceipt ? _jsx(StaticRow, { title: `Agent:${portableReceipt.agentMode === 'automatic' ? 'Skills CLI 默认选择' : portableReceipt.requestedAgents.join('、')}`, meta: `范围:${scopeLabel(portableReceipt.scope)}`, compact: true }) : null, portableReceipt?.recovery ? policy.mode === 'compact'
|
|
6946
7625
|
? _jsx(StaticRow, { title: "\u5B89\u5168\u6062\u590D", meta: portableReceipt.recovery, status: "warning", compact: true })
|
|
6947
7626
|
: _jsx(Callout, { status: "warning", title: "\u5B89\u5168\u6062\u590D", description: portableReceipt.recovery }) : null, showTechnical && portableReceipt ? _jsxs(TechnicalDetails, { children: [portableReceipt.failureCode ? _jsx(StaticRow, { title: "failure code", meta: portableReceipt.failureCode, compact: true }) : null, _jsx(StaticRow, { title: "verification missing", meta: String(portableReceipt.missing.length), compact: true }), _jsx(StaticRow, { title: "Skills CLI version", meta: portableReceipt.skillsCliVersion, compact: true }), partial || blocked
|
|
@@ -6980,7 +7659,7 @@ export function ConsoleAppV4(props) {
|
|
|
6980
7659
|
if (prioritizedComponents.length > componentLimit) {
|
|
6981
7660
|
help = 'PgUp/PgDn 查看全部组件 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6982
7661
|
}
|
|
6983
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: "canonical pluginId", meta: plugin.pluginId }), _jsx(StaticRow, { title: "\u6765\u6E90", meta: plugin.sourceId }), plugin.capabilityUnsupported ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "failure code", meta: "awCapabilityUnsupported" }), _jsx(StaticRow, { title: "capability \u72B6\u6001", meta: "\u5F53\u524D AW CLI \u4E0D\u652F\u6301", status: "error" }), _jsx(StaticRow, { title: "\u7F3A\u5C11 capability", meta: plugin.missingCapabilities?.join('、') ?? 'Status 未提供具体 capability' }), _jsx(StaticRow, { title: "\u6062\u590D\u65B9\u5411", meta: plugin.capabilityRecovery ?? '升级 AW CLI 到支持所需 capability 的版本后重试。' })] }) : null, plugin.installedPlacements.map((placement) => _jsx(StaticRow, { title: `${platformLabel(placement.platform)} target`, meta: placementStatus(placement).value }, placement.platform)), componentOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${componentOffset} 个组件`, status: "muted", compact: true }) : null, visibleComponents.map((component) => _jsx(StaticRow, { title: `组件 · ${component.state === 'absent' ? '缺失' : component.state}${component.errorCode ? ` · ${component.errorCode}` : ''} · ${component.key} · ${platformLabel(component.platform)} · ${scopeLabel(component.scope)}` }, `${component.platform}:${component.scope}:${component.key}`)), hiddenComponents > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${hiddenComponents} 个组件`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
7662
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [_jsx(StaticRow, { title: "canonical pluginId", meta: plugin.pluginId }), _jsx(StaticRow, { title: "\u6765\u6E90", meta: plugin.sourceId }), plugin.capabilityUnsupported ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "failure code", meta: "awCapabilityUnsupported" }), _jsx(StaticRow, { title: "capability \u72B6\u6001", meta: "\u5F53\u524D AW CLI \u4E0D\u652F\u6301", status: "error" }), _jsx(StaticRow, { title: "\u7F3A\u5C11 capability", meta: plugin.missingCapabilities?.join('、') ?? 'Status 未提供具体 capability' }), _jsx(StaticRow, { title: "\u6062\u590D\u65B9\u5411", meta: plugin.capabilityRecovery ?? '升级 AW CLI 到支持所需 capability 的版本后重试。' })] }) : null, plugin.installedPlacements.map((placement) => _jsx(StaticRow, { title: `${platformLabel(placement.platform)} target`, meta: placementStatus(placement).value }, placement.platform)), componentOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${componentOffset} 个组件`, status: "muted", compact: true }) : null, visibleComponents.map((component) => _jsx(StaticRow, { title: `组件 · ${component.state === 'absent' ? '缺失' : component.state}${component.errorCode ? ` · ${component.errorCode}` : ''} · ${component.key} · ${platformLabel(component.platform)} · ${component.shared ? '平台共享' : scopeLabel(component.scope)}` }, `${component.platform}:${component.shared ? 'shared' : component.scope}:${component.key}`)), hiddenComponents > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${hiddenComponents} 个组件`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
6984
7663
|
}
|
|
6985
7664
|
else {
|
|
6986
7665
|
const missingPlugin = definition.entity === 'plugin' && plugin === undefined;
|
|
@@ -7260,6 +7939,89 @@ export function ConsoleAppV4(props) {
|
|
|
7260
7939
|
}
|
|
7261
7940
|
return;
|
|
7262
7941
|
}
|
|
7942
|
+
if (operationConfirmation) {
|
|
7943
|
+
const activeReview = snapshot.activeReview;
|
|
7944
|
+
const closeConfirmation = () => {
|
|
7945
|
+
setOperationConfirmation(undefined);
|
|
7946
|
+
setFocus(operationConfirmation.returnFocus);
|
|
7947
|
+
setScrollOffset(operationConfirmation.returnScrollOffset);
|
|
7948
|
+
setOverlayFocus(0);
|
|
7949
|
+
};
|
|
7950
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
7951
|
+
closeConfirmation();
|
|
7952
|
+
}
|
|
7953
|
+
else if (key.ctrl && input.toLowerCase() === 'c') {
|
|
7954
|
+
closeConfirmation();
|
|
7955
|
+
requestExit({ code: 0, reason: 'user' });
|
|
7956
|
+
}
|
|
7957
|
+
else if (input.toLowerCase() === 'q') {
|
|
7958
|
+
closeConfirmation();
|
|
7959
|
+
setDiscardForExit(dirty);
|
|
7960
|
+
setOverlay(dirty ? 'discard' : 'exit');
|
|
7961
|
+
}
|
|
7962
|
+
else if (props.onSessionNavigation && (input === 'h' || input === 'H')) {
|
|
7963
|
+
const navigation = input === 'H' ? 'reload' : 'home';
|
|
7964
|
+
closeConfirmation();
|
|
7965
|
+
requestSessionNavigation(navigation);
|
|
7966
|
+
}
|
|
7967
|
+
else if (key.pageUp || key.pageDown) {
|
|
7968
|
+
const warningCount = activeReview
|
|
7969
|
+
? operationRiskLines(activeReview, Math.max(12, size.columns - 8)).length : 0;
|
|
7970
|
+
const warningLimit = policy.mode === 'compact' ? 3 : 7;
|
|
7971
|
+
setOperationConfirmation((current) => current ? {
|
|
7972
|
+
...current,
|
|
7973
|
+
warningOffset: Math.min(Math.max(0, warningCount - warningLimit), Math.max(0, current.warningOffset + (key.pageUp ? -1 : 1))),
|
|
7974
|
+
} : current);
|
|
7975
|
+
}
|
|
7976
|
+
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
7977
|
+
setOverlayFocus((value) => moveIndex(2, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
7978
|
+
}
|
|
7979
|
+
else if (key.return) {
|
|
7980
|
+
if (overlayFocus === 0) {
|
|
7981
|
+
closeConfirmation();
|
|
7982
|
+
}
|
|
7983
|
+
else if (activeReview?.reviewToken !== operationConfirmation.reviewToken) {
|
|
7984
|
+
closeConfirmation();
|
|
7985
|
+
setActionError('确认内容已变化。请重新检查操作汇总。');
|
|
7986
|
+
}
|
|
7987
|
+
else if (!operationBusy.current) {
|
|
7988
|
+
operationBusy.current = true;
|
|
7989
|
+
setOperationConfirmation(undefined);
|
|
7990
|
+
void props.controller.executeOperation(operationConfirmation.reviewToken).then(() => {
|
|
7991
|
+
setFocus(0);
|
|
7992
|
+
setScrollOffset(0);
|
|
7993
|
+
}).finally(() => {
|
|
7994
|
+
operationBusy.current = false;
|
|
7995
|
+
});
|
|
7996
|
+
}
|
|
7997
|
+
}
|
|
7998
|
+
return;
|
|
7999
|
+
}
|
|
8000
|
+
if (sourceTransportOverlay) {
|
|
8001
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
8002
|
+
setSourceTransportOverlay(undefined);
|
|
8003
|
+
setOverlayFocus(0);
|
|
8004
|
+
}
|
|
8005
|
+
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
8006
|
+
setOverlayFocus((value) => moveIndex(2, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
8007
|
+
}
|
|
8008
|
+
else if (key.return && route.name === 'sourceEdit') {
|
|
8009
|
+
const configured = props.controller.getConfiguredSource(route.sourceId);
|
|
8010
|
+
const current = sourceDraft?.owner === sourceTransportOverlay.owner
|
|
8011
|
+
? sourceDraft
|
|
8012
|
+
: configured ? sourceDraftFromSource(configured, sourceTransportOverlay.owner) : undefined;
|
|
8013
|
+
const location = overlayFocus === 0
|
|
8014
|
+
? OFFICIAL_CATALOG_SOURCE_URL : OFFICIAL_CATALOG_SOURCE_SSH_URL;
|
|
8015
|
+
if (current && current.type === 'git' && current.location !== location) {
|
|
8016
|
+
setSourceDraft({ ...current, location });
|
|
8017
|
+
setDirty(true);
|
|
8018
|
+
props.controller.replace({ name: 'sourceEdit', sourceId: route.sourceId, owner: current.owner });
|
|
8019
|
+
}
|
|
8020
|
+
setSourceTransportOverlay(undefined);
|
|
8021
|
+
setOverlayFocus(0);
|
|
8022
|
+
}
|
|
8023
|
+
return;
|
|
8024
|
+
}
|
|
7263
8025
|
if (overlay) {
|
|
7264
8026
|
if (key.ctrl && input.toLowerCase() === 'c') {
|
|
7265
8027
|
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
@@ -7369,6 +8131,13 @@ export function ConsoleAppV4(props) {
|
|
|
7369
8131
|
setReportSaveError(undefined);
|
|
7370
8132
|
return;
|
|
7371
8133
|
}
|
|
8134
|
+
if ((key.pageUp || key.pageDown)
|
|
8135
|
+
&& route.name === 'pluginDependencyNode' && route.view === 'references'
|
|
8136
|
+
&& referenceDetailPagination) {
|
|
8137
|
+
const { total, pageRows } = referenceDetailPagination;
|
|
8138
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
8139
|
+
return;
|
|
8140
|
+
}
|
|
7372
8141
|
if ((key.pageUp || key.pageDown)
|
|
7373
8142
|
&& route.name === 'pluginWriteTask') {
|
|
7374
8143
|
const total = props.controller.listPluginWriteTaskCandidates(route.goal)
|
|
@@ -7380,7 +8149,12 @@ export function ConsoleAppV4(props) {
|
|
|
7380
8149
|
if ((key.pageUp || key.pageDown)
|
|
7381
8150
|
&& route.name === 'review'
|
|
7382
8151
|
&& snapshot.activeReview) {
|
|
7383
|
-
const
|
|
8152
|
+
const userChangeCount = snapshot.activeReview.changes.filter((change) => !isTechnicalReviewGroup(change.group)
|
|
8153
|
+
|| change.kind === 'warning'
|
|
8154
|
+
|| change.kind === 'remove').length;
|
|
8155
|
+
const total = showTechnical
|
|
8156
|
+
? Math.max(snapshot.activeReview.changes.length, snapshot.activeReview.technicalDetails?.length ?? 0)
|
|
8157
|
+
: userChangeCount;
|
|
7384
8158
|
const pageRows = 1;
|
|
7385
8159
|
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
7386
8160
|
return;
|
|
@@ -7930,12 +8704,21 @@ export function ConsoleAppV4(props) {
|
|
|
7930
8704
|
return;
|
|
7931
8705
|
}
|
|
7932
8706
|
if (route.name === 'pluginList') {
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
8707
|
+
const nextQuery = input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)
|
|
8708
|
+
? ''
|
|
8709
|
+
: key.backspace || key.delete
|
|
8710
|
+
? removeLastGrapheme(query)
|
|
8711
|
+
: input && !key.ctrl && !key.meta && input >= ' '
|
|
8712
|
+
? query + input : query;
|
|
8713
|
+
setQuery(nextQuery);
|
|
8714
|
+
const matchingItems = props.controller.listPlugins({
|
|
8715
|
+
mode: route.mode,
|
|
8716
|
+
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
8717
|
+
...(nextQuery ? { query: nextQuery } : {}),
|
|
8718
|
+
installFilter,
|
|
8719
|
+
});
|
|
8720
|
+
const matchingRows = pluginListRowIds(route.mode, matchingItems, nextQuery);
|
|
8721
|
+
setFocus(matchingItems[0] ? matchingRows.indexOf(matchingItems[0].pluginId) : 0);
|
|
7939
8722
|
}
|
|
7940
8723
|
else {
|
|
7941
8724
|
setBatchDraft((draft) => draft ? {
|
|
@@ -7949,7 +8732,8 @@ export function ConsoleAppV4(props) {
|
|
|
7949
8732
|
showSelected: false,
|
|
7950
8733
|
} : draft);
|
|
7951
8734
|
}
|
|
7952
|
-
|
|
8735
|
+
if (route.name === 'batchSelection')
|
|
8736
|
+
setFocus(1);
|
|
7953
8737
|
setScrollOffset(0);
|
|
7954
8738
|
return;
|
|
7955
8739
|
}
|
|
@@ -8023,6 +8807,27 @@ export function ConsoleAppV4(props) {
|
|
|
8023
8807
|
back();
|
|
8024
8808
|
}
|
|
8025
8809
|
else if (globalAction === 'back' && route.name === 'batchPlacement') {
|
|
8810
|
+
if (batchDraft?.showPlacementTargets) {
|
|
8811
|
+
setBatchDraft((current) => current ? { ...current, showPlacementTargets: false } : current);
|
|
8812
|
+
setFocus(5);
|
|
8813
|
+
setScrollOffset(0);
|
|
8814
|
+
}
|
|
8815
|
+
else
|
|
8816
|
+
back();
|
|
8817
|
+
}
|
|
8818
|
+
else if (globalAction === 'back'
|
|
8819
|
+
&& route.name === 'batchSelection'
|
|
8820
|
+
&& batchDraft
|
|
8821
|
+
&& batchDraft.stage !== 'select'
|
|
8822
|
+
&& snapshot.router.currentFlowId === batchDraft.flowId) {
|
|
8823
|
+
moveBatchStage(batchDraft.stage === 'maintenance' ? 'task' : 'select');
|
|
8824
|
+
}
|
|
8825
|
+
else if (globalAction === 'back'
|
|
8826
|
+
&& batchDraft
|
|
8827
|
+
&& route.name !== 'batchSelection'
|
|
8828
|
+
&& snapshot.router.currentFlowId === batchDraft.flowId
|
|
8829
|
+
&& (route.name !== 'pluginWriteTask'
|
|
8830
|
+
|| route.batchOrigin?.flowId === batchDraft.flowId)) {
|
|
8026
8831
|
back();
|
|
8027
8832
|
}
|
|
8028
8833
|
else if (globalAction === 'back'
|
|
@@ -8032,8 +8837,7 @@ export function ConsoleAppV4(props) {
|
|
|
8032
8837
|
back();
|
|
8033
8838
|
}
|
|
8034
8839
|
else if (globalAction === 'back') {
|
|
8035
|
-
if (dirty && route.name !== 'review'
|
|
8036
|
-
&& !(batchDraft && (route.name === 'pluginPreview' || route.name === 'pluginDetail'))) {
|
|
8840
|
+
if (dirty && route.name !== 'review') {
|
|
8037
8841
|
setOverlayFocus(0);
|
|
8038
8842
|
setDiscardForExit(false);
|
|
8039
8843
|
setOverlay('discard');
|
|
@@ -8055,12 +8859,11 @@ export function ConsoleAppV4(props) {
|
|
|
8055
8859
|
...(query ? { query } : {}),
|
|
8056
8860
|
installFilter: nextFilter,
|
|
8057
8861
|
});
|
|
8058
|
-
const
|
|
8059
|
-
|
|
8060
|
-
: -1;
|
|
8862
|
+
const nextRowIds = pluginListRowIds(route.mode, nextItems, query);
|
|
8863
|
+
const retained = selectedPluginId ? nextRowIds.indexOf(selectedPluginId) : -1;
|
|
8061
8864
|
setInstallFilter(nextFilter);
|
|
8062
8865
|
props.controller.updatePageContext({ installFilter: nextFilter });
|
|
8063
|
-
setFocus(retained >= 0 ?
|
|
8866
|
+
setFocus(retained >= 0 ? retained : 0);
|
|
8064
8867
|
setScrollOffset(0);
|
|
8065
8868
|
}
|
|
8066
8869
|
else if (route.name === 'pluginList' && input.toLowerCase() === 'm') {
|
|
@@ -8090,11 +8893,11 @@ export function ConsoleAppV4(props) {
|
|
|
8090
8893
|
if (batchDraft.mode === 'browse') {
|
|
8091
8894
|
setBatchDraft((current) => current ? { ...current, action: 'install' } : current);
|
|
8092
8895
|
props.controller.push({ name: 'batchPlacement' });
|
|
8896
|
+
setFocus(0);
|
|
8897
|
+
setScrollOffset(0);
|
|
8093
8898
|
}
|
|
8094
8899
|
else
|
|
8095
|
-
|
|
8096
|
-
setFocus(0);
|
|
8097
|
-
setScrollOffset(0);
|
|
8900
|
+
moveBatchStage('task');
|
|
8098
8901
|
}
|
|
8099
8902
|
else
|
|
8100
8903
|
setActionError('请至少选择一个插件。');
|
|
@@ -8161,9 +8964,14 @@ export function ConsoleAppV4(props) {
|
|
|
8161
8964
|
const index = tabs.indexOf(current);
|
|
8162
8965
|
const next = tabs[(index + direction + tabs.length) % tabs.length];
|
|
8163
8966
|
const nextItems = props.controller.listPlugins({ mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}), ...(query ? { query } : {}), installFilter });
|
|
8164
|
-
const
|
|
8967
|
+
const nextSourceUnavailable = next !== 'all'
|
|
8968
|
+
&& props.controller.getSourceOverview(next).health?.state === 'unavailable';
|
|
8969
|
+
const nextRowIds = pluginListRowIds(route.mode, nextItems, query, nextSourceUnavailable);
|
|
8970
|
+
const retained = selectedPluginId ? nextRowIds.indexOf(selectedPluginId) : -1;
|
|
8165
8971
|
props.controller.replace({ name: 'pluginList', mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}) }, { ...snapshot.router.current.context, query, installFilter, sourceTabId: next });
|
|
8166
|
-
const retainedFocus = retained >= 0
|
|
8972
|
+
const retainedFocus = retained >= 0
|
|
8973
|
+
? retained
|
|
8974
|
+
: nextItems[0] ? nextRowIds.indexOf(nextItems[0].pluginId) : 0;
|
|
8167
8975
|
setFocus(retainedFocus);
|
|
8168
8976
|
setScrollOffset(retainedFocus >= 4 ? retainedFocus - 3 : 0);
|
|
8169
8977
|
}
|
|
@@ -8176,6 +8984,9 @@ export function ConsoleAppV4(props) {
|
|
|
8176
8984
|
props.controller.togglePortableSkill(candidate.name);
|
|
8177
8985
|
}
|
|
8178
8986
|
else if (key.upArrow || input.toLowerCase() === 'k') {
|
|
8987
|
+
if (route.name === 'pluginDependencyNode' && route.view === 'references') {
|
|
8988
|
+
setBatchTargetOffset(0);
|
|
8989
|
+
}
|
|
8179
8990
|
setFocus((value) => {
|
|
8180
8991
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8181
8992
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
@@ -8209,6 +9020,9 @@ export function ConsoleAppV4(props) {
|
|
|
8209
9020
|
});
|
|
8210
9021
|
}
|
|
8211
9022
|
else if (key.downArrow || input.toLowerCase() === 'j') {
|
|
9023
|
+
if (route.name === 'pluginDependencyNode' && route.view === 'references') {
|
|
9024
|
+
setBatchTargetOffset(0);
|
|
9025
|
+
}
|
|
8212
9026
|
setFocus((value) => {
|
|
8213
9027
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8214
9028
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
@@ -8260,6 +9074,48 @@ export function ConsoleAppV4(props) {
|
|
|
8260
9074
|
if (actionError)
|
|
8261
9075
|
description = actionError;
|
|
8262
9076
|
const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, help: help, size: size, children: content }));
|
|
9077
|
+
if (operationConfirmation && snapshot.activeReview) {
|
|
9078
|
+
const review = snapshot.activeReview;
|
|
9079
|
+
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
9080
|
+
const modalWarningLines = operationRiskLines(review, Math.max(12, modalWidth - 6));
|
|
9081
|
+
const shortWarningLimit = policy.mode === 'compact' ? 4 : 6;
|
|
9082
|
+
const shortConfirmation = modalWarningLines.length <= shortWarningLimit;
|
|
9083
|
+
const warningLines = shortConfirmation
|
|
9084
|
+
? modalWarningLines
|
|
9085
|
+
: operationRiskLines(review, Math.max(12, size.columns - 8));
|
|
9086
|
+
const warningLimit = Math.max(1, policy.mode === 'compact' ? 3 : 7);
|
|
9087
|
+
const warningOffset = Math.min(operationConfirmation.warningOffset, Math.max(0, warningLines.length - warningLimit));
|
|
9088
|
+
const visibleWarnings = shortConfirmation
|
|
9089
|
+
? warningLines : warningLines.slice(warningOffset, warningOffset + warningLimit);
|
|
9090
|
+
const confirmationActions = [
|
|
9091
|
+
{ label: '返回检查', description: '回到完整操作汇总' },
|
|
9092
|
+
{ label: `我已了解影响,${review.primaryLabel}`, status: 'error' },
|
|
9093
|
+
];
|
|
9094
|
+
const confirmationCard = (_jsxs(ReviewCard, { title: "\u8BF7\u786E\u8BA4\u672C\u6B21\u98CE\u9669", description: "\u786E\u8BA4\u53EA\u5BF9\u5F53\u524D\u64CD\u4F5C\u6C47\u603B\u6709\u6548\uFF1B\u5185\u5BB9\u53D8\u5316\u540E\u5FC5\u987B\u91CD\u65B0\u68C0\u67E5\u3002", children: [!shortConfirmation && warningOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${warningOffset} 行`, status: "muted", compact: true }) : null, visibleWarnings.length > 0
|
|
9095
|
+
? visibleWarnings.map((warning, index) => _jsx(StaticRow, { title: warning, status: warning.startsWith('! ') ? 'warning' : 'info', compact: true }, `${warningOffset + index}:${warning}`))
|
|
9096
|
+
: _jsx(StaticRow, { title: "! \u6B64\u64CD\u4F5C\u9700\u8981\u660E\u786E\u6388\u6743", status: "warning", compact: true }), !shortConfirmation && warningLines.length - warningOffset - visibleWarnings.length > 0
|
|
9097
|
+
? _jsx(StaticRow, { title: `↓ 后面还有 ${warningLines.length - warningOffset - visibleWarnings.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: confirmationActions, focus: overlayFocus, kind: "action", compact: true })] }));
|
|
9098
|
+
if (shortConfirmation) {
|
|
9099
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: modalWidth, flexDirection: "column", children: confirmationCard })] });
|
|
9100
|
+
}
|
|
9101
|
+
return _jsx(ScreenFrame, { title: `确认${review.title} ${review.subject.name}?`, breadcrumb: `${review.subject.name} / 风险确认`, context: "\u72EC\u7ACB\u6388\u6743", help: "PgUp/PgDn \u67E5\u770B\u5168\u90E8\u8B66\u544A \u00B7 \u2191\u2193 \u79FB\u52A8 \u00B7 Enter \u9009\u62E9 \u00B7 Esc \u8FD4\u56DE", size: size, children: confirmationCard });
|
|
9102
|
+
}
|
|
9103
|
+
if (sourceTransportOverlay) {
|
|
9104
|
+
const configuredSource = route.name === 'sourceEdit'
|
|
9105
|
+
? props.controller.getConfiguredSource(route.sourceId)
|
|
9106
|
+
: undefined;
|
|
9107
|
+
const currentLocation = sourceDraft?.owner === sourceTransportOverlay.owner
|
|
9108
|
+
&& sourceDraft.type === 'git'
|
|
9109
|
+
? sourceDraft.location
|
|
9110
|
+
: configuredSource?.type === 'git' ? configuredSource.url : undefined;
|
|
9111
|
+
const currentTransport = currentLocation === OFFICIAL_CATALOG_SOURCE_URL ? 'HTTPS'
|
|
9112
|
+
: currentLocation === OFFICIAL_CATALOG_SOURCE_SSH_URL ? 'SSH' : undefined;
|
|
9113
|
+
const transportActions = [
|
|
9114
|
+
{ label: 'HTTPS', description: '默认方式;无需预先配置 SSH 密钥', selected: currentTransport === 'HTTPS' },
|
|
9115
|
+
{ label: 'SSH', description: '适合已配置 GitHub SSH 访问的用户', selected: currentTransport === 'SSH' },
|
|
9116
|
+
];
|
|
9117
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", children: _jsx(ReviewCard, { title: "\u9009\u62E9\u4E0B\u8F7D\u65B9\u5F0F", description: `当前草稿:${currentTransport ?? '未识别'}。选择只更新当前草稿;保存前仍会显示统一汇总。`, children: _jsx(SemanticRows, { actions: transportActions, focus: overlayFocus, kind: "choice", compact: false }) }) })] });
|
|
9118
|
+
}
|
|
8263
9119
|
if (!overlay)
|
|
8264
9120
|
return base;
|
|
8265
9121
|
const helpRows = helpRowsForRoute(route, actions.length, supportsTechnicalDetails, busy, props.onSessionNavigation !== undefined);
|