@lesliechan721/aw 0.1.0-beta.18 → 0.1.0-beta.20
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/rebuild-command.js +0 -5
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +0 -1
- package/dist/src/commands/rebuild-integration.js +3 -152
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- 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 +1300 -437
- 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 +114 -36
- 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 +269 -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,11 +4924,18 @@ 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('、') ||
|
|
4384
|
-
: placement.actualState === 'needsSync'
|
|
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' ? '可用'
|
|
4928
|
+
: placement.actualState === 'needsSync'
|
|
4929
|
+
? placement.sharedNative?.reason === 'outdated'
|
|
4930
|
+
? placement.revision.state === 'upgradeAvailable' ? '有更新' : '版本待同步'
|
|
4931
|
+
: '需要同步'
|
|
4385
4932
|
: placement.actualState === 'blocked' ? '已阻断'
|
|
4386
4933
|
: placement.health === 'incomplete' ? '需要处理' : '暂时不可用', status: placement.actualState === 'ready' ? 'success' : placement.actualState === 'blocked' ? 'error' : 'warning', compact: true }), placement.health !== 'ready'
|
|
4387
|
-
? _jsx(StaticRow, { title: "\u539F\u56E0", meta: placement.blockingReason === 'artifactCollision'
|
|
4934
|
+
? _jsx(StaticRow, { title: "\u539F\u56E0", meta: placement.blockingReason === 'artifactCollision'
|
|
4935
|
+
? '输出位置存在冲突内容'
|
|
4936
|
+
: placement.sharedNative?.reason === 'outdated'
|
|
4937
|
+
? '已安装版本与当前 Catalog 版本不一致'
|
|
4938
|
+
: typedIssue?.explanation ?? '安装状态需要同步,请查看问题详情', status: "warning", compact: true })
|
|
4388
4939
|
: null] }) : null;
|
|
4389
4940
|
if (plugin.aggregateStatus === 'unavailable') {
|
|
4390
4941
|
const sourceOverview = capabilityUnavailable ? undefined : props.controller.getSourceOverview(plugin.sourceId);
|
|
@@ -4697,12 +5248,24 @@ export function ConsoleAppV4(props) {
|
|
|
4697
5248
|
description = route.layer.scope === 'project'
|
|
4698
5249
|
? '修改将保存到当前项目。'
|
|
4699
5250
|
: '修改将保存为个人默认,并影响个人的所有项目;项目设置仍可覆盖。';
|
|
4700
|
-
actions =
|
|
5251
|
+
actions = configSaveImpactError ? [
|
|
5252
|
+
{
|
|
5253
|
+
label: '重新检查影响',
|
|
5254
|
+
description: '重新读取当前安装位置;检查成功前不能保存',
|
|
5255
|
+
run: () => setConfigSaveImpactAttempt((value) => value + 1),
|
|
5256
|
+
},
|
|
5257
|
+
{ label: '返回修改', run: back },
|
|
5258
|
+
] : configSaveImpact ? [
|
|
4701
5259
|
hasConsumers ? {
|
|
4702
5260
|
label: '保存并生效',
|
|
4703
5261
|
description: `保存设置,并重新应用:${targetSummary}`,
|
|
4704
5262
|
status: 'success',
|
|
4705
5263
|
run: () => beginConfigSave(true),
|
|
5264
|
+
} : configSaveImpact.impactState === 'unknown' ? {
|
|
5265
|
+
label: '仅保存设置(应用范围尚未确认)',
|
|
5266
|
+
description: '保存所选设置层;不扩大到未确认的安装位置',
|
|
5267
|
+
status: 'warning',
|
|
5268
|
+
run: () => beginConfigSave(false),
|
|
4706
5269
|
} : {
|
|
4707
5270
|
label: '保存设置',
|
|
4708
5271
|
description: '当前修改没有需要重新应用的已安装目标',
|
|
@@ -4713,6 +5276,10 @@ export function ConsoleAppV4(props) {
|
|
|
4713
5276
|
label: '只保存,稍后应用',
|
|
4714
5277
|
description: '保存所选设置层,不重新应用当前目标',
|
|
4715
5278
|
run: () => beginConfigSave(false),
|
|
5279
|
+
}] : configSaveImpact.impactState === 'unknown' ? [{
|
|
5280
|
+
label: '重新检查影响',
|
|
5281
|
+
description: '再次读取当前安装位置和配置消费者',
|
|
5282
|
+
run: () => setConfigSaveImpactAttempt((value) => value + 1),
|
|
4716
5283
|
}] : []),
|
|
4717
5284
|
{ label: '返回修改', run: back },
|
|
4718
5285
|
] : [{ label: '返回修改', run: back }];
|
|
@@ -4720,9 +5287,11 @@ export function ConsoleAppV4(props) {
|
|
|
4720
5287
|
? _jsx(ProgressRow, { title: "\u5206\u6790\u5B9E\u9645\u914D\u7F6E\u6D88\u8D39\u8005", state: "active" })
|
|
4721
5288
|
: hasConsumers
|
|
4722
5289
|
? 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
|
-
:
|
|
5290
|
+
: _jsx(StaticRow, { title: configSaveImpactError
|
|
5291
|
+
? '影响检查失败;设置尚未保存'
|
|
5292
|
+
: configSaveImpact?.impactState === 'unknown'
|
|
5293
|
+
? '应用范围尚未确认;可以仅保存并稍后重查'
|
|
5294
|
+
: '没有需要重新应用的已安装目标', 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
5295
|
}
|
|
4727
5296
|
else if (route.name === 'configFieldAction' && plugin && activeField) {
|
|
4728
5297
|
const layer = configLayerLabel(route.layer);
|
|
@@ -5251,7 +5820,9 @@ export function ConsoleAppV4(props) {
|
|
|
5251
5820
|
const overview = props.controller.getSourceOverview(source.id);
|
|
5252
5821
|
const generation = props.controller.getCatalogGeneration(source.id);
|
|
5253
5822
|
const freshness = props.controller.getCatalogFreshness(source.id);
|
|
5254
|
-
const health =
|
|
5823
|
+
const health = generation?.state !== undefined && generation.state !== 'ready'
|
|
5824
|
+
? 'unavailable'
|
|
5825
|
+
: overview.health?.state ?? source.state;
|
|
5255
5826
|
const presentation = catalogSourceListPresentation({
|
|
5256
5827
|
freshness,
|
|
5257
5828
|
activeVersion: generation?.active?.catalogVersion,
|
|
@@ -5302,16 +5873,16 @@ export function ConsoleAppV4(props) {
|
|
|
5302
5873
|
: freshness?.state === 'incompatible'
|
|
5303
5874
|
? generationState === 'repairRequired'
|
|
5304
5875
|
? `待检查版本需要升级 AW 或修改来源后才能使用;损坏的当前启用版本仍可按旧版本精确修复。`
|
|
5305
|
-
: activeVersion
|
|
5876
|
+
: activeVersion && generationState === 'ready'
|
|
5306
5877
|
? `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能;当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
5307
5878
|
: `当前 AW 缺少 ${freshness.missingCapabilities?.length ?? 0} 项待检查版本所需功能;这个来源没有可用目录版本。`
|
|
5308
|
-
: freshness?.state === 'checkFailed' && activeVersion
|
|
5879
|
+
: freshness?.state === 'checkFailed' && activeVersion && generationState === 'ready'
|
|
5309
5880
|
? `更新检查失败,但当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
5310
|
-
: freshness?.state === 'updateAvailable'
|
|
5881
|
+
: freshness?.state === 'updateAvailable' && generationState === 'ready'
|
|
5311
5882
|
? `当前使用 ${activeVersion ?? '未启用'};待检查版本 ${candidateVersion ?? '未知'} 可更新。`
|
|
5312
|
-
: freshness?.state === 'localChangesAvailable'
|
|
5883
|
+
: freshness?.state === 'localChangesAvailable' && generationState === 'ready'
|
|
5313
5884
|
? '本地插件目录内容已变化;确认前仍使用当前启用版本。'
|
|
5314
|
-
: activeVersion ? `当前使用目录版本 ${activeVersion}。`
|
|
5885
|
+
: activeVersion && generationState === 'ready' ? `当前使用目录版本 ${activeVersion}。`
|
|
5315
5886
|
: generationState === 'stateInvalid' ? '目录版本状态无效。确认恢复后可以重新启用这个来源。'
|
|
5316
5887
|
: generationState === 'repairRequired' ? combinedRepairAndMismatch
|
|
5317
5888
|
? '当前目录版本损坏,且来源配置已变化。验证待检查版本后可精确修复同一版本,或明确切换到新版本。'
|
|
@@ -5384,7 +5955,7 @@ export function ConsoleAppV4(props) {
|
|
|
5384
5955
|
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
5385
5956
|
}];
|
|
5386
5957
|
const managementActions = [
|
|
5387
|
-
{ label: '
|
|
5958
|
+
{ label: '来源设置', description: '下载方式、版本、自动检查和高级地址目录', run: () => openSourceEdit(route.sourceId) },
|
|
5388
5959
|
...(generationState === 'incompatible'
|
|
5389
5960
|
|| (freshness?.state === 'incompatible' && generationState !== 'ready') ? [] : [{
|
|
5390
5961
|
label: generationState === 'ready' ? '设为主来源…' : '启用并设为主来源…',
|
|
@@ -5422,18 +5993,43 @@ export function ConsoleAppV4(props) {
|
|
|
5422
5993
|
run: () => props.controller.fullValidateCatalogSource(route.sourceId),
|
|
5423
5994
|
},
|
|
5424
5995
|
];
|
|
5996
|
+
const sourcePrimaryAction = generationAction.length === 0 ? [] : [{
|
|
5997
|
+
...generationAction[0],
|
|
5998
|
+
...(generationState === 'repairRequired' || generationState === 'stateInvalid'
|
|
5999
|
+
|| generationState === 'unavailable'
|
|
6000
|
+
? {
|
|
6001
|
+
label: '恢复来源',
|
|
6002
|
+
description: generationState === 'repairRequired'
|
|
6003
|
+
? '优先取得当前版本并精确修复;不会自动切换版本'
|
|
6004
|
+
: '取得并验证可用目录后再启用',
|
|
6005
|
+
status: 'warning',
|
|
6006
|
+
}
|
|
6007
|
+
: {}),
|
|
6008
|
+
}];
|
|
5425
6009
|
actions = [
|
|
5426
|
-
...
|
|
6010
|
+
...sourcePrimaryAction,
|
|
5427
6011
|
managementActions[0],
|
|
5428
6012
|
{
|
|
5429
|
-
label: '
|
|
5430
|
-
description: '
|
|
6013
|
+
label: '更多操作…',
|
|
6014
|
+
description: '替换候选、主来源、移除、检查和技术详情',
|
|
5431
6015
|
run: () => go({ name: 'sourceMore', sourceId: route.sourceId }),
|
|
5432
6016
|
},
|
|
5433
6017
|
];
|
|
5434
6018
|
const sourceActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 5));
|
|
5435
|
-
const freshnessLabel =
|
|
5436
|
-
|
|
6019
|
+
const freshnessLabel = freshness?.state === 'checkFailed' && generationState !== 'ready'
|
|
6020
|
+
? '检查失败' : catalogSourceFreshnessLabel(freshness);
|
|
6021
|
+
const sourceStateLabel = generationState === 'ready' ? '来源可用'
|
|
6022
|
+
: generationState === 'repairRequired' ? '来源目录损坏,当前无法使用'
|
|
6023
|
+
: generationState === 'incompatible' ? '当前 AW 无法使用这个目录版本'
|
|
6024
|
+
: generationState === 'configMismatch' ? '来源设置与当前目录不一致'
|
|
6025
|
+
: generationState === 'stateInvalid' ? '来源状态需要恢复'
|
|
6026
|
+
: '来源尚未启用';
|
|
6027
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusCard, { title: sourceStateLabel, status: generationState === 'ready' ? 'success'
|
|
6028
|
+
: generationState === 'incompatible' ? 'error' : 'warning', description: activeVersion ? `当前版本 ${activeVersion}` : '当前没有可用目录版本', children: [_jsx(StaticRow, { title: "\u5F71\u54CD", meta: generationState === 'ready'
|
|
6029
|
+
? freshness?.state === 'checkFailed' ? '更新检查失败;当前版本继续可用'
|
|
6030
|
+
: freshness?.state === 'updateAvailable' ? `可选择更新到 ${candidateVersion ?? '待检查版本'}`
|
|
6031
|
+
: '插件浏览和现有使用不受阻止'
|
|
6032
|
+
: '当前不能从这个来源浏览或恢复插件', 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
6033
|
}
|
|
5438
6034
|
else if (route.name === 'sourceMore') {
|
|
5439
6035
|
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
@@ -5618,32 +6214,37 @@ export function ConsoleAppV4(props) {
|
|
|
5618
6214
|
const draft = activeDraft;
|
|
5619
6215
|
breadcrumb = `插件来源 / ${route.sourceId} / 编辑`;
|
|
5620
6216
|
title = draft?.mode === 'add' ? `检查新来源 ${draft.sourceId}` : `编辑 ${route.sourceId}`;
|
|
5621
|
-
description = '
|
|
6217
|
+
description = '在一个草稿中修改来源设置,最后查看修改汇总。';
|
|
5622
6218
|
const locationSummary = draft
|
|
5623
6219
|
? projectCatalogSourceLocation(draft.location)
|
|
5624
6220
|
: 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
|
-
};
|
|
6221
|
+
const configuredSource = props.controller.getConfiguredSource(route.sourceId);
|
|
6222
|
+
const currentLocation = draft?.location
|
|
6223
|
+
?? (configuredSource?.type === 'git' ? configuredSource.url : undefined);
|
|
6224
|
+
const officialTransport = currentLocation === OFFICIAL_CATALOG_SOURCE_URL ? 'HTTPS'
|
|
6225
|
+
: currentLocation === OFFICIAL_CATALOG_SOURCE_SSH_URL ? 'SSH' : undefined;
|
|
6226
|
+
const catalogSubdir = draft ? draft.subdir : detail?.subdir;
|
|
5639
6227
|
actions = [
|
|
5640
|
-
...(route.sourceId === 'official' &&
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
6228
|
+
...(route.sourceId === 'official' && officialTransport ? [{
|
|
6229
|
+
label: '下载方式',
|
|
6230
|
+
meta: officialTransport,
|
|
6231
|
+
description: officialTransport === 'HTTPS'
|
|
6232
|
+
? '默认方式;无需预先配置 SSH 密钥'
|
|
6233
|
+
: '适合已配置 GitHub SSH 访问的用户',
|
|
6234
|
+
run: () => {
|
|
6235
|
+
const active = draft ?? (configuredSource
|
|
6236
|
+
? sourceDraftFromSource(configuredSource, `source-update:${route.sourceId}:${Date.now()}`)
|
|
6237
|
+
: undefined);
|
|
6238
|
+
if (!active)
|
|
6239
|
+
return;
|
|
6240
|
+
setSourceTransportOverlay({ owner: active.owner });
|
|
6241
|
+
setOverlayFocus(officialTransport === 'HTTPS' ? 0 : 1);
|
|
6242
|
+
},
|
|
6243
|
+
}] : [{ label: draft?.type === 'path' || detail?.type === 'path' ? '本地目录' : 'Git 仓库地址', description: locationSummary, run: () => startDraftField('location') }]),
|
|
5644
6244
|
...(draft?.type === 'git' || !draft && detail?.type === 'git' ? [
|
|
5645
6245
|
{ label: '版本引用', description: draft ? draft.refPolicy === 'explicit' ? `固定:${draft.ref}` : '跟随 AW 版本通道' : detail?.refPolicy === 'explicit' ? `固定:${detail.explicitRef}` : '跟随 AW 版本通道', run: () => startDraftField('refPolicy') },
|
|
5646
|
-
{ label: 'Catalog 子目录', description:
|
|
6246
|
+
{ label: '高级:Catalog 子目录', description: catalogSubdir === '' || catalogSubdir === undefined
|
|
6247
|
+
? '仓库根目录' : catalogSubdir, run: () => startDraftField('subdir') },
|
|
5647
6248
|
] : []),
|
|
5648
6249
|
{ label: '自动检查更新', description: (draft?.autoCheck ?? detail?.autoCheck) ? '已开启' : '已关闭', selected: draft?.autoCheck ?? detail?.autoCheck, run: () => {
|
|
5649
6250
|
if (!draft) {
|
|
@@ -5668,20 +6269,28 @@ export function ConsoleAppV4(props) {
|
|
|
5668
6269
|
go({ name: 'sourceSave', owner: draft.owner });
|
|
5669
6270
|
} },
|
|
5670
6271
|
];
|
|
5671
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
6272
|
+
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
6273
|
}
|
|
5673
6274
|
else if (route.name === 'sourceSave' && sourceDraft?.owner === route.owner) {
|
|
5674
6275
|
const changes = sourceDraftChanges(sourceDraft);
|
|
6276
|
+
const savedSource = props.controller.getConfiguredSource(sourceDraft.sourceId);
|
|
6277
|
+
const protocolOnly = changes.length === 1 && changes[0]?.title === 'Git 仓库地址'
|
|
6278
|
+
&& savedSource?.type === 'git'
|
|
6279
|
+
&& (savedSource.url === OFFICIAL_CATALOG_SOURCE_URL
|
|
6280
|
+
|| savedSource.url === OFFICIAL_CATALOG_SOURCE_SSH_URL)
|
|
6281
|
+
&& (sourceDraft.location === OFFICIAL_CATALOG_SOURCE_URL
|
|
6282
|
+
|| sourceDraft.location === OFFICIAL_CATALOG_SOURCE_SSH_URL);
|
|
5675
6283
|
breadcrumb = `插件来源 / ${sourceDraft.sourceId} / 修改汇总`;
|
|
5676
6284
|
context = `${changes.length} 项未保存`;
|
|
5677
6285
|
title = sourceDraft.mode === 'add' ? `保存新来源 ${sourceDraft.sourceId}?` : `保存 ${sourceDraft.sourceId} 的修改?`;
|
|
5678
6286
|
description = '选择是否在保存后验证候选并启用。保存成功与启用成功会分别报告。';
|
|
5679
6287
|
actions = [
|
|
5680
|
-
{ label: '保存并启用', description: '保存来源配置,再验证并切换当前目录版本', status: 'success', run: () => beginSourceReview(sourceDraft, true) },
|
|
5681
|
-
{ label: '只保存', description: '只保存来源配置;当前目录版本保持不变', run: () => beginSourceReview(sourceDraft, false) },
|
|
6288
|
+
...(protocolOnly ? [] : [{ label: '保存并启用', description: '保存来源配置,再验证并切换当前目录版本', status: 'success', run: () => beginSourceReview(sourceDraft, true) }]),
|
|
6289
|
+
{ label: protocolOnly ? '只保存下载方式' : '只保存', description: '只保存来源配置;当前目录版本保持不变', status: protocolOnly ? 'success' : undefined, run: () => beginSourceReview(sourceDraft, false) },
|
|
6290
|
+
...(protocolOnly ? [{ label: '保存并启用…', description: '保存后验证并切换当前目录版本', run: () => beginSourceReview(sourceDraft, true) }] : []),
|
|
5682
6291
|
{ label: '返回修改', run: back },
|
|
5683
6292
|
];
|
|
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:
|
|
6293
|
+
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
6294
|
}
|
|
5686
6295
|
else if (route.name === 'scriptTimeout' && pendingReviewIntent) {
|
|
5687
6296
|
breadcrumb = '高级选项 / 安装前检查脚本';
|
|
@@ -5796,12 +6405,12 @@ export function ConsoleAppV4(props) {
|
|
|
5796
6405
|
go({ name: 'pluginDependencies', pluginId: issue.pluginId });
|
|
5797
6406
|
}
|
|
5798
6407
|
else if (entry.id === 'viewDependencyIssue' && entry.pluginId) {
|
|
5799
|
-
const dependencyIssue = props.controller.listUserIssues().find((candidate) => candidate.pluginId
|
|
6408
|
+
const dependencyIssue = props.controller.listUserIssues().find((candidate) => issueMatchesTarget(candidate, entry.pluginId, issue.dependencyPlatform, issue.dependencyScope)
|
|
5800
6409
|
&& candidate.issueId !== issue.issueId);
|
|
5801
6410
|
if (dependencyIssue)
|
|
5802
6411
|
go({ name: 'issueDetail', issueId: dependencyIssue.issueId });
|
|
5803
|
-
else
|
|
5804
|
-
|
|
6412
|
+
else if (!showTechnical)
|
|
6413
|
+
toggleTechnical();
|
|
5805
6414
|
}
|
|
5806
6415
|
else
|
|
5807
6416
|
toggleTechnical();
|
|
@@ -6031,14 +6640,12 @@ export function ConsoleAppV4(props) {
|
|
|
6031
6640
|
const batchReview = review.operationKind === 'pluginWriteBatch';
|
|
6032
6641
|
const taskReview = review.operationKind === 'pluginWriteTask';
|
|
6033
6642
|
const catalogManagementReview = review.operationKind === 'catalogManagement';
|
|
6034
|
-
const sharedImpactConfirmed = !review.requiresConfirmation
|
|
6035
|
-
|| confirmedSharedReviewToken === review.reviewToken;
|
|
6036
6643
|
breadcrumb = `${review.subject.name} / ${review.title}`;
|
|
6037
6644
|
title = `${snapshot.result?.state === 'stale' ? '状态已变化 · ' : ''}${review.operationKind === 'portableInstall' || taskReview
|
|
6038
6645
|
? review.title : `确认${review.title}`} ${review.subject.name}`;
|
|
6039
6646
|
description = snapshot.result?.state === 'stale'
|
|
6040
6647
|
? '当前状态已变化。请检查新的变化后再次确认。'
|
|
6041
|
-
: review.
|
|
6648
|
+
: review.description;
|
|
6042
6649
|
if (taskReview) {
|
|
6043
6650
|
context = '精确目标 · 用户任务';
|
|
6044
6651
|
help = 'PgUp/PgDn 查看全部影响 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
@@ -6048,20 +6655,19 @@ export function ConsoleAppV4(props) {
|
|
|
6048
6655
|
help = 'PgUp/PgDn 查看全部根插件和依赖 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6049
6656
|
}
|
|
6050
6657
|
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: () => {
|
|
6658
|
+
{ label: review.primaryLabel, description: review.requiresConfirmation
|
|
6659
|
+
? '打开独立风险确认;执行时再次检查当前状态'
|
|
6660
|
+
: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, run: () => {
|
|
6661
|
+
if (review.requiresConfirmation) {
|
|
6662
|
+
setOperationConfirmation({
|
|
6663
|
+
reviewToken: review.reviewToken,
|
|
6664
|
+
returnFocus: focus,
|
|
6665
|
+
returnScrollOffset: scrollOffset,
|
|
6666
|
+
warningOffset: 0,
|
|
6667
|
+
});
|
|
6668
|
+
setOverlayFocus(0);
|
|
6669
|
+
return;
|
|
6670
|
+
}
|
|
6065
6671
|
if (operationBusy.current)
|
|
6066
6672
|
return;
|
|
6067
6673
|
operationBusy.current = true;
|
|
@@ -6072,6 +6678,65 @@ export function ConsoleAppV4(props) {
|
|
|
6072
6678
|
operationBusy.current = false;
|
|
6073
6679
|
});
|
|
6074
6680
|
} },
|
|
6681
|
+
...(taskReview
|
|
6682
|
+
&& pendingReviewIntent?.kind === 'pluginWriteTask'
|
|
6683
|
+
&& pendingReviewIntent.goal === 'update'
|
|
6684
|
+
&& batchDraft?.frozenPluginIds ? [{
|
|
6685
|
+
label: `调整选择(${new Set(pendingReviewIntent.targets
|
|
6686
|
+
.map((target) => target.pluginId)).size})`,
|
|
6687
|
+
description: '只可删减或恢复本次冻结的推荐插件;调整后重新读取证据并建立 Review',
|
|
6688
|
+
run: () => {
|
|
6689
|
+
if (!props.controller.returnToPluginWriteTaskBatchSelection(batchDraft.flowId)) {
|
|
6690
|
+
setActionError('推荐选择已失效。请返回我的插件重新开始。');
|
|
6691
|
+
return;
|
|
6692
|
+
}
|
|
6693
|
+
setBatchDraft((current) => current ? {
|
|
6694
|
+
...current,
|
|
6695
|
+
stage: 'select',
|
|
6696
|
+
showSelected: false,
|
|
6697
|
+
} : current);
|
|
6698
|
+
setFocus(0);
|
|
6699
|
+
setScrollOffset(0);
|
|
6700
|
+
setShowTechnical(false);
|
|
6701
|
+
setActionError(undefined);
|
|
6702
|
+
},
|
|
6703
|
+
}] : []),
|
|
6704
|
+
...(taskReview
|
|
6705
|
+
&& pendingReviewIntent?.kind === 'pluginWriteTask'
|
|
6706
|
+
&& route.name === 'review'
|
|
6707
|
+
&& batchDraft ? [{
|
|
6708
|
+
label: '修改处理范围',
|
|
6709
|
+
description: '只可删减或恢复本次已有位置;修改后重新读取证据',
|
|
6710
|
+
run: () => {
|
|
6711
|
+
const targets = pendingReviewIntent.targets;
|
|
6712
|
+
setBatchDraft((current) => current ? {
|
|
6713
|
+
...current,
|
|
6714
|
+
maintenanceGoal: pendingReviewIntent.goal,
|
|
6715
|
+
maintenanceTargets: current.maintenanceGoal === pendingReviewIntent.goal
|
|
6716
|
+
&& current.maintenanceTargets
|
|
6717
|
+
? current.maintenanceTargets : targets,
|
|
6718
|
+
selectedMaintenanceTargetKeys: targets.map(batchTargetKey),
|
|
6719
|
+
} : current);
|
|
6720
|
+
back();
|
|
6721
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
6722
|
+
setFocus(0);
|
|
6723
|
+
setScrollOffset(0);
|
|
6724
|
+
},
|
|
6725
|
+
}] : []),
|
|
6726
|
+
...(batchReview
|
|
6727
|
+
&& pendingReviewIntent?.kind === 'pluginWriteBatch'
|
|
6728
|
+
&& pendingReviewIntent.selection.kind === 'targets'
|
|
6729
|
+
&& pendingReviewIntent.action !== 'install'
|
|
6730
|
+
&& batchDraft?.maintenanceTargets ? [{
|
|
6731
|
+
label: '修改处理范围',
|
|
6732
|
+
description: '只可删减或恢复本次已有位置;修改后重新读取证据',
|
|
6733
|
+
run: () => {
|
|
6734
|
+
back();
|
|
6735
|
+
props.controller.push({ name: 'batchPlacement' });
|
|
6736
|
+
setFocus(0);
|
|
6737
|
+
setScrollOffset(0);
|
|
6738
|
+
},
|
|
6739
|
+
}] : []),
|
|
6075
6740
|
...(installReviewPluginId
|
|
6076
6741
|
&& (snapshot.installPlacementDraft?.targets.length ?? 0) > 0 ? [{
|
|
6077
6742
|
label: '查看/调整依赖位置',
|
|
@@ -6085,7 +6750,7 @@ export function ConsoleAppV4(props) {
|
|
|
6085
6750
|
setScrollOffset(0);
|
|
6086
6751
|
},
|
|
6087
6752
|
}] : []),
|
|
6088
|
-
...(
|
|
6753
|
+
...((pendingReviewIntent?.kind === 'pluginWrite'
|
|
6089
6754
|
&& (pendingReviewIntent.action === 'install'
|
|
6090
6755
|
|| pendingReviewIntent.action === 'refreshAndSync'
|
|
6091
6756
|
|| pendingReviewIntent.action === 'reinstall'
|
|
@@ -6111,7 +6776,7 @@ export function ConsoleAppV4(props) {
|
|
|
6111
6776
|
: back },
|
|
6112
6777
|
];
|
|
6113
6778
|
const groupedChanges = review.changes.some((change) => change.group);
|
|
6114
|
-
const compressedReview = policy.mode === 'compact'
|
|
6779
|
+
const compressedReview = policy.mode === 'compact';
|
|
6115
6780
|
const groupTitle = {
|
|
6116
6781
|
requested: review.operationKind === 'portableInstall' ? '已选择 Skills · 当前插件' : '你选择的插件',
|
|
6117
6782
|
dependencies: review.operationKind === 'portableInstall' ? '已选择 Skills · 来自依赖插件' : '必需依赖',
|
|
@@ -6149,8 +6814,15 @@ export function ConsoleAppV4(props) {
|
|
|
6149
6814
|
'projectIntent', 'userIntent', 'sharedCodexNative',
|
|
6150
6815
|
'userActivation', 'projectActivation', 'awExtensions', 'runtime',
|
|
6151
6816
|
]);
|
|
6152
|
-
const
|
|
6153
|
-
|
|
6817
|
+
const userReviewChanges = review.changes.filter((change) => change.group === undefined
|
|
6818
|
+
|| !isTechnicalReviewGroup(change.group)
|
|
6819
|
+
|| change.kind === 'warning'
|
|
6820
|
+
|| change.kind === 'remove');
|
|
6821
|
+
const visibleSpecs = groupedSpecs.map((group) => technicalGroups.has(group.key) ? {
|
|
6822
|
+
...group,
|
|
6823
|
+
label: '风险',
|
|
6824
|
+
changes: group.changes.filter((change) => change.kind === 'warning' || change.kind === 'remove'),
|
|
6825
|
+
} : group).filter((group) => group.changes.length > 0);
|
|
6154
6826
|
const headingLines = compressedReview ? 0 : visibleSpecs.filter((group) => group.label).length;
|
|
6155
6827
|
const staticRowBudget = Math.max(visibleSpecs.length, responsiveBudget(7, 12) - headingLines);
|
|
6156
6828
|
const allocations = new Map(visibleSpecs.map((group) => [group.key, 1]));
|
|
@@ -6186,15 +6858,15 @@ export function ConsoleAppV4(props) {
|
|
|
6186
6858
|
})] }, group.key);
|
|
6187
6859
|
});
|
|
6188
6860
|
const batchChangeLimit = Math.max(1, responsiveBudget(6, 10) - actions.length);
|
|
6189
|
-
const batchChanges =
|
|
6861
|
+
const batchChanges = userReviewChanges.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
6190
6862
|
const batchHiddenBefore = batchTargetOffset;
|
|
6191
6863
|
const prioritizedReviewChanges = [
|
|
6192
|
-
...
|
|
6193
|
-
...
|
|
6864
|
+
...userReviewChanges.filter((change) => change.kind === 'warning'),
|
|
6865
|
+
...userReviewChanges.filter((change) => change.kind !== 'warning'),
|
|
6194
6866
|
];
|
|
6195
6867
|
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,
|
|
6868
|
+
const changeGroups = batchReview || userReviewChanges.length > staticRowBudget
|
|
6869
|
+
? _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
6870
|
: standardChangeGroups;
|
|
6199
6871
|
const technicalDetails = review.technicalDetails ?? [];
|
|
6200
6872
|
const technicalDetailLimit = responsiveBudget(1, 3);
|
|
@@ -6231,11 +6903,11 @@ export function ConsoleAppV4(props) {
|
|
|
6231
6903
|
const widenedDependencyCount = new Set(installDependencyTargets
|
|
6232
6904
|
.filter((target) => target.affectsOtherProjects && target.action !== 'keep')
|
|
6233
6905
|
.map((target) => target.pluginId)).size;
|
|
6234
|
-
if (
|
|
6906
|
+
if (userReviewChanges.length > staticRowBudget || showTechnical
|
|
6235
6907
|
&& (review.changes.length > technicalChangeBudget || technicalDetails.length > technicalDetailLimit)) {
|
|
6236
6908
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6237
6909
|
}
|
|
6238
|
-
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: review.
|
|
6910
|
+
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
6911
|
}
|
|
6240
6912
|
else if (route.name === 'progress') {
|
|
6241
6913
|
const taskOperation = snapshot.activeReview?.operationKind === 'pluginWriteTask';
|
|
@@ -6250,15 +6922,26 @@ export function ConsoleAppV4(props) {
|
|
|
6250
6922
|
breadcrumb = catalogManagementOperation ? '目录管理 / 进度' : taskOperation ? '插件任务 / 进度' : portableOperation ? '通用 Skills / 进度' : issueOperation ? '需要处理 / 进度' : sourceOperation ? '插件来源 / 保存' : configOperation ? '设置 / 进度' : '插件操作 / 进度';
|
|
6251
6923
|
const configApply = snapshot.activeReview?.operationKind === 'configSave'
|
|
6252
6924
|
&& snapshot.activeReview.changes.some((change) => change.group === 'configImpact' && change.kind === 'change');
|
|
6925
|
+
const progressAction = snapshot.activeReview?.taskGoal === 'restore' ? '恢复'
|
|
6926
|
+
: snapshot.activeReview?.taskGoal === 'update' ? '更新'
|
|
6927
|
+
: configApply ? '应用设置'
|
|
6928
|
+
: snapshot.activeReview?.pluginAction === 'install' ? '添加'
|
|
6929
|
+
: snapshot.activeReview?.pluginAction === 'refreshAndSync' ? '更新'
|
|
6930
|
+
: snapshot.activeReview?.pluginAction === 'reinstall' ? '恢复'
|
|
6931
|
+
: snapshot.activeReview?.pluginAction === 'uninstall' ? '移除'
|
|
6932
|
+
: snapshot.activeReview?.pluginAction === 'cleanup' ? '清理'
|
|
6933
|
+
: snapshot.activeReview?.pluginAction === 'sync' ? '同步' : '处理';
|
|
6253
6934
|
const catalogGenerationOperation = snapshot.activeReview?.operationKind === 'catalogGeneration';
|
|
6254
6935
|
const catalogSourcesRecovery = snapshot.activeReview?.operationKind === 'catalogSourcesRecovery';
|
|
6255
|
-
title = catalogManagementOperation
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6936
|
+
title = catalogManagementOperation
|
|
6937
|
+
? `正在${snapshot.activeReview?.title.replace(/^确认/u, '') ?? '完成目录管理任务'}`
|
|
6938
|
+
: taskOperation ? `正在${progressAction}插件` : portableOperation ? '正在安装通用 Skills' : issueOperation ? `正在${progressAction}插件`
|
|
6939
|
+
: catalogGenerationOperation ? '正在验证并启用目录版本'
|
|
6940
|
+
: catalogSourcesRecovery ? '正在恢复插件来源配置'
|
|
6941
|
+
: sourceOperation ? '正在保存来源配置'
|
|
6942
|
+
: configOperation ? configApply ? '正在保存并应用设置' : '正在保存设置'
|
|
6943
|
+
: `正在${progressAction}插件`;
|
|
6944
|
+
description = catalogManagementOperation ? '正在完成已确认的目录管理任务。'
|
|
6262
6945
|
: issueOperation ? '完成后会重新读取状态并检查问题。'
|
|
6263
6946
|
: catalogGenerationOperation ? '正在验证并应用已确认的目录版本操作。'
|
|
6264
6947
|
: catalogSourcesRecovery ? '正在备份损坏配置并恢复官方默认来源。'
|
|
@@ -6289,7 +6972,7 @@ export function ConsoleAppV4(props) {
|
|
|
6289
6972
|
if (entry.state === 'completed')
|
|
6290
6973
|
return { label: '已完成', status: 'success' };
|
|
6291
6974
|
if (entry.state === 'running')
|
|
6292
|
-
return { label:
|
|
6975
|
+
return { label: `正在${progressAction}`, status: 'info' };
|
|
6293
6976
|
if (entry.state === 'failed')
|
|
6294
6977
|
return { label: '失败', status: 'error' };
|
|
6295
6978
|
if (entry.state === 'skipped')
|
|
@@ -6367,13 +7050,13 @@ export function ConsoleAppV4(props) {
|
|
|
6367
7050
|
return step === 2 ? '正在安装' : step === 3 ? '正在验证' : step === 1 ? '正在生成' : '正在检查';
|
|
6368
7051
|
};
|
|
6369
7052
|
const compactPortableProgress = policy.mode === 'compact' && showTechnical;
|
|
6370
|
-
content = catalogManagementOperation ? _jsxs(StatusCard, { status: failedProgress ? 'error' : 'info', title: "
|
|
7053
|
+
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
7054
|
const state = progressLabel(entry);
|
|
6372
7055
|
const relation = entry.relation === 'requested' ? '你选择的插件' : entry.relation === 'direct' ? '直接依赖' : '间接依赖';
|
|
6373
7056
|
const title = props.controller.getPluginOverview(entry.pluginId)?.displayName
|
|
6374
7057
|
?? entry.pluginId.split('/').at(-1);
|
|
6375
7058
|
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
|
|
7059
|
+
}), 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
7060
|
? dependencyMessageLimit > 0 ? technicalMessages.slice(-dependencyMessageLimit) : []
|
|
6378
7061
|
: 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
7062
|
}
|
|
@@ -6494,15 +7177,15 @@ export function ConsoleAppV4(props) {
|
|
|
6494
7177
|
: taskOperation
|
|
6495
7178
|
? blocked ? '任务被安全阻止。已完成和未完成目标按实际结果保留。'
|
|
6496
7179
|
: partial ? `${taskSummary?.retryTargets?.length ?? 0} 个失败目标可重试 · ${taskBlockedCount} 个目标需要决定。`
|
|
6497
|
-
: `${taskSummary?.rootResults.length ?? 0}
|
|
7180
|
+
: `${taskSummary?.rootResults.length ?? 0} 个所选插件已处理;下方显示使用位置。`
|
|
6498
7181
|
: batchOperation
|
|
6499
7182
|
? blocked
|
|
6500
7183
|
? '批量写入被阻止。已完成和未完成目标按实际结果保留。'
|
|
6501
7184
|
: partial
|
|
6502
7185
|
? (resultSummary?.batch?.retryTargets?.length ?? 0) > 0
|
|
6503
|
-
? `${resultSummary?.batch?.failedRequestedPluginIds.length ?? 0}
|
|
7186
|
+
? `${resultSummary?.batch?.failedRequestedPluginIds.length ?? 0} 个所选插件失败,涉及 ${resultSummary?.batch?.retryTargets?.length ?? 0} 个位置。成功目标不会重放。`
|
|
6504
7187
|
: '失败目标无法精确定位。请查看本次记录后重新操作。'
|
|
6505
|
-
: `${resultSummary?.batch?.requestedPluginIds.length ?? 0}
|
|
7188
|
+
: `${resultSummary?.batch?.requestedPluginIds.length ?? 0} 个所选插件已处理;下方显示使用位置和所需插件。`
|
|
6506
7189
|
: portableOperation
|
|
6507
7190
|
? blocked
|
|
6508
7191
|
? result.code === 'operationReviewExpired' || result.code === 'reviewTicketExpired'
|
|
@@ -6898,34 +7581,35 @@ export function ConsoleAppV4(props) {
|
|
|
6898
7581
|
return { label: '未开始', status: 'muted' };
|
|
6899
7582
|
return { label: '待确认', status: 'warning' };
|
|
6900
7583
|
};
|
|
6901
|
-
const
|
|
7584
|
+
const taskSelectedPluginRows = taskRootResults.map((root) => {
|
|
6902
7585
|
const rootState = taskRootState(root.state);
|
|
6903
7586
|
const rootName = props.controller.getPluginOverview(root.pluginId)?.displayName
|
|
6904
7587
|
?? root.pluginId.split('/').at(-1);
|
|
6905
7588
|
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
|
-
|
|
7589
|
+
return _jsx(ResultRow, { title: rootName, description: `${requestedTargets.length} 个使用位置`, meta: rootState.label, status: rootState.status, compact: true }, `root:${root.pluginId}`);
|
|
7590
|
+
});
|
|
7591
|
+
const taskLocationRows = taskRootResults.flatMap((root) => {
|
|
7592
|
+
const requestedTargets = taskSummary?.requestedTargets.filter((target) => target.pluginId === root.pluginId) ?? [];
|
|
7593
|
+
return requestedTargets.map((target) => {
|
|
7594
|
+
const key = [target.pluginId, target.platform, target.scope].join('\0');
|
|
7595
|
+
const blockedTarget = blockedTaskTargets.get(key);
|
|
7596
|
+
const targetResult = root.targets.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
|
|
7597
|
+
&& entry.scope === target.scope);
|
|
7598
|
+
const targetState = blockedTarget ? { label: '待确认', status: 'warning' }
|
|
7599
|
+
: targetResult?.state === 'completed' ? { label: '完成', status: 'success' }
|
|
7600
|
+
: targetResult?.state === 'kept' ? { label: '无需更改', status: 'success' }
|
|
7601
|
+
: targetResult?.state === 'failed' ? { label: '失败', status: 'error' }
|
|
7602
|
+
: { label: '未开始', status: 'muted' };
|
|
7603
|
+
return _jsx(ResultRow, { title: `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`, description: blockedTarget
|
|
7604
|
+
? pluginWriteTaskBlockLabel(blockedTarget.reason)
|
|
7605
|
+
: targetResult?.errorCode, meta: targetState.label, status: targetState.status, compact: !blockedTarget }, `target:${key}`);
|
|
7606
|
+
});
|
|
6923
7607
|
});
|
|
6924
7608
|
const batchResultRows = (resultSummary?.batch?.rootResults ?? []).map((root) => {
|
|
6925
7609
|
const rootState = taskRootState(root.state);
|
|
6926
7610
|
const rootName = props.controller.getPluginOverview(root.pluginId)?.displayName
|
|
6927
7611
|
?? root.pluginId.split('/').at(-1);
|
|
6928
|
-
return _jsx(ResultRow, { title: rootName, description: `${root.targets.length}
|
|
7612
|
+
return _jsx(ResultRow, { title: rootName, description: `${root.targets.length} 个使用位置`, meta: rootState.label, status: rootState.status, compact: true }, `batch-root:${root.pluginId}`);
|
|
6929
7613
|
}).concat(prioritizedTargets.map((target) => {
|
|
6930
7614
|
const state = operationTargetStateLabel(target.state);
|
|
6931
7615
|
const relation = target.relation === 'requested' ? '你选择的插件'
|
|
@@ -6934,14 +7618,16 @@ export function ConsoleAppV4(props) {
|
|
|
6934
7618
|
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
7619
|
}));
|
|
6936
7620
|
const taskResultLimit = Math.max(1, pageActionBudget(5) - resultActionViewport.limit);
|
|
6937
|
-
const rootResultRows = taskOperation
|
|
7621
|
+
const rootResultRows = taskOperation
|
|
7622
|
+
? [...taskSelectedPluginRows, ...taskLocationRows]
|
|
7623
|
+
: batchResultRows;
|
|
6938
7624
|
const taskResultOffset = Math.min(batchTargetOffset, Math.max(0, rootResultRows.length - 1));
|
|
6939
7625
|
const visibleTaskResultRows = rootResultRows.slice(taskResultOffset, taskResultOffset + taskResultLimit);
|
|
6940
7626
|
if (taskOperation || batchOperation || resultTargets.length > resultTargetLimit
|
|
6941
7627
|
|| prioritizedPortableSkills.length > portableSkillLimit) {
|
|
6942
7628
|
help = 'PgUp/PgDn 查看全部项目 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6943
7629
|
}
|
|
6944
|
-
content = catalogManagementOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: resultSummary?.message ?? '
|
|
7630
|
+
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
7631
|
? _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
7632
|
? _jsx(StaticRow, { title: "\u5B89\u5168\u6062\u590D", meta: portableReceipt.recovery, status: "warning", compact: true })
|
|
6947
7633
|
: _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 +7666,7 @@ export function ConsoleAppV4(props) {
|
|
|
6980
7666
|
if (prioritizedComponents.length > componentLimit) {
|
|
6981
7667
|
help = 'PgUp/PgDn 查看全部组件 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6982
7668
|
}
|
|
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' })] });
|
|
7669
|
+
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
7670
|
}
|
|
6985
7671
|
else {
|
|
6986
7672
|
const missingPlugin = definition.entity === 'plugin' && plugin === undefined;
|
|
@@ -7260,6 +7946,89 @@ export function ConsoleAppV4(props) {
|
|
|
7260
7946
|
}
|
|
7261
7947
|
return;
|
|
7262
7948
|
}
|
|
7949
|
+
if (operationConfirmation) {
|
|
7950
|
+
const activeReview = snapshot.activeReview;
|
|
7951
|
+
const closeConfirmation = () => {
|
|
7952
|
+
setOperationConfirmation(undefined);
|
|
7953
|
+
setFocus(operationConfirmation.returnFocus);
|
|
7954
|
+
setScrollOffset(operationConfirmation.returnScrollOffset);
|
|
7955
|
+
setOverlayFocus(0);
|
|
7956
|
+
};
|
|
7957
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
7958
|
+
closeConfirmation();
|
|
7959
|
+
}
|
|
7960
|
+
else if (key.ctrl && input.toLowerCase() === 'c') {
|
|
7961
|
+
closeConfirmation();
|
|
7962
|
+
requestExit({ code: 0, reason: 'user' });
|
|
7963
|
+
}
|
|
7964
|
+
else if (input.toLowerCase() === 'q') {
|
|
7965
|
+
closeConfirmation();
|
|
7966
|
+
setDiscardForExit(dirty);
|
|
7967
|
+
setOverlay(dirty ? 'discard' : 'exit');
|
|
7968
|
+
}
|
|
7969
|
+
else if (props.onSessionNavigation && (input === 'h' || input === 'H')) {
|
|
7970
|
+
const navigation = input === 'H' ? 'reload' : 'home';
|
|
7971
|
+
closeConfirmation();
|
|
7972
|
+
requestSessionNavigation(navigation);
|
|
7973
|
+
}
|
|
7974
|
+
else if (key.pageUp || key.pageDown) {
|
|
7975
|
+
const warningCount = activeReview
|
|
7976
|
+
? operationRiskLines(activeReview, Math.max(12, size.columns - 8)).length : 0;
|
|
7977
|
+
const warningLimit = policy.mode === 'compact' ? 3 : 7;
|
|
7978
|
+
setOperationConfirmation((current) => current ? {
|
|
7979
|
+
...current,
|
|
7980
|
+
warningOffset: Math.min(Math.max(0, warningCount - warningLimit), Math.max(0, current.warningOffset + (key.pageUp ? -1 : 1))),
|
|
7981
|
+
} : current);
|
|
7982
|
+
}
|
|
7983
|
+
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
7984
|
+
setOverlayFocus((value) => moveIndex(2, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
7985
|
+
}
|
|
7986
|
+
else if (key.return) {
|
|
7987
|
+
if (overlayFocus === 0) {
|
|
7988
|
+
closeConfirmation();
|
|
7989
|
+
}
|
|
7990
|
+
else if (activeReview?.reviewToken !== operationConfirmation.reviewToken) {
|
|
7991
|
+
closeConfirmation();
|
|
7992
|
+
setActionError('确认内容已变化。请重新检查操作汇总。');
|
|
7993
|
+
}
|
|
7994
|
+
else if (!operationBusy.current) {
|
|
7995
|
+
operationBusy.current = true;
|
|
7996
|
+
setOperationConfirmation(undefined);
|
|
7997
|
+
void props.controller.executeOperation(operationConfirmation.reviewToken).then(() => {
|
|
7998
|
+
setFocus(0);
|
|
7999
|
+
setScrollOffset(0);
|
|
8000
|
+
}).finally(() => {
|
|
8001
|
+
operationBusy.current = false;
|
|
8002
|
+
});
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
return;
|
|
8006
|
+
}
|
|
8007
|
+
if (sourceTransportOverlay) {
|
|
8008
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
8009
|
+
setSourceTransportOverlay(undefined);
|
|
8010
|
+
setOverlayFocus(0);
|
|
8011
|
+
}
|
|
8012
|
+
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
8013
|
+
setOverlayFocus((value) => moveIndex(2, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
8014
|
+
}
|
|
8015
|
+
else if (key.return && route.name === 'sourceEdit') {
|
|
8016
|
+
const configured = props.controller.getConfiguredSource(route.sourceId);
|
|
8017
|
+
const current = sourceDraft?.owner === sourceTransportOverlay.owner
|
|
8018
|
+
? sourceDraft
|
|
8019
|
+
: configured ? sourceDraftFromSource(configured, sourceTransportOverlay.owner) : undefined;
|
|
8020
|
+
const location = overlayFocus === 0
|
|
8021
|
+
? OFFICIAL_CATALOG_SOURCE_URL : OFFICIAL_CATALOG_SOURCE_SSH_URL;
|
|
8022
|
+
if (current && current.type === 'git' && current.location !== location) {
|
|
8023
|
+
setSourceDraft({ ...current, location });
|
|
8024
|
+
setDirty(true);
|
|
8025
|
+
props.controller.replace({ name: 'sourceEdit', sourceId: route.sourceId, owner: current.owner });
|
|
8026
|
+
}
|
|
8027
|
+
setSourceTransportOverlay(undefined);
|
|
8028
|
+
setOverlayFocus(0);
|
|
8029
|
+
}
|
|
8030
|
+
return;
|
|
8031
|
+
}
|
|
7263
8032
|
if (overlay) {
|
|
7264
8033
|
if (key.ctrl && input.toLowerCase() === 'c') {
|
|
7265
8034
|
requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
|
|
@@ -7369,6 +8138,13 @@ export function ConsoleAppV4(props) {
|
|
|
7369
8138
|
setReportSaveError(undefined);
|
|
7370
8139
|
return;
|
|
7371
8140
|
}
|
|
8141
|
+
if ((key.pageUp || key.pageDown)
|
|
8142
|
+
&& route.name === 'pluginDependencyNode' && route.view === 'references'
|
|
8143
|
+
&& referenceDetailPagination) {
|
|
8144
|
+
const { total, pageRows } = referenceDetailPagination;
|
|
8145
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
8146
|
+
return;
|
|
8147
|
+
}
|
|
7372
8148
|
if ((key.pageUp || key.pageDown)
|
|
7373
8149
|
&& route.name === 'pluginWriteTask') {
|
|
7374
8150
|
const total = props.controller.listPluginWriteTaskCandidates(route.goal)
|
|
@@ -7380,7 +8156,12 @@ export function ConsoleAppV4(props) {
|
|
|
7380
8156
|
if ((key.pageUp || key.pageDown)
|
|
7381
8157
|
&& route.name === 'review'
|
|
7382
8158
|
&& snapshot.activeReview) {
|
|
7383
|
-
const
|
|
8159
|
+
const userChangeCount = snapshot.activeReview.changes.filter((change) => !isTechnicalReviewGroup(change.group)
|
|
8160
|
+
|| change.kind === 'warning'
|
|
8161
|
+
|| change.kind === 'remove').length;
|
|
8162
|
+
const total = showTechnical
|
|
8163
|
+
? Math.max(snapshot.activeReview.changes.length, snapshot.activeReview.technicalDetails?.length ?? 0)
|
|
8164
|
+
: userChangeCount;
|
|
7384
8165
|
const pageRows = 1;
|
|
7385
8166
|
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
7386
8167
|
return;
|
|
@@ -7930,12 +8711,21 @@ export function ConsoleAppV4(props) {
|
|
|
7930
8711
|
return;
|
|
7931
8712
|
}
|
|
7932
8713
|
if (route.name === 'pluginList') {
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
8714
|
+
const nextQuery = input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)
|
|
8715
|
+
? ''
|
|
8716
|
+
: key.backspace || key.delete
|
|
8717
|
+
? removeLastGrapheme(query)
|
|
8718
|
+
: input && !key.ctrl && !key.meta && input >= ' '
|
|
8719
|
+
? query + input : query;
|
|
8720
|
+
setQuery(nextQuery);
|
|
8721
|
+
const matchingItems = props.controller.listPlugins({
|
|
8722
|
+
mode: route.mode,
|
|
8723
|
+
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
8724
|
+
...(nextQuery ? { query: nextQuery } : {}),
|
|
8725
|
+
installFilter,
|
|
8726
|
+
});
|
|
8727
|
+
const matchingRows = pluginListRowIds(route.mode, matchingItems, nextQuery);
|
|
8728
|
+
setFocus(matchingItems[0] ? matchingRows.indexOf(matchingItems[0].pluginId) : 0);
|
|
7939
8729
|
}
|
|
7940
8730
|
else {
|
|
7941
8731
|
setBatchDraft((draft) => draft ? {
|
|
@@ -7949,7 +8739,8 @@ export function ConsoleAppV4(props) {
|
|
|
7949
8739
|
showSelected: false,
|
|
7950
8740
|
} : draft);
|
|
7951
8741
|
}
|
|
7952
|
-
|
|
8742
|
+
if (route.name === 'batchSelection')
|
|
8743
|
+
setFocus(1);
|
|
7953
8744
|
setScrollOffset(0);
|
|
7954
8745
|
return;
|
|
7955
8746
|
}
|
|
@@ -8023,6 +8814,27 @@ export function ConsoleAppV4(props) {
|
|
|
8023
8814
|
back();
|
|
8024
8815
|
}
|
|
8025
8816
|
else if (globalAction === 'back' && route.name === 'batchPlacement') {
|
|
8817
|
+
if (batchDraft?.showPlacementTargets) {
|
|
8818
|
+
setBatchDraft((current) => current ? { ...current, showPlacementTargets: false } : current);
|
|
8819
|
+
setFocus(5);
|
|
8820
|
+
setScrollOffset(0);
|
|
8821
|
+
}
|
|
8822
|
+
else
|
|
8823
|
+
back();
|
|
8824
|
+
}
|
|
8825
|
+
else if (globalAction === 'back'
|
|
8826
|
+
&& route.name === 'batchSelection'
|
|
8827
|
+
&& batchDraft
|
|
8828
|
+
&& batchDraft.stage !== 'select'
|
|
8829
|
+
&& snapshot.router.currentFlowId === batchDraft.flowId) {
|
|
8830
|
+
moveBatchStage(batchDraft.stage === 'maintenance' ? 'task' : 'select');
|
|
8831
|
+
}
|
|
8832
|
+
else if (globalAction === 'back'
|
|
8833
|
+
&& batchDraft
|
|
8834
|
+
&& route.name !== 'batchSelection'
|
|
8835
|
+
&& snapshot.router.currentFlowId === batchDraft.flowId
|
|
8836
|
+
&& (route.name !== 'pluginWriteTask'
|
|
8837
|
+
|| route.batchOrigin?.flowId === batchDraft.flowId)) {
|
|
8026
8838
|
back();
|
|
8027
8839
|
}
|
|
8028
8840
|
else if (globalAction === 'back'
|
|
@@ -8032,8 +8844,7 @@ export function ConsoleAppV4(props) {
|
|
|
8032
8844
|
back();
|
|
8033
8845
|
}
|
|
8034
8846
|
else if (globalAction === 'back') {
|
|
8035
|
-
if (dirty && route.name !== 'review'
|
|
8036
|
-
&& !(batchDraft && (route.name === 'pluginPreview' || route.name === 'pluginDetail'))) {
|
|
8847
|
+
if (dirty && route.name !== 'review') {
|
|
8037
8848
|
setOverlayFocus(0);
|
|
8038
8849
|
setDiscardForExit(false);
|
|
8039
8850
|
setOverlay('discard');
|
|
@@ -8055,12 +8866,11 @@ export function ConsoleAppV4(props) {
|
|
|
8055
8866
|
...(query ? { query } : {}),
|
|
8056
8867
|
installFilter: nextFilter,
|
|
8057
8868
|
});
|
|
8058
|
-
const
|
|
8059
|
-
|
|
8060
|
-
: -1;
|
|
8869
|
+
const nextRowIds = pluginListRowIds(route.mode, nextItems, query);
|
|
8870
|
+
const retained = selectedPluginId ? nextRowIds.indexOf(selectedPluginId) : -1;
|
|
8061
8871
|
setInstallFilter(nextFilter);
|
|
8062
8872
|
props.controller.updatePageContext({ installFilter: nextFilter });
|
|
8063
|
-
setFocus(retained >= 0 ?
|
|
8873
|
+
setFocus(retained >= 0 ? retained : 0);
|
|
8064
8874
|
setScrollOffset(0);
|
|
8065
8875
|
}
|
|
8066
8876
|
else if (route.name === 'pluginList' && input.toLowerCase() === 'm') {
|
|
@@ -8090,11 +8900,11 @@ export function ConsoleAppV4(props) {
|
|
|
8090
8900
|
if (batchDraft.mode === 'browse') {
|
|
8091
8901
|
setBatchDraft((current) => current ? { ...current, action: 'install' } : current);
|
|
8092
8902
|
props.controller.push({ name: 'batchPlacement' });
|
|
8903
|
+
setFocus(0);
|
|
8904
|
+
setScrollOffset(0);
|
|
8093
8905
|
}
|
|
8094
8906
|
else
|
|
8095
|
-
|
|
8096
|
-
setFocus(0);
|
|
8097
|
-
setScrollOffset(0);
|
|
8907
|
+
moveBatchStage('task');
|
|
8098
8908
|
}
|
|
8099
8909
|
else
|
|
8100
8910
|
setActionError('请至少选择一个插件。');
|
|
@@ -8161,9 +8971,14 @@ export function ConsoleAppV4(props) {
|
|
|
8161
8971
|
const index = tabs.indexOf(current);
|
|
8162
8972
|
const next = tabs[(index + direction + tabs.length) % tabs.length];
|
|
8163
8973
|
const nextItems = props.controller.listPlugins({ mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}), ...(query ? { query } : {}), installFilter });
|
|
8164
|
-
const
|
|
8974
|
+
const nextSourceUnavailable = next !== 'all'
|
|
8975
|
+
&& props.controller.getSourceOverview(next).health?.state === 'unavailable';
|
|
8976
|
+
const nextRowIds = pluginListRowIds(route.mode, nextItems, query, nextSourceUnavailable);
|
|
8977
|
+
const retained = selectedPluginId ? nextRowIds.indexOf(selectedPluginId) : -1;
|
|
8165
8978
|
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
|
|
8979
|
+
const retainedFocus = retained >= 0
|
|
8980
|
+
? retained
|
|
8981
|
+
: nextItems[0] ? nextRowIds.indexOf(nextItems[0].pluginId) : 0;
|
|
8167
8982
|
setFocus(retainedFocus);
|
|
8168
8983
|
setScrollOffset(retainedFocus >= 4 ? retainedFocus - 3 : 0);
|
|
8169
8984
|
}
|
|
@@ -8176,6 +8991,9 @@ export function ConsoleAppV4(props) {
|
|
|
8176
8991
|
props.controller.togglePortableSkill(candidate.name);
|
|
8177
8992
|
}
|
|
8178
8993
|
else if (key.upArrow || input.toLowerCase() === 'k') {
|
|
8994
|
+
if (route.name === 'pluginDependencyNode' && route.view === 'references') {
|
|
8995
|
+
setBatchTargetOffset(0);
|
|
8996
|
+
}
|
|
8179
8997
|
setFocus((value) => {
|
|
8180
8998
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8181
8999
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
@@ -8209,6 +9027,9 @@ export function ConsoleAppV4(props) {
|
|
|
8209
9027
|
});
|
|
8210
9028
|
}
|
|
8211
9029
|
else if (key.downArrow || input.toLowerCase() === 'j') {
|
|
9030
|
+
if (route.name === 'pluginDependencyNode' && route.view === 'references') {
|
|
9031
|
+
setBatchTargetOffset(0);
|
|
9032
|
+
}
|
|
8212
9033
|
setFocus((value) => {
|
|
8213
9034
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8214
9035
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
@@ -8260,6 +9081,48 @@ export function ConsoleAppV4(props) {
|
|
|
8260
9081
|
if (actionError)
|
|
8261
9082
|
description = actionError;
|
|
8262
9083
|
const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, help: help, size: size, children: content }));
|
|
9084
|
+
if (operationConfirmation && snapshot.activeReview) {
|
|
9085
|
+
const review = snapshot.activeReview;
|
|
9086
|
+
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
9087
|
+
const modalWarningLines = operationRiskLines(review, Math.max(12, modalWidth - 6));
|
|
9088
|
+
const shortWarningLimit = policy.mode === 'compact' ? 4 : 6;
|
|
9089
|
+
const shortConfirmation = modalWarningLines.length <= shortWarningLimit;
|
|
9090
|
+
const warningLines = shortConfirmation
|
|
9091
|
+
? modalWarningLines
|
|
9092
|
+
: operationRiskLines(review, Math.max(12, size.columns - 8));
|
|
9093
|
+
const warningLimit = Math.max(1, policy.mode === 'compact' ? 3 : 7);
|
|
9094
|
+
const warningOffset = Math.min(operationConfirmation.warningOffset, Math.max(0, warningLines.length - warningLimit));
|
|
9095
|
+
const visibleWarnings = shortConfirmation
|
|
9096
|
+
? warningLines : warningLines.slice(warningOffset, warningOffset + warningLimit);
|
|
9097
|
+
const confirmationActions = [
|
|
9098
|
+
{ label: '返回检查', description: '回到完整操作汇总' },
|
|
9099
|
+
{ label: `我已了解影响,${review.primaryLabel}`, status: 'error' },
|
|
9100
|
+
];
|
|
9101
|
+
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
|
|
9102
|
+
? visibleWarnings.map((warning, index) => _jsx(StaticRow, { title: warning, status: warning.startsWith('! ') ? 'warning' : 'info', compact: true }, `${warningOffset + index}:${warning}`))
|
|
9103
|
+
: _jsx(StaticRow, { title: "! \u6B64\u64CD\u4F5C\u9700\u8981\u660E\u786E\u6388\u6743", status: "warning", compact: true }), !shortConfirmation && warningLines.length - warningOffset - visibleWarnings.length > 0
|
|
9104
|
+
? _jsx(StaticRow, { title: `↓ 后面还有 ${warningLines.length - warningOffset - visibleWarnings.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: confirmationActions, focus: overlayFocus, kind: "action", compact: true })] }));
|
|
9105
|
+
if (shortConfirmation) {
|
|
9106
|
+
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 })] });
|
|
9107
|
+
}
|
|
9108
|
+
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 });
|
|
9109
|
+
}
|
|
9110
|
+
if (sourceTransportOverlay) {
|
|
9111
|
+
const configuredSource = route.name === 'sourceEdit'
|
|
9112
|
+
? props.controller.getConfiguredSource(route.sourceId)
|
|
9113
|
+
: undefined;
|
|
9114
|
+
const currentLocation = sourceDraft?.owner === sourceTransportOverlay.owner
|
|
9115
|
+
&& sourceDraft.type === 'git'
|
|
9116
|
+
? sourceDraft.location
|
|
9117
|
+
: configuredSource?.type === 'git' ? configuredSource.url : undefined;
|
|
9118
|
+
const currentTransport = currentLocation === OFFICIAL_CATALOG_SOURCE_URL ? 'HTTPS'
|
|
9119
|
+
: currentLocation === OFFICIAL_CATALOG_SOURCE_SSH_URL ? 'SSH' : undefined;
|
|
9120
|
+
const transportActions = [
|
|
9121
|
+
{ label: 'HTTPS', description: '默认方式;无需预先配置 SSH 密钥', selected: currentTransport === 'HTTPS' },
|
|
9122
|
+
{ label: 'SSH', description: '适合已配置 GitHub SSH 访问的用户', selected: currentTransport === 'SSH' },
|
|
9123
|
+
];
|
|
9124
|
+
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 }) }) })] });
|
|
9125
|
+
}
|
|
8263
9126
|
if (!overlay)
|
|
8264
9127
|
return base;
|
|
8265
9128
|
const helpRows = helpRowsForRoute(route, actions.length, supportsTechnicalDetails, busy, props.onSessionNavigation !== undefined);
|