@lesliechan721/aw 0.1.0-beta.31 → 0.1.0-beta.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +5 -2
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +2 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/tui/catalog-sources/session.d.ts +5 -1
- package/dist/src/tui/catalog-sources/session.js +6 -1
- package/dist/src/tui/catalog-sources/session.js.map +1 -1
- package/dist/src/tui/catalog-sources/types.d.ts +1 -0
- package/dist/src/tui/clipboard.d.ts +13 -0
- package/dist/src/tui/clipboard.js +95 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/components.d.ts +21 -11
- package/dist/src/tui/components.js +69 -56
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-value-presentation.d.ts +19 -0
- package/dist/src/tui/config-value-presentation.js +77 -0
- package/dist/src/tui/config-value-presentation.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +1 -1
- package/dist/src/tui/console-app-v4.js +1205 -1377
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +5 -0
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +12 -1
- package/dist/src/tui/controller.js +171 -46
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostics.d.ts +2 -1
- package/dist/src/tui/diagnostics.js +8 -1
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +1 -0
- package/dist/src/tui/input-display.js +13 -0
- package/dist/src/tui/input-display.js.map +1 -1
- package/dist/src/tui/model.d.ts +5 -3
- package/dist/src/tui/model.js +17 -33
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +6 -3
- package/dist/src/tui/operation-diagnostic-screens.js +31 -9
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -1
- package/dist/src/tui/operation-history.js +5 -4
- package/dist/src/tui/operation-history.js.map +1 -1
- package/dist/src/tui/router.d.ts +5 -7
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +13 -22
- package/dist/src/tui/screen-registry.js +3 -4
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/dist/src/tui/viewport.d.ts +1 -0
- package/dist/src/tui/viewport.js +23 -0
- package/dist/src/tui/viewport.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { presentConfigValue, safeConfigText } from './config-value-presentation.js';
|
|
2
3
|
import { Box, Text, useInput, useWindowSize } from 'ink';
|
|
3
4
|
import { useEffect, useRef, useState } from 'react';
|
|
4
5
|
import path from 'node:path';
|
|
@@ -11,21 +12,22 @@ import { normalizePortableAgents, SKILLS_CLI_SUPPORTED_VERSION_LABEL, } from '..
|
|
|
11
12
|
import { selectLifecycleRetryAction, } from '../commands/operation-planner.js';
|
|
12
13
|
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, SCRIPT_TIMEOUT_MAX_MS, SCRIPT_TIMEOUT_MIN_MS, } from '../commands/options.js';
|
|
13
14
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
14
|
-
import { ActionRow, ObjectCardRow, objectCardRowLayout, StatusBadge, diagnosticDetailLines, Callout, ChoiceRow, ContentViewport,
|
|
15
|
+
import { ActionRow, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
15
16
|
import { safeDiagnosticCode, sanitizeDiagnosticText, } from './diagnostics.js';
|
|
16
17
|
import { extractPortableError, TuiConfigInvalidStoreError, userIssueKey, } from './controller.js';
|
|
17
18
|
import { pluginWriteTaskVersionEvidence } from './plugin-write-task.js';
|
|
18
19
|
import { catalogSourceFreshnessLabel, catalogSourceListPresentation, } from './catalog-sources/screens.js';
|
|
19
|
-
import { operationDiagnosticLineCount, operationHistoryMeta, operationHistoryState, operationReportLineCount, OperationDiagnosticScreen, OperationReportPreviewScreen, } from './operation-diagnostic-screens.js';
|
|
20
|
+
import { operationDiagnosticLineCount, operationDiagnosticText, operationHistoryMeta, operationHistoryState, operationReportLineCount, OperationDiagnosticScreen, OperationReportPreviewScreen, } from './operation-diagnostic-screens.js';
|
|
21
|
+
import { copyToClipboard } from './clipboard.js';
|
|
20
22
|
import { resolveTuiSizePolicy } from './layout.js';
|
|
21
23
|
import { buildDependencyGraphProjection, buildUnifiedDependencyGraphProjection, } from './dependency-graph.js';
|
|
22
24
|
import { platformLabel, scopeLabel } from './glossary.js';
|
|
23
25
|
import { InputDisplay, removeLastGrapheme } from './input-display.js';
|
|
24
26
|
import { catalogRefPolicyForPreset } from './source-ref-presets.js';
|
|
25
|
-
import { buildPlatformActions,
|
|
27
|
+
import { buildPlatformActions, configObjectHasSensitiveChild, hasInstalledPlacement, configValuesEqual, } from './model.js';
|
|
26
28
|
import { resolveTuiGlobalKeyAction, resolveTuiSessionNavigationKey } from './keys.js';
|
|
27
29
|
import { tuiTheme } from './theme.js';
|
|
28
|
-
import { contentRowBudget, rowViewport } from './viewport.js';
|
|
30
|
+
import { contentRowBudget, rowViewport, wrapDisplayText } from './viewport.js';
|
|
29
31
|
import { resolveRouteScreenId, routeDefinition, TUI_SCREEN_IDS, unreachableTuiRoute, } from './screen-registry.js';
|
|
30
32
|
export const CONSOLE_V4_SCREEN_IDS = TUI_SCREEN_IDS;
|
|
31
33
|
const PLUGIN_FILTERS = [
|
|
@@ -40,22 +42,6 @@ function pluginFilterLabel(filter) {
|
|
|
40
42
|
attention: '需处理',
|
|
41
43
|
}[filter];
|
|
42
44
|
}
|
|
43
|
-
function paginationActions(options) {
|
|
44
|
-
return [
|
|
45
|
-
{
|
|
46
|
-
actionId: 'pagination-previous',
|
|
47
|
-
label: 'p 上一页',
|
|
48
|
-
disabled: options.offset <= 0 ? '已在第一页' : undefined,
|
|
49
|
-
run: () => options.move(-1),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
actionId: 'pagination-next',
|
|
53
|
-
label: 'n 下一页',
|
|
54
|
-
disabled: options.offset + options.pageRows >= options.total ? '已在最后一页' : undefined,
|
|
55
|
-
run: () => options.move(1),
|
|
56
|
-
},
|
|
57
|
-
];
|
|
58
|
-
}
|
|
59
45
|
function sourceOperationBlockLabel(reason) {
|
|
60
46
|
switch (reason) {
|
|
61
47
|
case 'configDraft': return '未保存的插件设置';
|
|
@@ -175,17 +161,17 @@ function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy, se
|
|
|
175
161
|
if (route.name === 'pluginList') {
|
|
176
162
|
rows.push(['←/→ · [ / ]', '切换来源']);
|
|
177
163
|
rows.push(['/', '进入搜索']);
|
|
178
|
-
|
|
179
|
-
|
|
164
|
+
rows.push(['f', '切换插件状态']);
|
|
165
|
+
rows.push(['m', '原位选择多个插件']);
|
|
180
166
|
}
|
|
181
167
|
if (route.name === 'portableSkillSelect') {
|
|
182
168
|
rows.push(['Space', '切换当前 Skill']);
|
|
183
|
-
rows.push(['a', '
|
|
169
|
+
rows.push(['a', '定位下方操作 / 回到原项']);
|
|
184
170
|
}
|
|
185
171
|
if (route.name === 'batchSelection') {
|
|
186
172
|
rows.push(['Space', '切换当前插件']);
|
|
187
|
-
rows.push(['a', '
|
|
188
|
-
rows.push(['c', '
|
|
173
|
+
rows.push(['a', '定位下方操作 / 回到原项']);
|
|
174
|
+
rows.push(['c', '继续选择任务或查看汇总']);
|
|
189
175
|
}
|
|
190
176
|
if (route.name === 'configFields' && route.layer.scope !== 'effective') {
|
|
191
177
|
rows.push(['s', '查看保存汇总']);
|
|
@@ -305,12 +291,16 @@ function pluginUpdateDescription(plugin) {
|
|
|
305
291
|
}
|
|
306
292
|
return '当前启用目录已验证;可直接进入更新确认。';
|
|
307
293
|
}
|
|
294
|
+
function pluginPlatformUnknown(plugin, platform) {
|
|
295
|
+
const placements = plugin.installedPlacements.filter((entry) => entry.platform === platform);
|
|
296
|
+
return plugin.sourceUnavailable || placements.length === 0
|
|
297
|
+
|| placements.some((entry) => entry.state === 'installed' && entry.sharedNative?.state === 'unobserved');
|
|
298
|
+
}
|
|
308
299
|
function pluginPlatformBadges(plugin) {
|
|
309
300
|
const items = plugin.supportedPlatforms.map((platform) => {
|
|
310
301
|
const placements = plugin.installedPlacements.filter((entry) => entry.platform === platform);
|
|
311
302
|
const installed = placements.some((entry) => entry.state === 'installed' || entry.state === 'scopeConflict');
|
|
312
|
-
const unknown = plugin
|
|
313
|
-
|| placements.some((entry) => entry.state === 'installed' && entry.sharedNative?.state === 'unobserved');
|
|
303
|
+
const unknown = pluginPlatformUnknown(plugin, platform);
|
|
314
304
|
const attention = placements.some((entry) => entry.state === 'blocked' || entry.state === 'scopeConflict'
|
|
315
305
|
|| entry.state === 'installed' && (entry.health !== 'ready' || entry.blockingReason !== undefined));
|
|
316
306
|
const update = plugin.update.targets.some((target) => target.platform === platform
|
|
@@ -390,7 +380,7 @@ function moveFocus(actions, current, direction) {
|
|
|
390
380
|
return 0;
|
|
391
381
|
for (let offset = 1; offset <= actions.length; offset += 1) {
|
|
392
382
|
const next = (current + direction * offset + actions.length) % actions.length;
|
|
393
|
-
if (!actions[next]?.disabled)
|
|
383
|
+
if (!actions[next]?.disabled && actions[next]?.role !== 'navigation')
|
|
394
384
|
return next;
|
|
395
385
|
}
|
|
396
386
|
return current;
|
|
@@ -431,6 +421,31 @@ function wrapConsoleText(value, columns) {
|
|
|
431
421
|
lines.push(line);
|
|
432
422
|
return lines;
|
|
433
423
|
}
|
|
424
|
+
function sourceRefSummary(value) {
|
|
425
|
+
if (!value)
|
|
426
|
+
return '未知';
|
|
427
|
+
if (/^[0-9a-f]{40}(?:[0-9a-f]{24})?$/iu.test(value))
|
|
428
|
+
return value.slice(0, 8);
|
|
429
|
+
return value.replace(/^refs\/(?:heads|tags)\//u, '');
|
|
430
|
+
}
|
|
431
|
+
function relativeCheckTime(value, now = Date.now()) {
|
|
432
|
+
if (!value)
|
|
433
|
+
return '尚未检查';
|
|
434
|
+
const checkedAt = Date.parse(value);
|
|
435
|
+
if (!Number.isFinite(checkedAt))
|
|
436
|
+
return '时间未知';
|
|
437
|
+
const elapsed = Math.max(0, now - checkedAt);
|
|
438
|
+
if (elapsed < 60_000)
|
|
439
|
+
return '刚刚';
|
|
440
|
+
if (elapsed < 3_600_000)
|
|
441
|
+
return `${Math.floor(elapsed / 60_000)} 分钟前`;
|
|
442
|
+
if (elapsed < 86_400_000)
|
|
443
|
+
return `${Math.floor(elapsed / 3_600_000)} 小时前`;
|
|
444
|
+
return `${Math.floor(elapsed / 86_400_000)} 天前`;
|
|
445
|
+
}
|
|
446
|
+
function parseConfigNumberInput(input) {
|
|
447
|
+
return input.trim().length === 0 ? undefined : Number(input);
|
|
448
|
+
}
|
|
434
449
|
function changeDetailLines(changes, columns) {
|
|
435
450
|
const valueLines = (label, value) => [
|
|
436
451
|
label,
|
|
@@ -440,8 +455,19 @@ function changeDetailLines(changes, columns) {
|
|
|
440
455
|
`修改项 · ${change.title}`,
|
|
441
456
|
...valueLines('修改前', change.before),
|
|
442
457
|
...valueLines('修改后', change.after),
|
|
458
|
+
...(change.children ?? []).flatMap((child) => [child.property, ...valueLines('修改前', child.before), ...valueLines('修改后', child.after)]),
|
|
443
459
|
]);
|
|
444
460
|
}
|
|
461
|
+
function changeDetailText(changes) {
|
|
462
|
+
return changes.flatMap((change) => [
|
|
463
|
+
`修改项 · ${change.title}`,
|
|
464
|
+
`修改前\n ${change.before ?? '未设置'}`,
|
|
465
|
+
`修改后\n ${change.after ?? '未设置'}`,
|
|
466
|
+
...(change.children ?? []).flatMap((child) => [
|
|
467
|
+
child.property, `修改前\n ${child.before}`, `修改后\n ${child.after}`,
|
|
468
|
+
]),
|
|
469
|
+
]).join('\n');
|
|
470
|
+
}
|
|
445
471
|
function operationRiskWarnings(review) {
|
|
446
472
|
return [...new Set([
|
|
447
473
|
...(review.sharedImpact.warning ? [review.sharedImpact.warning] : []),
|
|
@@ -464,29 +490,12 @@ function operationRiskLines(review, columns) {
|
|
|
464
490
|
}
|
|
465
491
|
function actionViewport(actions, compact, focus, requestedOffset, lineBudget) {
|
|
466
492
|
return rowViewport({
|
|
467
|
-
rowHeights: actions.map((action) => compact || (!action.description && !action.disabled) ? 1 : 2),
|
|
493
|
+
rowHeights: actions.map((action) => action.role === 'navigation' ? 0 : compact || (!action.description && !action.disabled) ? 1 : 2),
|
|
468
494
|
focus,
|
|
469
495
|
requestedOffset,
|
|
470
496
|
rowBudget: lineBudget,
|
|
471
497
|
});
|
|
472
498
|
}
|
|
473
|
-
function objectCardActionViewport(actions, width, compact, focus, requestedOffset, lineBudget) {
|
|
474
|
-
return rowViewport({
|
|
475
|
-
rowHeights: actions.map((action) => objectCardRowLayout({
|
|
476
|
-
width,
|
|
477
|
-
description: action.disabled ?? action.description ?? '',
|
|
478
|
-
meta: action.meta,
|
|
479
|
-
hasTags: action.tags !== undefined,
|
|
480
|
-
compact,
|
|
481
|
-
}).height),
|
|
482
|
-
focus,
|
|
483
|
-
requestedOffset,
|
|
484
|
-
rowBudget: lineBudget,
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
function portableSkillCandidateViewport(actions, width, compact, focus, requestedOffset, rowBudget) {
|
|
488
|
-
return objectCardActionViewport(actions, width, compact, focus, requestedOffset, rowBudget);
|
|
489
|
-
}
|
|
490
499
|
function prefixRowLimit(rowHeights, rowBudget) {
|
|
491
500
|
let used = 0;
|
|
492
501
|
let visible = 0;
|
|
@@ -662,17 +671,40 @@ function configUnavailableDiagnostic(route) {
|
|
|
662
671
|
}
|
|
663
672
|
function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
664
673
|
const unavailable = state === 'unavailable' || state === 'error';
|
|
674
|
+
if (generation?.errorCode === 'catalogLogicalIdentityChanged') {
|
|
675
|
+
return {
|
|
676
|
+
severity: 'error',
|
|
677
|
+
title: '来源发布身份已变化',
|
|
678
|
+
userMessage: '待检查内容与当前来源不是同一发布身份。当前状态不能据此判定为健康。',
|
|
679
|
+
impact: [
|
|
680
|
+
{ state: 'blocked', message: '不能把待检查内容作为当前来源的更新。' },
|
|
681
|
+
{ state: 'degraded', message: '旧 Active 是否仍可用需要单独验证。' },
|
|
682
|
+
],
|
|
683
|
+
nextStep: '检查来源地址和发布身份,然后编辑来源,或移除后重新添加。',
|
|
684
|
+
technical: {
|
|
685
|
+
code: 'catalogLogicalIdentityChanged',
|
|
686
|
+
stage: 'compare-source-identity',
|
|
687
|
+
sourceId,
|
|
688
|
+
checks: ['核对来源地址、ref 和 logical identity。', '确认后编辑来源,或移除后重新添加。'],
|
|
689
|
+
},
|
|
690
|
+
};
|
|
691
|
+
}
|
|
665
692
|
if (generation?.compatibility?.state === 'unsupported') {
|
|
666
693
|
const { requiredCapabilities, missingCapabilities } = generation.compatibility;
|
|
694
|
+
const missingKnown = missingCapabilities.length > 0;
|
|
667
695
|
return {
|
|
668
696
|
severity: 'error',
|
|
669
697
|
title: '当前目录版本不兼容',
|
|
670
|
-
userMessage:
|
|
698
|
+
userMessage: missingKnown
|
|
699
|
+
? `当前 AW 缺少 ${missingCapabilities.length} 项所需功能支持。`
|
|
700
|
+
: '当前目录版本所需功能无法确认。',
|
|
671
701
|
impact: [
|
|
672
702
|
{ state: 'blocked', message: '这个目录版本不能用于浏览、安装或同步。' },
|
|
673
703
|
{ state: 'preserved', message: '当前启用版本和已下载内容保持不变。' },
|
|
674
704
|
],
|
|
675
|
-
nextStep:
|
|
705
|
+
nextStep: missingKnown
|
|
706
|
+
? '升级 AW 到支持所需能力的版本,或修改来源后重新启用。'
|
|
707
|
+
: '检查来源内容和 capability 诊断,再决定修复来源或更新 AW。',
|
|
676
708
|
technical: {
|
|
677
709
|
code: 'awCapabilityUnsupported',
|
|
678
710
|
stage: 'resolve-source',
|
|
@@ -681,7 +713,9 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
681
713
|
key: 'compatibility',
|
|
682
714
|
value: `required: ${requiredCapabilities.join('、')}; missing: ${missingCapabilities.join('、')}`,
|
|
683
715
|
}],
|
|
684
|
-
checks:
|
|
716
|
+
checks: missingKnown
|
|
717
|
+
? ['检查所需和缺少的 Catalog capability。', '升级 AW 后重新加载来源。']
|
|
718
|
+
: ['检查来源的 capability 声明和验证错误。', '确认原因后重新加载来源。'],
|
|
685
719
|
},
|
|
686
720
|
};
|
|
687
721
|
}
|
|
@@ -704,18 +738,24 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
704
738
|
};
|
|
705
739
|
}
|
|
706
740
|
if (freshness?.state === 'incompatible') {
|
|
707
|
-
const
|
|
741
|
+
const activeHealthy = generation?.state === 'ready' && generation.active !== undefined;
|
|
742
|
+
const missingCount = freshness.missingCapabilities?.length ?? 0;
|
|
743
|
+
const missingSummary = missingCount && missingCount > 0
|
|
744
|
+
? `缺少 ${missingCount} 项待检查版本所需功能`
|
|
745
|
+
: '待检查版本所需功能无法确认';
|
|
708
746
|
return {
|
|
709
747
|
severity: 'error',
|
|
710
748
|
title: '待检查版本不兼容',
|
|
711
|
-
userMessage:
|
|
712
|
-
? `当前 AW
|
|
713
|
-
: `当前 AW
|
|
749
|
+
userMessage: activeHealthy
|
|
750
|
+
? `当前 AW ${missingSummary}。普通更新已阻止;已验证的当前目录版本保持不变。`
|
|
751
|
+
: `当前 AW ${missingSummary}。这个来源没有已验证可用的目录版本。`,
|
|
714
752
|
impact: [
|
|
715
753
|
{ state: 'blocked', message: '不能直接切换到这个待检查版本。' },
|
|
716
|
-
...(
|
|
754
|
+
...(activeHealthy ? [{ state: 'preserved', message: '已验证的当前目录版本和插件浏览仍可用。' }] : []),
|
|
717
755
|
],
|
|
718
|
-
nextStep:
|
|
756
|
+
nextStep: missingCount > 0
|
|
757
|
+
? '升级 AW 到支持待检查版本所需功能的版本,或编辑来源后重新验证。'
|
|
758
|
+
: '检查来源的 capability 声明和安全诊断,再决定修正来源或升级 AW。',
|
|
719
759
|
technical: {
|
|
720
760
|
code: safeDiagnosticCode(freshness.errorCode, 'catalogincompatible'),
|
|
721
761
|
stage: 'compare-candidate',
|
|
@@ -723,7 +763,9 @@ function sourceDiagnostic(sourceId, state, detail, generation, freshness) {
|
|
|
723
763
|
...(freshness.missingCapabilities?.length ? {
|
|
724
764
|
context: [{ key: 'compatibility', value: freshness.missingCapabilities.join('、') }],
|
|
725
765
|
} : {}),
|
|
726
|
-
checks:
|
|
766
|
+
checks: missingCount > 0
|
|
767
|
+
? ['检查缺少的 Catalog capability。', '升级 AW 后执行完整验证。']
|
|
768
|
+
: ['检查来源的 capability 声明。', '查看安全错误代码后重新验证。'],
|
|
727
769
|
},
|
|
728
770
|
};
|
|
729
771
|
}
|
|
@@ -813,6 +855,8 @@ function SemanticRows(props) {
|
|
|
813
855
|
? props.actions.slice(offset)
|
|
814
856
|
: props.actions.slice(offset, offset + props.limit);
|
|
815
857
|
return (_jsx(Box, { flexDirection: "column", children: actions.map((action, visibleIndex) => {
|
|
858
|
+
if (action.role === 'navigation')
|
|
859
|
+
return null;
|
|
816
860
|
const index = visibleIndex + offset;
|
|
817
861
|
const rowProps = {
|
|
818
862
|
title: action.label,
|
|
@@ -824,7 +868,7 @@ function SemanticRows(props) {
|
|
|
824
868
|
compact: props.compact,
|
|
825
869
|
};
|
|
826
870
|
if (props.cards)
|
|
827
|
-
return _jsx(ObjectCardRow, { width: props.columns ?? 58, title: action.label, description: action.disabled ?? action.description ?? '', meta: action.meta, tags: action.tags, focused: index === props.focus, compact: props.compact }, `${action.label}:${index}`);
|
|
871
|
+
return _jsx(ObjectCardRow, { width: props.columns ?? 58, title: action.label, description: action.disabled ?? action.description ?? '', meta: action.meta, sourceLabel: action.sourceLabel, maxDescriptionRows: action.maxDescriptionRows, tags: action.tags, focused: index === props.focus, compact: props.compact }, `${action.label}:${index}`);
|
|
828
872
|
if (props.kind === 'section')
|
|
829
873
|
return _jsx(SectionRow, { ...rowProps }, `${action.label}:${index}`);
|
|
830
874
|
if (props.kind === 'choice') {
|
|
@@ -838,9 +882,36 @@ function SemanticRows(props) {
|
|
|
838
882
|
return _jsx(TaskRow, { ...rowProps }, `${action.label}:${index}`);
|
|
839
883
|
}) }));
|
|
840
884
|
}
|
|
885
|
+
function SequenceRows(props) {
|
|
886
|
+
const configLines = (action) => [
|
|
887
|
+
...wrapConsoleText(`${action.label}${action.meta ? ` · ${action.meta}` : ''}`, Math.max(1, props.columns - 2)),
|
|
888
|
+
...(action.description ? wrapConsoleText(action.description, Math.max(1, props.columns - 2)) : []),
|
|
889
|
+
];
|
|
890
|
+
const viewport = rowViewport({
|
|
891
|
+
rowHeights: props.actions.map((action, index) => action.role === 'navigation' ? 0 : index < props.itemCount
|
|
892
|
+
? props.wrapValues ? configLines(action).length : props.cards ? objectCardRowLayout({
|
|
893
|
+
width: props.columns, description: action.disabled ?? action.description ?? '',
|
|
894
|
+
meta: action.meta, sourceLabel: action.sourceLabel, maxDescriptionRows: action.maxDescriptionRows,
|
|
895
|
+
hasTags: action.tags !== undefined, compact: props.compact,
|
|
896
|
+
}).height : action.description ? 2 : 1
|
|
897
|
+
: 1),
|
|
898
|
+
focus: props.focus, requestedOffset: props.scrollOffset, rowBudget: props.rowBudget,
|
|
899
|
+
});
|
|
900
|
+
return _jsx(ContentViewport, { children: props.actions.slice(viewport.offset, viewport.offset + viewport.limit).map((action, visibleIndex) => {
|
|
901
|
+
if (action.role === 'navigation')
|
|
902
|
+
return null;
|
|
903
|
+
const index = viewport.offset + visibleIndex;
|
|
904
|
+
const focused = !props.inactive && index === props.focus;
|
|
905
|
+
if (index < props.itemCount && props.wrapValues)
|
|
906
|
+
return _jsx(Box, { flexDirection: "column", children: configLines(action).map((line, lineIndex) => _jsxs(Text, { bold: focused && lineIndex === 0, color: action.status === 'error' ? 'red' : undefined, children: [lineIndex === 0 ? focused ? '› ' : ' ' : ' ', line] }, lineIndex)) }, action.configKey ?? action.label);
|
|
907
|
+
return index < props.itemCount && props.cards
|
|
908
|
+
? _jsx(ObjectCardRow, { width: props.columns, title: `${action.selected === undefined ? '' : action.selected ? '[x] ' : '[ ] '}${action.label}`, description: action.disabled ?? action.description ?? '', meta: action.meta, sourceLabel: action.sourceLabel, maxDescriptionRows: action.maxDescriptionRows, tags: action.tags, focused: focused, compact: props.compact }, action.actionId ?? action.pluginId ?? action.label)
|
|
909
|
+
: _jsx(SemanticRows, { actions: [action], focus: focused ? 0 : -1, kind: index < props.itemCount ? 'section' : 'action', compact: index >= props.itemCount }, action.actionId ?? action.label);
|
|
910
|
+
}) });
|
|
911
|
+
}
|
|
841
912
|
function PluginRows(props) {
|
|
842
913
|
return (_jsx(Box, { flexDirection: "column", children: props.rows.slice(props.viewport.offset, props.viewport.offset + props.viewport.limit)
|
|
843
|
-
.map((row, visibleIndex) => row.menuAction ? (_jsx(TaskRow, { title: row.name, description: row.description, meta: row.statusLabel, status: row.status, focused: props.active && props.viewport.offset + visibleIndex === props.focus, compact: true }, row.pluginId)) : (_jsx(PluginListRow, { name: row.name, sourceLabel: row.sourceLabel, description: row.description, attention: row.attention, placementSummary: row.placementSummary, tags: row.tags, statusLabel: row.statusLabel, status: row.status, focused: props.active && props.viewport.offset + visibleIndex === props.focus, anchored: !props.active && props.viewport.offset + visibleIndex === props.focus }, row.pluginId))) }));
|
|
914
|
+
.map((row, visibleIndex) => row.menuAction ? (_jsx(TaskRow, { title: row.name, description: row.description, meta: row.statusLabel, status: row.status, focused: props.active && props.viewport.offset + visibleIndex === props.focus, compact: true }, row.pluginId)) : (_jsx(PluginListRow, { width: props.columns, name: row.name, sourceLabel: row.sourceLabel, description: row.description, attention: row.attention, placementSummary: row.placementSummary, tags: row.tags, statusLabel: row.statusLabel, status: row.status, focused: props.active && props.viewport.offset + visibleIndex === props.focus, anchored: !props.active && props.viewport.offset + visibleIndex === props.focus }, row.pluginId))) }));
|
|
844
915
|
}
|
|
845
916
|
function IssueTechnicalDetails(props) {
|
|
846
917
|
const visible = props.details.slice(0, props.compact ? 1 : 2);
|
|
@@ -868,9 +939,10 @@ function SourceTabs(props) {
|
|
|
868
939
|
const state = 'state' in tab ? tab.state : 'ready';
|
|
869
940
|
return `${tab.id === props.current ? '[' : ''}${tab.label}${badge(state, tab.count)}${tab.id === props.current ? ']' : ''} `;
|
|
870
941
|
}).join('')}`;
|
|
871
|
-
if (props.compact || terminalTextWidth(fullText) > props.columns) {
|
|
942
|
+
if (props.compact || terminalTextWidth(`${fullText}←→ 切换`) > props.columns) {
|
|
872
943
|
const tab = tabs[index];
|
|
873
|
-
|
|
944
|
+
const neighbour = tabs[(index + 1) % tabs.length];
|
|
945
|
+
return _jsxs(Text, { wrap: "truncate-end", children: ["\u6765\u6E90 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", tab.label, "]"] }), " \u2190\u2192 \u5207\u6362 ", tabs.length > 1 ? neighbour?.label : ''] });
|
|
874
946
|
}
|
|
875
947
|
return (_jsxs(Text, { wrap: "truncate-end", children: ["\u6765\u6E90 ", tabs.map((tab) => {
|
|
876
948
|
const active = tab.id === props.current;
|
|
@@ -879,7 +951,7 @@ function SourceTabs(props) {
|
|
|
879
951
|
: state === 'error' ? tuiTheme.statusColors.error
|
|
880
952
|
: state === 'update' || state === 'recovery' ? tuiTheme.statusColors.warning
|
|
881
953
|
: tuiTheme.colors.muted, children: [active ? '[' : '', tab.label, badge(state, tab.count), active ? ']' : '', ' '] }, tab.id));
|
|
882
|
-
})] }));
|
|
954
|
+
}), "\u2190\u2192 \u5207\u6362"] }));
|
|
883
955
|
}
|
|
884
956
|
function configTabs(page) {
|
|
885
957
|
return [
|
|
@@ -987,9 +1059,6 @@ function dependencyReferenceUsageLabel(reference, consumerName) {
|
|
|
987
1059
|
: reference.consumer.project === 'current' ? '当前项目' : '其他项目';
|
|
988
1060
|
return `${consumerName} · ${location} · ${platformLabel(reference.consumer.platform)}`;
|
|
989
1061
|
}
|
|
990
|
-
function dependencySummaryText(summary) {
|
|
991
|
-
return `${summary.directCount} 个直接 · ${summary.totalCount} 个总计`;
|
|
992
|
-
}
|
|
993
1062
|
function dependencyGraphRowStatus(row) {
|
|
994
1063
|
if (row.nodeState === 'ready')
|
|
995
1064
|
return { value: '正常', status: 'success' };
|
|
@@ -1064,14 +1133,13 @@ export function ConsoleAppV4(props) {
|
|
|
1064
1133
|
const [overlayFocus, setOverlayFocus] = useState(0);
|
|
1065
1134
|
const [operationConfirmation, setOperationConfirmation] = useState();
|
|
1066
1135
|
const [sourceTransportOverlay, setSourceTransportOverlay] = useState();
|
|
1067
|
-
const [pluginListChooser, setPluginListChooser] = useState();
|
|
1068
1136
|
const [projectReminderOpen, setProjectReminderOpen] = useState(false);
|
|
1069
1137
|
const [discardForExit, setDiscardForExit] = useState(false);
|
|
1070
1138
|
const [discardForSessionNavigation, setDiscardForSessionNavigation] = useState();
|
|
1071
1139
|
const [discardDraftConfirmation, setDiscardDraftConfirmation] = useState();
|
|
1072
1140
|
const [focus, setFocus] = useState(0);
|
|
1073
|
-
const
|
|
1074
|
-
const [
|
|
1141
|
+
const actionJumpAnchor = useRef(undefined);
|
|
1142
|
+
const [searchCursor, setSearchCursor] = useState(0);
|
|
1075
1143
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
1076
1144
|
const [searchFocus, setSearchFocus] = useState(false);
|
|
1077
1145
|
const [query, setQuery] = useState(snapshot.router.current.context.query ?? '');
|
|
@@ -1081,6 +1149,7 @@ export function ConsoleAppV4(props) {
|
|
|
1081
1149
|
const [scope, setScope] = useState('user');
|
|
1082
1150
|
const [selectedTargets, setSelectedTargets] = useState([]);
|
|
1083
1151
|
const [installPlatformScopes, setInstallPlatformScopes] = useState({});
|
|
1152
|
+
const [installPlatformIndex, setInstallPlatformIndex] = useState(0);
|
|
1084
1153
|
const [uninstallTargetKeys, setUninstallTargetKeys] = useState([]);
|
|
1085
1154
|
const [technicalRouteKey, setTechnicalRouteKey] = useState();
|
|
1086
1155
|
const [exitAfterBusy, setExitAfterBusy] = useState(false);
|
|
@@ -1098,12 +1167,12 @@ export function ConsoleAppV4(props) {
|
|
|
1098
1167
|
const [inputValue, setInputValue] = useState('');
|
|
1099
1168
|
const [inputError, setInputError] = useState();
|
|
1100
1169
|
const [configActionError, setConfigActionError] = useState();
|
|
1101
|
-
const [configGroupChooserOpen, setConfigGroupChooserOpen] = useState(false);
|
|
1102
|
-
const [configGroupChoiceFocus, setConfigGroupChoiceFocus] = useState(0);
|
|
1103
1170
|
const [actionError, setActionError] = useState();
|
|
1171
|
+
const [copyFeedback, setCopyFeedback] = useState();
|
|
1104
1172
|
const [configResetAvailability, setConfigResetAvailability] = useState({});
|
|
1105
1173
|
const [sourceDraft, setSourceDraft] = useState();
|
|
1106
1174
|
const [batchDraft, setBatchDraft] = useState();
|
|
1175
|
+
const batchSelectionAnchor = useRef(undefined);
|
|
1107
1176
|
const [batchTargetOffset, setBatchTargetOffset] = useState(0);
|
|
1108
1177
|
const [portableResultDetailOpen, setPortableResultDetailOpen] = useState(false);
|
|
1109
1178
|
const [portableResultDetailOffset, setPortableResultDetailOffset] = useState(0);
|
|
@@ -1134,6 +1203,7 @@ export function ConsoleAppV4(props) {
|
|
|
1134
1203
|
const [draftOwner, setDraftOwner] = useState();
|
|
1135
1204
|
const dynamicMenuFocus = useRef(undefined);
|
|
1136
1205
|
const pluginListFocusId = useRef(undefined);
|
|
1206
|
+
const listFocusAnchor = useRef(undefined);
|
|
1137
1207
|
const dependencyGraphContexts = useRef(new Map());
|
|
1138
1208
|
const selectedDependencyReferenceIndex = useRef(0);
|
|
1139
1209
|
const externalDraftRevision = useRef({ config: 0, source: 0, batch: 0 });
|
|
@@ -1159,10 +1229,6 @@ export function ConsoleAppV4(props) {
|
|
|
1159
1229
|
...(configDrafts.user?.updates ?? {}),
|
|
1160
1230
|
...(configDrafts.project?.updates ?? {}),
|
|
1161
1231
|
};
|
|
1162
|
-
const draftRemoves = [...new Set([
|
|
1163
|
-
...(configDrafts.user?.removes ?? []),
|
|
1164
|
-
...(configDrafts.project?.removes ?? []),
|
|
1165
|
-
])];
|
|
1166
1232
|
const definition = routeDefinition(route);
|
|
1167
1233
|
const rendererFamily = definition.rendererFamily;
|
|
1168
1234
|
const routeKey = JSON.stringify(route);
|
|
@@ -1378,10 +1444,6 @@ export function ConsoleAppV4(props) {
|
|
|
1378
1444
|
setInputValue('');
|
|
1379
1445
|
}
|
|
1380
1446
|
}, [route.name]);
|
|
1381
|
-
useEffect(() => {
|
|
1382
|
-
if (route.name !== 'configFields')
|
|
1383
|
-
setConfigGroupChooserOpen(false);
|
|
1384
|
-
}, [route.name]);
|
|
1385
1447
|
const configRoute = route.name === 'configFields'
|
|
1386
1448
|
|| route.name === 'configFieldEditor'
|
|
1387
1449
|
|| route.name === 'configObjectPropertyEditor'
|
|
@@ -1607,42 +1669,6 @@ export function ConsoleAppV4(props) {
|
|
|
1607
1669
|
return;
|
|
1608
1670
|
props.controller.updatePageContext({ configGroupId: tabs[0].id });
|
|
1609
1671
|
}, [props.controller, route.name, currentConfigPage, snapshot.router.current.context.configGroupId]);
|
|
1610
|
-
useEffect(() => {
|
|
1611
|
-
let count;
|
|
1612
|
-
if (route.name === 'sourceList')
|
|
1613
|
-
count = props.controller.listSources().length;
|
|
1614
|
-
else if (route.name === 'issueList') {
|
|
1615
|
-
const issueCount = props.controller.listUserIssues().length;
|
|
1616
|
-
count = issueCount || 2;
|
|
1617
|
-
}
|
|
1618
|
-
else if (route.name === 'operationHistory') {
|
|
1619
|
-
count = snapshot.operationHistorySummaries.length + 1;
|
|
1620
|
-
}
|
|
1621
|
-
else if (route.name === 'pluginList') {
|
|
1622
|
-
const items = props.controller.listPlugins({
|
|
1623
|
-
mode: route.mode,
|
|
1624
|
-
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
1625
|
-
...(query ? { query } : {}),
|
|
1626
|
-
installFilter,
|
|
1627
|
-
});
|
|
1628
|
-
const sourceUnavailable = route.sourceId !== undefined
|
|
1629
|
-
&& props.controller.getSourceOverview(route.sourceId).health?.state === 'unavailable';
|
|
1630
|
-
count = pluginListRowIds(route.mode, items, query, sourceUnavailable).length || 1;
|
|
1631
|
-
}
|
|
1632
|
-
if (count === undefined)
|
|
1633
|
-
return;
|
|
1634
|
-
setFocus((value) => Math.min(value, Math.max(0, count - 1)));
|
|
1635
|
-
setScrollOffset((value) => Math.min(value, Math.max(0, count - 1)));
|
|
1636
|
-
}, [
|
|
1637
|
-
policy.mode,
|
|
1638
|
-
props.controller,
|
|
1639
|
-
query,
|
|
1640
|
-
installFilter,
|
|
1641
|
-
route.name,
|
|
1642
|
-
route.name === 'pluginList' ? route.mode : '',
|
|
1643
|
-
route.name === 'pluginList' ? route.sourceId : '',
|
|
1644
|
-
snapshot.viewModel,
|
|
1645
|
-
]);
|
|
1646
1672
|
useEffect(() => {
|
|
1647
1673
|
if (route.name !== 'pluginWriteTask' || route.goal !== 'update')
|
|
1648
1674
|
return;
|
|
@@ -1800,6 +1826,11 @@ export function ConsoleAppV4(props) {
|
|
|
1800
1826
|
layer: configRoute.layer,
|
|
1801
1827
|
}, snapshot.router.current.context);
|
|
1802
1828
|
}, [props.controller, configRoute, currentConfigPage, activeField, snapshot.router.current.context]);
|
|
1829
|
+
useEffect(() => {
|
|
1830
|
+
if (route.name === 'configFieldAction' && activeField) {
|
|
1831
|
+
props.controller.replace({ ...route, name: 'configFieldEditor', editor: activeField.type });
|
|
1832
|
+
}
|
|
1833
|
+
}, [props.controller, route, activeField]);
|
|
1803
1834
|
const activeObjectKey = activeField?.type === 'object' && configRoute
|
|
1804
1835
|
? `${configOwnerKey(configRoute)}\0${activeField.key}`
|
|
1805
1836
|
: undefined;
|
|
@@ -1938,18 +1969,24 @@ export function ConsoleAppV4(props) {
|
|
|
1938
1969
|
let description = '为 Codex 和 Claude Code 添加、设置或移除开发能力。';
|
|
1939
1970
|
let breadcrumb = '首页';
|
|
1940
1971
|
let context;
|
|
1941
|
-
let help =
|
|
1972
|
+
let help = route.name === 'home'
|
|
1973
|
+
? '↑↓ 移动 · Enter 选择 · ? 帮助 · q 退出'
|
|
1974
|
+
: '↑↓ 移动 · Enter 选择 · Esc 返回 · ? 帮助';
|
|
1942
1975
|
let content;
|
|
1943
1976
|
let actions = [];
|
|
1977
|
+
let readingText;
|
|
1978
|
+
let confirmationText;
|
|
1944
1979
|
let referenceDetailPagination;
|
|
1945
1980
|
let confirmationPagination;
|
|
1946
1981
|
let pluginRows = [];
|
|
1947
|
-
let
|
|
1982
|
+
let bottomActions = [];
|
|
1983
|
+
let listQueryActions = [];
|
|
1948
1984
|
let selectAllPortableCandidates;
|
|
1949
1985
|
let togglePluginListFilter;
|
|
1950
1986
|
let movePluginListSource;
|
|
1951
1987
|
let choosePluginListFilter;
|
|
1952
1988
|
let choosePluginListSource;
|
|
1989
|
+
let returnToPreviousInstallPlatform;
|
|
1953
1990
|
let visibleConfigTabs = [];
|
|
1954
1991
|
let activeConfigTabId;
|
|
1955
1992
|
const dynamicMenu = plugin && route.name === 'platform'
|
|
@@ -1971,11 +2008,14 @@ export function ConsoleAppV4(props) {
|
|
|
1971
2008
|
: focus;
|
|
1972
2009
|
const pageActionBudget = (staticRows = 0) => contentRowBudget({
|
|
1973
2010
|
terminalRows: size.rows,
|
|
1974
|
-
description: Boolean(description),
|
|
2011
|
+
description: Boolean(actionError ?? description),
|
|
1975
2012
|
compact: policy.mode === 'compact',
|
|
1976
|
-
staticRows
|
|
2013
|
+
staticRows: staticRows
|
|
2014
|
+
+ Math.max(0, wrapConsoleText(actionError ?? description ?? '', size.columns - 2).length - 1)
|
|
2015
|
+
+ Math.max(0, wrapConsoleText(title, size.columns - 2).length - 1),
|
|
1977
2016
|
});
|
|
1978
|
-
const dependencyPlacementStaticRows = policy.mode === 'compact' ? 3 : 10
|
|
2017
|
+
const dependencyPlacementStaticRows = (policy.mode === 'compact' ? 3 : 10)
|
|
2018
|
+
+ (snapshot.installPlacementDraft?.exactRootTargets?.length ?? 0);
|
|
1979
2019
|
const actionViewportStaticRows = route.name === 'dependencyPlacement'
|
|
1980
2020
|
? dependencyPlacementStaticRows
|
|
1981
2021
|
: route.name === 'pluginDetail' ? policy.mode === 'compact' ? 6 : 8
|
|
@@ -1988,12 +2028,42 @@ export function ConsoleAppV4(props) {
|
|
|
1988
2028
|
const base = policy.mode === 'compact' ? compactRows : standardRows;
|
|
1989
2029
|
return base + Math.max(0, size.rows - baseline);
|
|
1990
2030
|
};
|
|
2031
|
+
const operationDetailPageRows = responsiveBudget(3, 6);
|
|
2032
|
+
const operationReportPageRows = responsiveBudget(3, 4);
|
|
1991
2033
|
const sourceTechnicalPageRows = Math.max(1, contentRowBudget({
|
|
1992
2034
|
terminalRows: size.rows, description: false, compact: policy.mode === 'compact', staticRows: 6,
|
|
1993
2035
|
}));
|
|
1994
2036
|
const resolveSourceTechnicalDiagnostic = (sourceId) => {
|
|
1995
2037
|
const overview = props.controller.getSourceOverview(sourceId);
|
|
1996
|
-
|
|
2038
|
+
const freshness = props.controller.getCatalogFreshness(sourceId);
|
|
2039
|
+
const diagnostic = sourceDiagnostic(sourceId, overview.health?.state ?? overview.source?.state, overview.detail, props.controller.getCatalogGeneration(sourceId), freshness);
|
|
2040
|
+
const evidenceContext = [
|
|
2041
|
+
{ key: 'sourcePolicy', value: overview.detail?.type === 'path'
|
|
2042
|
+
? 'path' : overview.detail?.refPolicy === 'explicit' ? 'explicit' : 'cliChannel' },
|
|
2043
|
+
...(freshness?.checkedAt
|
|
2044
|
+
? [{ key: 'freshness', value: freshness.checkedAt }]
|
|
2045
|
+
: []),
|
|
2046
|
+
...(freshness?.candidate
|
|
2047
|
+
? [{ key: 'candidate', value: JSON.stringify({
|
|
2048
|
+
catalogVersion: freshness.candidate.catalogVersion,
|
|
2049
|
+
requestedRef: freshness.candidate.requestedRef,
|
|
2050
|
+
resolvedCommit: freshness.candidate.resolvedCommit,
|
|
2051
|
+
}) }]
|
|
2052
|
+
: []),
|
|
2053
|
+
];
|
|
2054
|
+
if (diagnostic)
|
|
2055
|
+
return {
|
|
2056
|
+
...diagnostic,
|
|
2057
|
+
...(diagnostic.technical ? { technical: {
|
|
2058
|
+
...diagnostic.technical,
|
|
2059
|
+
...(diagnostic.technical.requestedRef || !overview.detail?.requestedRef
|
|
2060
|
+
? {} : { requestedRef: overview.detail.requestedRef }),
|
|
2061
|
+
...(diagnostic.technical.resolvedCommit || !overview.detail?.resolvedCommit
|
|
2062
|
+
? {} : { resolvedCommit: overview.detail.resolvedCommit }),
|
|
2063
|
+
context: [...(diagnostic.technical.context ?? []), ...evidenceContext],
|
|
2064
|
+
} } : {}),
|
|
2065
|
+
};
|
|
2066
|
+
return {
|
|
1997
2067
|
severity: 'warning',
|
|
1998
2068
|
title: '来源解析详情',
|
|
1999
2069
|
userMessage: '来源当前可用。',
|
|
@@ -2005,6 +2075,7 @@ export function ConsoleAppV4(props) {
|
|
|
2005
2075
|
sourceId,
|
|
2006
2076
|
...(overview.detail?.requestedRef ? { requestedRef: overview.detail.requestedRef } : {}),
|
|
2007
2077
|
...(overview.detail?.resolvedCommit ? { resolvedCommit: overview.detail.resolvedCommit } : {}),
|
|
2078
|
+
context: evidenceContext,
|
|
2008
2079
|
},
|
|
2009
2080
|
};
|
|
2010
2081
|
};
|
|
@@ -2026,12 +2097,11 @@ export function ConsoleAppV4(props) {
|
|
|
2026
2097
|
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
2027
2098
|
focusedEntityId: route.name === 'pluginList'
|
|
2028
2099
|
? pluginRows[focus]?.pluginId
|
|
2029
|
-
: route.name === '
|
|
2100
|
+
: route.name === 'batchSelection' ? actions[focus]?.pluginId
|
|
2101
|
+
: route.name === 'sourceList' ? props.controller.listSources()[focus]?.id : undefined,
|
|
2030
2102
|
});
|
|
2031
2103
|
props.controller.push(next);
|
|
2032
2104
|
setFocus(0);
|
|
2033
|
-
setFocusZone('content');
|
|
2034
|
-
setPinnedActionFocus(0);
|
|
2035
2105
|
setScrollOffset(0);
|
|
2036
2106
|
setSearchFocus(false);
|
|
2037
2107
|
if (next.name === 'pluginList') {
|
|
@@ -2040,6 +2110,19 @@ export function ConsoleAppV4(props) {
|
|
|
2040
2110
|
setShowTechnical(false);
|
|
2041
2111
|
setActionError(undefined);
|
|
2042
2112
|
};
|
|
2113
|
+
const closeBatchSelectionTools = () => {
|
|
2114
|
+
if (!batchDraft)
|
|
2115
|
+
return;
|
|
2116
|
+
const candidates = batchCandidates(batchDraft);
|
|
2117
|
+
const anchor = batchSelectionAnchor.current;
|
|
2118
|
+
const retained = anchor?.frameId === snapshot.router.currentFrameId
|
|
2119
|
+
? candidates.findIndex((candidate) => candidate.pluginId === anchor.pluginId) : -1;
|
|
2120
|
+
const nextFocus = retained >= 0 ? retained
|
|
2121
|
+
: Math.min(anchor?.index ?? 0, Math.max(0, candidates.length - 1));
|
|
2122
|
+
setBatchDraft((current) => current ? { ...current, showSelectionTools: false } : current);
|
|
2123
|
+
setFocus(nextFocus);
|
|
2124
|
+
setScrollOffset(Math.min(anchor?.scrollOffset ?? 0, nextFocus));
|
|
2125
|
+
};
|
|
2043
2126
|
const toggleTechnical = () => {
|
|
2044
2127
|
if (portableResultDetailOpen)
|
|
2045
2128
|
setFocus(portableResultDetailReturnFocus.current);
|
|
@@ -2072,6 +2155,12 @@ export function ConsoleAppV4(props) {
|
|
|
2072
2155
|
? restoredEntityIndex
|
|
2073
2156
|
: Math.min(restoredFocus, Math.max(0, rowIds.length - 1));
|
|
2074
2157
|
}
|
|
2158
|
+
else if (restoredFrame.route.name === 'batchSelection' && batchDraft) {
|
|
2159
|
+
const candidates = batchCandidates(batchDraft);
|
|
2160
|
+
const entityIndex = candidates.findIndex((candidate) => candidate.pluginId === restored.focusedEntityId);
|
|
2161
|
+
itemCount = candidates.length + 3;
|
|
2162
|
+
restoredFocus = entityIndex >= 0 ? entityIndex : Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2163
|
+
}
|
|
2075
2164
|
else if (restoredFrame.route.name === 'sourceList') {
|
|
2076
2165
|
const sources = props.controller.listSources();
|
|
2077
2166
|
itemCount = sources.length + 3;
|
|
@@ -2139,8 +2228,6 @@ export function ConsoleAppV4(props) {
|
|
|
2139
2228
|
else
|
|
2140
2229
|
props.controller.back();
|
|
2141
2230
|
restorePageContext();
|
|
2142
|
-
setFocusZone('content');
|
|
2143
|
-
setPinnedActionFocus(0);
|
|
2144
2231
|
setActionError(undefined);
|
|
2145
2232
|
};
|
|
2146
2233
|
const acceptSourceFieldAndBack = () => {
|
|
@@ -2237,8 +2324,7 @@ export function ConsoleAppV4(props) {
|
|
|
2237
2324
|
showSelected: false,
|
|
2238
2325
|
});
|
|
2239
2326
|
setDirty(true);
|
|
2240
|
-
setFocus(0);
|
|
2241
|
-
setScrollOffset(0);
|
|
2327
|
+
setFocus(Math.min(focus, Math.max(0, pluginRows.length - 1)));
|
|
2242
2328
|
setSearchFocus(false);
|
|
2243
2329
|
};
|
|
2244
2330
|
const batchCandidates = (draft) => {
|
|
@@ -2433,7 +2519,13 @@ export function ConsoleAppV4(props) {
|
|
|
2433
2519
|
};
|
|
2434
2520
|
const beginPortable = async (pluginId) => {
|
|
2435
2521
|
try {
|
|
2436
|
-
|
|
2522
|
+
if (route.name === 'portableBlocker' && snapshot.portableDraft?.pluginId === pluginId) {
|
|
2523
|
+
await props.controller.refreshPortableInstallDraft(pluginId);
|
|
2524
|
+
props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
|
|
2525
|
+
}
|
|
2526
|
+
else {
|
|
2527
|
+
await props.controller.preparePortableInstall(pluginId, route.name === 'portableBlocker' ? 'replace' : 'push');
|
|
2528
|
+
}
|
|
2437
2529
|
setFocus(0);
|
|
2438
2530
|
setPortableSelectionView('all');
|
|
2439
2531
|
setScrollOffset(0);
|
|
@@ -2443,8 +2535,13 @@ export function ConsoleAppV4(props) {
|
|
|
2443
2535
|
if (error instanceof Error && error.name === 'AbortError')
|
|
2444
2536
|
return;
|
|
2445
2537
|
const blocker = extractPortableError(error);
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2538
|
+
const operationRecordId = props.controller.recordUiFailure(error, {
|
|
2539
|
+
code: blocker.diagnosticCode,
|
|
2540
|
+
stage: 'prepare-portable',
|
|
2541
|
+
});
|
|
2542
|
+
const target = { name: 'portableBlocker', pluginId, ...blocker, operationRecordId };
|
|
2543
|
+
if (props.controller.snapshot.router.current.route.name === 'portableBlocker'
|
|
2544
|
+
|| props.controller.snapshot.router.current.route.name === 'portablePreparing') {
|
|
2448
2545
|
props.controller.replace(target);
|
|
2449
2546
|
}
|
|
2450
2547
|
else {
|
|
@@ -2476,7 +2573,11 @@ export function ConsoleAppV4(props) {
|
|
|
2476
2573
|
if (error instanceof Error && error.name === 'AbortError')
|
|
2477
2574
|
return false;
|
|
2478
2575
|
const blocker = extractPortableError(error);
|
|
2479
|
-
const
|
|
2576
|
+
const operationRecordId = props.controller.recordUiFailure(error, {
|
|
2577
|
+
code: blocker.diagnosticCode,
|
|
2578
|
+
stage: 'prepare-portable',
|
|
2579
|
+
});
|
|
2580
|
+
const target = { name: 'portableBlocker', pluginId, ...blocker, operationRecordId };
|
|
2480
2581
|
if (route.name === 'result')
|
|
2481
2582
|
leaveResult(target);
|
|
2482
2583
|
else
|
|
@@ -2735,43 +2836,14 @@ export function ConsoleAppV4(props) {
|
|
|
2735
2836
|
const configLayerLabel = (layer) => layer.scope === 'effective' ? '有效设置'
|
|
2736
2837
|
: layer.scope === 'user' ? '个人默认'
|
|
2737
2838
|
: '当前项目';
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
const keys = Object.keys(value).sort((left, right) => left.localeCompare(right, 'en'));
|
|
2747
|
-
const count = keys.length === 0
|
|
2748
|
-
? '空对象'
|
|
2749
|
-
: `${keys.length} 项(${keys.slice(0, 2).join('、')}${keys.length > 2 ? '…' : ''})`;
|
|
2750
|
-
return preset ? `方案“${preset.title}” · ${count}` : `自定义 · ${count}`;
|
|
2751
|
-
};
|
|
2752
|
-
const scalarSummary = (field, value) => {
|
|
2753
|
-
if (field.sensitive)
|
|
2754
|
-
return '将更新';
|
|
2755
|
-
const preset = field.choices?.find((choice) => configValuesEqual(choice.value, value));
|
|
2756
|
-
if (preset)
|
|
2757
|
-
return preset.title;
|
|
2758
|
-
if (field.type === 'boolean')
|
|
2759
|
-
return value ? '开启' : '关闭';
|
|
2760
|
-
return `${String(value)}${field.unit ? ` ${field.unit}` : ''}`;
|
|
2761
|
-
};
|
|
2762
|
-
const currentFieldDisplay = (field) => {
|
|
2763
|
-
if (draftRemoves.includes(field.key))
|
|
2764
|
-
return '恢复继承';
|
|
2765
|
-
if (Object.hasOwn(draftUpdates, field.key)) {
|
|
2766
|
-
const value = draftUpdates[field.key];
|
|
2767
|
-
return typeof value === 'object'
|
|
2768
|
-
? objectSummary(field, value)
|
|
2769
|
-
: scalarSummary(field, value);
|
|
2770
|
-
}
|
|
2771
|
-
if (field.type === 'boolean' && field.value !== undefined)
|
|
2772
|
-
return field.value ? '开启' : '关闭';
|
|
2773
|
-
return `${field.displayValue}${field.unit && field.displayValue !== '未设置' ? ` ${field.unit}` : ''}`;
|
|
2774
|
-
};
|
|
2839
|
+
const fieldValueText = (field, value, full = false, configured = value !== undefined) => presentConfigValue({
|
|
2840
|
+
definition: field.definition,
|
|
2841
|
+
value,
|
|
2842
|
+
configured,
|
|
2843
|
+
privateValue: field.sensitive,
|
|
2844
|
+
summary: field.summary,
|
|
2845
|
+
full,
|
|
2846
|
+
}).text;
|
|
2775
2847
|
const effectiveFieldDisplay = (field) => {
|
|
2776
2848
|
if (field.type === 'boolean' && field.effectiveValue !== undefined) {
|
|
2777
2849
|
return field.effectiveValue ? '开启' : '关闭';
|
|
@@ -2789,7 +2861,7 @@ export function ConsoleAppV4(props) {
|
|
|
2789
2861
|
const value = draft.updates[field.key];
|
|
2790
2862
|
if (field.sensitive)
|
|
2791
2863
|
return '将更新';
|
|
2792
|
-
return
|
|
2864
|
+
return fieldValueText(field, value);
|
|
2793
2865
|
};
|
|
2794
2866
|
const pendingFieldTargets = (field) => ['project', 'user'].flatMap((scope) => {
|
|
2795
2867
|
const value = pendingFieldDisplay(field, scope);
|
|
@@ -3020,7 +3092,7 @@ export function ConsoleAppV4(props) {
|
|
|
3020
3092
|
return '已设置';
|
|
3021
3093
|
if (typeof value === 'boolean')
|
|
3022
3094
|
return value ? '开启' : '关闭';
|
|
3023
|
-
return String(value);
|
|
3095
|
+
return value === '' ? '(空字符串)' : safeConfigText(String(value));
|
|
3024
3096
|
};
|
|
3025
3097
|
const updatedChildren = beforeObject && afterObject
|
|
3026
3098
|
? [...new Set([...Object.keys(beforeObject), ...Object.keys(afterObject)])]
|
|
@@ -3059,13 +3131,11 @@ export function ConsoleAppV4(props) {
|
|
|
3059
3131
|
: updatedChildren;
|
|
3060
3132
|
const after = draft.removes.includes(key)
|
|
3061
3133
|
? `恢复继承(${field?.fallbackDisplayValue ?? '未设置'} · ${field?.fallbackSourceLabel ?? '未设置'})`
|
|
3062
|
-
:
|
|
3063
|
-
? objectSummary(field, updatedValue)
|
|
3064
|
-
: scalarSummary(field, updatedValue);
|
|
3134
|
+
: fieldValueText(field, updatedValue, true);
|
|
3065
3135
|
return {
|
|
3066
3136
|
key,
|
|
3067
3137
|
title: field?.title ?? key,
|
|
3068
|
-
before: field
|
|
3138
|
+
before: field ? fieldValueText(field, field.draftValue ?? field.value, true, field.configured) : '未设置',
|
|
3069
3139
|
after,
|
|
3070
3140
|
...(children && children.length > 0 ? { children } : {}),
|
|
3071
3141
|
};
|
|
@@ -3184,34 +3254,8 @@ export function ConsoleAppV4(props) {
|
|
|
3184
3254
|
const projectIntentIssue = snapshot.projectIntentIssue;
|
|
3185
3255
|
const projectPendingCount = (projectReminder?.actionableTargets.length ?? 0)
|
|
3186
3256
|
+ (projectReminder?.blockedTargets.length ?? 0);
|
|
3187
|
-
const projectTargetCount = projectPendingCount + (projectReminder?.unconfirmedTargets.length ?? 0);
|
|
3188
3257
|
context = '当前项目';
|
|
3189
3258
|
actions = [
|
|
3190
|
-
...(projectIntentIssue ? [{
|
|
3191
|
-
actionId: 'project-intent-issue',
|
|
3192
|
-
label: projectIntentIssue.state === 'invalid'
|
|
3193
|
-
? '本项目插件声明无效'
|
|
3194
|
-
: '无法读取本项目插件声明',
|
|
3195
|
-
description: projectIntentIssue.state === 'invalid'
|
|
3196
|
-
? '本项目插件声明无效;修复后重新加载'
|
|
3197
|
-
: '无法读取本项目插件声明;检查项目位置或权限后重新加载',
|
|
3198
|
-
status: 'warning',
|
|
3199
|
-
run: () => go({ name: 'projectStatus' }),
|
|
3200
|
-
}] : []),
|
|
3201
|
-
...(projectTargetCount > 0 ? [{
|
|
3202
|
-
label: '项目插件',
|
|
3203
|
-
description: projectReminder.actionableTargets.length > 0
|
|
3204
|
-
? `${projectReminder.actionableTargets.length} 个位置可同步;`
|
|
3205
|
-
+ `${projectReminder.blockedTargets.length} 个位置需要先处理;`
|
|
3206
|
-
+ `${projectReminder.unconfirmedTargets.length} 个位置未确认`
|
|
3207
|
-
: projectReminder.blockedTargets.length > 0
|
|
3208
|
-
? `${projectReminder.blockedTargets.length} 个位置需要先处理;`
|
|
3209
|
-
+ `${projectReminder.unconfirmedTargets.length} 个位置未确认`
|
|
3210
|
-
: `${projectReminder.unconfirmedTargets.length} 个位置未确认`,
|
|
3211
|
-
meta: String(projectTargetCount),
|
|
3212
|
-
status: 'warning',
|
|
3213
|
-
run: () => go({ name: 'projectStatus' }),
|
|
3214
|
-
}] : []),
|
|
3215
3259
|
{ label: '插件', description: '浏览、安装和管理所有插件', meta: `${home.installedPlugins} 已安装`, status: home.pluginUpdateCount > 0 ? 'warning' : undefined, run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
3216
3260
|
{ label: '需要处理', description: '解决安装、来源或设置问题', meta: String(home.issueCount), status: 'warning', run: () => go({ name: 'issueList' }) },
|
|
3217
3261
|
{
|
|
@@ -3224,18 +3268,12 @@ export function ConsoleAppV4(props) {
|
|
|
3224
3268
|
run: () => go({ name: 'sourceList' }),
|
|
3225
3269
|
},
|
|
3226
3270
|
];
|
|
3227
|
-
const homeViewport = rowViewport({
|
|
3228
|
-
rowHeights: actions.map((action) => showTechnical ? 1 : objectCardRowLayout({
|
|
3229
|
-
description: action.description ?? '', meta: action.meta, width: size.columns - 2,
|
|
3230
|
-
compact: policy.mode === 'compact',
|
|
3231
|
-
}).height), focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(showTechnical ? 7 : 2),
|
|
3232
|
-
});
|
|
3233
3271
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["[\u5DF2\u5B89\u88C5 ", home.installedPlugins, "] [\u53EF\u66F4\u65B0 ", home.pluginUpdateCount, "]", home.unavailableSourceCount > 0 ? ` [不可用来源 ${home.unavailableSourceCount}]` : ''] }), _jsx(Text, { color: tuiTheme.colors.muted, children: projectIntentIssue ? '! 本项目插件声明需要检查'
|
|
3234
3272
|
: projectReminder && projectReminder.unconfirmedTargets.length > 0
|
|
3235
3273
|
? '? 部分项目插件状态无法确认' : projectPendingCount > 0
|
|
3236
3274
|
? `! ${projectPendingCount} 个位置待处理`
|
|
3237
3275
|
: snapshot.projectDeclarationAbsent
|
|
3238
|
-
? '当前目录没有共享插件声明;不会自动安装插件' : '没有待同步提醒' }), projectIntentIssue && showTechnical ? _jsxs(TechnicalDetails, { title: "\u9879\u76EE\u58F0\u660E\u8BCA\u65AD", compact: true, children: [_jsx(StaticRow, { title: "\u9519\u8BEF\u4EE3\u7801", meta: projectIntentIssue.code, compact: true }), _jsx(StaticRow, { title: "\u8BF4\u660E", meta: sanitizeDiagnosticText(projectIntentIssue.message), compact: true })] }) : null, _jsx(SemanticRows, { actions: actions, focus: projectReminderOpen ? -1 : focus, kind: "task",
|
|
3276
|
+
? '当前目录没有共享插件声明;不会自动安装插件' : '没有待同步提醒' }), projectIntentIssue && showTechnical ? _jsxs(TechnicalDetails, { title: "\u9879\u76EE\u58F0\u660E\u8BCA\u65AD", compact: true, children: [_jsx(StaticRow, { title: "\u9519\u8BEF\u4EE3\u7801", meta: projectIntentIssue.code, compact: true }), _jsx(StaticRow, { title: "\u8BF4\u660E", meta: sanitizeDiagnosticText(projectIntentIssue.message), compact: true })] }) : null, _jsx(SemanticRows, { actions: actions, focus: projectReminderOpen ? -1 : focus, kind: "task", compact: false })] });
|
|
3239
3277
|
}
|
|
3240
3278
|
else if (route.name === 'projectStatus') {
|
|
3241
3279
|
const reminder = snapshot.projectIntentReminder;
|
|
@@ -3321,7 +3359,7 @@ export function ConsoleAppV4(props) {
|
|
|
3321
3359
|
: () => go({ name: 'pluginDetail', pluginId: target.pluginId }),
|
|
3322
3360
|
};
|
|
3323
3361
|
});
|
|
3324
|
-
|
|
3362
|
+
bottomActions = [
|
|
3325
3363
|
...(actionable.length > 0 ? [{
|
|
3326
3364
|
actionId: 'project-sync-actionable',
|
|
3327
3365
|
label: `同步明确的 ${actionable.length} 个位置`,
|
|
@@ -3348,56 +3386,41 @@ export function ConsoleAppV4(props) {
|
|
|
3348
3386
|
description: '查看现有安全错误代码和诊断,不执行写操作',
|
|
3349
3387
|
run: () => setShowTechnical((value) => !value),
|
|
3350
3388
|
}] : []),
|
|
3351
|
-
{
|
|
3352
|
-
actionId: 'project-back-home',
|
|
3353
|
-
label: '返回首页',
|
|
3354
|
-
run: back,
|
|
3355
|
-
},
|
|
3356
3389
|
];
|
|
3357
3390
|
help = showTechnical
|
|
3358
|
-
? '
|
|
3359
|
-
:
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
const
|
|
3363
|
-
rowHeights: actions.map((action) => objectCardRowLayout({
|
|
3364
|
-
description: action.description ?? '', meta: action.meta,
|
|
3365
|
-
width: size.columns - 2, hasTags: true, compact: policy.mode === 'compact',
|
|
3366
|
-
}).height),
|
|
3367
|
-
focus,
|
|
3368
|
-
requestedOffset: scrollOffset,
|
|
3369
|
-
rowBudget: pageActionBudget(showTechnical ? 5
|
|
3370
|
-
: 1 + (projectIntentIssue || unconfirmed.length > 0 ? 1 : 0)
|
|
3371
|
-
+ (actionable.length > 0 && blocked.length + unconfirmed.length > 0 ? 1 : 0)),
|
|
3372
|
-
});
|
|
3373
|
-
const technicalLines = [
|
|
3391
|
+
? '←→ 或 PgUp/PgDn 阅读 · d 返回项目位置 · Esc 返回'
|
|
3392
|
+
: '↑↓ 移动 · Enter 查看 · Esc 返回';
|
|
3393
|
+
const projectItemCount = showTechnical ? 0 : actions.length;
|
|
3394
|
+
actions = [...(showTechnical ? [] : actions), ...bottomActions];
|
|
3395
|
+
const technicalFields = [
|
|
3374
3396
|
...(projectIntentIssue ? [
|
|
3375
3397
|
`项目声明 · ${safeDiagnosticCode(projectIntentIssue.code, 'projectIntentUnavailable')}`,
|
|
3376
|
-
|
|
3398
|
+
`说明 · ${sanitizeDiagnosticText(projectIntentIssue.message)}`,
|
|
3377
3399
|
] : []),
|
|
3378
3400
|
...allTargets.flatMap(({ target, state }) => [
|
|
3379
|
-
|
|
3401
|
+
`${target.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
3380
3402
|
...(target.reasonCodes.length > 0
|
|
3381
|
-
? target.reasonCodes.
|
|
3403
|
+
? target.reasonCodes.map((code) => `原因 · ${safeDiagnosticCode(code, 'projectTargetUnknown')}`)
|
|
3382
3404
|
: [state === 'actionable' ? '原因 · confirmedDifference'
|
|
3383
3405
|
: state === 'blocked' ? '原因 · blocked' : '原因 · unconfirmed']),
|
|
3384
3406
|
]),
|
|
3385
3407
|
];
|
|
3408
|
+
const technicalLines = technicalFields.flatMap((field) => wrapConsoleText(field, Math.max(20, size.columns - 8)));
|
|
3409
|
+
if (showTechnical)
|
|
3410
|
+
readingText = technicalFields.join('\n');
|
|
3411
|
+
if (showTechnical)
|
|
3412
|
+
description = undefined;
|
|
3386
3413
|
const technicalPageRows = responsiveBudget(4, 8);
|
|
3387
3414
|
const technicalOffset = Math.min(batchTargetOffset, Math.max(0, technicalLines.length - 1));
|
|
3388
3415
|
const visibleTechnicalLines = technicalLines.slice(technicalOffset, technicalOffset + technicalPageRows);
|
|
3389
3416
|
referenceDetailPagination = showTechnical && technicalLines.length > 0
|
|
3390
3417
|
? { total: technicalLines.length, pageRows: technicalPageRows }
|
|
3391
3418
|
: undefined;
|
|
3392
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
3393
|
-
id: action.actionId ?? `${action.label}:${index}`,
|
|
3394
|
-
label: action.label,
|
|
3395
|
-
disabled: Boolean(action.disabled),
|
|
3396
|
-
})), active: focusZone === 'actions', focus: pinnedActionFocus, visibleCount: policy.mode === 'compact' ? 2 : 3 }), projectIntentIssue || unconfirmed.length > 0 ? _jsx(StaticRow, { status: "warning", title: "? \u672A\u77E5\u72B6\u6001\u4E0D\u7B49\u4E8E\u672A\u5B89\u88C5\uFF1B\u6CA1\u6709\u81EA\u52A8\u6267\u884C\u5B89\u88C5\u6216\u6062\u590D\u3002", compact: true }) : null, actionable.length > 0 && blocked.length + unconfirmed.length > 0
|
|
3419
|
+
content = _jsxs(Box, { flexDirection: "column", children: [!showTechnical && (projectIntentIssue || unconfirmed.length > 0) ? _jsx(StaticRow, { status: "warning", title: "? \u672A\u77E5\u72B6\u6001\u4E0D\u7B49\u4E8E\u672A\u5B89\u88C5\uFF1B\u6CA1\u6709\u81EA\u52A8\u6267\u884C\u5B89\u88C5\u6216\u6062\u590D\u3002", compact: true }) : null, !showTechnical && actionable.length > 0 && blocked.length + unconfirmed.length > 0
|
|
3397
3420
|
? _jsx(StaticRow, { status: "info", title: `只同步明确项;保留 ${blocked.length} 个受阻位置和 ${unconfirmed.length} 个未确认位置。`, compact: true })
|
|
3398
3421
|
: null, showTechnical ? _jsxs(TechnicalDetails, { title: "\u9879\u76EE\u72B6\u6001\u5B89\u5168\u8BE6\u60C5", compact: true, children: [technicalOffset > 0
|
|
3399
3422
|
? _jsx(StaticRow, { title: `↑ 前面还有 ${technicalOffset} 行`, status: "muted", compact: true }) : null, visibleTechnicalLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${technicalOffset + index}:${line}`)), technicalOffset + visibleTechnicalLines.length < technicalLines.length
|
|
3400
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${technicalLines.length - technicalOffset - visibleTechnicalLines.length} 行`, status: "muted", compact: true }) : null] }) : _jsx(
|
|
3423
|
+
? _jsx(StaticRow, { title: `↓ 后面还有 ${technicalLines.length - technicalOffset - visibleTechnicalLines.length} 行`, status: "muted", compact: true }) : null] }) : _jsx(SequenceRows, { actions: actions, itemCount: projectItemCount, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2), compact: policy.mode === 'compact', cards: true }), showTechnical ? _jsx(SemanticRows, { actions: actions, offset: projectItemCount, focus: focus, kind: "action", compact: true }) : null] });
|
|
3401
3424
|
}
|
|
3402
3425
|
else if (route.name === 'pluginList') {
|
|
3403
3426
|
const sourceId = route.sourceId ?? 'all';
|
|
@@ -3416,12 +3439,10 @@ export function ConsoleAppV4(props) {
|
|
|
3416
3439
|
}).length,
|
|
3417
3440
|
]));
|
|
3418
3441
|
breadcrumb = '插件';
|
|
3419
|
-
title =
|
|
3420
|
-
description =
|
|
3442
|
+
title = `插件 · ${items.length} 个 [m] 多选`;
|
|
3443
|
+
description = undefined;
|
|
3421
3444
|
context = `${items.length} 个插件`;
|
|
3422
|
-
help =
|
|
3423
|
-
? '←→/↑↓ 操作 · Enter 执行 · Tab 插件列表 · Esc 返回'
|
|
3424
|
-
: '↑↓ 插件 · Enter 查看 · Tab 操作栏 · m 多选 · / 搜索 · f 状态 · Esc 返回';
|
|
3445
|
+
help = '↑↓ 移动 · Enter 查看 · Esc 返回';
|
|
3425
3446
|
const sourceError = sourceId !== 'all'
|
|
3426
3447
|
&& props.controller.getSourceOverview(sourceId).health?.state === 'unavailable';
|
|
3427
3448
|
actions = sourceError
|
|
@@ -3439,7 +3460,7 @@ export function ConsoleAppV4(props) {
|
|
|
3439
3460
|
const retained = selectedPluginId ? nextRowIds.indexOf(selectedPluginId) : -1;
|
|
3440
3461
|
setInstallFilter(nextFilter);
|
|
3441
3462
|
props.controller.updatePageContext({ installFilter: nextFilter });
|
|
3442
|
-
setFocus(retained >= 0 ? retained : 0);
|
|
3463
|
+
setFocus(retained >= 0 ? retained : Math.min(focus, Math.max(0, nextRowIds.length - 1)));
|
|
3443
3464
|
setScrollOffset(0);
|
|
3444
3465
|
};
|
|
3445
3466
|
togglePluginListFilter = () => {
|
|
@@ -3462,7 +3483,7 @@ export function ConsoleAppV4(props) {
|
|
|
3462
3483
|
props.controller.replace({ name: 'pluginList', mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}) }, { ...snapshot.router.current.context, query, installFilter, sourceTabId: next });
|
|
3463
3484
|
const retainedFocus = retained >= 0
|
|
3464
3485
|
? retained
|
|
3465
|
-
:
|
|
3486
|
+
: Math.min(focus, Math.max(0, nextRowIds.length - 1));
|
|
3466
3487
|
setFocus(retainedFocus);
|
|
3467
3488
|
setScrollOffset(retainedFocus >= 4 ? retainedFocus - 3 : 0);
|
|
3468
3489
|
};
|
|
@@ -3504,32 +3525,24 @@ export function ConsoleAppV4(props) {
|
|
|
3504
3525
|
}] : []),
|
|
3505
3526
|
{
|
|
3506
3527
|
actionId: 'plugin-list-multi',
|
|
3507
|
-
label:
|
|
3528
|
+
label: '选择多个插件 [m]',
|
|
3508
3529
|
description: '先选择插件,再选择安装、更新、恢复、卸载或维护任务',
|
|
3509
3530
|
status: 'info',
|
|
3510
3531
|
run: () => openBatchSelection(),
|
|
3511
3532
|
},
|
|
3512
3533
|
];
|
|
3513
|
-
const
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
focus: Math.max(0, PLUGIN_FILTERS.indexOf(installFilter)),
|
|
3520
|
-
}),
|
|
3521
|
-
},
|
|
3522
|
-
{ actionId: 'plugin-list-search', label: '/ 搜索', run: () => setSearchFocus(true) },
|
|
3523
|
-
{
|
|
3524
|
-
actionId: 'plugin-list-source',
|
|
3525
|
-
label: `来源:${sourceId === 'all' ? '全部' : sourceId}`,
|
|
3526
|
-
run: () => setPluginListChooser({
|
|
3527
|
-
kind: 'source',
|
|
3528
|
-
focus: Math.max(0, ['all', ...props.controller.listSources().map((source) => source.id)]
|
|
3529
|
-
.indexOf(sourceId)),
|
|
3530
|
-
}),
|
|
3534
|
+
const searchAction = {
|
|
3535
|
+
actionId: 'plugin-list-search', label: '搜索 [/]',
|
|
3536
|
+
run: () => {
|
|
3537
|
+
setSearchCursor([...new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
3538
|
+
.segment(query)].length);
|
|
3539
|
+
setSearchFocus(true);
|
|
3531
3540
|
},
|
|
3532
|
-
|
|
3541
|
+
};
|
|
3542
|
+
const filterAction = {
|
|
3543
|
+
actionId: 'plugin-list-filter', label: '切换筛选 [f]', run: togglePluginListFilter,
|
|
3544
|
+
};
|
|
3545
|
+
listQueryActions = [searchAction, filterAction];
|
|
3533
3546
|
const pluginRowsById = new Map([
|
|
3534
3547
|
...items.map((item) => [item.pluginId, {
|
|
3535
3548
|
pluginId: item.pluginId,
|
|
@@ -3600,38 +3613,32 @@ export function ConsoleAppV4(props) {
|
|
|
3600
3613
|
} } : { disabled: '没有可打开的插件' }),
|
|
3601
3614
|
}];
|
|
3602
3615
|
}
|
|
3603
|
-
|
|
3616
|
+
bottomActions = dedupeActions([
|
|
3604
3617
|
...(sourceError ? actions : []),
|
|
3605
|
-
...
|
|
3606
|
-
...
|
|
3618
|
+
...actions,
|
|
3619
|
+
...menuActions,
|
|
3607
3620
|
]);
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
}
|
|
3620
|
-
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
disabled: Boolean(action.disabled),
|
|
3624
|
-
})), active: !pluginListChooser && focusZone === 'actions', focus: pinnedActionFocus, visibleCount: policy.mode === 'compact' ? 2 : 3 }), _jsx(SourceTabs, { controller: props.controller, current: sourceId, compact: policy.mode === 'compact', columns: size.columns - 2, mode: route.mode, query: query || undefined, installFilter: installFilter }), _jsxs(Text, { color: searchFocus ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["\u641C\u7D22 / ", searchFocus
|
|
3625
|
-
? _jsx(InputDisplay, { value: query, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3626
|
-
: _jsxs(_Fragment, { children: [query || '搜索名称、用途或来源…', query ? ' / 编辑' : ''] })] }), _jsxs(Text, { wrap: "truncate-end", color: tuiTheme.colors.muted, children: ["\u72B6\u6001 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", pluginFilterLabel(installFilter), " ", filterCounts[installFilter], "]"] }), " \u5168\u90E8 ", filterCounts.all, " \u00B7 \u5DF2\u88C5 ", filterCounts.installed, " \u00B7 \u672A\u88C5 ", filterCounts.uninstalled, " \u00B7 \u66F4\u65B0 ", filterCounts.updateAvailable, " \u00B7 \u5904\u7406 ", filterCounts.attention, " f \u5207\u6362"] }), sourceError
|
|
3621
|
+
actions = [...pluginRows.map((row) => ({
|
|
3622
|
+
actionId: `plugin:${row.pluginId}`, pluginId: row.pluginId, label: row.name,
|
|
3623
|
+
run: () => go({ name: 'pluginDetail', pluginId: row.pluginId }),
|
|
3624
|
+
})), ...bottomActions];
|
|
3625
|
+
const viewport = rowViewport({
|
|
3626
|
+
rowHeights: actions.map((_, index) => index < pluginRows.length
|
|
3627
|
+
? pluginListRowLayout({
|
|
3628
|
+
...pluginRows[index], width: size.columns - 2,
|
|
3629
|
+
hasTags: Boolean(pluginRows[index].tags),
|
|
3630
|
+
}).height : 1),
|
|
3631
|
+
focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(3),
|
|
3632
|
+
});
|
|
3633
|
+
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(SourceTabs, { controller: props.controller, current: sourceId, compact: policy.mode === 'compact', columns: size.columns - 2, mode: route.mode, query: query || undefined, installFilter: installFilter }), _jsxs(Text, { color: searchFocus ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["\u641C\u7D22 [/] ", searchFocus
|
|
3634
|
+
? _jsx(InputDisplay, { value: query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3635
|
+
: _jsxs(_Fragment, { children: [query || '输入名称或用途', query ? ' / 编辑' : ''] })] }), _jsxs(Text, { wrap: "truncate-end", color: tuiTheme.colors.muted, children: ["\u7B5B\u9009 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", pluginFilterLabel(installFilter), " ", filterCounts[installFilter], "]"] }), " [f] \u5207\u6362"] }), sourceError
|
|
3627
3636
|
? _jsx(Callout, { status: "error", title: `${sourceId} 暂时无法提供插件`, description: "\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })
|
|
3628
3637
|
: route.mode === 'installed' && items.length === 0 && !query && sourceId === 'all'
|
|
3629
3638
|
? installFilter === 'updateAvailable'
|
|
3630
3639
|
? _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" })
|
|
3631
3640
|
: _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" })
|
|
3632
|
-
: null,
|
|
3633
|
-
? _jsx(PluginRows, { rows: pluginRows, focus: focus, viewport: viewport, active: !pluginListChooser && focusZone === 'content' })
|
|
3634
|
-
: _jsx(SemanticRows, { actions: actions, focus: !pluginListChooser && focusZone === 'content' ? focus : -1, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit }) })] }));
|
|
3641
|
+
: null, _jsxs(ContentViewport, { children: [_jsx(PluginRows, { columns: size.columns - 2, rows: pluginRows, focus: focus, viewport: viewport, active: !searchFocus }), _jsx(SemanticRows, { actions: actions, focus: searchFocus ? -1 : focus, kind: "action", compact: true, offset: Math.max(pluginRows.length, viewport.offset), limit: Math.max(0, viewport.offset + viewport.limit - Math.max(pluginRows.length, viewport.offset)) })] })] }));
|
|
3635
3642
|
}
|
|
3636
3643
|
else if (route.name === 'batchSelection') {
|
|
3637
3644
|
breadcrumb = '插件 / 选择多个';
|
|
@@ -3639,12 +3646,12 @@ export function ConsoleAppV4(props) {
|
|
|
3639
3646
|
: batchDraft?.stage === 'maintenance' ? '其他维护方式'
|
|
3640
3647
|
: '选择插件';
|
|
3641
3648
|
description = batchDraft?.stage === 'select'
|
|
3642
|
-
?
|
|
3649
|
+
? undefined
|
|
3643
3650
|
: batchDraft?.stage === 'task'
|
|
3644
3651
|
? '更新和恢复沿用已经添加的位置;移除会逐项显示已有位置。'
|
|
3645
3652
|
: '同步、重新安装和普通清理使用所选插件的已有位置。';
|
|
3646
3653
|
if (!batchDraft) {
|
|
3647
|
-
actions = [{ label: '返回插件列表', run: back }];
|
|
3654
|
+
actions = [{ role: 'navigation', label: '返回插件列表', run: back }];
|
|
3648
3655
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true });
|
|
3649
3656
|
}
|
|
3650
3657
|
else if (batchDraft.stage === 'select') {
|
|
@@ -3658,8 +3665,10 @@ export function ConsoleAppV4(props) {
|
|
|
3658
3665
|
const plugin = candidate.plugin;
|
|
3659
3666
|
return {
|
|
3660
3667
|
label: plugin
|
|
3661
|
-
?
|
|
3668
|
+
? plugin.displayName
|
|
3662
3669
|
: `${candidate.pluginId}(已不可用,可取消选择)`,
|
|
3670
|
+
sourceLabel: plugin?.sourceLabel,
|
|
3671
|
+
maxDescriptionRows: 2,
|
|
3663
3672
|
description: plugin
|
|
3664
3673
|
? `${plugin.description} · ${blockReason
|
|
3665
3674
|
? `${isSelected ? '可取消选择' : '不可选择'}:${blockReason}`
|
|
@@ -3695,9 +3704,11 @@ export function ConsoleAppV4(props) {
|
|
|
3695
3704
|
}
|
|
3696
3705
|
moveBatchStage('task');
|
|
3697
3706
|
};
|
|
3707
|
+
const continueLabel = batchDraft.recommendedGoal === 'update' ? '查看升级汇总'
|
|
3708
|
+
: batchDraft.recommendedGoal ? '查看恢复汇总' : '继续选择任务';
|
|
3698
3709
|
const continueAction = {
|
|
3699
3710
|
actionId: 'batch-selection-continue',
|
|
3700
|
-
label:
|
|
3711
|
+
label: `${continueLabel}(${batchDraft.selectedPluginIds.length}) [c]`,
|
|
3701
3712
|
description: batchDraft.selectedPluginIds.length > 0
|
|
3702
3713
|
? batchDraft.recommendedGoal ? '重新读取更新信息并显示操作汇总' : '继续选择任务和确认范围'
|
|
3703
3714
|
: '先选择至少一个插件',
|
|
@@ -3715,19 +3726,13 @@ export function ConsoleAppV4(props) {
|
|
|
3715
3726
|
} : current);
|
|
3716
3727
|
setFocus(0);
|
|
3717
3728
|
setScrollOffset(0);
|
|
3718
|
-
setFocusZone('content');
|
|
3719
3729
|
},
|
|
3720
3730
|
};
|
|
3721
3731
|
const selectionToolActions = [
|
|
3722
3732
|
{
|
|
3723
|
-
label: '返回插件选择',
|
|
3733
|
+
role: 'navigation', label: '返回插件选择',
|
|
3724
3734
|
description: '保留当前已选插件',
|
|
3725
|
-
run:
|
|
3726
|
-
setBatchDraft((current) => current ? { ...current, showSelectionTools: false } : current);
|
|
3727
|
-
setFocus(0);
|
|
3728
|
-
setScrollOffset(0);
|
|
3729
|
-
setFocusZone('content');
|
|
3730
|
-
},
|
|
3735
|
+
run: closeBatchSelectionTools,
|
|
3731
3736
|
},
|
|
3732
3737
|
{
|
|
3733
3738
|
label: '全选当前结果',
|
|
@@ -3755,35 +3760,25 @@ export function ConsoleAppV4(props) {
|
|
|
3755
3760
|
label: batchDraft.showSelectionTools ? '返回插件选择' : '选择工具',
|
|
3756
3761
|
description: '全选、取消当前结果或清空全部选择',
|
|
3757
3762
|
run: () => {
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
}
|
|
3763
|
+
if (batchDraft.showSelectionTools) {
|
|
3764
|
+
closeBatchSelectionTools();
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
setBatchDraft((current) => current ? { ...current, showSelectionTools: true } : current);
|
|
3762
3768
|
setFocus(0);
|
|
3763
3769
|
setScrollOffset(0);
|
|
3764
|
-
setFocusZone('content');
|
|
3765
3770
|
},
|
|
3766
3771
|
};
|
|
3767
|
-
|
|
3768
|
-
|
|
3772
|
+
bottomActions = [continueAction, selectedSetAction, selectionToolsAction];
|
|
3773
|
+
actions = [...(batchDraft.showSelectionTools ? selectionToolActions : candidateActions), ...bottomActions];
|
|
3769
3774
|
const batchFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
3770
3775
|
context = `已选 ${batchDraft.selectedPluginIds.length} 个 · 显示 ${candidates.length} 个${hiddenSelected > 0 ? ` · 隐藏 ${hiddenSelected} 个` : ''}`;
|
|
3771
3776
|
help = batchDraft.showSelectionTools
|
|
3772
|
-
? '↑↓ 移动 · Enter 执行 ·
|
|
3773
|
-
: '↑↓ 移动 · Space 选择 · Enter 查看 ·
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
: objectCardActionViewport(actions, cardWidth, policy.mode === 'compact', batchFocus, scrollOffset, Math.max(1, pageActionBudget(4)));
|
|
3778
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action) => ({
|
|
3779
|
-
id: action.actionId ?? action.label,
|
|
3780
|
-
label: action.label,
|
|
3781
|
-
disabled: Boolean(action.disabled),
|
|
3782
|
-
})), active: focusZone === 'actions', focus: pinnedActionFocus, visibleCount: policy.mode === 'compact' ? 2 : 3 }), _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
|
|
3783
|
-
? _jsx(InputDisplay, { value: batchDraft.query, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3784
|
-
: _jsxs(_Fragment, { children: [batchDraft.query || '搜索名称、用途或来源…', batchDraft.query ? ' / 编辑' : ''] })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u72B6\u6001 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", pluginFilterLabel(batchDraft.installFilter), "]"] }), " f \u5207\u6362"] }), _jsx(ContentViewport, { children: batchDraft.showSelectionTools
|
|
3785
|
-
? _jsx(SemanticRows, { actions: actions, focus: focusZone === 'content' ? batchFocus : -1, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit })
|
|
3786
|
-
: _jsx(Box, { flexDirection: "column", children: actions.slice(viewport.offset, viewport.offset + viewport.limit).map((action, visibleIndex) => (_jsx(ObjectCardRow, { width: cardWidth, title: `${action.selected ? '[x]' : '[ ]'} ${action.label}`, description: action.disabled ?? action.description ?? '', meta: action.meta, tags: action.tags, focused: focusZone === 'content' && viewport.offset + visibleIndex === batchFocus, anchored: focusZone !== 'content' && viewport.offset + visibleIndex === batchFocus, compact: policy.mode === 'compact' }, action.pluginId ?? action.label))) }) })] });
|
|
3777
|
+
? '↑↓ 移动 · Enter 执行 · Esc 返回选择'
|
|
3778
|
+
: '↑↓ 移动 · Space 选择 · Enter 查看 · Esc 返回';
|
|
3779
|
+
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
|
|
3780
|
+
? _jsx(InputDisplay, { value: batchDraft.query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3781
|
+
: _jsxs(_Fragment, { children: [batchDraft.query || '输入名称或用途', batchDraft.query ? ' / 编辑' : ''] })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u72B6\u6001 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", pluginFilterLabel(batchDraft.installFilter), "]"] }), " [f] \u5207\u6362"] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5DF2\u9009 ", selected.size, " \u00B7 \u9690\u85CF\u5DF2\u9009 ", hiddenSelected, " [c] ", continueLabel] }), _jsx(SequenceRows, { actions: actions, itemCount: batchDraft.showSelectionTools ? 0 : candidateActions.length, focus: batchFocus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(4), compact: policy.mode === 'compact', cards: true, inactive: searchFocus })] });
|
|
3787
3782
|
}
|
|
3788
3783
|
else {
|
|
3789
3784
|
const selectedIds = batchDraft.selectedPluginIds;
|
|
@@ -3824,7 +3819,7 @@ export function ConsoleAppV4(props) {
|
|
|
3824
3819
|
{ label: '重新安装', description: `重新生成 ${props.controller.listPluginWriteBatchTargets('reinstall', selectedIds).length} 个已保存或现场目标`, run: () => startExistingBatch('reinstall') },
|
|
3825
3820
|
{ label: '清理遗留内容', description: `${props.controller.listPluginWriteBatchTargets('cleanup', selectedIds).length} 个已核验位置;保留不属于插件或被共用的内容`, run: () => startExistingBatch('cleanup') },
|
|
3826
3821
|
{
|
|
3827
|
-
label: '返回任务选择',
|
|
3822
|
+
role: 'navigation', label: '返回任务选择',
|
|
3828
3823
|
run: () => moveBatchStage('task'),
|
|
3829
3824
|
},
|
|
3830
3825
|
];
|
|
@@ -3866,7 +3861,7 @@ export function ConsoleAppV4(props) {
|
|
|
3866
3861
|
description: '同步、重新安装和普通清理',
|
|
3867
3862
|
run: () => moveBatchStage('maintenance'),
|
|
3868
3863
|
},
|
|
3869
|
-
{ label: '返回调整选择', run: () => moveBatchStage('select') },
|
|
3864
|
+
{ role: 'navigation', label: '返回调整选择', run: () => moveBatchStage('select') },
|
|
3870
3865
|
];
|
|
3871
3866
|
}
|
|
3872
3867
|
context = `${selectedIds.length} 个插件 · ${syncTargets.length} 个已保存或现场目标`;
|
|
@@ -3905,7 +3900,7 @@ export function ConsoleAppV4(props) {
|
|
|
3905
3900
|
};
|
|
3906
3901
|
const confirmPlatforms = {
|
|
3907
3902
|
actionId: 'batch-install-platforms-confirm',
|
|
3908
|
-
label: '确定所选平台',
|
|
3903
|
+
label: '确定所选平台 [c]',
|
|
3909
3904
|
description: `${[...selectedPlatforms].map(platformLabel).join('、')} · 继续选择目标`,
|
|
3910
3905
|
run: () => {
|
|
3911
3906
|
props.controller.push({ name: 'batchInstallScope' });
|
|
@@ -3930,9 +3925,9 @@ export function ConsoleAppV4(props) {
|
|
|
3930
3925
|
},
|
|
3931
3926
|
confirmPlatforms,
|
|
3932
3927
|
];
|
|
3933
|
-
|
|
3934
|
-
help = '↑↓ 移动 · Enter
|
|
3935
|
-
content =
|
|
3928
|
+
bottomActions = [confirmPlatforms];
|
|
3929
|
+
help = '↑↓ 移动 · Space/Enter 切换 · c 确定所选平台 · Esc 返回任务选择';
|
|
3930
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ContentViewport, { children: [_jsx(SemanticRows, { actions: actions.slice(0, 2), focus: focus, kind: "choice", compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: 2 })] }) });
|
|
3936
3931
|
}
|
|
3937
3932
|
else if (route.name === 'batchInstallScope' || route.name === 'batchPlacement') {
|
|
3938
3933
|
breadcrumb = route.name === 'batchInstallScope'
|
|
@@ -3960,7 +3955,7 @@ export function ConsoleAppV4(props) {
|
|
|
3960
3955
|
let installPlacementActions = [];
|
|
3961
3956
|
let maintenanceTargetCount = 0;
|
|
3962
3957
|
if (!batchDraft?.action && !batchDraft?.maintenanceGoal) {
|
|
3963
|
-
actions = [{ label: '返回批量选择', run: back }];
|
|
3958
|
+
actions = [{ role: 'navigation', label: '返回批量选择', run: back }];
|
|
3964
3959
|
}
|
|
3965
3960
|
else if (maintenanceMode) {
|
|
3966
3961
|
const currentTargets = batchDraft.maintenanceGoal
|
|
@@ -4014,9 +4009,9 @@ export function ConsoleAppV4(props) {
|
|
|
4014
4009
|
actions = [
|
|
4015
4010
|
...targetActions,
|
|
4016
4011
|
confirmMaintenance,
|
|
4017
|
-
{ label: '返回任务选择', run: back },
|
|
4012
|
+
{ role: 'navigation', label: '返回任务选择', run: back },
|
|
4018
4013
|
];
|
|
4019
|
-
|
|
4014
|
+
bottomActions = [confirmMaintenance];
|
|
4020
4015
|
}
|
|
4021
4016
|
else {
|
|
4022
4017
|
const projectIntent = batchDraft.selection === 'currentProjectIntentRoots';
|
|
@@ -4106,7 +4101,7 @@ export function ConsoleAppV4(props) {
|
|
|
4106
4101
|
if (route.name === 'batchInstallScope') {
|
|
4107
4102
|
const confirmPlacement = installPlacementActions.find((action) => action.actionId === 'batch-placement-confirm');
|
|
4108
4103
|
if (confirmPlacement)
|
|
4109
|
-
|
|
4104
|
+
bottomActions = [confirmPlacement];
|
|
4110
4105
|
}
|
|
4111
4106
|
if (batchDraft.showPlacementTargets) {
|
|
4112
4107
|
actions = [
|
|
@@ -4118,7 +4113,7 @@ export function ConsoleAppV4(props) {
|
|
|
4118
4113
|
: target.state === 'blocked' ? 'warning' : 'muted',
|
|
4119
4114
|
})),
|
|
4120
4115
|
{
|
|
4121
|
-
label: '返回位置选择',
|
|
4116
|
+
role: 'navigation', label: '返回位置选择',
|
|
4122
4117
|
run: () => {
|
|
4123
4118
|
setBatchDraft((current) => current ? { ...current, showPlacementTargets: false } : current);
|
|
4124
4119
|
restoreActionFocus('batch-placement-details');
|
|
@@ -4137,35 +4132,26 @@ export function ConsoleAppV4(props) {
|
|
|
4137
4132
|
help = batchDraft?.showPlacementTargets
|
|
4138
4133
|
? '↑↓ 查看全部位置 · Esc 返回位置选择'
|
|
4139
4134
|
: maintenanceMode
|
|
4140
|
-
? '↑↓ 移动 · Enter
|
|
4135
|
+
? '↑↓ 移动 · Enter 选择· c 查看确认 · Esc 返回修改'
|
|
4141
4136
|
: '↑↓ 移动 · Enter 选择 · Esc 返回修改';
|
|
4142
4137
|
const placementDetail = Boolean(batchDraft?.showPlacementTargets);
|
|
4143
|
-
const viewport = actionViewport(actions, placementDetail ? false : policy.mode === 'compact', focus, scrollOffset, pageActionBudget(maintenanceMode ?
|
|
4138
|
+
const viewport = actionViewport(actions, placementDetail ? false : policy.mode === 'compact', focus, scrollOffset, pageActionBudget(maintenanceMode ? 4 : 1));
|
|
4144
4139
|
if (maintenanceMode) {
|
|
4145
4140
|
const targetOffset = Math.min(viewport.offset, maintenanceTargetCount);
|
|
4146
4141
|
const targetLimit = Math.max(0, Math.min(maintenanceTargetCount, viewport.offset + viewport.limit) - targetOffset);
|
|
4147
4142
|
const actionOffset = Math.max(0, viewport.offset - maintenanceTargetCount);
|
|
4148
4143
|
const actionLimit = Math.max(0, Math.min(actions.length - maintenanceTargetCount, viewport.offset + viewport.limit - maintenanceTargetCount) - actionOffset);
|
|
4149
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
4150
|
-
id: pinnedActions[0].actionId,
|
|
4151
|
-
label: pinnedActions[0].label,
|
|
4152
|
-
disabled: Boolean(pinnedActions[0].disabled),
|
|
4153
|
-
}], active: focusZone === 'actions', focus: pinnedActionFocus }), _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] });
|
|
4144
|
+
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] });
|
|
4154
4145
|
}
|
|
4155
4146
|
else if (!placementDetail && batchDraft?.action === 'install') {
|
|
4156
|
-
content =
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
const localLimit = Math.max(0, Math.min(group.actions.length - localOffset, viewport.offset + viewport.limit - groupOffset - localOffset));
|
|
4165
|
-
if (localLimit === 0)
|
|
4166
|
-
return null;
|
|
4167
|
-
return _jsxs(Box, { flexDirection: "column", children: [group.title ? _jsx(Text, { color: tuiTheme.colors.muted, children: group.title }) : null, _jsx(SemanticRows, { actions: group.actions, focus: focusZone === 'content' ? focus - groupOffset : -1, kind: group.kind, compact: group.kind === 'choice' || policy.mode === 'compact', offset: localOffset, limit: localLimit })] }, group.title ?? 'actions');
|
|
4168
|
-
}) })] });
|
|
4147
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsx(ContentViewport, { children: installPlacementGroups.map((group) => {
|
|
4148
|
+
const groupOffset = installPlacementActions.findIndex((action) => action.actionId === group.actions[0]?.actionId);
|
|
4149
|
+
const localOffset = Math.max(0, viewport.offset - groupOffset);
|
|
4150
|
+
const localLimit = Math.max(0, Math.min(group.actions.length - localOffset, viewport.offset + viewport.limit - groupOffset - localOffset));
|
|
4151
|
+
if (localLimit === 0)
|
|
4152
|
+
return null;
|
|
4153
|
+
return _jsxs(Box, { flexDirection: "column", children: [group.title ? _jsx(Text, { color: tuiTheme.colors.muted, children: group.title }) : null, _jsx(SemanticRows, { actions: group.actions, focus: focus - groupOffset, kind: group.kind, compact: group.kind === 'choice' || policy.mode === 'compact', offset: localOffset, limit: localLimit })] }, group.title ?? 'actions');
|
|
4154
|
+
}) }) });
|
|
4169
4155
|
}
|
|
4170
4156
|
else
|
|
4171
4157
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: placementDetail ? 'action' : 'choice', compact: placementDetail ? false : policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit });
|
|
@@ -4193,11 +4179,7 @@ export function ConsoleAppV4(props) {
|
|
|
4193
4179
|
meta: `${candidate.providerDisplayName} · ${candidate.relation === 'requested' ? '当前插件'
|
|
4194
4180
|
: candidate.relation === 'direct' ? '直接依赖插件' : '间接依赖插件'}`,
|
|
4195
4181
|
tags: _jsxs(Text, { color: tuiTheme.colors.muted, children: ["[", candidate.availableScopes.map(scopeLabel).join('、') || '范围待确认', "]", candidate.dynamic ? ' [按设置生成]' : ''] }),
|
|
4196
|
-
run: () =>
|
|
4197
|
-
name: 'portableSkillSummary',
|
|
4198
|
-
pluginId: plugin.pluginId,
|
|
4199
|
-
skillName: candidate.name,
|
|
4200
|
-
}),
|
|
4182
|
+
run: () => props.controller.togglePortableSkill(candidate.name),
|
|
4201
4183
|
}));
|
|
4202
4184
|
selectAllPortableCandidates = { actionId: 'portable-selection-select-all', label: '全选当前结果', run: () => {
|
|
4203
4185
|
for (const candidate of visibleCandidates) {
|
|
@@ -4221,63 +4203,45 @@ export function ConsoleAppV4(props) {
|
|
|
4221
4203
|
setScrollOffset(0);
|
|
4222
4204
|
} },
|
|
4223
4205
|
...(plugin.dependencySummary?.totalCount ? [{ actionId: 'portable-selection-dependencies', label: '查看依赖关系', run: () => go({ name: 'portableDependencies', pluginId: plugin.pluginId }) }] : []),
|
|
4224
|
-
{ actionId: 'portable-selection-tools-back', label: '返回 Skills 列表', run: () => {
|
|
4206
|
+
{ role: 'navigation', actionId: 'portable-selection-tools-back', label: '返回 Skills 列表', run: () => {
|
|
4225
4207
|
setPortableToolsOpen(false);
|
|
4226
4208
|
setFocus(0);
|
|
4227
4209
|
setScrollOffset(0);
|
|
4228
4210
|
} },
|
|
4229
4211
|
];
|
|
4230
|
-
const continueAction = { actionId: 'portable-selection-continue', label: `继续(${draft.selectedCount}
|
|
4212
|
+
const continueAction = { actionId: 'portable-selection-continue', label: `继续(${draft.selectedCount}) [c]`, run: continuePortableSelection };
|
|
4231
4213
|
const openToolsAction = { actionId: 'portable-selection-tools', label: '选择工具', run: () => {
|
|
4232
4214
|
setPortableToolsOpen(true);
|
|
4233
|
-
setFocusZone('content');
|
|
4234
4215
|
setFocus(0);
|
|
4235
4216
|
setScrollOffset(0);
|
|
4236
4217
|
} };
|
|
4237
|
-
|
|
4238
|
-
actions = portableToolsOpen ? selectionToolActions : candidateActions;
|
|
4239
|
-
const
|
|
4240
|
-
const viewport = showTechnical
|
|
4241
|
-
? { offset: focus < visibleCandidates.length ? focus : candidateViewport.offset, limit: 1 }
|
|
4242
|
-
: candidateViewport;
|
|
4243
|
-
const visibleCandidateEntries = visibleCandidates
|
|
4244
|
-
.map((candidate, index) => ({ candidate, action: candidateActions[index], index }))
|
|
4245
|
-
.slice(viewport.offset, viewport.offset + viewport.limit);
|
|
4218
|
+
bottomActions = [continueAction, openToolsAction];
|
|
4219
|
+
actions = portableToolsOpen ? selectionToolActions : [...candidateActions, ...bottomActions];
|
|
4220
|
+
const visibleCandidateEntries = visibleCandidates.map((candidate, index) => ({ candidate, index }));
|
|
4246
4221
|
const variantCandidates = portableVariantCandidates;
|
|
4247
4222
|
const focusedVariant = variantCandidates.find((candidate) => candidate.name === visibleCandidates[focus]?.name);
|
|
4248
4223
|
const visibleVariant = visibleCandidateEntries.find((entry) => portableScopeVariantsDiffer(entry.candidate))?.candidate;
|
|
4249
4224
|
const technicalVariant = focusedVariant ?? visibleVariant ?? variantCandidates[0];
|
|
4250
|
-
help = portableToolsOpen
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
: _jsx(Callout, { status: "warning", title: `! ${variantCandidates.length} 个 Skill 在不同安装范围会生成不同内容。选择范围后会重新确认最终内容。` })
|
|
4263
|
-
: null, !portableToolsOpen && visibleCandidates.length === 0
|
|
4264
|
-
? _jsx(StaticRow, { title: "\u6CA1\u6709\u5DF2\u9009 Skill", description: "\u9009\u62E9\u201C\u67E5\u770B\u5168\u90E8\u5019\u9009\u201D\u540E\u53EF\u7EE7\u7EED\u52FE\u9009", status: "muted", compact: true })
|
|
4265
|
-
: null, !portableToolsOpen && !draft.canContinue
|
|
4266
|
-
? _jsx(StaticRow, { title: "\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A Skill", status: "warning", compact: true })
|
|
4267
|
-
: null, !portableToolsOpen && showTechnical && technicalVariant ? _jsxs(TechnicalDetails, { title: "\u8303\u56F4\u53D8\u4F53", children: [_jsx(StaticRow, { title: `${technicalVariant.name} · ${portableScopeVariantLabel('project', technicalVariant.scopeDetails.project)}`, compact: true }, `${technicalVariant.name}:project`), _jsx(StaticRow, { title: `${technicalVariant.name} · ${portableScopeVariantLabel('user', technicalVariant.scopeDetails.user)}`, compact: true }, `${technicalVariant.name}:user`), variantCandidates.length > 1 ? _jsx(StaticRow, { title: `另有 ${variantCandidates.length - 1} 个 Skill 的范围变体已折叠`, status: "muted", compact: true }) : null] }) : null] })] });
|
|
4225
|
+
help = portableToolsOpen ? '↑↓ 移动 · Enter 选择 · Esc 返回'
|
|
4226
|
+
: '↑↓ 移动 · Space/Enter 切换 · Esc 返回';
|
|
4227
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ContentViewport, { children: [_jsx(SequenceRows, { actions: actions, itemCount: portableToolsOpen ? 0 : candidateActions.length, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget((variantCandidates.length > 0 ? 3 : 0) + (!draft.canContinue ? 1 : 0)
|
|
4228
|
+
+ (showTechnical && technicalVariant ? 5 : 0)), compact: policy.mode === 'compact', cards: true, inactive: portableEmptySelectionOpen }), !portableToolsOpen && variantCandidates.length > 0
|
|
4229
|
+
? showTechnical
|
|
4230
|
+
? _jsx(StaticRow, { title: `! ${variantCandidates.length} 个 Skill 存在范围变体`, status: "warning", compact: true })
|
|
4231
|
+
: _jsx(Callout, { status: "warning", title: `! ${variantCandidates.length} 个 Skill 在不同安装范围会生成不同内容。选择范围后会重新确认最终内容。` })
|
|
4232
|
+
: null, !portableToolsOpen && visibleCandidates.length === 0
|
|
4233
|
+
? _jsx(StaticRow, { title: "\u6CA1\u6709\u5DF2\u9009 Skill", description: "\u9009\u62E9\u201C\u67E5\u770B\u5168\u90E8\u5019\u9009\u201D\u540E\u53EF\u7EE7\u7EED\u52FE\u9009", status: "muted", compact: true })
|
|
4234
|
+
: null, !portableToolsOpen && !draft.canContinue
|
|
4235
|
+
? _jsx(StaticRow, { title: "\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A Skill", status: "warning", compact: true })
|
|
4236
|
+
: null, !portableToolsOpen && showTechnical && technicalVariant ? _jsxs(TechnicalDetails, { title: "\u8303\u56F4\u53D8\u4F53", children: [_jsx(StaticRow, { title: `${technicalVariant.name} · ${portableScopeVariantLabel('project', technicalVariant.scopeDetails.project)}`, compact: true }, `${technicalVariant.name}:project`), _jsx(StaticRow, { title: `${technicalVariant.name} · ${portableScopeVariantLabel('user', technicalVariant.scopeDetails.user)}`, compact: true }, `${technicalVariant.name}:user`), variantCandidates.length > 1 ? _jsx(StaticRow, { title: `另有 ${variantCandidates.length - 1} 个 Skill 的范围变体已折叠`, status: "muted", compact: true }) : null] }) : null] }) });
|
|
4268
4237
|
}
|
|
4269
|
-
else if (route.name === '
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
{ label: '返回 Skill 选择', run: back },
|
|
4277
|
-
{ label: '继续选择 Agent', disabled: snapshot.portableDraft.canContinue ? undefined : '至少选择一个 Skill', run: () => go({ name: 'portableAgentMode', pluginId: plugin.pluginId }) },
|
|
4278
|
-
];
|
|
4279
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { title: "\u5185\u5BB9\u6458\u8981", children: [_jsx(StaticRow, { title: "\u6240\u5C5E\u63D2\u4EF6", meta: candidate?.providerDisplayName ?? '未知', compact: true }), _jsx(StaticRow, { title: "\u751F\u6210\u65B9\u5F0F", meta: candidate?.dynamic ? '按当前插件设置生成' : '目录中的静态 Skill', compact: true }), _jsx(StaticRow, { title: "\u53EF\u7528\u8303\u56F4", meta: candidate?.availableScopes.map(scopeLabel).join('、') ?? '未确认', compact: true })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4280
|
-
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4238
|
+
else if (route.name === 'portablePreparing' && plugin) {
|
|
4239
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills`;
|
|
4240
|
+
context = '只读检查';
|
|
4241
|
+
title = '正在准备可安装的 Skills';
|
|
4242
|
+
description = '正在检查当前项目和所有项目范围的完整依赖。此步骤不会写入文件或调用 Skills CLI。';
|
|
4243
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(StatusCard, { status: "info", title: "\u51C6\u5907\u5019\u9009", children: [_jsx(StaticRow, { title: "\u25CF \u68C0\u67E5\u5F53\u524D\u9879\u76EE\u8303\u56F4", compact: true }), _jsx(StaticRow, { title: "\u25CF \u68C0\u67E5\u6240\u6709\u9879\u76EE\u8303\u56F4", compact: true }), _jsx(StaticRow, { title: "\u25CB \u5408\u5E76\u53EF\u9009 Skill \u4E0E Agent \u4FE1\u606F", status: "muted", compact: true })] }) });
|
|
4244
|
+
help = 'Esc 取消检查并返回目标平台';
|
|
4281
4245
|
}
|
|
4282
4246
|
else if (route.name === 'portableDependencies' && plugin && snapshot.portableDraft) {
|
|
4283
4247
|
const overview = dependencyOverview(plugin);
|
|
@@ -4286,14 +4250,20 @@ export function ConsoleAppV4(props) {
|
|
|
4286
4250
|
title = '生成这些候选 Skills 需要的插件';
|
|
4287
4251
|
description = '插件依赖用于生成,不能在这里取消。';
|
|
4288
4252
|
actions = [
|
|
4289
|
-
{ label: '返回 Skill 选择', run: back },
|
|
4253
|
+
{ role: 'navigation', label: '返回 Skill 选择', run: back },
|
|
4290
4254
|
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
4291
4255
|
];
|
|
4292
4256
|
const dependencies = overview?.dependencies ?? [];
|
|
4293
|
-
|
|
4294
|
-
const
|
|
4257
|
+
readingText = [plugin.displayName, ...dependencies.map((entry) => `${entry.displayName} · ${entry.pluginId} · ${entry.relation === 'direct' ? '直接依赖' : '间接依赖'} · ${entry.requiredBy.map((parent) => parent.displayName).join('、')} 需要它`)].join('\n');
|
|
4258
|
+
const dependencyLimit = showTechnical ? 1 : responsiveBudget(2, 3);
|
|
4259
|
+
const dependencyOffset = Math.min(batchTargetOffset, Math.max(0, dependencies.length - dependencyLimit));
|
|
4260
|
+
const visibleDependencies = dependencies.slice(dependencyOffset, dependencyOffset + dependencyLimit);
|
|
4261
|
+
const technicalDependencies = dependencies.slice(dependencyOffset, dependencyOffset + 1);
|
|
4295
4262
|
const compactTechnicalSummary = policy.mode === 'compact' && showTechnical;
|
|
4296
|
-
|
|
4263
|
+
help = dependencies.length > dependencyLimit
|
|
4264
|
+
? '↑↓ 移动 · PageUp/PageDown 或 ←→ 阅读依赖 · Enter 选择 · Esc 返回'
|
|
4265
|
+
: '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4266
|
+
content = _jsxs(Box, { flexDirection: "column", children: [compactTechnicalSummary ? _jsxs(SectionCard, { title: "provider \u6458\u8981", meta: `${dependencies.length + 1} 个`, compact: true, children: [_jsx(StaticRow, { title: plugin.displayName, meta: "\u6839 provider", compact: true }), _jsx(StaticRow, { title: `${dependencies.length} 个依赖 providers`, meta: "\u7528\u4E8E\u751F\u6210", compact: true })] }) : _jsx(SectionCard, { title: "\u5F53\u524D\u63D2\u4EF6", children: _jsx(StaticRow, { title: plugin.displayName, description: "\u63D0\u4F9B\u672C\u6B21\u5B89\u88C5\u5165\u53E3", meta: `提供 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.relation === 'requested').length} 个 Skills`, compact: policy.mode === 'compact' }) }), compactTechnicalSummary ? null : _jsxs(SectionCard, { title: "\u4F9D\u8D56\u63D2\u4EF6", meta: `${dependencies.length} 个`, children: [dependencyOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${dependencyOffset} 个依赖插件`, status: "muted", compact: true }) : null, visibleDependencies.map((dependency) => _jsx(StaticRow, { title: dependency.displayName, description: `${dependency.relation === 'direct' ? '当前插件直接需要' : '由依赖插件继续需要'} · ${dependency.requiredBy.map((entry) => entry.displayName).join('、')} 需要它`, meta: `提供 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.providerPluginId === dependency.pluginId).length} 个 Skills`, compact: policy.mode === 'compact' }, dependency.pluginId)), dependencies.length > dependencyOffset + visibleDependencies.length ? _jsx(StaticRow, { title: `↓ 后面还有 ${dependencies.length - dependencyOffset - visibleDependencies.length} 个依赖插件`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true }), showTechnical ? _jsx(TechnicalDetails, { children: technicalDependencies.map((entry) => _jsx(StaticRow, { title: entry.displayName, meta: entry.pluginId, compact: true }, entry.pluginId)) }) : null] });
|
|
4297
4267
|
}
|
|
4298
4268
|
else if (route.name === 'portableAgentMode' && plugin && snapshot.portableDraft) {
|
|
4299
4269
|
breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
|
|
@@ -4357,13 +4327,27 @@ export function ConsoleAppV4(props) {
|
|
|
4357
4327
|
setScrollOffset(0);
|
|
4358
4328
|
} }] : []),
|
|
4359
4329
|
{ label: '重新检查', run: () => beginPortable(plugin.pluginId) },
|
|
4330
|
+
...(route.operationRecordId ? [{
|
|
4331
|
+
label: '查看诊断与报告',
|
|
4332
|
+
description: `安全记录 ${route.operationRecordId}`,
|
|
4333
|
+
run: () => go({ name: 'operationDiagnostic', recordId: route.operationRecordId }),
|
|
4334
|
+
}] : []),
|
|
4360
4335
|
{ label: snapshot.installPlacementDraft?.pluginId === plugin.pluginId ? '返回目标平台' : '返回插件详情', run: () => props.controller.resetPortableFlowToPreview(plugin.pluginId) },
|
|
4361
4336
|
...(cliUnavailable
|
|
4362
4337
|
? []
|
|
4363
4338
|
: [{ label: showTechnical ? '返回问题摘要' : '技术详情', run: () => toggleTechnical() }]),
|
|
4364
4339
|
];
|
|
4340
|
+
const technicalPageRows = responsiveBudget(2, 4);
|
|
4341
|
+
if (showTechnical && !showPortableRequirements) {
|
|
4342
|
+
readingText = diagnosticDetailText(diagnostic);
|
|
4343
|
+
referenceDetailPagination = {
|
|
4344
|
+
total: diagnosticDetailLines(diagnostic, size.columns - 6).length,
|
|
4345
|
+
pageRows: technicalPageRows,
|
|
4346
|
+
};
|
|
4347
|
+
help = '←→ 或 PgUp/PgDn 阅读技术字段 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4348
|
+
}
|
|
4365
4349
|
const blockerActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 4));
|
|
4366
|
-
content = _jsxs(Box, { flexDirection: "column", children: [showPortableRequirements ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u5728 PATH \u4E2D\u63D0\u4F9B\u517C\u5BB9\u7684 skills \u53EF\u6267\u884C\u6587\u4EF6\u3002", compact: true }), _jsx(StaticRow, { title: `当前 AW adapter 支持:${SKILLS_CLI_SUPPORTED_VERSION_LABEL}`, compact: true }), _jsx(StaticRow, { title: "\u51C6\u5907\u5B8C\u6210\u540E\u8FD4\u56DE\u95EE\u9898\u6458\u8981\u5E76\u91CD\u65B0\u68C0\u67E5\u3002", compact: true }), _jsx(StaticRow, { title: "\u4E0D\u4F1A\u81EA\u52A8\u8FD0\u884C npx latest\u3001sudo \u6216\u5728\u7EBF\u5B89\u88C5\u811A\u672C\u3002", status: "warning", compact: true }), _jsx(StaticRow, { title: "\u9009\u5B9A\u63D2\u4EF6\u3001\u5B89\u88C5\u76EE\u6807\u548C Portable \u8349\u7A3F\u4FDD\u6301\u4E0D\u53D8\u3002", status: "success", compact: true }), _jsx(StaticRow, { title: "AW \u4E0D\u4F1A\u5728\u6B64\u9875\u5B89\u88C5\u5916\u90E8\u4F9D\u8D56\u3002", status: "warning", compact: true })] }) : _jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockerActionViewport.offset, limit: blockerActionViewport.limit })] });
|
|
4350
|
+
content = _jsxs(Box, { flexDirection: "column", children: [showPortableRequirements ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u5728 PATH \u4E2D\u63D0\u4F9B\u517C\u5BB9\u7684 skills \u53EF\u6267\u884C\u6587\u4EF6\u3002", compact: true }), _jsx(StaticRow, { title: `当前 AW adapter 支持:${SKILLS_CLI_SUPPORTED_VERSION_LABEL}`, compact: true }), _jsx(StaticRow, { title: "\u51C6\u5907\u5B8C\u6210\u540E\u8FD4\u56DE\u95EE\u9898\u6458\u8981\u5E76\u91CD\u65B0\u68C0\u67E5\u3002", compact: true }), _jsx(StaticRow, { title: "\u4E0D\u4F1A\u81EA\u52A8\u8FD0\u884C npx latest\u3001sudo \u6216\u5728\u7EBF\u5B89\u88C5\u811A\u672C\u3002", status: "warning", compact: true }), _jsx(StaticRow, { title: "\u9009\u5B9A\u63D2\u4EF6\u3001\u5B89\u88C5\u76EE\u6807\u548C Portable \u8349\u7A3F\u4FDD\u6301\u4E0D\u53D8\u3002", status: "success", compact: true }), _jsx(StaticRow, { title: "AW \u4E0D\u4F1A\u5728\u6B64\u9875\u5B89\u88C5\u5916\u90E8\u4F9D\u8D56\u3002", status: "warning", compact: true })] }) : _jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, columns: size.columns, offset: showTechnical ? batchTargetOffset : undefined, limit: showTechnical ? technicalPageRows : undefined, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockerActionViewport.offset, limit: blockerActionViewport.limit })] });
|
|
4367
4351
|
}
|
|
4368
4352
|
else if (route.name === 'pluginDependencies' && plugin) {
|
|
4369
4353
|
const overview = dependencyOverview(plugin);
|
|
@@ -4382,6 +4366,8 @@ export function ConsoleAppV4(props) {
|
|
|
4382
4366
|
}), entries.length > safeOffset + visible.length ? _jsx(StaticRow, { title: `↓ 后面还有 ${entries.length - safeOffset - visible.length} 项`, status: "muted", compact: true }) : null] });
|
|
4383
4367
|
};
|
|
4384
4368
|
if (route.view === 'removeBlocked') {
|
|
4369
|
+
if (!showTechnical)
|
|
4370
|
+
readingText = visibleDependents.map((entry) => `${entry.displayName} · ${dependencyRelationDescription(entry)} · ${dependentTargetLabel(entry)}`).join('\n');
|
|
4385
4371
|
breadcrumb = `${plugin.displayName} / 移除`;
|
|
4386
4372
|
context = '已阻止';
|
|
4387
4373
|
title = `无法移除 ${plugin.displayName}`;
|
|
@@ -4392,24 +4378,16 @@ export function ConsoleAppV4(props) {
|
|
|
4392
4378
|
setFocus(0);
|
|
4393
4379
|
setScrollOffset(0);
|
|
4394
4380
|
} },
|
|
4395
|
-
{ label: '返回插件详情', run: back },
|
|
4381
|
+
{ role: 'navigation', label: '返回插件详情', run: back },
|
|
4396
4382
|
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
4397
4383
|
];
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
offset: batchTargetOffset,
|
|
4401
|
-
total: visibleDependents.length,
|
|
4402
|
-
pageRows: dependentLimit,
|
|
4403
|
-
move: (direction) => moveBatchTargetPage(visibleDependents.length, dependentLimit, direction),
|
|
4404
|
-
})
|
|
4405
|
-
: [];
|
|
4406
|
-
actions = [...blockedRemovalPagination, ...blockedRemovalActions];
|
|
4407
|
-
if (blockedRemovalPagination.length > 0) {
|
|
4384
|
+
actions = blockedRemovalActions;
|
|
4385
|
+
if (!showTechnical && visibleDependents.length > dependentLimit) {
|
|
4408
4386
|
referenceDetailPagination = { total: visibleDependents.length, pageRows: dependentLimit };
|
|
4409
4387
|
}
|
|
4410
4388
|
const blockedRemovalViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(5));
|
|
4411
4389
|
if (!showTechnical && visibleDependents.length > dependentLimit) {
|
|
4412
|
-
help = '
|
|
4390
|
+
help = '←→ 查看全部被依赖关系 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4413
4391
|
}
|
|
4414
4392
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StaticRow, { title: `${dependentCount} 个已安装插件仍在使用`, status: "error", compact: true }), _jsx(StaticRow, { title: "\u6CA1\u6709\u4FEE\u6539\u4EFB\u4F55\u5B89\u88C5\u3002", status: "muted", compact: true }), showTechnical ? null : _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", meta: "\u53CD\u5411\u5F15\u7528", compact: true }), dependencyRows(visibleDependents, dependentLimit, true, batchTargetOffset)] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockedRemovalViewport.offset, limit: blockedRemovalViewport.limit }), showTechnical ? _jsx(IssueTechnicalDetails, { details: [{
|
|
4415
4393
|
code: 'dependencyStillRequired',
|
|
@@ -4556,38 +4534,7 @@ export function ConsoleAppV4(props) {
|
|
|
4556
4534
|
meta: '依赖当前可用',
|
|
4557
4535
|
};
|
|
4558
4536
|
});
|
|
4559
|
-
if (route.view === '
|
|
4560
|
-
const detailSections = [
|
|
4561
|
-
['用途', node?.description ?? '当前目录没有用途说明'],
|
|
4562
|
-
['为何需要', description],
|
|
4563
|
-
['影响', state?.value ?? (node ? statusText(node) : '尚未检查当前状态')],
|
|
4564
|
-
];
|
|
4565
|
-
const detailLines = detailSections.flatMap(([label, value]) => [
|
|
4566
|
-
label,
|
|
4567
|
-
...wrapConsoleText(value, Math.max(1, size.columns - 6)).map((line) => ` ${line}`),
|
|
4568
|
-
]);
|
|
4569
|
-
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5)));
|
|
4570
|
-
const detailOffset = Math.min(batchTargetOffset, Math.max(0, detailLines.length - 1));
|
|
4571
|
-
const visibleDetailLines = detailLines.slice(detailOffset, detailOffset + pageRows);
|
|
4572
|
-
referenceDetailPagination = { total: detailLines.length, pageRows };
|
|
4573
|
-
breadcrumb = `${plugin.displayName} / 依赖关系 / ${node?.displayName ?? route.nodePluginId} / 完整说明`;
|
|
4574
|
-
context = `${Math.min(detailOffset + 1, detailLines.length)}–${Math.min(detailOffset + visibleDetailLines.length, detailLines.length)} / ${detailLines.length}`;
|
|
4575
|
-
title = `${node?.displayName ?? route.nodePluginId} 的完整说明`;
|
|
4576
|
-
description = '用途、关系原因和当前影响。';
|
|
4577
|
-
help = 'p/n 或 PgUp/PgDn 阅读 · ↑↓ 移动 · Enter 选择 · Esc 返回节点详情';
|
|
4578
|
-
actions = [
|
|
4579
|
-
...paginationActions({
|
|
4580
|
-
offset: detailOffset,
|
|
4581
|
-
total: detailLines.length,
|
|
4582
|
-
pageRows,
|
|
4583
|
-
move: (direction) => moveBatchTargetPage(detailLines.length, pageRows, direction),
|
|
4584
|
-
}),
|
|
4585
|
-
{ label: '返回节点详情', run: back },
|
|
4586
|
-
];
|
|
4587
|
-
content = _jsxs(Box, { flexDirection: "column", children: [detailOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${detailOffset} 行`, status: "muted", compact: true }) : null, visibleDetailLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${detailOffset + index}:${line}`)), detailOffset + visibleDetailLines.length < detailLines.length
|
|
4588
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${detailLines.length - detailOffset - visibleDetailLines.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
4589
|
-
}
|
|
4590
|
-
else if (route.view === 'references') {
|
|
4537
|
+
if (route.view === 'references') {
|
|
4591
4538
|
breadcrumb = `${plugin.displayName} / 依赖关系 / ${node?.displayName ?? route.nodePluginId} / 使用位置`;
|
|
4592
4539
|
context = showTechnical ? '技术详情' : '使用位置';
|
|
4593
4540
|
title = `当前可用依赖位置 ${references.length} 个`;
|
|
@@ -4611,6 +4558,15 @@ export function ConsoleAppV4(props) {
|
|
|
4611
4558
|
`dependency · platform=${platformLabel(selectedReference.dependency.platform)} · scope=${selectedReference.dependency.scope}`,
|
|
4612
4559
|
`dependency · project=${selectedReference.dependency.project ?? 'none'}`,
|
|
4613
4560
|
] : [];
|
|
4561
|
+
if (showTechnical && selectedReference)
|
|
4562
|
+
readingText = [
|
|
4563
|
+
`consumer · ${selectedReference.consumer.pluginId}`,
|
|
4564
|
+
`consumer · platform=${platformLabel(selectedReference.consumer.platform)} · scope=${selectedReference.consumer.scope}`,
|
|
4565
|
+
`consumer · project=${selectedReference.consumer.project ?? 'none'}`,
|
|
4566
|
+
`dependency · ${selectedReference.dependency.pluginId}`,
|
|
4567
|
+
`dependency · platform=${platformLabel(selectedReference.dependency.platform)} · scope=${selectedReference.dependency.scope}`,
|
|
4568
|
+
`dependency · project=${selectedReference.dependency.project ?? 'none'}`,
|
|
4569
|
+
].join('\n');
|
|
4614
4570
|
const referenceDetailPageRows = referenceDetailLines.length <= 6
|
|
4615
4571
|
? 6
|
|
4616
4572
|
: responsiveBudget(4, 8);
|
|
@@ -4620,24 +4576,14 @@ export function ConsoleAppV4(props) {
|
|
|
4620
4576
|
total: referenceDetailLines.length,
|
|
4621
4577
|
pageRows: referenceDetailPageRows,
|
|
4622
4578
|
} : undefined;
|
|
4623
|
-
const referencePaginationActions = showTechnical
|
|
4624
|
-
&& referenceDetailLines.length > referenceDetailPageRows
|
|
4625
|
-
? paginationActions({
|
|
4626
|
-
offset: referenceDetailOffset,
|
|
4627
|
-
total: referenceDetailLines.length,
|
|
4628
|
-
pageRows: referenceDetailPageRows,
|
|
4629
|
-
move: (direction) => moveBatchTargetPage(referenceDetailLines.length, referenceDetailPageRows, direction),
|
|
4630
|
-
})
|
|
4631
|
-
: [];
|
|
4632
4579
|
actions = [
|
|
4633
4580
|
...(showTechnical && referenceActions[selectedReferenceIndex]
|
|
4634
4581
|
? [referenceActions[selectedReferenceIndex]] : referenceActions),
|
|
4635
|
-
|
|
4636
|
-
{ label: '返回节点详情', run: back },
|
|
4582
|
+
{ role: 'navigation', label: '返回节点详情', run: back },
|
|
4637
4583
|
];
|
|
4638
4584
|
const referenceViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(3, 6));
|
|
4639
4585
|
help = showTechnical
|
|
4640
|
-
? '
|
|
4586
|
+
? '←→ 查看当前位置详情 · ↑↓ 移动 · Enter 选择 · d 隐藏技术详情 · Esc 返回节点详情'
|
|
4641
4587
|
: '↑↓ 查看全部位置 · d 技术详情 · Enter 返回节点详情 · Esc 返回节点详情';
|
|
4642
4588
|
content = _jsxs(Box, { flexDirection: "column", children: [showTechnical ? null
|
|
4643
4589
|
: _jsx(StaticRow, { title: "\u672C\u6B21\u68C0\u67E5", value: "\u53EA\u68C0\u67E5\u5F53\u524D\u9879\u76EE\u548C\u4E2A\u4EBA\u4F4D\u7F6E\uFF1B\u5176\u4ED6\u9879\u76EE\u672A\u68C0\u67E5", status: "muted", compact: true }), showTechnical && referenceDetailOffset > 0
|
|
@@ -4660,20 +4606,33 @@ export function ConsoleAppV4(props) {
|
|
|
4660
4606
|
direction: route.direction,
|
|
4661
4607
|
}),
|
|
4662
4608
|
}] : []),
|
|
4663
|
-
{
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
}
|
|
4671
|
-
{
|
|
4609
|
+
{ role: 'navigation', label: '返回关系图', run: back },
|
|
4610
|
+
];
|
|
4611
|
+
const reason = description;
|
|
4612
|
+
description = undefined;
|
|
4613
|
+
const summary = [
|
|
4614
|
+
`用途:${node?.description ?? '当前目录没有用途说明'}`,
|
|
4615
|
+
`为何需要:${reason}`,
|
|
4616
|
+
`影响:${state?.value ?? (node ? statusText(node) : '尚未检查当前状态')}`,
|
|
4617
|
+
...(showTechnical ? [`pluginId · ${route.nodePluginId}`, `relationship · ${relationship ?? 'unknown'}`] : []),
|
|
4672
4618
|
];
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4619
|
+
readingText = [title, ...summary].join('\n');
|
|
4620
|
+
const summaryLines = summary.flatMap((line) => wrapConsoleText(line, size.columns - 2));
|
|
4621
|
+
const actionCount = actions.filter((action) => action.role !== 'navigation').length;
|
|
4622
|
+
const availableSummaryRows = pageActionBudget(Math.min(2, actionCount));
|
|
4623
|
+
const pageRows = Math.max(1, availableSummaryRows - Number(summaryLines.length > availableSummaryRows));
|
|
4624
|
+
const offset = Math.min(batchTargetOffset, Math.max(0, summaryLines.length - pageRows));
|
|
4625
|
+
const visible = summaryLines.slice(offset, offset + pageRows);
|
|
4626
|
+
const paginated = summaryLines.length > pageRows;
|
|
4627
|
+
if (paginated) {
|
|
4628
|
+
referenceDetailPagination = { total: summaryLines.length, pageRows };
|
|
4629
|
+
context = `${offset + 1}–${offset + visible.length} / ${summaryLines.length}`;
|
|
4630
|
+
help = '←→ 翻页 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
4631
|
+
}
|
|
4632
|
+
currentActionViewportLineBudget = pageActionBudget(visible.length + Number(paginated));
|
|
4633
|
+
const nodeActionViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
4634
|
+
content = _jsxs(Box, { flexDirection: "column", children: [visible.map((line, index) => _jsx(Text, { wrap: "truncate-end", children: line }, `${offset + index}`)), paginated ? _jsx(Text, { color: tuiTheme.colors.muted, children: offset + visible.length < summaryLines.length
|
|
4635
|
+
? `↓ 后面还有 ${summaryLines.length - offset - visible.length} 行` : '已到正文末尾' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: nodeActionViewport.offset, limit: nodeActionViewport.limit })] });
|
|
4677
4636
|
}
|
|
4678
4637
|
}
|
|
4679
4638
|
else if (route.name === 'installTargets' && plugin) {
|
|
@@ -4705,7 +4664,7 @@ export function ConsoleAppV4(props) {
|
|
|
4705
4664
|
description = '根插件或必需依赖不支持当前目标平台组合,因此不会进入确认页。';
|
|
4706
4665
|
actions = [
|
|
4707
4666
|
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
|
|
4708
|
-
{ label: '返回插件详情', run: back },
|
|
4667
|
+
{ role: 'navigation', label: '返回插件详情', run: back },
|
|
4709
4668
|
];
|
|
4710
4669
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "warning", title: "\u6240\u9009\u76EE\u6807\u5E73\u53F0\u4E0D\u53EF\u7528", children: choices.map((choice) => _jsx(StaticRow, { title: `${platformLabel(choice.platform)} · ${choice.disabled ? `不可选 · ${choice.disabled}` : '不可选'}`, status: "muted", compact: policy.mode === 'compact' }, choice.platform)) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
4711
4670
|
}
|
|
@@ -4717,6 +4676,7 @@ export function ConsoleAppV4(props) {
|
|
|
4717
4676
|
}
|
|
4718
4677
|
setDirty(false);
|
|
4719
4678
|
props.controller.setInstallPlatforms(validSelectedTargets);
|
|
4679
|
+
setInstallPlatformIndex(0);
|
|
4720
4680
|
go({ name: 'installPlacement', pluginId: plugin.pluginId });
|
|
4721
4681
|
};
|
|
4722
4682
|
const nativeActions = choices.map((choice) => {
|
|
@@ -4781,26 +4741,16 @@ export function ConsoleAppV4(props) {
|
|
|
4781
4741
|
};
|
|
4782
4742
|
const confirmAction = {
|
|
4783
4743
|
actionId: 'confirm-install-platforms',
|
|
4784
|
-
label: '确定所选平台',
|
|
4744
|
+
label: '确定所选平台 [c]',
|
|
4785
4745
|
disabled: validSelectedTargets.length === 0 ? '至少选择一个目标平台' : undefined,
|
|
4786
4746
|
run: confirmNativePlatforms,
|
|
4787
4747
|
};
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
const installTargetStaticRows = (validSelectedTargets.length === 0 ? 2 : 1)
|
|
4795
|
-
+ (policy.mode === 'compact' ? 4 : 0);
|
|
4796
|
-
const platformViewport = objectCardActionViewport(actions, cardWidth, policy.mode === 'compact', focus, scrollOffset, Math.max(1, pageActionBudget(installTargetStaticRows)));
|
|
4797
|
-
help = '↑↓ 移动 · Enter/Space 勾选平台 · Enter 打开通用 Skills · Tab 操作栏 · c 继续 · Esc 返回';
|
|
4798
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action) => ({
|
|
4799
|
-
id: action.actionId, label: action.label, disabled: Boolean(action.disabled),
|
|
4800
|
-
})), active: focusZone === 'actions', focus: pinnedActionFocus }), validSelectedTargets.length === 0
|
|
4801
|
-
? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u76EE\u6807\u5E73\u53F0" }) : null, _jsx(ContentViewport, { children: actions.slice(platformViewport.offset, platformViewport.offset + platformViewport.limit)
|
|
4802
|
-
.map((action, visibleIndex) => _jsx(ObjectCardRow, { width: cardWidth, title: action.actionId?.startsWith('install-platform-')
|
|
4803
|
-
? `${action.selected ? '[x]' : '[ ]'} ${action.label}` : action.label, description: action.disabled ?? action.description ?? '', tags: action.tags, focused: focusZone === 'content' && platformViewport.offset + visibleIndex === focus, anchored: focusZone !== 'content' && platformViewport.offset + visibleIndex === focus, compact: policy.mode === 'compact' }, action.actionId)) })] });
|
|
4748
|
+
actions = [...nativeActions, portableAction, confirmAction];
|
|
4749
|
+
bottomActions = [confirmAction];
|
|
4750
|
+
help = '↑↓ 移动 · Enter/Space 选择 · Esc 返回';
|
|
4751
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions.slice(0, nativeActions.length).map((action) => ({
|
|
4752
|
+
...action, label: `${action.selected ? '[x]' : '[ ]'} ${action.label}`,
|
|
4753
|
+
})), focus: focus, kind: "action", compact: false }), _jsx(SemanticRows, { actions: actions, focus: focus, offset: nativeActions.length, kind: "action", compact: false })] });
|
|
4804
4754
|
}
|
|
4805
4755
|
}
|
|
4806
4756
|
else if (route.name === 'installPlacement' && plugin) {
|
|
@@ -4838,35 +4788,53 @@ export function ConsoleAppV4(props) {
|
|
|
4838
4788
|
}
|
|
4839
4789
|
else {
|
|
4840
4790
|
description = '所选平台没有共同范围。请分别确定每个平台的合法位置。';
|
|
4841
|
-
const
|
|
4842
|
-
|
|
4791
|
+
const platformIndex = Math.min(installPlatformIndex, Math.max(0, selectedPlatforms.length - 1));
|
|
4792
|
+
const platform = selectedPlatforms[platformIndex];
|
|
4793
|
+
title = platform ? `${platformLabel(platform)} 的使用范围` : '请返回选择目标平台';
|
|
4794
|
+
context = `平台 ${platformIndex + 1} / ${selectedPlatforms.length}`;
|
|
4795
|
+
if (platformIndex > 0)
|
|
4796
|
+
returnToPreviousInstallPlatform = () => {
|
|
4797
|
+
setInstallPlatformIndex(platformIndex - 1);
|
|
4798
|
+
setFocus(0);
|
|
4799
|
+
setScrollOffset(0);
|
|
4800
|
+
};
|
|
4801
|
+
actions = platform ? legalScopes(platform).map((candidateScope) => ({
|
|
4802
|
+
label: scopeLabel(candidateScope),
|
|
4843
4803
|
selected: installPlatformScopes[platform] === candidateScope,
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
?
|
|
4860
|
-
|
|
4861
|
-
|
|
4804
|
+
description: platformIndex + 1 < selectedPlatforms.length
|
|
4805
|
+
? `下一步:${platformLabel(selectedPlatforms[platformIndex + 1])} 的使用范围`
|
|
4806
|
+
: '下一步:查看这些精确目标的操作汇总',
|
|
4807
|
+
run: async () => {
|
|
4808
|
+
const nextScopes = { ...installPlatformScopes, [platform]: candidateScope };
|
|
4809
|
+
setInstallPlatformScopes(nextScopes);
|
|
4810
|
+
if (platformIndex + 1 < selectedPlatforms.length) {
|
|
4811
|
+
setInstallPlatformIndex(platformIndex + 1);
|
|
4812
|
+
setFocus(0);
|
|
4813
|
+
setScrollOffset(0);
|
|
4814
|
+
return;
|
|
4815
|
+
}
|
|
4816
|
+
const exactTargets = selectedPlatforms.flatMap((selectedPlatform) => {
|
|
4817
|
+
const selectedScope = nextScopes[selectedPlatform];
|
|
4818
|
+
return selectedScope && legalScopes(selectedPlatform).includes(selectedScope)
|
|
4819
|
+
? [{ pluginId: plugin.pluginId, platform: selectedPlatform, scope: selectedScope }] : [];
|
|
4820
|
+
});
|
|
4821
|
+
if (exactTargets.length !== selectedPlatforms.length) {
|
|
4822
|
+
setInstallPlatformIndex(0);
|
|
4823
|
+
setFocus(0);
|
|
4824
|
+
setScrollOffset(0);
|
|
4825
|
+
setActionError('合法位置已变化。请重新确定每个平台的范围。');
|
|
4826
|
+
return;
|
|
4827
|
+
}
|
|
4828
|
+
props.controller.setInstallRootTargets(exactTargets);
|
|
4829
|
+
if (hasDependencies && await props.controller.prepareInstallDependencyPlacement()) {
|
|
4830
|
+
go({ name: 'dependencyPlacement', pluginId: plugin.pluginId });
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
beginReview(props.controller.installPlacementIntent());
|
|
4862
4834
|
},
|
|
4863
|
-
|
|
4864
|
-
];
|
|
4835
|
+
})) : [];
|
|
4865
4836
|
}
|
|
4866
|
-
|
|
4867
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: commonScopes.length > 0 ? 'choice' : 'toggle', compact: policy.mode === 'compact', limit: scopeChoiceCount }), commonScopes.length === 0
|
|
4868
|
-
? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: scopeChoiceCount })
|
|
4869
|
-
: null, hasDependencies ? _jsx(Callout, { title: "\u4F9D\u8D56\u8BA1\u5212", description: "\u6240\u9700\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u6392\uFF1B\u786E\u8BA4\u524D\u53EF\u67E5\u770B\u5F71\u54CD\u3002" }) : null] });
|
|
4837
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' }), hasDependencies ? _jsx(Callout, { title: "\u4F9D\u8D56\u8BA1\u5212", description: "\u6240\u9700\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u6392\uFF1B\u786E\u8BA4\u524D\u53EF\u67E5\u770B\u5F71\u54CD\u3002" }) : null] });
|
|
4870
4838
|
help = '↑↓ 移动 · Enter 选择/继续 · Esc 返回';
|
|
4871
4839
|
}
|
|
4872
4840
|
else if (route.name === 'uninstallTargets' && plugin) {
|
|
@@ -4920,7 +4888,7 @@ export function ConsoleAppV4(props) {
|
|
|
4920
4888
|
});
|
|
4921
4889
|
},
|
|
4922
4890
|
},
|
|
4923
|
-
{ label: '返回插件详情', run: back },
|
|
4891
|
+
{ role: 'navigation', label: '返回插件详情', run: back },
|
|
4924
4892
|
];
|
|
4925
4893
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SemanticRows, { actions: actions, focus: focus, kind: "toggle", compact: policy.mode === 'compact', limit: targetActions.length + 1 }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: targetActions.length + 1 })] });
|
|
4926
4894
|
help = '↑↓ 移动 · Enter 选择/继续 · Esc 返回';
|
|
@@ -4928,6 +4896,9 @@ export function ConsoleAppV4(props) {
|
|
|
4928
4896
|
else if (route.name === 'dependencyPlacement' && plugin) {
|
|
4929
4897
|
const draft = snapshot.installPlacementDraft?.pluginId === plugin.pluginId
|
|
4930
4898
|
? snapshot.installPlacementDraft : undefined;
|
|
4899
|
+
const rootScopeSummary = draft?.exactRootTargets ? '按平台设置'
|
|
4900
|
+
: draft ? scopeLabel(draft.rootScope) : '未就绪';
|
|
4901
|
+
const exactRootRows = draft?.exactRootTargets?.map((target) => _jsx(StaticRow, { title: `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`, compact: true }, target.platform));
|
|
4931
4902
|
breadcrumb = `${plugin.displayName} / 添加 / 依赖位置`;
|
|
4932
4903
|
context = '依赖位置';
|
|
4933
4904
|
title = '选择每个平台的依赖位置';
|
|
@@ -4958,7 +4929,7 @@ export function ConsoleAppV4(props) {
|
|
|
4958
4929
|
description: '恢复依赖自动安排的建议位置',
|
|
4959
4930
|
run: () => props.controller.resetDependencyScopes(),
|
|
4960
4931
|
},
|
|
4961
|
-
{ label: '返回目标平台', run: back },
|
|
4932
|
+
{ role: 'navigation', label: '返回目标平台', run: back },
|
|
4962
4933
|
];
|
|
4963
4934
|
const placementViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(dependencyPlacementStaticRows));
|
|
4964
4935
|
const userCount = draft?.targets.filter((target) => target.scope === 'user').length ?? 0;
|
|
@@ -4971,8 +4942,8 @@ export function ConsoleAppV4(props) {
|
|
|
4971
4942
|
...(promotedCount > 0 ? [`因依赖自动调整 ${promotedCount} 项`] : []),
|
|
4972
4943
|
].join(';');
|
|
4973
4944
|
content = _jsxs(Box, { flexDirection: "column", children: [policy.mode === 'compact'
|
|
4974
|
-
? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `根插件 · ${plugin.displayName}`, meta:
|
|
4975
|
-
: _jsxs(_Fragment, { children: [
|
|
4945
|
+
? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `根插件 · ${plugin.displayName}`, meta: rootScopeSummary, compact: true }), exactRootRows, _jsx(StaticRow, { title: `位置原因 · ${reasonSummary || '由平台或上游约束固定'}`, status: userCount > 0 ? 'warning' : 'info', compact: true }), _jsx(StaticRow, { title: "\u8303\u56F4\u63D0\u793A \u00B7 \u6240\u6709\u9879\u76EE\u4F4D\u7F6E\u4F1A\u5F71\u54CD\u5176\u4ED6\u9879\u76EE\u548C\u7528\u6237\u73AF\u5883", status: userCount > 0 ? 'warning' : 'muted', compact: true })] })
|
|
4946
|
+
: _jsxs(_Fragment, { children: [_jsxs(SectionCard, { title: "\u6839\u63D2\u4EF6\u4F4D\u7F6E", children: [_jsx(StaticRow, { title: plugin.displayName, meta: rootScopeSummary, compact: true }), exactRootRows] }), _jsx(Callout, { status: userCount > 0 ? 'warning' : 'info', title: userCount > 0 ? `${userCount} 个依赖平台目标使用所有项目位置` : '位置计算原因', description: `${reasonSummary || '依赖位置由平台或上游约束固定'}${userCount > 0 ? ';所有项目位置对其他项目也可见,并会改变你的用户环境' : ''}。` })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: placementViewport.offset, limit: placementViewport.limit })] });
|
|
4976
4947
|
help = '↑↓ 移动 · Enter 打开/继续 · Esc 返回';
|
|
4977
4948
|
}
|
|
4978
4949
|
else if (route.name === 'dependencyPlacementEditor' && plugin) {
|
|
@@ -5207,9 +5178,21 @@ export function ConsoleAppV4(props) {
|
|
|
5207
5178
|
setScrollOffset(0);
|
|
5208
5179
|
},
|
|
5209
5180
|
}] : []),
|
|
5210
|
-
{ label: route.recommendedPluginIds ? '返回调整选择' : '返回插件详情', run: back },
|
|
5181
|
+
{ role: 'navigation', label: route.recommendedPluginIds ? '返回调整选择' : '返回插件详情', run: back },
|
|
5211
5182
|
];
|
|
5212
5183
|
const taskTargetLimit = policy.mode === 'compact' ? 3 : 6;
|
|
5184
|
+
readingText = candidates.map((candidate) => {
|
|
5185
|
+
const target = candidate.target;
|
|
5186
|
+
const version = target.expectedUpdate
|
|
5187
|
+
? `${target.expectedUpdate.installedVersion} → ${target.expectedUpdate.activeVersion}`
|
|
5188
|
+
: '保持当前版本';
|
|
5189
|
+
return `${candidate.displayName} · ${candidate.sourceId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)} · ${candidate.eligible
|
|
5190
|
+
? pluginWriteTaskMethodLabel(candidate.recommendedMethod)
|
|
5191
|
+
: pluginWriteTaskBlockLabel(candidate.blockReason)} · ${version}`;
|
|
5192
|
+
}).join('\n');
|
|
5193
|
+
if (candidates.length > taskTargetLimit) {
|
|
5194
|
+
referenceDetailPagination = { total: candidates.length, pageRows: taskTargetLimit };
|
|
5195
|
+
}
|
|
5213
5196
|
const taskTargetOffset = Math.min(batchTargetOffset, Math.max(0, candidates.length - 1));
|
|
5214
5197
|
const visibleTaskCandidates = candidates.slice(taskTargetOffset, taskTargetOffset + taskTargetLimit);
|
|
5215
5198
|
const targetRows = visibleTaskCandidates.map((candidate) => {
|
|
@@ -5243,7 +5226,7 @@ export function ConsoleAppV4(props) {
|
|
|
5243
5226
|
? plugin.version && plugin.version !== installedVersions[0]
|
|
5244
5227
|
? `${installedVersions[0]} → ${plugin.version}`
|
|
5245
5228
|
: `已安装 ${installedVersions[0]}`
|
|
5246
|
-
: plugin.version
|
|
5229
|
+
: plugin.version ?? '待确认';
|
|
5247
5230
|
context = `${plugin.sourceLabel} · ${versionSummary}`;
|
|
5248
5231
|
const updateTaskCandidates = plugin.configOnly ? []
|
|
5249
5232
|
: props.controller.listPluginWriteTaskCandidates('update')
|
|
@@ -5357,18 +5340,20 @@ export function ConsoleAppV4(props) {
|
|
|
5357
5340
|
actionId: 'content',
|
|
5358
5341
|
label: '内容详情',
|
|
5359
5342
|
description: contentOverview
|
|
5360
|
-
? `${contentOverview.items.length}
|
|
5343
|
+
? `${contentOverview.items.length} 项当前目录内容`
|
|
5361
5344
|
: '打开时读取当前目录版本的内容',
|
|
5362
5345
|
run: () => go({ name: 'pluginContent', pluginId: plugin.pluginId }),
|
|
5363
5346
|
} : undefined;
|
|
5364
5347
|
const dependencyOverviewForDetail = dependencyOverview(plugin);
|
|
5365
|
-
const dependencyAction =
|
|
5366
|
-
&& (dependencyOverviewForDetail.dependencies.length > 0
|
|
5367
|
-
|| dependencyOverviewForDetail.dependents.length > 0
|
|
5368
|
-
|| dependencyOverviewForDetail.blockers.length > 0) ? {
|
|
5348
|
+
const dependencyAction = !plugin.configOnly ? {
|
|
5369
5349
|
actionId: 'dependencies',
|
|
5370
5350
|
label: '依赖关系',
|
|
5371
|
-
description:
|
|
5351
|
+
description: dependencyOverviewForDetail
|
|
5352
|
+
&& (dependencyOverviewForDetail.dependencies.length > 0
|
|
5353
|
+
|| dependencyOverviewForDetail.dependents.length > 0
|
|
5354
|
+
|| dependencyOverviewForDetail.blockers.length > 0)
|
|
5355
|
+
? '在同一关系图中查看使用者和所需插件'
|
|
5356
|
+
: '当前没有已声明的所需插件或使用者',
|
|
5372
5357
|
run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }),
|
|
5373
5358
|
} : undefined;
|
|
5374
5359
|
const uninstallAction = installed ? {
|
|
@@ -5409,126 +5394,70 @@ export function ConsoleAppV4(props) {
|
|
|
5409
5394
|
&& dependencyOverviewForDetail.summary.totalCount > 0
|
|
5410
5395
|
? `需要 ${dependencyOverviewForDetail.summary.totalCount} 个插件;当前可用关系按当前范围检查,未显示的关系尚未检查通过。`
|
|
5411
5396
|
: undefined;
|
|
5412
|
-
const
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
const
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
const
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
})
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5397
|
+
const detailPlatforms = [...new Set([
|
|
5398
|
+
...plugin.installedPlacements.map((placement) => placement.platform), ...plugin.supportedPlatforms,
|
|
5399
|
+
])];
|
|
5400
|
+
const detailPositions = detailPlatforms.flatMap((platform) => {
|
|
5401
|
+
const placements = plugin.installedPlacements.filter((placement) => placement.platform === platform && placement.state !== 'notInstalled');
|
|
5402
|
+
return placements.length > 0
|
|
5403
|
+
? placements.map((placement) => `${platformLabel(platform)}:${pluginPlacementConclusions(placement).map((entry) => entry.text).join(' · ')}`)
|
|
5404
|
+
: [`${platformLabel(platform)} · ${pluginPlatformUnknown(plugin, platform) ? '未知 · 尚未确认' : '尚未安装'}`];
|
|
5405
|
+
});
|
|
5406
|
+
const platformSummary = detailPositions.length > 0 ? detailPositions.join(';') : '当前没有可用平台';
|
|
5407
|
+
context = '插件详情';
|
|
5408
|
+
const summary = [
|
|
5409
|
+
plugin.description,
|
|
5410
|
+
`版本 · ${versionSummary}`,
|
|
5411
|
+
`来源 · ${plugin.sourceLabel}`,
|
|
5412
|
+
...(detailPositions.length > 0 ? detailPositions : [platformSummary]),
|
|
5413
|
+
...(plugin.portableSkills ? ['[Skills 可选]'] : []),
|
|
5414
|
+
...(plugin.configOnly ? ['[已保存设置]'] : []),
|
|
5415
|
+
...(updateDescription ? [`有可用更新 · ${updateDescription}`] : []),
|
|
5416
|
+
...(dependencyDetailText ? [dependencyDetailText] : []),
|
|
5417
|
+
];
|
|
5418
|
+
readingText = [plugin.displayName, ...summary].join('\n');
|
|
5419
|
+
const summaryLines = summary.flatMap((line) => wrapConsoleText(line, size.columns - 2));
|
|
5420
|
+
const completion = snapshot.completionNotice?.pluginId === plugin.pluginId
|
|
5421
|
+
? snapshot.completionNotice : undefined;
|
|
5422
|
+
const completionLines = completion
|
|
5423
|
+
? wrapConsoleText(`${completion.title} · ${completion.description}`, size.columns - 2) : [];
|
|
5424
|
+
const availableSummaryRows = pageActionBudget(Math.min(3, actions.length) + completionLines.length);
|
|
5425
|
+
const summaryPageRows = Math.max(1, availableSummaryRows - Number(summaryLines.length > availableSummaryRows));
|
|
5426
|
+
const summaryOffset = Math.min(batchTargetOffset, Math.max(0, summaryLines.length - summaryPageRows));
|
|
5427
|
+
const visibleSummary = summaryLines.slice(summaryOffset, summaryOffset + summaryPageRows);
|
|
5428
|
+
const summaryPaginated = summaryLines.length > summaryPageRows;
|
|
5429
|
+
if (summaryPaginated) {
|
|
5430
|
+
referenceDetailPagination = { total: summaryLines.length, pageRows: summaryPageRows };
|
|
5431
|
+
context = `${summaryOffset + 1}–${summaryOffset + visibleSummary.length} / ${summaryLines.length}`;
|
|
5432
|
+
help = '←→ 翻页 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
5433
|
+
}
|
|
5434
|
+
currentActionViewportLineBudget = pageActionBudget(visibleSummary.length
|
|
5435
|
+
+ Number(summaryPaginated) + completionLines.length);
|
|
5434
5436
|
const detailViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
5435
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
? _jsx(StaticRow, { title: "\u6709\u53EF\u7528\u66F4\u65B0", meta: updateDescription, status: "warning", compact: true })
|
|
5439
|
-
: null, dependencyDetailText ? _jsx(StaticRow, { title: dependencyDetailText, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: detailViewport.offset, limit: detailViewport.limit })] });
|
|
5437
|
+
content = _jsxs(Box, { flexDirection: "column", children: [completionLines.map((line, index) => _jsx(Text, { color: tuiTheme.statusColors.success, children: line }, `completion:${index}`)), visibleSummary.map((line, index) => _jsx(Text, { wrap: "truncate-end", children: line }, `${summaryOffset + index}`)), summaryPaginated ? _jsx(Text, { color: tuiTheme.colors.muted, children: summaryOffset + visibleSummary.length < summaryLines.length
|
|
5438
|
+
? `↓ 后面还有 ${summaryLines.length - summaryOffset - visibleSummary.length} 行`
|
|
5439
|
+
: '已到正文末尾' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: detailViewport.offset, limit: detailViewport.limit })] });
|
|
5440
5440
|
}
|
|
5441
5441
|
else if (route.name === 'pluginMore' && plugin) {
|
|
5442
5442
|
breadcrumb = `插件 / ${plugin.displayName} / 更多操作`;
|
|
5443
5443
|
context = plugin.sourceLabel;
|
|
5444
5444
|
title = '更多操作';
|
|
5445
5445
|
description = '选择平台可使用同步、重新安装、移除和受限清理。';
|
|
5446
|
-
const dependencySummary = plugin.dependencySummary;
|
|
5447
|
-
const detailMenu = buildPluginDetailActions(plugin);
|
|
5448
|
-
const updateTaskCandidates = props.controller.listPluginWriteTaskCandidates('update')
|
|
5449
|
-
.filter((candidate) => candidate.target.pluginId === plugin.pluginId);
|
|
5450
|
-
const restoreTaskCandidates = props.controller.listPluginWriteTaskCandidates('restore')
|
|
5451
|
-
.filter((candidate) => candidate.target.pluginId === plugin.pluginId);
|
|
5452
5446
|
actions = [
|
|
5453
|
-
...
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
run: () => go({
|
|
5458
|
-
name: 'pluginWriteTask', pluginId: plugin.pluginId, goal: 'update',
|
|
5459
|
-
targetKeys: updateTaskCandidates.map(pluginWriteTaskTargetKey),
|
|
5460
|
-
versionEvidence: pluginWriteTaskVersionEvidence(plugin, updateTaskCandidates),
|
|
5461
|
-
}),
|
|
5462
|
-
}] : []),
|
|
5463
|
-
...(restoreTaskCandidates.length > 0 ? [{
|
|
5464
|
-
label: '恢复正常',
|
|
5465
|
-
description: `${restoreTaskCandidates.length} 个现有位置需要检查`,
|
|
5466
|
-
status: 'warning',
|
|
5467
|
-
run: () => go({
|
|
5468
|
-
name: 'pluginWriteTask', pluginId: plugin.pluginId, goal: 'restore',
|
|
5469
|
-
targetKeys: restoreTaskCandidates.map(pluginWriteTaskTargetKey),
|
|
5470
|
-
}),
|
|
5471
|
-
}] : []),
|
|
5472
|
-
...detailMenu.filter((action) => action.id !== 'refreshAndSync' && action.id !== 'about')
|
|
5473
|
-
.map((action) => {
|
|
5474
|
-
if (action.id === 'platform' || action.id === 'scopeConflict') {
|
|
5475
|
-
const placement = plugin.installedPlacements.find((entry) => entry.platform === action.platform);
|
|
5476
|
-
const projected = placementStatus(placement);
|
|
5477
|
-
return {
|
|
5478
|
-
label: `${platformLabel(action.platform)} 平台与维护`,
|
|
5479
|
-
description: `${projected.description};同步、重装、移除和清理入口`,
|
|
5480
|
-
meta: projected.value,
|
|
5481
|
-
status: projected.status,
|
|
5482
|
-
run: () => action.id === 'scopeConflict'
|
|
5483
|
-
? go({ name: 'issueDetail', issueId: `scope:${plugin.pluginId}:${action.platform}` })
|
|
5484
|
-
: go({ name: 'platform', pluginId: plugin.pluginId, platform: action.platform }),
|
|
5485
|
-
};
|
|
5486
|
-
}
|
|
5487
|
-
if (action.id === 'resolveScopeConflict')
|
|
5488
|
-
return {
|
|
5489
|
-
label: `处理 ${platformLabel(action.platform)} 位置冲突`,
|
|
5490
|
-
description: action.description,
|
|
5491
|
-
status: 'error',
|
|
5492
|
-
run: () => beginScopeConflictRecovery(action),
|
|
5493
|
-
};
|
|
5494
|
-
if (action.id === 'dependencies')
|
|
5495
|
-
return {
|
|
5496
|
-
label: '依赖与当前可用关系',
|
|
5497
|
-
description: dependencySummary?.issueCount ? `${dependencySummary.issueCount} 项需要处理` : '查看完整关系',
|
|
5498
|
-
meta: dependencySummary ? dependencySummaryText(dependencySummary) : undefined,
|
|
5499
|
-
run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }),
|
|
5500
|
-
};
|
|
5501
|
-
if (action.id === 'settings')
|
|
5502
|
-
return {
|
|
5503
|
-
label: plugin.configOnly ? '查看已保存设置' : '插件设置',
|
|
5504
|
-
run: () => plugin.configOnly
|
|
5505
|
-
? props.controller.beginSavedConfigRecovery(plugin.pluginId)
|
|
5506
|
-
: beginConfigFlow(plugin.pluginId),
|
|
5507
|
-
};
|
|
5508
|
-
if (action.id === 'sourceProblem')
|
|
5509
|
-
return {
|
|
5510
|
-
label: '恢复插件来源', status: 'warning',
|
|
5511
|
-
run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }),
|
|
5512
|
-
};
|
|
5513
|
-
if (action.id === 'upgradeAwCli' || action.id === 'capabilityTechnical')
|
|
5514
|
-
return {
|
|
5515
|
-
label: action.id === 'upgradeAwCli' ? '升级 AW CLI' : '查看功能支持详情',
|
|
5516
|
-
status: action.id === 'upgradeAwCli' ? 'warning' : undefined,
|
|
5517
|
-
run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }),
|
|
5518
|
-
};
|
|
5447
|
+
...plugin.installedPlacements
|
|
5448
|
+
.filter((placement) => placement.state !== 'notInstalled')
|
|
5449
|
+
.map((placement) => {
|
|
5450
|
+
const projected = placementStatus(placement);
|
|
5519
5451
|
return {
|
|
5520
|
-
label:
|
|
5521
|
-
|
|
5452
|
+
label: `${platformLabel(placement.platform)} 平台维护`,
|
|
5453
|
+
description: '同步、重新安装和受限清理',
|
|
5454
|
+
meta: projected.value,
|
|
5455
|
+
status: projected.status,
|
|
5456
|
+
run: () => go({ name: 'platform', pluginId: plugin.pluginId, platform: placement.platform }),
|
|
5522
5457
|
};
|
|
5523
5458
|
}),
|
|
5524
|
-
...(!detailMenu.some((action) => action.id === 'dependencies') ? [{
|
|
5525
|
-
label: '依赖与当前可用关系',
|
|
5526
|
-
description: '查看必需依赖、声明者和当前可用关系;当前没有声明的必需依赖',
|
|
5527
|
-
run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }),
|
|
5528
|
-
}] : []),
|
|
5529
|
-
{ label: '插件定义信息', run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }) },
|
|
5530
5459
|
{ label: '查看插件来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) },
|
|
5531
|
-
{ label: '
|
|
5460
|
+
{ label: '诊断与技术详情', description: '查看插件身份、组件状态和安全错误代码', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
5532
5461
|
];
|
|
5533
5462
|
const moreViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(1));
|
|
5534
5463
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: moreViewport.offset, limit: moreViewport.limit });
|
|
@@ -5587,7 +5516,7 @@ export function ConsoleAppV4(props) {
|
|
|
5587
5516
|
run: () => beginSecondScope(plugin, action.platform, action.scope),
|
|
5588
5517
|
};
|
|
5589
5518
|
if (action.id === 'about')
|
|
5590
|
-
return { label: '查看支持信息', description: '插件定义与平台支持', run: () => go({ name: '
|
|
5519
|
+
return { label: '查看支持信息', description: '插件定义与平台支持', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) };
|
|
5591
5520
|
if (action.id === 'source')
|
|
5592
5521
|
return { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) };
|
|
5593
5522
|
if (action.id === 'outputCollision')
|
|
@@ -5645,7 +5574,7 @@ export function ConsoleAppV4(props) {
|
|
|
5645
5574
|
return typedIssue
|
|
5646
5575
|
? { label: '查看对应问题', description: typedIssue.explanation, run: () => go({ name: 'issueDetail', issueId: typedIssue.issueId }) }
|
|
5647
5576
|
: { label: placement?.state === 'installed' && placement.health === 'error' ? '查看安全技术详情' : placement?.state === 'installed' && placement.health === 'incomplete' ? '查看影响' : '查看安装详情', description: '组件与诊断信息', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) };
|
|
5648
|
-
return { label: '返回插件', run: back };
|
|
5577
|
+
return { role: 'navigation', label: '返回插件', run: back };
|
|
5649
5578
|
};
|
|
5650
5579
|
const offlineCleanupAction = typedIssue?.actions.find((action) => action.id === 'cleanup');
|
|
5651
5580
|
const offlineCleanup = typedIssue !== undefined
|
|
@@ -5790,9 +5719,7 @@ export function ConsoleAppV4(props) {
|
|
|
5790
5719
|
const candidateSummary = selectedField && selectedMutation
|
|
5791
5720
|
? selectedMutation.kind === 'unset' ? '恢复继承'
|
|
5792
5721
|
: selectedField.sensitive ? '已更新'
|
|
5793
|
-
:
|
|
5794
|
-
? objectSummary(selectedField, selectedMutation.value)
|
|
5795
|
-
: scalarSummary(selectedField, selectedMutation.value)
|
|
5722
|
+
: fieldValueText(selectedField, selectedMutation.value)
|
|
5796
5723
|
: selectedPreset && configCandidate
|
|
5797
5724
|
? `方案“${selectedPreset.title}” · ${Object.keys(configCandidate.mutations).length} 项修改`
|
|
5798
5725
|
: undefined;
|
|
@@ -5842,10 +5769,10 @@ export function ConsoleAppV4(props) {
|
|
|
5842
5769
|
const readonly = false;
|
|
5843
5770
|
breadcrumb = `${plugin.displayName} / 设置`;
|
|
5844
5771
|
title = '插件设置';
|
|
5845
|
-
description = '
|
|
5772
|
+
description = '先编辑值,再选择保存位置。←→ 切换分组。';
|
|
5846
5773
|
if (configActionError)
|
|
5847
5774
|
description = configActionError;
|
|
5848
|
-
help = '↑↓ 移动 · Enter
|
|
5775
|
+
help = '↑↓ 移动 · Enter 修改· ←→ 分组 · s 查看保存汇总 · u 恢复继承 · Esc 返回';
|
|
5849
5776
|
if (!currentConfigPage) {
|
|
5850
5777
|
content = _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u8BBE\u7F6E", children: _jsx(ProgressRow, { title: "\u8BFB\u53D6\u8BBE\u7F6E\u5B57\u6BB5", state: "active" }) });
|
|
5851
5778
|
}
|
|
@@ -5871,36 +5798,24 @@ export function ConsoleAppV4(props) {
|
|
|
5871
5798
|
}))
|
|
5872
5799
|
: (activeGroup?.fields ?? []).filter((field) => readonly || configFieldCanSet(field) || configFieldCanUnset(field)).map((field) => ({
|
|
5873
5800
|
label: field.title,
|
|
5874
|
-
description: field.invalidOverrideIssue ?? [
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5801
|
+
description: field.invalidOverrideIssue ?? (focus === (activeGroup?.fields ?? []).indexOf(field)
|
|
5802
|
+
? [field.description, ...pendingFieldTargets(field).filter(() => pendingFieldTargets(field).length > 1
|
|
5803
|
+
|| field.effectiveSourceLabel === '当前项目' && pendingFieldDisplay(field, 'project') === undefined).map(({ scope, value }) => `待保存到${configLayerLabel({ scope })}:${value}`),
|
|
5804
|
+
pendingFieldDisplay(field, 'user') !== undefined
|
|
5805
|
+
&& field.effectiveSourceLabel === '当前项目'
|
|
5806
|
+
&& pendingFieldDisplay(field, 'project') === undefined
|
|
5807
|
+
? '个人默认待修改,当前项目不受此值影响' : undefined,
|
|
5808
|
+
].filter(Boolean).join(' · ') : undefined),
|
|
5879
5809
|
meta: (() => {
|
|
5880
|
-
const
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
}
|
|
5884
|
-
if (targets.length === 1) {
|
|
5885
|
-
const target = targets[0];
|
|
5886
|
-
return `${target.value} · 拟保存:${target.scope === 'project' ? '当前项目' : '个人默认'}`;
|
|
5887
|
-
}
|
|
5888
|
-
return targets.map((target) => `${target.scope === 'project' ? '当前项目' : '个人默认'}:${target.value}`).join(' · ');
|
|
5810
|
+
const project = pendingFieldDisplay(field, 'project');
|
|
5811
|
+
const user = pendingFieldDisplay(field, 'user');
|
|
5812
|
+
const pending = project ?? (field.effectiveSourceLabel === '当前项目' ? undefined : user);
|
|
5813
|
+
return `${pending ?? effectiveFieldDisplay(field)} · ${pending !== undefined ? `拟保存:${project !== undefined ? '当前项目' : '个人默认'} · 待保存` : field.effectiveSourceLabel}${pendingFieldTargets(field).length > 0 && pending === undefined ? ' · 有草稿' : ''}`;
|
|
5889
5814
|
})(),
|
|
5890
5815
|
configKey: field.key,
|
|
5891
|
-
run: () => go(
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
pluginId: plugin.pluginId,
|
|
5895
|
-
layer: mutableLayer,
|
|
5896
|
-
fieldKey: field.key,
|
|
5897
|
-
} : {
|
|
5898
|
-
name: 'configFieldEditor',
|
|
5899
|
-
pluginId: plugin.pluginId,
|
|
5900
|
-
layer: mutableLayer,
|
|
5901
|
-
fieldKey: field.key,
|
|
5902
|
-
editor: fieldEditor(field),
|
|
5903
|
-
...fieldEditorMode(field),
|
|
5816
|
+
run: () => go({
|
|
5817
|
+
name: 'configFieldEditor', pluginId: plugin.pluginId, layer: mutableLayer,
|
|
5818
|
+
fieldKey: field.key, editor: fieldEditor(field), ...fieldEditorMode(field),
|
|
5904
5819
|
}),
|
|
5905
5820
|
}));
|
|
5906
5821
|
const operationActions = [
|
|
@@ -5935,7 +5850,7 @@ export function ConsoleAppV4(props) {
|
|
|
5935
5850
|
];
|
|
5936
5851
|
const configSaveAction = {
|
|
5937
5852
|
actionId: 'config-save-summary',
|
|
5938
|
-
label: configDraftCount > 0 ? `保存设置(${configDraftCount}
|
|
5853
|
+
label: configDraftCount > 0 ? `保存设置(${configDraftCount}) [s]` : '保存设置 [s]',
|
|
5939
5854
|
description: '确认全部设置层的变化,再选择只保存或保存并应用',
|
|
5940
5855
|
status: configDraftCount > 0 ? 'success' : 'muted',
|
|
5941
5856
|
disabled: configDraftCount === 0 ? '没有待保存设置'
|
|
@@ -5946,20 +5861,18 @@ export function ConsoleAppV4(props) {
|
|
|
5946
5861
|
go({ name: 'configSave', pluginId: route.pluginId, layer: { scope: 'effective' } });
|
|
5947
5862
|
},
|
|
5948
5863
|
};
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
}
|
|
5961
|
-
pinnedActions = [configSaveAction, configGroupAction];
|
|
5962
|
-
actions = [...tabActions, ...operationActions];
|
|
5864
|
+
bottomActions = [configSaveAction, {
|
|
5865
|
+
label: '设置选项', run: () => go({ ...route, view: 'options' }),
|
|
5866
|
+
}];
|
|
5867
|
+
actions = [...tabActions, ...bottomActions];
|
|
5868
|
+
if (route.view === 'options') {
|
|
5869
|
+
title = '设置选项';
|
|
5870
|
+
description = '查看各层存储值,或清空一个保存位置。';
|
|
5871
|
+
actions = [{ label: '查看当前值的来源', run: () => {
|
|
5872
|
+
setBatchTargetOffset(0);
|
|
5873
|
+
go({ ...route, view: 'values' });
|
|
5874
|
+
} }, ...operationActions];
|
|
5875
|
+
}
|
|
5963
5876
|
const effectiveConfigFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
5964
5877
|
const focusedConfigField = effectiveConfigFocus < (activeGroup?.fields.length ?? 0)
|
|
5965
5878
|
? activeGroup?.fields[effectiveConfigFocus]
|
|
@@ -5969,43 +5882,30 @@ export function ConsoleAppV4(props) {
|
|
|
5969
5882
|
? ['project', 'user'].some((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
5970
5883
|
.some((field) => field.key === focusedConfigField.key && configFieldCanUnset(field)))
|
|
5971
5884
|
: false;
|
|
5972
|
-
help =
|
|
5973
|
-
? '↑↓ 移动 · Enter
|
|
5974
|
-
:
|
|
5975
|
-
? '↑↓ 移动 · Enter 修改 · ←→ 分组 · Tab 保存栏 · s 查看汇总 · u 恢复继承 · Esc 返回'
|
|
5976
|
-
: '↑↓ 移动 · Enter 修改 · ←→ 分组 · Tab 保存栏 · s 查看汇总 · Esc 返回';
|
|
5885
|
+
help = canUnsetFocused
|
|
5886
|
+
? '↑↓ 移动 · Enter 修改 · ←→ 分组· s 查看汇总 · u 恢复继承 · Esc 返回'
|
|
5887
|
+
: '↑↓ 移动 · Enter 修改 · ←→ 分组· s 查看汇总 · Esc 返回';
|
|
5977
5888
|
}
|
|
5978
|
-
const fieldFocus = tabActions.length > 0
|
|
5979
|
-
? effectiveConfigFocus < tabActions.length ? effectiveConfigFocus : Math.min(scrollOffset, tabActions.length - 1)
|
|
5980
|
-
: 0;
|
|
5981
|
-
const operationRows = operationActions.reduce((rows, action) => rows + (policy.mode === 'compact' || !action.description ? 1 : 2), 0);
|
|
5982
|
-
const viewport = actionViewport(tabActions, false, fieldFocus, scrollOffset, pageActionBudget(2 + operationRows + 3 + (configDraftIssues.length > 0 ? 3 : 0)));
|
|
5983
5889
|
const totalFields = currentConfigPage.groups.reduce((count, group) => count + group.fields.length, 0);
|
|
5984
5890
|
const countLabel = `${totalFields} 项${currentConfigPage.presets.length > 0 ? ` · ${currentConfigPage.presets.length} 个方案` : ''}`;
|
|
5985
5891
|
context = configDraftCount > 0 ? `${configDraftCount} 项待保存` : countLabel;
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
disabled: Boolean(action.disabled),
|
|
6004
|
-
})), active: focusZone === 'actions', focus: pinnedActionFocus }), !readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab ? _jsx(ConfigGroupTabs, { tabs: visibleConfigTabs, current: activeTab.id, compact: policy.mode === 'compact', columns: size.columns - 2 }) : null, activeTab && tabActions.length > 0
|
|
6005
|
-
? _jsx(SectionCard, { title: activeTab.label, meta: viewport.hiddenBefore > 0 || viewport.hiddenAfter > 0 ? `${viewport.position}/${viewport.total}` : undefined, children: _jsx(SemanticRows, { actions: tabActions, focus: focusZone === 'content' ? effectiveConfigFocus : -1, kind: "section", compact: false, offset: viewport.offset, limit: viewport.limit }) })
|
|
6006
|
-
: null, operationActions.length > 0
|
|
6007
|
-
? _jsx(SemanticRows, { actions: actions, focus: focusZone === 'content' ? effectiveConfigFocus : -1, kind: "action", compact: policy.mode === 'compact', offset: tabActions.length, limit: operationActions.length })
|
|
6008
|
-
: null] });
|
|
5892
|
+
content = route.view === 'options' ? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })
|
|
5893
|
+
: route.view === 'values' ? (() => {
|
|
5894
|
+
title = '完整设置值与来源';
|
|
5895
|
+
description = '显示各保存位置的完整安全值;敏感值不回显。';
|
|
5896
|
+
const lines = ['effective', 'project', 'user'].flatMap((scope) => ((scope === 'effective' ? currentConfigPage : configLayerPages[scope])?.groups.flatMap((group) => group.fields) ?? []).flatMap((field) => wrapConsoleText(`${field.title} · ${configLayerLabel({ scope })}:${scope === 'effective'
|
|
5897
|
+
? field.invalidOverrideIssue ? '需修正' : fieldValueText(field, field.draftValue ?? field.effectiveValue, true, field.effectiveSourceLabel !== '未设置')
|
|
5898
|
+
: fieldValueText(field, field.draftValue ?? field.value, true, field.configured)}`, size.columns - 4)));
|
|
5899
|
+
readingText = ['effective', 'project', 'user'].flatMap((scope) => ((scope === 'effective' ? currentConfigPage : configLayerPages[scope])?.groups.flatMap((group) => group.fields) ?? []).map((field) => `${field.title} · ${configLayerLabel({ scope })}:${scope === 'effective'
|
|
5900
|
+
? field.invalidOverrideIssue ? '需修正' : fieldValueText(field, field.draftValue ?? field.effectiveValue, true, field.effectiveSourceLabel !== '未设置')
|
|
5901
|
+
: fieldValueText(field, field.draftValue ?? field.value, true, field.configured)}`)).join('\n');
|
|
5902
|
+
const pageRows = Math.max(1, pageActionBudget(4));
|
|
5903
|
+
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
5904
|
+
referenceDetailPagination = { total: lines.length, pageRows };
|
|
5905
|
+
actions = [{ label: '返回设置', run: back }];
|
|
5906
|
+
help = '←→ 或 PgUp/PgDn 阅读 · Esc 返回';
|
|
5907
|
+
return _jsxs(Box, { flexDirection: "column", children: [lines.slice(offset, offset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
5908
|
+
})() : _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab ? _jsx(ConfigGroupTabs, { tabs: visibleConfigTabs, current: activeTab.id, compact: policy.mode === 'compact', columns: size.columns - 2 }) : null, _jsx(SequenceRows, { actions: actions, itemCount: tabActions.length, wrapValues: true, focus: effectiveConfigFocus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2 + (configDraftIssues.length > 0 ? 3 : 0)), compact: policy.mode === 'compact' })] });
|
|
6009
5909
|
}
|
|
6010
5910
|
}
|
|
6011
5911
|
else if (route.name === 'configSave' && plugin) {
|
|
@@ -6020,6 +5920,7 @@ export function ConsoleAppV4(props) {
|
|
|
6020
5920
|
})));
|
|
6021
5921
|
if (route.view === 'changes') {
|
|
6022
5922
|
const lines = changeDetailLines(changes, size.columns - 4);
|
|
5923
|
+
readingText = changeDetailText(changes);
|
|
6023
5924
|
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5)));
|
|
6024
5925
|
const detailOffset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
6025
5926
|
const visibleLines = lines.slice(detailOffset, detailOffset + pageRows);
|
|
@@ -6028,21 +5929,15 @@ export function ConsoleAppV4(props) {
|
|
|
6028
5929
|
context = `${Math.min(detailOffset + 1, lines.length)}–${Math.min(detailOffset + visibleLines.length, lines.length)} / ${lines.length}`;
|
|
6029
5930
|
title = '完整设置变化';
|
|
6030
5931
|
description = '逐项显示修改前和修改后的完整值。';
|
|
6031
|
-
help = '
|
|
5932
|
+
help = '←→ 或 PgUp/PgDn 阅读 · ↑↓ 移动 · Enter 选择 · Esc 返回保存汇总';
|
|
6032
5933
|
actions = [
|
|
6033
|
-
...paginationActions({
|
|
6034
|
-
offset: detailOffset,
|
|
6035
|
-
total: lines.length,
|
|
6036
|
-
pageRows,
|
|
6037
|
-
move: (direction) => moveBatchTargetPage(lines.length, pageRows, direction),
|
|
6038
|
-
}),
|
|
6039
5934
|
{ label: '返回保存汇总', run: back },
|
|
6040
5935
|
];
|
|
6041
5936
|
content = _jsxs(Box, { flexDirection: "column", children: [detailOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${detailOffset} 行`, status: "muted", compact: true }) : null, visibleLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${detailOffset + index}:${line}`)), detailOffset + visibleLines.length < lines.length
|
|
6042
5937
|
? _jsx(StaticRow, { title: `↓ 后面还有 ${lines.length - detailOffset - visibleLines.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
6043
5938
|
}
|
|
6044
5939
|
else {
|
|
6045
|
-
const visibleChanges = changes.slice(0,
|
|
5940
|
+
const visibleChanges = changes.slice(0, 2);
|
|
6046
5941
|
breadcrumb = `${plugin.displayName} / 设置 / 保存汇总`;
|
|
6047
5942
|
context = `${changes.length} 项未保存`;
|
|
6048
5943
|
title = `保存 ${changes.length} 项设置修改?`;
|
|
@@ -6069,55 +5964,27 @@ export function ConsoleAppV4(props) {
|
|
|
6069
5964
|
go({ ...route, view: 'changes' });
|
|
6070
5965
|
},
|
|
6071
5966
|
});
|
|
6072
|
-
const
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
content = _jsxs(Box, { flexDirection: "column", children: [
|
|
5967
|
+
const summaryLines = visibleChanges.flatMap((change) => {
|
|
5968
|
+
const lines = wrapConsoleText(`${change.title} · ${change.before ?? '未设置'} → ${change.after}`, Math.max(1, size.columns - 4));
|
|
5969
|
+
return lines.length > 2 ? [lines[0], `${lines[1]}…`] : lines;
|
|
5970
|
+
});
|
|
5971
|
+
const configSaveActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(summaryLines.length + 2));
|
|
5972
|
+
content = _jsxs(Box, { flexDirection: "column", children: [summaryLines.map((line, index) => _jsx(Text, { children: line }, index)), changes.length > visibleChanges.length ? _jsxs(Text, { children: ["\u53E6\u6709 ", changes.length - visibleChanges.length, " \u9879\uFF1B\u5B8C\u6574\u53D8\u5316\u4E2D\u53EF\u8BFB"] }) : null, _jsx(Text, { children: "\u6BCF\u5C42\u72EC\u7ACB\u63D0\u4EA4\uFF1B\u5931\u8D25\u5C42\u4FDD\u7559\u8349\u7A3F" }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: configSaveActionViewport.offset, limit: configSaveActionViewport.limit })] });
|
|
6078
5973
|
}
|
|
6079
5974
|
}
|
|
6080
|
-
else if (route.name === 'configFieldAction' && plugin && activeField) {
|
|
6081
|
-
const layer = configLayerLabel(route.layer);
|
|
6082
|
-
breadcrumb = `${plugin.displayName} / 设置 / ${layer} / ${activeField.title}`;
|
|
6083
|
-
title = activeField.title;
|
|
6084
|
-
description = `这个字段在${layer}中已经有值。`;
|
|
6085
|
-
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6086
|
-
const targetFields = ['project', 'user'].flatMap((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
6087
|
-
.filter((field) => field.key === activeField.key).map((field) => ({ scope, field })) ?? []);
|
|
6088
|
-
const canSet = targetFields.some(({ field }) => configFieldCanSet(field));
|
|
6089
|
-
const canUnset = targetFields.some(({ field }) => configFieldCanUnset(field));
|
|
6090
|
-
actions = [
|
|
6091
|
-
...(canSet ? [{
|
|
6092
|
-
label: '修改值',
|
|
6093
|
-
run: () => go({
|
|
6094
|
-
name: 'configFieldEditor',
|
|
6095
|
-
pluginId: plugin.pluginId,
|
|
6096
|
-
layer: route.layer,
|
|
6097
|
-
fieldKey: activeField.key,
|
|
6098
|
-
editor: fieldEditor(activeField),
|
|
6099
|
-
...fieldEditorMode(activeField),
|
|
6100
|
-
}),
|
|
6101
|
-
}] : []),
|
|
6102
|
-
...(canUnset ? [{
|
|
6103
|
-
label: '恢复继承',
|
|
6104
|
-
description: '选择要删除覆盖值的位置',
|
|
6105
|
-
status: 'error',
|
|
6106
|
-
run: () => removeDraft(activeField.key),
|
|
6107
|
-
}] : []),
|
|
6108
|
-
];
|
|
6109
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { children: [activeField.invalidOverrideIssue
|
|
6110
|
-
? _jsx(StaticRow, { title: "\u5F53\u524D\u8986\u76D6\u65E0\u6548", meta: activeField.invalidOverrideIssue, status: "warning" })
|
|
6111
|
-
: null, !canSet && canUnset
|
|
6112
|
-
? _jsx(StaticRow, { title: "\u672C\u9879\u76EE\u65E7\u503C\u4E0D\u518D\u4F7F\u7528", meta: "\u5F53\u524D\u7EE7\u627F\u4E2A\u4EBA\u6216\u63D2\u4EF6\u9ED8\u8BA4\uFF1B\u5220\u9664\u65E7\u503C\u540E\u4FDD\u6301\u7EE7\u627F", status: "warning" })
|
|
6113
|
-
: null, _jsx(StaticRow, { title: `${layer}中的值`, meta: currentFieldDisplay(activeField) }), _jsx(StaticRow, { title: "\u5F53\u524D\u5B9E\u9645\u751F\u6548", meta: `${effectiveFieldDisplay(activeField)} · 来自${activeField.effectiveSourceLabel}` }), !activeField.invalidOverrideIssue || activeField.invalidOverrideOwnedByLayer
|
|
6114
|
-
? _jsx(StaticRow, { title: "\u5220\u9664\u540E\u5C06\u6062\u590D", meta: `${activeField.fallbackDisplayValue ?? '未设置'} · 来自${activeField.fallbackSourceLabel ?? '未设置'}` })
|
|
6115
|
-
: null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
6116
|
-
}
|
|
6117
5975
|
else if (route.name === 'configFieldEditor' && plugin && activeField) {
|
|
6118
5976
|
breadcrumb = `${plugin.displayName} / 设置 / ${activeField.title}`;
|
|
6119
5977
|
const showChoices = (activeField.choices?.length ?? 0) > 0 && route.mode !== 'custom';
|
|
6120
|
-
|
|
5978
|
+
const targetFields = ['project', 'user'].flatMap((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields).filter((field) => field.key === activeField.key) ?? []);
|
|
5979
|
+
const canSet = targetFields.some(configFieldCanSet);
|
|
5980
|
+
const canUnset = targetFields.some(configFieldCanUnset);
|
|
5981
|
+
if (!canSet) {
|
|
5982
|
+
title = activeField.title;
|
|
5983
|
+
description = activeField.invalidOverrideIssue ?? '此旧值不允许修改;可以删除覆盖并恢复继承。';
|
|
5984
|
+
actions = [];
|
|
5985
|
+
content = _jsx(StatusCard, { status: "warning", title: "\u4EC5\u53EF\u5220\u9664\u65E7\u503C", description: description });
|
|
5986
|
+
}
|
|
5987
|
+
else if (showChoices) {
|
|
6121
5988
|
title = `选择${activeField.title}`;
|
|
6122
5989
|
description = activeField.type !== 'boolean' && activeField.allowCustom
|
|
6123
5990
|
? '先选择插件提供的方案,也可以继续自定义。'
|
|
@@ -6180,18 +6047,12 @@ export function ConsoleAppV4(props) {
|
|
|
6180
6047
|
.filter((property) => !fixedKeys.has(property))
|
|
6181
6048
|
.sort((left, right) => left.localeCompare(right, 'en'));
|
|
6182
6049
|
const propertySummary = (property, definition) => {
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
const preset = definition.choices?.presets.find((choice) => choice.value === value);
|
|
6190
|
-
if (preset)
|
|
6191
|
-
return preset.title;
|
|
6192
|
-
if (typeof value === 'boolean')
|
|
6193
|
-
return value ? '开启' : '关闭';
|
|
6194
|
-
return String(value);
|
|
6050
|
+
const presentation = presentConfigValue({ definition,
|
|
6051
|
+
value: activeObjectDraft[property], privateValue: activeField.sensitive,
|
|
6052
|
+
configured: Object.hasOwn(activeObjectDraft, property),
|
|
6053
|
+
});
|
|
6054
|
+
return presentation.state === 'unset' && 'required' in definition && definition.required
|
|
6055
|
+
? '必填 · 未设置' : presentation.text;
|
|
6195
6056
|
};
|
|
6196
6057
|
const propertyActions = [
|
|
6197
6058
|
...fixedEntries.map(([property, definition]) => ({
|
|
@@ -6242,6 +6103,16 @@ export function ConsoleAppV4(props) {
|
|
|
6242
6103
|
: activeField.sensitive ? '当前值不会回显;留空不会自动删除旧值。' : activeField.description ?? '';
|
|
6243
6104
|
content = _jsx(InputCard, { focused: true, title: _jsx(InputDisplay, { value: inputValue, sensitive: activeField.sensitive, placeholder: activeField.placeholder, columns: Math.max(8, size.columns - 8) }), description: inputError ?? (activeField.sensitive ? '输入已隐藏。' : activeField.placeholder ?? '按 Enter 使用输入值。'), status: inputError ? 'error' : undefined });
|
|
6244
6105
|
}
|
|
6106
|
+
if (canUnset) {
|
|
6107
|
+
const unset = { label: '恢复继承', description: '选择要删除覆盖值的位置', run: () => removeDraft(activeField.key) };
|
|
6108
|
+
const inputEditor = canSet && !showChoices && (route.editor === 'number' || route.editor === 'string');
|
|
6109
|
+
help += ' · Ctrl-R 恢复继承';
|
|
6110
|
+
if (!inputEditor) {
|
|
6111
|
+
const offset = actions.length;
|
|
6112
|
+
actions.push(unset);
|
|
6113
|
+
content = _jsxs(Box, { flexDirection: "column", children: [content, _jsx(SemanticRows, { actions: actions, offset: offset, focus: focus, kind: "action", compact: true })] });
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6245
6116
|
}
|
|
6246
6117
|
else if (route.name === 'configObjectPropertyEditor' && plugin
|
|
6247
6118
|
&& activeField?.definition.type === 'object' && activeObjectDraft) {
|
|
@@ -6348,6 +6219,8 @@ export function ConsoleAppV4(props) {
|
|
|
6348
6219
|
breadcrumb = `${unavailablePluginName} / 设置`;
|
|
6349
6220
|
context = '暂时不可编辑';
|
|
6350
6221
|
const diagnostic = configUnavailableDiagnostic(route);
|
|
6222
|
+
if (showTechnical)
|
|
6223
|
+
readingText = diagnosticDetailText(diagnostic);
|
|
6351
6224
|
const invalidLayer = (route.invalidStores?.length ?? 0) > 0;
|
|
6352
6225
|
const canRecoverSource = configUnavailableCanRecoverSource(route);
|
|
6353
6226
|
title = diagnostic.title;
|
|
@@ -6380,16 +6253,25 @@ export function ConsoleAppV4(props) {
|
|
|
6380
6253
|
},
|
|
6381
6254
|
] : []),
|
|
6382
6255
|
{ label: showTechnical ? '返回问题摘要' : '技术详情', description: '查看脱敏错误代码和阶段', run: () => toggleTechnical() },
|
|
6383
|
-
{ label: '返回', run: back },
|
|
6256
|
+
{ role: 'navigation', label: '返回', run: back },
|
|
6384
6257
|
];
|
|
6385
6258
|
const unsupportedScopes = resetScopes.filter((scope) => configResetAvailability[scope]?.resettable === false);
|
|
6386
6259
|
const unsupportedRows = unsupportedScopes.map((scope) => _jsx(StaticRow, { title: `${scope === 'user' ? '个人默认' : '当前项目'}设置层不能自动重置:特殊节点,请手动移动`, status: "warning", compact: true }, `unsupported-reset:${scope}`));
|
|
6260
|
+
const technicalPageRows = responsiveBudget(2, 4);
|
|
6261
|
+
if (showTechnical && !invalidLayer) {
|
|
6262
|
+
referenceDetailPagination = {
|
|
6263
|
+
total: diagnosticDetailLines(diagnostic, size.columns - 6).length,
|
|
6264
|
+
pageRows: technicalPageRows,
|
|
6265
|
+
};
|
|
6266
|
+
help = '←→ 或 PgUp/PgDn 阅读技术字段 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6267
|
+
}
|
|
6268
|
+
const unavailableActionViewport = actionViewport(actions, true, focus, scrollOffset, pageActionBudget(technicalPageRows + 6 + unsupportedRows.length));
|
|
6387
6269
|
content = invalidLayer
|
|
6388
6270
|
? _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u4E0D\u53EF\u8BFB\u53D6\u7684\u8BBE\u7F6E\u5C42", children: (route.invalidStores ?? []).map((store) => _jsx(StaticRow, { title: store.scope === 'user' ? '个人默认设置' : '当前项目设置', meta: "\u9700\u8981\u91CD\u7F6E\u6216\u624B\u52A8\u79FB\u5F00", status: "warning", compact: true }, store.scope)) }), showTechnical ? _jsxs(TechnicalDetails, { children: [(route.invalidStores ?? []).flatMap((store) => [
|
|
6389
6271
|
_jsx(StaticRow, { title: scopeLabel(store.scope), meta: store.code, compact: true }, store.scope),
|
|
6390
6272
|
_jsx(StaticRow, { title: "path summary", meta: store.pathSummary, compact: true }, `${store.scope}:path`),
|
|
6391
6273
|
]), _jsx(StaticRow, { title: "stage", meta: safeDiagnosticCode(route.diagnosticStage, 'read-config'), compact: true })] }) : null, unsupportedRows, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] })
|
|
6392
|
-
: _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, compact: policy.mode === 'compact' }), unsupportedRows, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
6274
|
+
: _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, columns: size.columns, offset: showTechnical ? batchTargetOffset : undefined, limit: showTechnical ? technicalPageRows : undefined, compact: policy.mode === 'compact' }), unsupportedRows, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: showTechnical || policy.mode === 'compact', offset: showTechnical ? unavailableActionViewport.offset : undefined, limit: showTechnical ? unavailableActionViewport.limit : undefined })] });
|
|
6393
6275
|
}
|
|
6394
6276
|
else if (route.name === 'sourceList') {
|
|
6395
6277
|
breadcrumb = '插件来源';
|
|
@@ -6415,6 +6297,10 @@ export function ConsoleAppV4(props) {
|
|
|
6415
6297
|
: configured?.type === 'git'
|
|
6416
6298
|
&& (configured.url.startsWith('git@') || configured.url.startsWith('ssh://'))
|
|
6417
6299
|
? 'SSH' : 'HTTPS';
|
|
6300
|
+
const policy = overview.detail?.type === 'path' ? 'Path'
|
|
6301
|
+
: overview.detail?.refPolicy === 'explicit'
|
|
6302
|
+
? `固定 ${sourceRefSummary(overview.detail.explicitRef ?? overview.detail.requestedRef)}`
|
|
6303
|
+
: `通道 ${sourceRefSummary(overview.detail?.requestedRef)}`;
|
|
6418
6304
|
const version = `${generation?.active?.catalogVersion ?? '未启用'}${freshness?.candidate?.catalogVersion
|
|
6419
6305
|
? `→${freshness.candidate.catalogVersion}` : ''}`;
|
|
6420
6306
|
const available = generation?.state === 'ready' || health === 'ready';
|
|
@@ -6422,7 +6308,8 @@ export function ConsoleAppV4(props) {
|
|
|
6422
6308
|
return {
|
|
6423
6309
|
label: source.id,
|
|
6424
6310
|
description: sourceLocationSummary(overview.detail?.type ?? 'git', overview.detail?.locationSummary),
|
|
6425
|
-
|
|
6311
|
+
sourceLabel: policy,
|
|
6312
|
+
meta: `当前 ${version} · ${source.count === undefined ? '插件数未知' : `${source.count} 个插件`} · ${relativeCheckTime(freshness?.checkedAt)}`,
|
|
6426
6313
|
tags: _jsxs(Box, { children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["[", source.primary ? '主来源' : transport, "] "] }), _jsx(StatusBadge, { items: [{ key: 'health', label: available ? '可用' : unavailable ? '不可用' : '未确认',
|
|
6427
6314
|
symbol: available ? '✓' : unavailable ? '!' : '?', status: available ? 'success' : unavailable ? 'warning' : 'muted' }] }), freshness && freshness.state !== 'current' && freshness.state !== 'fixed'
|
|
6428
6315
|
? _jsxs(Text, { color: tuiTheme.colors.accent, children: [" [", freshness.state === 'updateAvailable' ? '↑ ' : '', presentation.label, "]"] })
|
|
@@ -6431,29 +6318,19 @@ export function ConsoleAppV4(props) {
|
|
|
6431
6318
|
run: () => go({ name: 'sourceDetail', sourceId: source.id }),
|
|
6432
6319
|
};
|
|
6433
6320
|
});
|
|
6434
|
-
|
|
6321
|
+
bottomActions = [
|
|
6435
6322
|
{ label: '+ 添加来源', description: 'Git 仓库或本地目录', run: beginAddSource },
|
|
6436
6323
|
{
|
|
6437
|
-
label: '检查全部来源',
|
|
6324
|
+
actionId: 'source-check-all', label: '检查全部来源 [r]',
|
|
6438
6325
|
description: '并行检查各来源;必要时完整验证;结果不会自动启用待检查版本',
|
|
6439
6326
|
run: () => props.controller.recheckAllCatalogSources(),
|
|
6440
6327
|
},
|
|
6441
6328
|
{ label: 'Catalog 本地存储', description: '检查占用和预览安全清理', run: () => go({ name: 'catalogStoreOverview' }) },
|
|
6442
6329
|
];
|
|
6443
|
-
help =
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
rowHeights: actions.map((action) => objectCardRowLayout({
|
|
6448
|
-
description: action.description ?? '', meta: action.meta, width: size.columns - 2, hasTags: true,
|
|
6449
|
-
}).height),
|
|
6450
|
-
focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(1),
|
|
6451
|
-
});
|
|
6452
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action, index) => ({
|
|
6453
|
-
id: action.actionId ?? `${action.label}:${index}`,
|
|
6454
|
-
label: action.label,
|
|
6455
|
-
disabled: Boolean(action.disabled),
|
|
6456
|
-
})), active: focusZone === 'actions', focus: pinnedActionFocus, visibleCount: policy.mode === 'compact' ? 2 : 3 }), _jsx(ContentViewport, { children: _jsx(SemanticRows, { actions: actions, focus: focusZone === 'content' ? focus : -1, kind: "section", cards: true, columns: size.columns - 2, compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit }) })] });
|
|
6330
|
+
help = '↑↓ 移动 · Enter 打开 · Esc 返回';
|
|
6331
|
+
const sourceCount = actions.length;
|
|
6332
|
+
actions = [...actions, ...bottomActions];
|
|
6333
|
+
content = _jsx(SequenceRows, { actions: actions, itemCount: sourceCount, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(), compact: policy.mode === 'compact', cards: true });
|
|
6457
6334
|
}
|
|
6458
6335
|
else if (route.name === 'catalogStoreOverview') {
|
|
6459
6336
|
breadcrumb = '插件来源 / 本地存储';
|
|
@@ -6475,7 +6352,7 @@ export function ConsoleAppV4(props) {
|
|
|
6475
6352
|
},
|
|
6476
6353
|
{ actionId: 'catalog-store-rules', label: '查看保留规则', description: '了解哪些目录会保留,以及旧版本何时可以清理', run: () => go({ name: 'catalogStoreRules' }) },
|
|
6477
6354
|
{ actionId: 'catalog-store-refresh', label: '重新检查', description: '重新读取当前本地存储', run: () => setCatalogStoreRefresh((value) => value + 1) },
|
|
6478
|
-
{ actionId: 'catalog-store-back', label: '返回来源列表', run: back },
|
|
6355
|
+
{ role: 'navigation', actionId: 'catalog-store-back', label: '返回来源列表', run: back },
|
|
6479
6356
|
];
|
|
6480
6357
|
content = _jsxs(Box, { flexDirection: "column", children: [catalogStoreError
|
|
6481
6358
|
? _jsx(Callout, { status: "warning", title: "\u672C\u5730\u5B58\u50A8\u68C0\u67E5\u5931\u8D25", description: "\u672A\u6267\u884C\u5220\u9664\u3002\u8BF7\u68C0\u67E5 Catalog \u914D\u7F6E\u548C\u672C\u5730\u5B58\u50A8\u540E\u91CD\u8BD5\u3002" })
|
|
@@ -6490,7 +6367,7 @@ export function ConsoleAppV4(props) {
|
|
|
6490
6367
|
? '' : '清理前会再次检查每个项目,只删除能够确认安全的旧内容。';
|
|
6491
6368
|
actions = [
|
|
6492
6369
|
{ actionId: 'catalog-store-rules-technical', label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
6493
|
-
{ actionId: 'catalog-store-rules-back', label: '返回存储概览', run: back },
|
|
6370
|
+
{ role: 'navigation', actionId: 'catalog-store-rules-back', label: '返回存储概览', run: back },
|
|
6494
6371
|
];
|
|
6495
6372
|
content = _jsxs(Box, { flexDirection: "column", children: [!showTechnical ? _jsxs(_Fragment, { children: [_jsx(SectionCard, { title: "\u59CB\u7EC8\u4FDD\u7559 \u00B7 \u5F53\u524D\u4F7F\u7528\u7684\u76EE\u5F55\u4E0D\u4F1A\u5220\u9664", children: _jsx(StaticRow, { title: "\u4E0D\u5C5E\u4E8E AW\u3001\u65E0\u6CD5\u786E\u8BA4\u5B89\u5168\u6216\u4ECD\u5728\u4F7F\u7528\u7684\u5185\u5BB9\u4F1A\u4FDD\u7559", status: "muted", compact: true }) }), _jsxs(SectionCard, { title: "\u6EE1\u8DB3\u65F6\u95F4\u548C\u5B8C\u6574\u9A8C\u8BC1\u540E\u53EF\u56DE\u6536", children: [_jsx(StaticRow, { title: "\u65E7\u7248\u672C\u6EE1\u8DB3\u4FDD\u7559\u671F\u9650\u540E\u624D\u6E05\u7406", compact: true }), _jsx(StaticRow, { title: "\u672A\u5B8C\u6210\u7684\u4E0B\u8F7D\u6216\u66FF\u6362\u5185\u5BB9\u81F3\u5C11\u4FDD\u7559 24 \u5C0F\u65F6", compact: true }), _jsx(StaticRow, { title: "\u9694\u79BB\u5185\u5BB9\u81F3\u5C11\u4FDD\u7559 24 \u5C0F\u65F6", compact: true }), _jsx(StaticRow, { title: "\u5BF9\u5E94\u6B63\u5F0F\u7248\u672C\u5B8C\u6574\u53EF\u7528\u540E\u624D\u6E05\u7406", compact: true })] })] }) : null, showTechnical ? _jsxs(TechnicalDetails, { title: "\u6E05\u7406\u5224\u5B9A\u8BC1\u636E", children: [_jsx(StaticRow, { title: "\u9501", meta: "AW_HOME \u5199\u9501\u5185\u91CD\u65B0\u68C0\u67E5", compact: true }), _jsx(StaticRow, { title: "\u6B63\u5F0F\u76EE\u5F55\u7248\u672C", meta: "\u975E Active \u9879\u4FDD\u7559 30 \u5929", compact: true }), _jsx(StaticRow, { title: "staging\u3001replacement\u3001delete claim", meta: "\u4FDD\u7559 24 \u5C0F\u65F6", compact: true }), _jsx(StaticRow, { title: "quarantine", meta: "\u4FDD\u7559 24 \u5C0F\u65F6\uFF1B\u5BF9\u5E94\u6B63\u5F0F\u76EE\u5F55\u7248\u672C\u5FC5\u987B\u5B8C\u6574\u5065\u5EB7", compact: true }), _jsx(StaticRow, { title: "\u4FDD\u7559\u8282\u70B9", meta: "foreign\u3001\u7B26\u53F7\u94FE\u63A5\u3001unsafe\u3001\u635F\u574F\u6216\u672A\u77E5\u9879\u76EE", compact: true })] }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
6496
6373
|
}
|
|
@@ -6506,17 +6383,26 @@ export function ConsoleAppV4(props) {
|
|
|
6506
6383
|
&& generation?.configMismatch === true;
|
|
6507
6384
|
const logicalIdentityChanged = generation?.errorCode === 'catalogLogicalIdentityChanged';
|
|
6508
6385
|
const activationBlocked = props.controller.catalogSourceRecoveryBlockReason();
|
|
6386
|
+
const generationMissingCount = generation?.compatibility?.state === 'unsupported'
|
|
6387
|
+
? generation.compatibility.missingCapabilities.length : undefined;
|
|
6388
|
+
const freshnessMissingCount = freshness?.missingCapabilities?.length;
|
|
6509
6389
|
breadcrumb = `插件来源 / ${route.sourceId}`;
|
|
6510
6390
|
context = detail?.type === 'path' ? '本地目录' : 'Git';
|
|
6511
6391
|
title = route.sourceId;
|
|
6512
6392
|
description = generationState === 'incompatible'
|
|
6513
|
-
?
|
|
6393
|
+
? generationMissingCount && generationMissingCount > 0
|
|
6394
|
+
? `当前目录版本与 AW 不兼容;缺少 ${generationMissingCount} 项功能支持。`
|
|
6395
|
+
: '当前目录版本与 AW 不兼容;所需功能无法确认。'
|
|
6514
6396
|
: freshness?.state === 'incompatible'
|
|
6515
6397
|
? generationState === 'repairRequired'
|
|
6516
6398
|
? `待检查版本需要升级 AW 或修改来源后才能使用;损坏的当前启用版本仍可按旧版本精确修复。`
|
|
6517
6399
|
: activeVersion && generationState === 'ready'
|
|
6518
|
-
?
|
|
6519
|
-
|
|
6400
|
+
? freshnessMissingCount && freshnessMissingCount > 0
|
|
6401
|
+
? `当前 AW 缺少 ${freshnessMissingCount} 项待检查版本所需功能;当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
6402
|
+
: `待检查版本所需功能无法确认;已验证的当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
6403
|
+
: freshnessMissingCount && freshnessMissingCount > 0
|
|
6404
|
+
? `当前 AW 缺少 ${freshnessMissingCount} 项待检查版本所需功能;这个来源没有已验证可用的目录版本。`
|
|
6405
|
+
: '待检查版本所需功能无法确认;这个来源没有已验证可用的目录版本。'
|
|
6520
6406
|
: freshness?.state === 'checkFailed' && activeVersion && generationState === 'ready'
|
|
6521
6407
|
? `更新检查失败,但当前目录版本 ${activeVersion} 仍可浏览和使用。`
|
|
6522
6408
|
: freshness?.state === 'updateAvailable' && generationState === 'ready'
|
|
@@ -6529,7 +6415,7 @@ export function ConsoleAppV4(props) {
|
|
|
6529
6415
|
? '当前目录版本损坏,且来源配置已变化。验证待检查版本后可精确修复同一版本,或明确切换到新版本。'
|
|
6530
6416
|
: '当前目录版本缺失或损坏。可在这里精确修复。'
|
|
6531
6417
|
: generationState === 'configMismatch' ? '来源配置与当前启用版本不一致。可验证待检查版本并切换。'
|
|
6532
|
-
: logicalIdentityChanged ? '
|
|
6418
|
+
: logicalIdentityChanged ? '来源发布身份已变化;旧 Active 是否仍可用需要单独验证。请编辑来源,或移除后重新添加。'
|
|
6533
6419
|
: '来源没有可用的启用版本。可在这里取得并启用。';
|
|
6534
6420
|
help = '↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
6535
6421
|
if (policy.mode === 'compact')
|
|
@@ -6599,7 +6485,7 @@ export function ConsoleAppV4(props) {
|
|
|
6599
6485
|
}]
|
|
6600
6486
|
: [{
|
|
6601
6487
|
actionId: 'source-check',
|
|
6602
|
-
label: freshness?.state === 'checking' ? '正在检查' : '检查来源',
|
|
6488
|
+
label: freshness?.state === 'checking' ? '正在检查' : '检查来源 [r]',
|
|
6603
6489
|
description: '检查这个来源;检测到变化或异常时完整验证',
|
|
6604
6490
|
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
6605
6491
|
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
@@ -6626,6 +6512,12 @@ export function ConsoleAppV4(props) {
|
|
|
6626
6512
|
}]),
|
|
6627
6513
|
{ actionId: 'source-remove', label: '移除来源', description: '移除来源配置', status: 'error', disabled: props.controller.listSources().length <= 1 ? '至少保留一个来源' : detail?.primary ? '主来源不能直接移除' : undefined, run: () => beginReview({ kind: 'sourceMutation', action: 'remove', sourceId: route.sourceId, changes: [{ title: `移除来源 ${route.sourceId}` }] }) },
|
|
6628
6514
|
{ actionId: 'source-technical', label: '技术详情', description: '查看脱敏错误代码和当前版本证据', run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }) },
|
|
6515
|
+
...(freshness?.operationRecordId ? [{
|
|
6516
|
+
actionId: 'source-check-diagnostic',
|
|
6517
|
+
label: '查看检查诊断与报告',
|
|
6518
|
+
description: `安全记录 ${freshness.operationRecordId}`,
|
|
6519
|
+
run: () => go({ name: 'operationDiagnostic', recordId: freshness.operationRecordId }),
|
|
6520
|
+
}] : []),
|
|
6629
6521
|
...(generationState !== 'ready'
|
|
6630
6522
|
|| freshness?.state === 'updateAvailable'
|
|
6631
6523
|
|| freshness?.state === 'localChangesAvailable'
|
|
@@ -6633,18 +6525,11 @@ export function ConsoleAppV4(props) {
|
|
|
6633
6525
|
|| freshness?.state === 'rollbackDetected'
|
|
6634
6526
|
|| freshness?.state === 'incompatible' ? [{
|
|
6635
6527
|
actionId: 'source-check',
|
|
6636
|
-
label: freshness?.state === 'checking' ? '正在检查' : '检查来源',
|
|
6528
|
+
label: freshness?.state === 'checking' ? '正在检查' : '检查来源 [r]',
|
|
6637
6529
|
description: '检查这个来源;检测到变化或异常时完整验证',
|
|
6638
6530
|
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
6639
6531
|
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
6640
6532
|
}] : []),
|
|
6641
|
-
{
|
|
6642
|
-
actionId: 'source-full-validate',
|
|
6643
|
-
label: '完整验证',
|
|
6644
|
-
description: '强制重新取得并完整验证这个来源',
|
|
6645
|
-
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
6646
|
-
run: () => props.controller.fullValidateCatalogSource(route.sourceId),
|
|
6647
|
-
},
|
|
6648
6533
|
];
|
|
6649
6534
|
const sourcePrimaryAction = generationAction.length === 0 ? [] : [{
|
|
6650
6535
|
...generationAction[0],
|
|
@@ -6664,27 +6549,31 @@ export function ConsoleAppV4(props) {
|
|
|
6664
6549
|
: configuredSource?.type === 'git'
|
|
6665
6550
|
&& (configuredSource.url.startsWith('git@') || configuredSource.url.startsWith('ssh://'))
|
|
6666
6551
|
? 'SSH' : 'HTTPS';
|
|
6667
|
-
const versionPolicy = detail?.type === 'path' ? '当前本地内容'
|
|
6668
|
-
: detail?.refPolicy === 'cliChannel' ?
|
|
6669
|
-
:
|
|
6552
|
+
const versionPolicy = detail?.type === 'path' ? 'Path · 当前本地内容'
|
|
6553
|
+
: detail?.refPolicy === 'cliChannel' ? `跟随 AW 通道 · ${sourceRefSummary(detail.requestedRef)}`
|
|
6554
|
+
: `固定 ref · ${sourceRefSummary(detail?.explicitRef ?? detail?.requestedRef)}`;
|
|
6670
6555
|
const sourceCount = snapshot.viewModel?.sourceTabs.find((source) => source.id === route.sourceId)?.count;
|
|
6671
6556
|
const checkAction = {
|
|
6672
6557
|
actionId: 'source-check',
|
|
6673
|
-
label: freshness?.state === 'checking' ? '正在检查' : '检查来源',
|
|
6558
|
+
label: freshness?.state === 'checking' ? '正在检查' : '检查来源 [r]',
|
|
6674
6559
|
description: '检查这个来源;检测到变化或异常时完整验证;不会自动启用更新',
|
|
6675
6560
|
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
6676
6561
|
run: () => props.controller.recheckCatalogSource(route.sourceId),
|
|
6677
6562
|
};
|
|
6563
|
+
const viewPluginsAction = {
|
|
6564
|
+
actionId: 'source-view-plugins',
|
|
6565
|
+
label: '查看此来源插件',
|
|
6566
|
+
description: sourceCount === undefined ? '插件数未知' : `${sourceCount} 个插件`,
|
|
6567
|
+
run: () => go({ name: 'pluginList', mode: 'browse', sourceId: route.sourceId }),
|
|
6568
|
+
};
|
|
6569
|
+
const healthyWithoutUpdate = generationState === 'ready'
|
|
6570
|
+
&& (freshness?.state === 'current' || freshness?.state === 'fixed');
|
|
6678
6571
|
actions = [
|
|
6572
|
+
...(healthyWithoutUpdate ? [viewPluginsAction] : []),
|
|
6679
6573
|
...sourcePrimaryAction,
|
|
6680
6574
|
...(sourcePrimaryAction.some((action) => action.actionId === 'source-check')
|
|
6681
6575
|
? [] : [checkAction]),
|
|
6682
|
-
|
|
6683
|
-
actionId: 'source-view-plugins',
|
|
6684
|
-
label: '查看此来源插件',
|
|
6685
|
-
description: sourceCount === undefined ? '插件数未知' : `${sourceCount} 个插件`,
|
|
6686
|
-
run: () => go({ name: 'pluginList', mode: 'browse', sourceId: route.sourceId }),
|
|
6687
|
-
},
|
|
6576
|
+
...(healthyWithoutUpdate ? [] : [viewPluginsAction]),
|
|
6688
6577
|
managementActions[0],
|
|
6689
6578
|
managementActions.find((action) => action.actionId === 'source-remove'),
|
|
6690
6579
|
{
|
|
@@ -6706,7 +6595,9 @@ export function ConsoleAppV4(props) {
|
|
|
6706
6595
|
`${transport} · ${sourceLocationSummary(detail?.type ?? 'git', detail?.locationSummary)}`,
|
|
6707
6596
|
`当前 ${activeVersion ?? '未启用'}${candidateVersion ? ` → ${candidateVersion}` : ''} · ${sourceCount === undefined ? '插件数未知' : `${sourceCount} 个插件`}`,
|
|
6708
6597
|
`版本策略:${versionPolicy}`,
|
|
6709
|
-
`检查:${freshnessLabel} · ${freshness?.
|
|
6598
|
+
`检查:${freshnessLabel} · ${freshness?.state === 'checking'
|
|
6599
|
+
? '正在检查这个来源;必要时验证内容'
|
|
6600
|
+
: relativeCheckTime(freshness?.checkedAt)}`,
|
|
6710
6601
|
].join('\n');
|
|
6711
6602
|
const sourceCardHeight = objectCardRowLayout({
|
|
6712
6603
|
description: sourceSummary, width: size.columns - 2, hasTags: true,
|
|
@@ -6791,29 +6682,32 @@ export function ConsoleAppV4(props) {
|
|
|
6791
6682
|
: detail?.primary ? '主来源不能直接移除' : undefined,
|
|
6792
6683
|
run: () => beginReview({ kind: 'sourceMutation', action: 'remove', sourceId: route.sourceId,
|
|
6793
6684
|
changes: [{ title: `移除来源 ${route.sourceId}` }] }) },
|
|
6794
|
-
{ label: freshness?.state === 'checking' ? '正在检查' : '检查来源',
|
|
6685
|
+
{ label: freshness?.state === 'checking' ? '正在检查' : '检查来源 [r]',
|
|
6795
6686
|
description: '检查这个来源;检测到变化或异常时完整验证;不会自动启用更新',
|
|
6796
6687
|
disabled: freshness?.state === 'checking' ? '检查正在进行' : undefined,
|
|
6797
6688
|
run: () => props.controller.recheckCatalogSource(route.sourceId) },
|
|
6798
6689
|
{ label: '技术详情', description: '查看脱敏错误代码和版本证据',
|
|
6799
6690
|
run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }) },
|
|
6691
|
+
...(freshness?.operationRecordId ? [{
|
|
6692
|
+
label: '查看检查诊断与报告',
|
|
6693
|
+
description: `安全记录 ${freshness.operationRecordId}`,
|
|
6694
|
+
run: () => go({ name: 'operationDiagnostic', recordId: freshness.operationRecordId }),
|
|
6695
|
+
}] : []),
|
|
6800
6696
|
];
|
|
6801
6697
|
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(1));
|
|
6802
6698
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit });
|
|
6803
6699
|
}
|
|
6804
6700
|
else if (route.name === 'sourceTechnical') {
|
|
6805
6701
|
const diagnostic = resolveSourceTechnicalDiagnostic(route.sourceId);
|
|
6702
|
+
readingText = diagnosticDetailText(diagnostic);
|
|
6806
6703
|
breadcrumb = `插件来源 / ${route.sourceId} / 技术详情`;
|
|
6807
6704
|
const lines = diagnosticDetailLines(diagnostic, size.columns - 6);
|
|
6808
6705
|
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - sourceTechnicalPageRows));
|
|
6809
6706
|
context = `${offset + 1}–${Math.min(lines.length, offset + sourceTechnicalPageRows)} / ${lines.length} 行`;
|
|
6810
6707
|
title = '来源技术诊断';
|
|
6811
6708
|
description = '';
|
|
6812
|
-
help = '
|
|
6813
|
-
const sourceTechnicalLastOffset = Math.max(0, lines.length - sourceTechnicalPageRows);
|
|
6709
|
+
help = '←→ 或 PgUp/PgDn 翻页 · Esc 返回';
|
|
6814
6710
|
actions = [
|
|
6815
|
-
{ label: 'p 上一页', run: () => setBatchTargetOffset((value) => Math.max(0, value - sourceTechnicalPageRows)) },
|
|
6816
|
-
{ label: 'n 下一页', run: () => setBatchTargetOffset((value) => Math.min(sourceTechnicalLastOffset, value + sourceTechnicalPageRows)) },
|
|
6817
6711
|
{ label: '返回来源详情', run: back },
|
|
6818
6712
|
];
|
|
6819
6713
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u5B8C\u6574\u5B89\u5168\u5B57\u6BB5", compact: true, children: lines.slice(offset, offset + sourceTechnicalPageRows).map((line, index) => _jsx(Text, { children: line }, offset + index)) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
@@ -6954,6 +6848,7 @@ export function ConsoleAppV4(props) {
|
|
|
6954
6848
|
const changes = sourceDraftChanges(sourceDraft);
|
|
6955
6849
|
if (route.view === 'changes') {
|
|
6956
6850
|
const lines = changeDetailLines(changes, size.columns - 4);
|
|
6851
|
+
readingText = changeDetailText(changes);
|
|
6957
6852
|
const pageRows = Math.max(1, Math.min(responsiveBudget(5, 10), pageActionBudget(5)));
|
|
6958
6853
|
const detailOffset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
6959
6854
|
const visibleLines = lines.slice(detailOffset, detailOffset + pageRows);
|
|
@@ -6962,14 +6857,8 @@ export function ConsoleAppV4(props) {
|
|
|
6962
6857
|
context = `${Math.min(detailOffset + 1, lines.length)}–${Math.min(detailOffset + visibleLines.length, lines.length)} / ${lines.length}`;
|
|
6963
6858
|
title = '完整来源变化';
|
|
6964
6859
|
description = '逐项显示修改前和修改后的完整值。';
|
|
6965
|
-
help = '
|
|
6860
|
+
help = '←→ 或 PgUp/PgDn 阅读 · ↑↓ 移动 · Enter 选择 · Esc 返回修改汇总';
|
|
6966
6861
|
actions = [
|
|
6967
|
-
...paginationActions({
|
|
6968
|
-
offset: detailOffset,
|
|
6969
|
-
total: lines.length,
|
|
6970
|
-
pageRows,
|
|
6971
|
-
move: (direction) => moveBatchTargetPage(lines.length, pageRows, direction),
|
|
6972
|
-
}),
|
|
6973
6862
|
{ label: '返回修改汇总', run: back },
|
|
6974
6863
|
];
|
|
6975
6864
|
content = _jsxs(Box, { flexDirection: "column", children: [detailOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${detailOffset} 行`, status: "muted", compact: true }) : null, visibleLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${detailOffset + index}:${line}`)), detailOffset + visibleLines.length < lines.length
|
|
@@ -7022,22 +6911,18 @@ export function ConsoleAppV4(props) {
|
|
|
7022
6911
|
actions = [{ label: sourceDependenciesExpanded ? '收起依赖内容' : '查看依赖内容', description: '只显示插件、来源、平台、位置和类别聚合', run: () => {
|
|
7023
6912
|
setSourceDependenciesExpanded((value) => !value);
|
|
7024
6913
|
setBatchTargetOffset(0);
|
|
7025
|
-
} }, { label: '返回来源详情', run: back }];
|
|
6914
|
+
} }, { role: 'navigation', label: '返回来源详情', run: back }];
|
|
7026
6915
|
const countLabel = (value, unit) => value === undefined ? '未知' : `${value} ${unit}`;
|
|
7027
6916
|
const referenceLimit = Math.max(1, pageActionBudget(12));
|
|
7028
6917
|
const references = sourceReferenceSummary?.dependencies ?? [];
|
|
6918
|
+
if (sourceDependenciesExpanded)
|
|
6919
|
+
readingText = references.map((dependency) => `${dependency.category} · ${dependency.pluginId ?? dependency.sourceId ?? '身份未知'} · ${dependency.platform ? platformLabel(dependency.platform) : ''}${dependency.scope ? ` · ${scopeLabel(dependency.scope)}` : ''}`).join('\n');
|
|
7029
6920
|
const referenceOffset = Math.min(batchTargetOffset, Math.max(0, references.length - 1));
|
|
7030
6921
|
const visibleReferences = references.slice(referenceOffset, referenceOffset + referenceLimit);
|
|
7031
6922
|
const hiddenReferences = Math.max(0, references.length - referenceOffset - visibleReferences.length);
|
|
7032
6923
|
if (sourceDependenciesExpanded && references.length > referenceLimit) {
|
|
7033
|
-
actions.unshift(...paginationActions({
|
|
7034
|
-
offset: referenceOffset,
|
|
7035
|
-
total: references.length,
|
|
7036
|
-
pageRows: referenceLimit,
|
|
7037
|
-
move: (direction) => moveBatchTargetPage(references.length, referenceLimit, direction),
|
|
7038
|
-
}));
|
|
7039
6924
|
referenceDetailPagination = { total: references.length, pageRows: referenceLimit };
|
|
7040
|
-
help = '
|
|
6925
|
+
help = '←→ 查看全部引用 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7041
6926
|
}
|
|
7042
6927
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "error", title: sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整' : '发现活动引用' }), _jsxs(SectionCard, { title: sourceDependenciesExpanded
|
|
7043
6928
|
? referenceOffset > 0 ? `引用内容 · ↑ 前面还有 ${referenceOffset} 项` : '引用内容'
|
|
@@ -7053,7 +6938,7 @@ export function ConsoleAppV4(props) {
|
|
|
7053
6938
|
: '当前状态检查没有发现需要处理的安装、来源或设置问题。';
|
|
7054
6939
|
if (issues.length === 0) {
|
|
7055
6940
|
actions = [
|
|
7056
|
-
{ label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
6941
|
+
{ role: 'navigation', label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
7057
6942
|
{ label: '浏览插件', run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
7058
6943
|
];
|
|
7059
6944
|
}
|
|
@@ -7073,8 +6958,18 @@ export function ConsoleAppV4(props) {
|
|
|
7073
6958
|
: go({ name: 'issueDetail', issueId: entry.issueId }),
|
|
7074
6959
|
}));
|
|
7075
6960
|
}
|
|
6961
|
+
const reminder = snapshot.projectIntentReminder;
|
|
6962
|
+
const pendingProjectTargets = (reminder?.actionableTargets.length ?? 0)
|
|
6963
|
+
+ (reminder?.blockedTargets.length ?? 0) + (reminder?.unconfirmedTargets.length ?? 0);
|
|
6964
|
+
if (snapshot.projectIntentIssue || pendingProjectTargets > 0) {
|
|
6965
|
+
title = '需要处理';
|
|
6966
|
+
description = '查看项目插件、安装、来源或设置问题。';
|
|
6967
|
+
actions.unshift({ actionId: 'project-status', label: '项目插件',
|
|
6968
|
+
description: snapshot.projectIntentIssue ? '项目声明需要检查' : `${pendingProjectTargets} 个位置待处理或未确认`,
|
|
6969
|
+
status: 'warning', run: () => go({ name: 'projectStatus' }) });
|
|
6970
|
+
}
|
|
7076
6971
|
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(8, 12));
|
|
7077
|
-
if (issues.length === 0) {
|
|
6972
|
+
if (issues.length === 0 || snapshot.projectIntentIssue || pendingProjectTargets > 0) {
|
|
7078
6973
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit });
|
|
7079
6974
|
}
|
|
7080
6975
|
else {
|
|
@@ -7144,35 +7039,26 @@ export function ConsoleAppV4(props) {
|
|
|
7144
7039
|
|| entry.id === 'keepUserScope' || entry.id === 'keepProjectScope' ? 'error' : undefined,
|
|
7145
7040
|
run: () => runIssueAction(entry),
|
|
7146
7041
|
}));
|
|
7147
|
-
const issueDetailRows =
|
|
7042
|
+
const issueDetailRows = diagnosticDetailLines(issue.diagnostic, size.columns - 6).length;
|
|
7043
|
+
if (showTechnical)
|
|
7044
|
+
readingText = diagnosticDetailText(issue.diagnostic);
|
|
7148
7045
|
const issueDetailPageRows = policy.mode === 'compact'
|
|
7149
7046
|
? 1
|
|
7150
|
-
: Math.max(1, responsiveBudget(
|
|
7151
|
-
|
|
7152
|
-
? paginationActions({
|
|
7153
|
-
offset: batchTargetOffset,
|
|
7154
|
-
total: issueDetailRows,
|
|
7155
|
-
pageRows: issueDetailPageRows,
|
|
7156
|
-
move: (direction) => moveBatchTargetPage(issueDetailRows, issueDetailPageRows, direction),
|
|
7157
|
-
})
|
|
7158
|
-
: [];
|
|
7159
|
-
actions.push(...issuePaginationActions);
|
|
7160
|
-
if (issuePaginationActions.length > 0) {
|
|
7047
|
+
: Math.max(1, responsiveBudget(1, 4));
|
|
7048
|
+
if (showTechnical && issueDetailRows > issueDetailPageRows) {
|
|
7161
7049
|
referenceDetailPagination = { total: issueDetailRows, pageRows: issueDetailPageRows };
|
|
7162
|
-
help = '
|
|
7050
|
+
help = '←→ 或 PgUp/PgDn 阅读全部技术字段 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7163
7051
|
}
|
|
7164
7052
|
const issuePlugin = issue.pluginId ? props.controller.getPluginOverview(issue.pluginId) : undefined;
|
|
7165
7053
|
const issueOverview = issue.pluginId ? props.controller.getPluginDependencyOverview(issue.pluginId) : undefined;
|
|
7166
|
-
const issueDiagnosticSummary = _jsx(DiagnosticSummary, { diagnostic: issue.diagnostic, technical: showTechnical, offset: showTechnical ? batchTargetOffset : undefined, limit: showTechnical ? issueDetailPageRows : undefined, compact: policy.mode === 'compact' || issue.category.startsWith('dependency')
|
|
7054
|
+
const issueDiagnosticSummary = _jsx(DiagnosticSummary, { diagnostic: issue.diagnostic, columns: size.columns, technical: showTechnical, offset: showTechnical ? batchTargetOffset : undefined, limit: showTechnical ? issueDetailPageRows : undefined, compact: policy.mode === 'compact' || issue.category.startsWith('dependency')
|
|
7167
7055
|
|| issue.category === 'scopeConflict' });
|
|
7168
7056
|
const issueActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(6));
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
: {};
|
|
7175
|
-
if (issue.category === 'dependencyStillRequired' && issuePlugin) {
|
|
7057
|
+
if (showTechnical) {
|
|
7058
|
+
const technicalActionViewport = actionViewport(actions, true, focus, scrollOffset, pageActionBudget(issueDetailPageRows + 6));
|
|
7059
|
+
content = _jsxs(Box, { flexDirection: "column", children: [issueDiagnosticSummary, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: technicalActionViewport.offset, limit: technicalActionViewport.limit })] });
|
|
7060
|
+
}
|
|
7061
|
+
else if (issue.category === 'dependencyStillRequired' && issuePlugin) {
|
|
7176
7062
|
const blockingDependents = issue.platform && issue.scope
|
|
7177
7063
|
? props.controller.getBlockingDependents(issuePlugin.pluginId, issue.platform, issue.scope)
|
|
7178
7064
|
: issueOverview?.dependents ?? [];
|
|
@@ -7182,7 +7068,6 @@ export function ConsoleAppV4(props) {
|
|
|
7182
7068
|
{ label: '查看完整依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
|
|
7183
7069
|
{ label: '返回插件详情', run: () => go({ name: 'pluginDetail', pluginId: issuePlugin.pluginId }) },
|
|
7184
7070
|
...(issueHasTechnicalDetails ? [{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() }] : []),
|
|
7185
|
-
...issuePaginationActions,
|
|
7186
7071
|
];
|
|
7187
7072
|
const dependencyBlockedViewport = policy.mode === 'compact' && showTechnical
|
|
7188
7073
|
? { offset: Math.min(Math.max(0, focus - 1), Math.max(0, actions.length - 2)), limit: 2 }
|
|
@@ -7197,7 +7082,7 @@ export function ConsoleAppV4(props) {
|
|
|
7197
7082
|
issue.dependencyPlatform ? platformLabel(issue.dependencyPlatform) : undefined,
|
|
7198
7083
|
issue.dependencyScope ? scopeLabel(issue.dependencyScope) : undefined,
|
|
7199
7084
|
].filter((entry) => Boolean(entry)).join(' · ');
|
|
7200
|
-
content = _jsxs(Box, { flexDirection: "column", children: [issueDiagnosticSummary, !showTechnical && policy.mode !== 'compact' ? _jsxs(StatusCard, { status: "warning", title: dependencyName, description: "\u53D7\u5F71\u54CD\u7684\u4F9D\u8D56\u76EE\u6807", children: [_jsx(StaticRow, { title: issuePlugin.displayName, description: "\u53D7\u5F71\u54CD\u7684\u6839\u63D2\u4EF6", meta: issue.platform && issue.scope ? `${platformLabel(issue.platform)} · ${scopeLabel(issue.scope)}` : undefined }), dependencyTarget ? _jsx(StaticRow, { title: "\u4F9D\u8D56\u76EE\u6807", meta: dependencyTarget, status: "warning" }) : null] }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact'
|
|
7085
|
+
content = _jsxs(Box, { flexDirection: "column", children: [issueDiagnosticSummary, !showTechnical && policy.mode !== 'compact' ? _jsxs(StatusCard, { status: "warning", title: dependencyName, description: "\u53D7\u5F71\u54CD\u7684\u4F9D\u8D56\u76EE\u6807", children: [_jsx(StaticRow, { title: issuePlugin.displayName, description: "\u53D7\u5F71\u54CD\u7684\u6839\u63D2\u4EF6", meta: issue.platform && issue.scope ? `${platformLabel(issue.platform)} · ${scopeLabel(issue.scope)}` : undefined }), dependencyTarget ? _jsx(StaticRow, { title: "\u4F9D\u8D56\u76EE\u6807", meta: dependencyTarget, status: "warning" }) : null] }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
7201
7086
|
}
|
|
7202
7087
|
else if (issue.category === 'dependencyTargetUnsupported' && issuePlugin) {
|
|
7203
7088
|
const issueScope = issue.scope ?? scope;
|
|
@@ -7223,7 +7108,6 @@ export function ConsoleAppV4(props) {
|
|
|
7223
7108
|
} },
|
|
7224
7109
|
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
|
|
7225
7110
|
...(issueHasTechnicalDetails ? [{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() }] : []),
|
|
7226
|
-
...issuePaginationActions,
|
|
7227
7111
|
];
|
|
7228
7112
|
const unsupportedTargetViewport = policy.mode === 'compact' && showTechnical
|
|
7229
7113
|
? { offset: Math.min(Math.max(0, focus - 1), Math.max(0, actions.length - 2)), limit: 2 }
|
|
@@ -7269,7 +7153,7 @@ export function ConsoleAppV4(props) {
|
|
|
7269
7153
|
status: operationHistoryState(record).status,
|
|
7270
7154
|
run: () => go({ name: 'operationDiagnostic', recordId: record.recordId }),
|
|
7271
7155
|
})),
|
|
7272
|
-
{ label: '返回上一页', run: back },
|
|
7156
|
+
{ role: 'navigation', label: '返回上一页', run: back },
|
|
7273
7157
|
];
|
|
7274
7158
|
const viewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, pageActionBudget(4));
|
|
7275
7159
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: records.some((record) => record.state !== 'succeeded') ? 'warning' : 'success', title: records.length === 0 ? '没有可回看的安全结果' : '操作结果与问题', description: "\u9009\u62E9\u4E00\u9879\u53EF\u67E5\u770B\u5B8C\u6574\u5B89\u5168\u8BE6\u60C5\u6216\u62A5\u544A\u9884\u89C8\u3002", compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit })] });
|
|
@@ -7277,30 +7161,31 @@ export function ConsoleAppV4(props) {
|
|
|
7277
7161
|
else if (route.name === 'operationDiagnostic') {
|
|
7278
7162
|
const detail = props.controller.getOperationDiagnostic(route.recordId);
|
|
7279
7163
|
breadcrumb = '操作记录 / 技术详情';
|
|
7280
|
-
context = '
|
|
7164
|
+
context = '安全详情';
|
|
7281
7165
|
title = detail ? '安全技术详情' : '操作记录已不可用';
|
|
7282
7166
|
description = detail
|
|
7283
7167
|
? '可滚动查看所有目标、阶段、代码和已保留的安全原因。'
|
|
7284
7168
|
: '这项会话记录已经不再可用。';
|
|
7285
7169
|
const preview = detail ? props.controller.previewOperationReport(route.recordId) : undefined;
|
|
7286
|
-
const detailLineLimit =
|
|
7287
|
-
|
|
7288
|
-
|
|
7170
|
+
const detailLineLimit = operationDetailPageRows;
|
|
7171
|
+
if (detail) {
|
|
7172
|
+
readingText = operationDiagnosticText(detail);
|
|
7173
|
+
const total = operationDiagnosticLineCount(detail, size.columns);
|
|
7174
|
+
context = `${Math.min(total, scrollOffset + 1)}–${Math.min(total, scrollOffset + detailLineLimit)} / ${total} 行`;
|
|
7175
|
+
}
|
|
7289
7176
|
actions = detail ? [
|
|
7290
7177
|
preview
|
|
7291
7178
|
? { label: '预览安全报告', description: '确认内容后选择本地保存位置', run: () => go({ name: 'operationReportPreview', recordId: route.recordId }) }
|
|
7292
7179
|
: { label: '报告当前不可用', disabled: '无法生成这项记录的报告' },
|
|
7293
|
-
{
|
|
7294
|
-
|
|
7295
|
-
{ label: '返回操作记录', run: back },
|
|
7296
|
-
] : [{ label: '返回操作记录', run: back }];
|
|
7180
|
+
{ role: 'navigation', label: '返回操作记录', run: back },
|
|
7181
|
+
] : [{ role: 'navigation', label: '返回操作记录', run: back }];
|
|
7297
7182
|
const diagnosticActionViewport = actionViewport(actions, policy.mode === 'compact', focus, 0, policy.mode === 'compact' ? 2 : 3);
|
|
7298
|
-
content = detail ? _jsxs(Box, { flexDirection: "column", children: [_jsx(ContentViewport, { children: _jsx(OperationDiagnosticScreen, { detail: detail, compact: policy.mode === 'compact', offset: scrollOffset, limit: detailLineLimit }) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: diagnosticActionViewport.offset, limit: diagnosticActionViewport.limit })] }) : _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "warning", title: "\u8BB0\u5F55\u5DF2\u4E0D\u53EF\u7528", description: "\u8FD4\u56DE\u64CD\u4F5C\u8BB0\u5F55\u540E\u53EF\u67E5\u770B\u4ECD\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u7684\u7ED3\u679C\u3002" }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
7183
|
+
content = detail ? _jsxs(Box, { flexDirection: "column", children: [_jsx(ContentViewport, { children: _jsx(OperationDiagnosticScreen, { detail: detail, compact: policy.mode === 'compact', columns: size.columns, offset: scrollOffset, limit: detailLineLimit }) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: diagnosticActionViewport.offset, limit: diagnosticActionViewport.limit })] }) : _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "warning", title: "\u8BB0\u5F55\u5DF2\u4E0D\u53EF\u7528", description: "\u8FD4\u56DE\u64CD\u4F5C\u8BB0\u5F55\u540E\u53EF\u67E5\u770B\u4ECD\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u7684\u7ED3\u679C\u3002" }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
7299
7184
|
}
|
|
7300
7185
|
else if (route.name === 'operationReportPreview') {
|
|
7301
7186
|
const preview = props.controller.previewOperationReport(route.recordId);
|
|
7302
7187
|
breadcrumb = '操作记录 / 报告预览';
|
|
7303
|
-
context = '
|
|
7188
|
+
context = '安全报告';
|
|
7304
7189
|
title = reportSaveMode ? '选择报告保存位置' : preview ? '安全报告预览' : '报告已不可用';
|
|
7305
7190
|
description = reportSaveMode
|
|
7306
7191
|
? '输入一个新的本地文件路径,然后按 Enter 保存。'
|
|
@@ -7312,9 +7197,10 @@ export function ConsoleAppV4(props) {
|
|
|
7312
7197
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(InputCard, { title: "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84", focused: true, compact: policy.mode === 'compact', children: _jsx(InputDisplay, { value: reportSavePath, placeholder: "/tmp/aw-operation-report.md", columns: Math.max(12, size.columns - 10) }) }), reportSaveError ? _jsx(Callout, { status: "error", title: "\u62A5\u544A\u672A\u4FDD\u5B58", description: reportSaveError, compact: policy.mode === 'compact' }) : null, _jsx(StaticRow, { title: "\u4EC5\u4FDD\u5B58\u5F53\u524D\u9884\u89C8\u4E2D\u7684\u5B89\u5168\u5185\u5BB9\u3002", status: "muted", compact: true })] });
|
|
7313
7198
|
}
|
|
7314
7199
|
else if (preview) {
|
|
7315
|
-
|
|
7316
|
-
const
|
|
7317
|
-
const
|
|
7200
|
+
readingText = preview.content;
|
|
7201
|
+
const reportLineLimit = operationReportPageRows;
|
|
7202
|
+
const total = operationReportLineCount(preview, size.columns);
|
|
7203
|
+
context = `${Math.min(total, scrollOffset + 1)}–${Math.min(total, scrollOffset + reportLineLimit)} / ${total} 行`;
|
|
7318
7204
|
actions = [
|
|
7319
7205
|
{ label: '选择本地保存位置', description: '保存前输入明确的文件路径', run: () => {
|
|
7320
7206
|
setReportSaveMode(true);
|
|
@@ -7329,18 +7215,16 @@ export function ConsoleAppV4(props) {
|
|
|
7329
7215
|
setReportSaveError('报告未保存。请检查保存位置后重试。');
|
|
7330
7216
|
});
|
|
7331
7217
|
} },
|
|
7332
|
-
{
|
|
7333
|
-
{ label: 'n 下一页', run: () => setScrollOffset((value) => Math.min(lastReportOffset, value + reportLineLimit)) },
|
|
7334
|
-
{ label: '返回技术详情', run: back },
|
|
7218
|
+
{ role: 'navigation', label: '返回技术详情', run: back },
|
|
7335
7219
|
];
|
|
7336
7220
|
const reportFeedbackVisible = Boolean(reportSaveResult || reportSaveError);
|
|
7337
7221
|
const compactReportActions = policy.mode === 'compact' || reportFeedbackVisible;
|
|
7338
7222
|
const reportActionLimit = policy.mode === 'compact' || reportFeedbackVisible ? 2 : 3;
|
|
7339
7223
|
const reportActionOffset = Math.min(Math.max(0, focus - 1), Math.max(0, actions.length - reportActionLimit));
|
|
7340
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ContentViewport, { children: _jsx(OperationReportPreviewScreen, { preview: preview, compact: policy.mode === 'compact', offset: scrollOffset, limit: reportLineLimit }) }), reportSaveResult ? _jsx(Callout, { status: "success", title: "\u62A5\u544A\u5DF2\u4FDD\u5B58", description: reportSaveResult, compact: policy.mode === 'compact' }) : null, reportSaveError ? _jsx(Callout, { status: "error", title: "\u62A5\u544A\u672A\u4FDD\u5B58", description: reportSaveError, compact: policy.mode === 'compact' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: compactReportActions, offset: reportActionOffset, limit: reportActionLimit })] });
|
|
7224
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ContentViewport, { children: _jsx(OperationReportPreviewScreen, { preview: preview, compact: policy.mode === 'compact', columns: size.columns, offset: scrollOffset, limit: reportLineLimit }) }), reportSaveResult ? _jsx(Callout, { status: "success", title: "\u62A5\u544A\u5DF2\u4FDD\u5B58", description: reportSaveResult, compact: policy.mode === 'compact' }) : null, reportSaveError ? _jsx(Callout, { status: "error", title: "\u62A5\u544A\u672A\u4FDD\u5B58", description: reportSaveError, compact: policy.mode === 'compact' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: compactReportActions, offset: reportActionOffset, limit: reportActionLimit })] });
|
|
7341
7225
|
}
|
|
7342
7226
|
else {
|
|
7343
|
-
actions = [{ label: '返回技术详情', run: back }];
|
|
7227
|
+
actions = [{ role: 'navigation', label: '返回技术详情', run: back }];
|
|
7344
7228
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Callout, { status: "warning", title: "\u62A5\u544A\u5DF2\u4E0D\u53EF\u7528", description: "\u8FD4\u56DE\u6280\u672F\u8BE6\u60C5\u540E\u53EF\u67E5\u770B\u4ECD\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u7684\u8BB0\u5F55\u3002" }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
7345
7229
|
}
|
|
7346
7230
|
}
|
|
@@ -7395,6 +7279,17 @@ export function ConsoleAppV4(props) {
|
|
|
7395
7279
|
}
|
|
7396
7280
|
else if (route.name === 'review' && snapshot.activeReview) {
|
|
7397
7281
|
const review = snapshot.activeReview;
|
|
7282
|
+
readingText = [
|
|
7283
|
+
`${review.title} · ${review.subject.name}`,
|
|
7284
|
+
review.description,
|
|
7285
|
+
...(review.equivalentCommand && !review.requiresConfirmation
|
|
7286
|
+
? [`等价命令:${review.equivalentCommand}`] : []),
|
|
7287
|
+
...(review.rootSummary ?? []).map((root) => `${root.pluginId}${root.version ? ` · ${root.version}` : ''} · ${root.targets.map((target) => `${target.platform}/${target.scope}`).join('、')}`),
|
|
7288
|
+
...review.changes.map((change) => `${change.sourceId ? `${change.sourceId} · ` : ''}${reviewSymbol(change)} ${change.title}${change.description ? ` · ${change.description}` : ''}`),
|
|
7289
|
+
...(review.technicalDetails ?? []),
|
|
7290
|
+
...(review.confirmationDetails ?? []),
|
|
7291
|
+
...(review.sharedImpact.warning ? [review.sharedImpact.warning] : []),
|
|
7292
|
+
].join('\n');
|
|
7398
7293
|
const technicalDetails = (review.technicalDetails ?? []).flatMap((detail) => review.operationKind === 'catalogGeneration' && detail.startsWith('引用影响:')
|
|
7399
7294
|
? wrapConsoleText(detail, Math.max(20, size.columns - 10))
|
|
7400
7295
|
: [detail]);
|
|
@@ -7604,12 +7499,6 @@ export function ConsoleAppV4(props) {
|
|
|
7604
7499
|
: userReviewChanges.length;
|
|
7605
7500
|
const reviewPageRows = Math.max(1, responsiveBudget(6, 10) - actions.length - 2);
|
|
7606
7501
|
if (reviewPaginationTotal > reviewPageRows) {
|
|
7607
|
-
actions.push(...paginationActions({
|
|
7608
|
-
offset: batchTargetOffset,
|
|
7609
|
-
total: reviewPaginationTotal,
|
|
7610
|
-
pageRows: 1,
|
|
7611
|
-
move: (direction) => moveBatchTargetPage(reviewPaginationTotal, 1, direction),
|
|
7612
|
-
}));
|
|
7613
7502
|
referenceDetailPagination = { total: reviewPaginationTotal, pageRows: 1 };
|
|
7614
7503
|
}
|
|
7615
7504
|
const reviewActionViewport = actionViewport(actions, true, focus, scrollOffset, responsiveBudget(2, 6));
|
|
@@ -7698,6 +7587,18 @@ export function ConsoleAppV4(props) {
|
|
|
7698
7587
|
? `另有 ${hiddenTechnicalChanges} 项` : undefined, status: changeStatus(change), compact: false }, `${change.title}:${index}`);
|
|
7699
7588
|
});
|
|
7700
7589
|
const showEquivalentCommand = Boolean(review.equivalentCommand && showTechnical && !review.requiresConfirmation);
|
|
7590
|
+
const reviewRootPluginIds = taskReview || batchReview
|
|
7591
|
+
? review.rootSummary?.map((root) => root.pluginId) ?? [] : [];
|
|
7592
|
+
const reviewRootSummary = reviewRootPluginIds.length === 1
|
|
7593
|
+
? props.controller.getPluginOverview(reviewRootPluginIds[0])?.displayName
|
|
7594
|
+
?? reviewRootPluginIds[0].split('/').at(-1)
|
|
7595
|
+
: reviewRootPluginIds.length > 1 ? `${reviewRootPluginIds.length} 个` : undefined;
|
|
7596
|
+
const reviewVersionSummary = review.rootSummary && review.rootSummary.length > 0
|
|
7597
|
+
? [...new Set(review.rootSummary.map((root) => root.version ?? '未知'))].join('、')
|
|
7598
|
+
: undefined;
|
|
7599
|
+
const reviewRangeSummary = review.rootSummary && review.rootSummary.length > 0
|
|
7600
|
+
? [...new Set(review.rootSummary.flatMap((root) => root.targets.map((target) => `${target.platform === 'both' ? 'Codex/Claude Code' : platformLabel(target.platform)}/${scopeLabel(target.scope)}`)))].join('、')
|
|
7601
|
+
: undefined;
|
|
7701
7602
|
const installDependencyTargets = installReviewPluginId
|
|
7702
7603
|
? snapshot.installPlacementDraft?.targets ?? [] : [];
|
|
7703
7604
|
const installDependencyPluginIds = new Set(installDependencyTargets.map((target) => target.pluginId));
|
|
@@ -7713,7 +7614,7 @@ export function ConsoleAppV4(props) {
|
|
|
7713
7614
|
&& (review.changes.length > technicalChangeBudget || technicalDetails.length > technicalDetailLimit)) {
|
|
7714
7615
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7715
7616
|
}
|
|
7716
|
-
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, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, portableReview ? null : _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" }), showTechnical ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: catalogReferenceDetailOffset >= 0 && batchTargetOffset + index >= catalogReferenceDetailOffset ? undefined : index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, `${batchTargetOffset + index}:${detail}`)) }) : null] }) });
|
|
7617
|
+
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: reviewRootSummary ? `本次插件 · ${reviewRootSummary}` : review.subject.name, meta: reviewRootSummary ? `版本 ${reviewVersionSummary} · 范围 ${reviewRangeSummary || '未知'}` : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, portableReview ? null : _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }), compressedReview || showTechnical ? null : _jsx(StaticRow, { title: "\u6280\u672F\u8BE6\u60C5\uFF08d\uFF09", status: "muted" }), showTechnical ? _jsx(TechnicalDetails, { children: visibleTechnicalDetails.map((detail, index) => _jsx(StaticRow, { title: detail, meta: catalogReferenceDetailOffset >= 0 && batchTargetOffset + index >= catalogReferenceDetailOffset ? undefined : index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, `${batchTargetOffset + index}:${detail}`)) }) : null] }) });
|
|
7717
7618
|
}
|
|
7718
7619
|
else if (route.name === 'progress') {
|
|
7719
7620
|
const taskOperation = snapshot.activeReview?.operationKind === 'pluginWriteTask';
|
|
@@ -7796,6 +7697,11 @@ export function ConsoleAppV4(props) {
|
|
|
7796
7697
|
: entry.state === 'skipped' ? 3 : 4;
|
|
7797
7698
|
return priority(left) - priority(right);
|
|
7798
7699
|
});
|
|
7700
|
+
readingText = [
|
|
7701
|
+
title,
|
|
7702
|
+
...prioritizedProgressTargets.map((entry) => `${entry.pluginId} · ${platformLabel(entry.platform)} · ${scopeLabel(entry.scope)} · ${progressLabel(entry).label}`),
|
|
7703
|
+
...snapshot.progress.messages.map(productResultMessage),
|
|
7704
|
+
].join('\n');
|
|
7799
7705
|
const progressStepRows = policy.mode === 'compact' ? 4 : 5;
|
|
7800
7706
|
const progressTargetAndMessageBudget = pageActionBudget()
|
|
7801
7707
|
- 2
|
|
@@ -7809,7 +7715,10 @@ export function ConsoleAppV4(props) {
|
|
|
7809
7715
|
const progressTargetBudget = progressTargetAndMessageBudget - dependencyMessageLimit;
|
|
7810
7716
|
const progressTargetLimit = prefixRowLimit(prioritizedProgressTargets.map(() => policy.mode === 'compact' ? 1 : 2), progressTargetBudget);
|
|
7811
7717
|
help = prioritizedProgressTargets.length > progressTargetLimit
|
|
7812
|
-
? '
|
|
7718
|
+
? '←→ 翻页 · [y] 复制全文 · Ctrl-C 完成后退出'
|
|
7719
|
+
: '[y] 复制全文 · Ctrl-C 完成后退出';
|
|
7720
|
+
if (copyFeedback?.routeKey === routeKey)
|
|
7721
|
+
description = copyFeedback.message;
|
|
7813
7722
|
const progressTargetOffset = Math.min(batchTargetOffset, Math.max(0, prioritizedProgressTargets.length - 1));
|
|
7814
7723
|
const visibleProgressTargets = prioritizedProgressTargets.slice(progressTargetOffset, progressTargetOffset + progressTargetLimit);
|
|
7815
7724
|
const hiddenProgressTargets = Math.max(0, snapshot.progressTargets.length - progressTargetOffset - visibleProgressTargets.length);
|
|
@@ -8117,11 +8026,20 @@ export function ConsoleAppV4(props) {
|
|
|
8117
8026
|
: [];
|
|
8118
8027
|
const safeCleanupKeys = new Set(props.controller.listPluginWriteBatchTargets('cleanup', [...new Set(failedUninstallTargets.map((target) => target.pluginId))]).map(batchTargetKey));
|
|
8119
8028
|
const failedCleanupTargets = failedUninstallTargets.filter((target) => safeCleanupKeys.has(batchTargetKey(target)));
|
|
8029
|
+
const sourceRecoveryRoute = 'returnRoute' in result && result.returnRoute?.name === 'sourceDetail'
|
|
8030
|
+
? result.returnRoute : undefined;
|
|
8031
|
+
const resumeConfigSourceDraft = operationKind === 'catalogGeneration'
|
|
8032
|
+
&& sourceRecoveryRoute !== undefined
|
|
8033
|
+
&& dirty
|
|
8034
|
+
&& draftOwner?.startsWith(`${sourceRecoveryRoute.sourceId}/`)
|
|
8035
|
+
&& props.controller.externalDrafts.some((draft) => draft.kind === 'configDraft' && draft.owner === draftOwner);
|
|
8120
8036
|
actions = storeOperation
|
|
8121
8037
|
? [
|
|
8122
|
-
{ label: '返回存储概览', run: () => leaveResult({ name: 'catalogStoreOverview' }) },
|
|
8123
|
-
{ label: '查看本次记录', run: () =>
|
|
8124
|
-
|
|
8038
|
+
{ role: 'navigation', label: '返回存储概览', run: () => leaveResult({ name: 'catalogStoreOverview' }) },
|
|
8039
|
+
{ label: '查看本次记录', run: () => {
|
|
8040
|
+
if (result.operationRecordId)
|
|
8041
|
+
go({ name: 'operationDiagnostic', recordId: result.operationRecordId });
|
|
8042
|
+
} },
|
|
8125
8043
|
]
|
|
8126
8044
|
: taskOperation
|
|
8127
8045
|
? [
|
|
@@ -8146,7 +8064,8 @@ export function ConsoleAppV4(props) {
|
|
|
8146
8064
|
status: 'warning',
|
|
8147
8065
|
run: () => leaveResult(taskReturnRoute),
|
|
8148
8066
|
}] : []),
|
|
8149
|
-
...(!nestedTaskReturn ? [{
|
|
8067
|
+
...(!nestedTaskReturn ? [{ role: result.state === 'succeeded' ? 'navigation' : undefined,
|
|
8068
|
+
label: '完成,返回我的插件', run: () => {
|
|
8150
8069
|
if (taskBatchOrigin?.owner === 'batch-write'
|
|
8151
8070
|
&& taskBatchOrigin.flowId === batchDraft?.flowId) {
|
|
8152
8071
|
setBatchDraft(undefined);
|
|
@@ -8154,8 +8073,10 @@ export function ConsoleAppV4(props) {
|
|
|
8154
8073
|
}
|
|
8155
8074
|
leaveResult({ name: 'pluginList', mode: 'installed' });
|
|
8156
8075
|
} }] : []),
|
|
8157
|
-
{ label: '查看本次记录', run: () =>
|
|
8158
|
-
|
|
8076
|
+
{ label: '查看本次记录', run: () => {
|
|
8077
|
+
if (result.operationRecordId)
|
|
8078
|
+
go({ name: 'operationDiagnostic', recordId: result.operationRecordId });
|
|
8079
|
+
} },
|
|
8159
8080
|
]
|
|
8160
8081
|
: batchOperation
|
|
8161
8082
|
? [
|
|
@@ -8168,9 +8089,12 @@ export function ConsoleAppV4(props) {
|
|
|
8168
8089
|
setScrollOffset(0);
|
|
8169
8090
|
},
|
|
8170
8091
|
}] : []),
|
|
8171
|
-
{
|
|
8172
|
-
|
|
8173
|
-
|
|
8092
|
+
{ role: result.state === 'succeeded' ? 'navigation' : undefined,
|
|
8093
|
+
label: '返回我的插件', run: () => leaveResult() },
|
|
8094
|
+
{ label: '查看本次记录', run: () => {
|
|
8095
|
+
if (result.operationRecordId)
|
|
8096
|
+
go({ name: 'operationDiagnostic', recordId: result.operationRecordId });
|
|
8097
|
+
} },
|
|
8174
8098
|
]
|
|
8175
8099
|
: portableOperation
|
|
8176
8100
|
? blocked || partial ? [
|
|
@@ -8220,7 +8144,7 @@ export function ConsoleAppV4(props) {
|
|
|
8220
8144
|
{ label: portableReturnLabel, run: () => { if (pluginId)
|
|
8221
8145
|
props.controller.resetPortableFlowToPreview(pluginId); } },
|
|
8222
8146
|
] : [
|
|
8223
|
-
{ label: portableReturnLabel, run: () => {
|
|
8147
|
+
{ role: 'navigation', label: portableReturnLabel, run: () => {
|
|
8224
8148
|
props.controller.discardPortableDraft();
|
|
8225
8149
|
leaveResult();
|
|
8226
8150
|
} },
|
|
@@ -8244,15 +8168,20 @@ export function ConsoleAppV4(props) {
|
|
|
8244
8168
|
{ label: '重新检查问题', run: () => leaveResult({ name: 'issueList' }) },
|
|
8245
8169
|
]
|
|
8246
8170
|
: [
|
|
8247
|
-
{ label: '查看问题列表', run: () => leaveResult({ name: 'issueList' }) },
|
|
8248
|
-
{ label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
8171
|
+
{ role: 'navigation', label: '查看问题列表', run: () => leaveResult({ name: 'issueList' }) },
|
|
8172
|
+
{ role: 'navigation', label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
8249
8173
|
]
|
|
8250
8174
|
: sourceOperation
|
|
8251
8175
|
? [{
|
|
8252
8176
|
label: !blocked && 'returnRoute' in result && result.returnRoute?.name === 'pluginWriteTask'
|
|
8253
|
-
? '重新准备插件更新' : blocked ? '返回修改' : partial ? '现在启用 / 重试'
|
|
8177
|
+
? '重新准备插件更新' : blocked ? '返回修改' : partial ? '现在启用 / 重试'
|
|
8178
|
+
: resumeConfigSourceDraft ? '继续编辑未保存设置' : '查看来源',
|
|
8179
|
+
role: !blocked && !partial && !resumeConfigSourceDraft
|
|
8180
|
+
&& !('returnRoute' in result && result.returnRoute?.name === 'pluginWriteTask')
|
|
8181
|
+
? 'navigation' : undefined,
|
|
8254
8182
|
run: () => blocked ? props.controller.resumeResultDraft() : leaveResult(),
|
|
8255
|
-
}, {
|
|
8183
|
+
}, { role: !blocked && !partial ? 'navigation' : undefined,
|
|
8184
|
+
label: '返回首页', run: () => requestSessionNavigation('home') }]
|
|
8256
8185
|
: configOperation && partial
|
|
8257
8186
|
? [
|
|
8258
8187
|
...configRetryScopes.map((scope) => ({
|
|
@@ -8277,7 +8206,8 @@ export function ConsoleAppV4(props) {
|
|
|
8277
8206
|
{ label: '返回设置', run: () => props.controller.resumeResultDraft() },
|
|
8278
8207
|
{ label: '返回首页', run: () => requestSessionNavigation('home') },
|
|
8279
8208
|
]
|
|
8280
|
-
: [{
|
|
8209
|
+
: [{ role: !partial && !blocked && !blockedDependencyFailure ? 'navigation' : undefined,
|
|
8210
|
+
label: partial || blockedDependencyFailure ? '查看失败项' : successfulConfigOperation && configOrigin
|
|
8281
8211
|
? configOrigin.name === 'pluginPreview' ? '返回插件预览'
|
|
8282
8212
|
: configOrigin.name === 'pluginWriteTask' ? '返回恢复任务' : '返回插件详情'
|
|
8283
8213
|
: configOperation ? '查看插件'
|
|
@@ -8317,7 +8247,18 @@ export function ConsoleAppV4(props) {
|
|
|
8317
8247
|
scope: failedTarget.scope,
|
|
8318
8248
|
});
|
|
8319
8249
|
},
|
|
8320
|
-
}] : []), {
|
|
8250
|
+
}] : []), { role: !partial && !blocked ? 'navigation' : undefined,
|
|
8251
|
+
label: '返回首页', run: () => requestSessionNavigation('home') }];
|
|
8252
|
+
if (snapshot.canResumeInstallDraft) {
|
|
8253
|
+
actions.unshift({ label: '返回修改', run: async () => {
|
|
8254
|
+
props.controller.resumeResultDraft();
|
|
8255
|
+
if (props.controller.snapshot.router.current.route.name === 'dependencyPlacement') {
|
|
8256
|
+
await props.controller.prepareInstallDependencyPlacement();
|
|
8257
|
+
}
|
|
8258
|
+
setFocus(0);
|
|
8259
|
+
setScrollOffset(0);
|
|
8260
|
+
} });
|
|
8261
|
+
}
|
|
8321
8262
|
if (failedCleanupTargets.length > 0) {
|
|
8322
8263
|
actions.unshift({
|
|
8323
8264
|
label: '查看清理建议',
|
|
@@ -8428,23 +8369,36 @@ export function ConsoleAppV4(props) {
|
|
|
8428
8369
|
? wrapPortableField('materialization digest', portableReceipt.materializationDigest) : []),
|
|
8429
8370
|
...(resultSummary?.elapsedMs !== undefined ? [`elapsed · ${resultSummary.elapsedMs} ms`] : []),
|
|
8430
8371
|
] : ['当前结果已变化。请返回结果摘要。'];
|
|
8372
|
+
readingText = portableResultDetailOpen ? selectedPortablePair ? [
|
|
8373
|
+
`Skill · ${selectedPortablePair.skill}`,
|
|
8374
|
+
`Agent · ${selectedPortablePair.agent ?? '无法确认'}`,
|
|
8375
|
+
`状态 · ${portablePairStateLabel(selectedPortablePair)}`,
|
|
8376
|
+
...(portableResultScope ? [`范围 · ${scopeLabel(portableResultScope)}`] : []),
|
|
8377
|
+
...(portableReceipt?.recovery ? [`安全恢复 · ${portableReceipt.recovery}`] : []),
|
|
8378
|
+
...(portableReceipt?.failureCode ? [`failure code · ${portableReceipt.failureCode}`] : []),
|
|
8379
|
+
...(portableReceipt ? [`verification missing · ${portableReceipt.missing.length}`,
|
|
8380
|
+
`Skills CLI version · ${portableReceipt.skillsCliVersion}`,
|
|
8381
|
+
`materialization digest · ${portableReceipt.materializationDigest}`] : []),
|
|
8382
|
+
...(resultSummary?.elapsedMs !== undefined ? [`elapsed · ${resultSummary.elapsedMs} ms`] : []),
|
|
8383
|
+
].join('\n') : '当前结果已变化。请返回结果摘要。' : [
|
|
8384
|
+
resultSummary?.message ?? (blocked ? result.message : '操作结果'),
|
|
8385
|
+
...(resultSummary?.messages ?? []).map(productResultMessage),
|
|
8386
|
+
...(resultSummary?.changes ?? []),
|
|
8387
|
+
...(resultSummary?.warnings ?? []),
|
|
8388
|
+
...(resultSummary?.recoveryActions ?? []),
|
|
8389
|
+
...prioritizedTargets.map((target) => `${resultTargetName(target)} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)} · ${operationTargetStateLabel(target.state).label}`),
|
|
8390
|
+
...prioritizedPortablePairs.map((pair) => `${pair.skill} · ${pair.agent ?? 'Agent 未确认'} · ${portablePairStateLabel(pair)}`),
|
|
8391
|
+
...(taskSummary?.rootResults ?? []).map((root) => `${root.pluginId} · ${root.state}`),
|
|
8392
|
+
...(resultSummary?.batch?.rootResults ?? []).map((root) => `${root.pluginId} · ${root.state}`),
|
|
8393
|
+
].join('\n');
|
|
8431
8394
|
const portableResultDetailPageRows = responsiveBudget(5, 10);
|
|
8432
8395
|
const safePortableResultDetailOffset = Math.min(portableResultDetailOffset, Math.max(0, portableResultDetailLines.length - 1));
|
|
8433
8396
|
const visiblePortableResultDetailLines = portableResultDetailLines.slice(safePortableResultDetailOffset, safePortableResultDetailOffset + portableResultDetailPageRows);
|
|
8434
8397
|
if (portableOperation && portableResultDetailOpen) {
|
|
8435
8398
|
title = '当前结果完整字段';
|
|
8436
8399
|
description = '按行显示当前 Skill、Agent、状态和技术证据。';
|
|
8437
|
-
help = '
|
|
8438
|
-
const detailPaginationActions = portableResultDetailLines.length > portableResultDetailPageRows
|
|
8439
|
-
? paginationActions({
|
|
8440
|
-
offset: safePortableResultDetailOffset,
|
|
8441
|
-
total: portableResultDetailLines.length,
|
|
8442
|
-
pageRows: portableResultDetailPageRows,
|
|
8443
|
-
move: (direction) => setPortableResultDetailOffset((value) => Math.min(Math.max(0, portableResultDetailLines.length - 1), Math.max(0, value + direction * portableResultDetailPageRows))),
|
|
8444
|
-
})
|
|
8445
|
-
: [];
|
|
8400
|
+
help = '←→ 阅读全部字段 · ↑↓ 移动 · Enter 选择 · Esc 返回结果摘要';
|
|
8446
8401
|
actions = [
|
|
8447
|
-
...detailPaginationActions,
|
|
8448
8402
|
{ label: '返回结果摘要', run: () => {
|
|
8449
8403
|
setPortableResultDetailOpen(false);
|
|
8450
8404
|
setPortableResultDetailOffset(0);
|
|
@@ -8470,29 +8424,16 @@ export function ConsoleAppV4(props) {
|
|
|
8470
8424
|
setScrollOffset(0);
|
|
8471
8425
|
},
|
|
8472
8426
|
} : undefined;
|
|
8473
|
-
const pagination = resultPaginationTotal > 1 ? paginationActions({
|
|
8474
|
-
offset: batchTargetOffset,
|
|
8475
|
-
total: resultPaginationTotal,
|
|
8476
|
-
pageRows: resultPageRows,
|
|
8477
|
-
move: (direction) => moveBatchTargetPage(resultPaginationTotal, resultPageRows, direction),
|
|
8478
|
-
}) : [];
|
|
8479
8427
|
actions = [
|
|
8480
8428
|
...actions.slice(0, 1),
|
|
8481
|
-
...pagination,
|
|
8482
8429
|
...(detailAction ? [detailAction] : []),
|
|
8483
8430
|
...actions.slice(1),
|
|
8484
8431
|
];
|
|
8485
|
-
if (
|
|
8432
|
+
if (resultPaginationTotal > resultPageRows) {
|
|
8486
8433
|
referenceDetailPagination = { total: resultPaginationTotal, pageRows: resultPageRows };
|
|
8487
8434
|
}
|
|
8488
8435
|
}
|
|
8489
8436
|
else if (resultPaginationTotal > 1) {
|
|
8490
|
-
actions.push(...paginationActions({
|
|
8491
|
-
offset: batchTargetOffset,
|
|
8492
|
-
total: resultPaginationTotal,
|
|
8493
|
-
pageRows: resultPageRows,
|
|
8494
|
-
move: (direction) => moveBatchTargetPage(resultPaginationTotal, resultPageRows, direction),
|
|
8495
|
-
}));
|
|
8496
8437
|
referenceDetailPagination = { total: resultPaginationTotal, pageRows: resultPageRows };
|
|
8497
8438
|
}
|
|
8498
8439
|
const portableTechnicalFieldCount = portableReceipt
|
|
@@ -8586,47 +8527,70 @@ export function ConsoleAppV4(props) {
|
|
|
8586
8527
|
const requestedTargets = taskSummary?.requestedTargets.filter((target) => target.pluginId === root.pluginId) ?? [];
|
|
8587
8528
|
return _jsx(ResultRow, { title: rootName, description: `${requestedTargets.length} 个使用位置`, meta: rootState.label, status: rootState.status, compact: true }, `root:${root.pluginId}`);
|
|
8588
8529
|
});
|
|
8589
|
-
const
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
const key = [
|
|
8530
|
+
const taskLocationItems = taskRootResults.flatMap((root) => {
|
|
8531
|
+
return root.targets.map((target) => {
|
|
8532
|
+
const targetPluginId = target.pluginId ?? root.pluginId;
|
|
8533
|
+
const key = [targetPluginId, target.platform, target.scope].join('\0');
|
|
8593
8534
|
const blockedTarget = blockedTaskTargets.get(key);
|
|
8594
|
-
const targetResult = root.targets.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
|
|
8595
|
-
&& entry.scope === target.scope);
|
|
8596
8535
|
const targetState = blockedTarget ? { label: '待确认', status: 'warning' }
|
|
8597
|
-
:
|
|
8598
|
-
:
|
|
8599
|
-
:
|
|
8536
|
+
: target.state === 'completed' ? { label: '完成', status: 'success' }
|
|
8537
|
+
: target.state === 'kept' ? { label: '无需更改', status: 'success' }
|
|
8538
|
+
: target.state === 'failed' ? { label: '失败', status: 'error' }
|
|
8600
8539
|
: { label: '未开始', status: 'muted' };
|
|
8601
|
-
|
|
8540
|
+
const relation = target.relation === 'direct' ? '直接依赖'
|
|
8541
|
+
: target.relation === 'transitive' ? '间接依赖' : '所选插件位置';
|
|
8542
|
+
return {
|
|
8543
|
+
key: `target:${root.pluginId}:${key}`,
|
|
8544
|
+
title: `${root.pluginId}${targetPluginId === root.pluginId ? '' : ` → ${targetPluginId}`} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
8545
|
+
description: blockedTarget
|
|
8602
8546
|
? pluginWriteTaskBlockLabel(blockedTarget.reason)
|
|
8603
|
-
:
|
|
8547
|
+
: `${relation}${target.errorCode ? ` · ${target.errorCode}` : ''}`,
|
|
8548
|
+
meta: `${targetState.label}${target.errorCode ? ` · ${target.errorCode}` : ''}`,
|
|
8549
|
+
status: targetState.status,
|
|
8550
|
+
compact: !blockedTarget,
|
|
8551
|
+
};
|
|
8604
8552
|
});
|
|
8605
8553
|
});
|
|
8606
|
-
const
|
|
8554
|
+
const taskLocationRows = taskLocationItems.map(({ key, ...item }) => _jsx(ResultRow, { ...item }, key));
|
|
8555
|
+
if (taskOperation)
|
|
8556
|
+
readingText = [
|
|
8557
|
+
resultSummary?.message ?? (blocked ? result.message : '操作结果'),
|
|
8558
|
+
...(resultSummary?.messages ?? []).map(productResultMessage),
|
|
8559
|
+
...(resultSummary?.changes ?? []),
|
|
8560
|
+
...(resultSummary?.warnings ?? []),
|
|
8561
|
+
...(resultSummary?.recoveryActions ?? []),
|
|
8562
|
+
...taskRootResults.map((root) => `${root.pluginId} · ${taskRootState(root.state).label}`),
|
|
8563
|
+
...taskLocationItems.flatMap((item) => [`${item.title} · ${item.meta}`, item.description]),
|
|
8564
|
+
].join('\n');
|
|
8565
|
+
const batchPluginResultRows = (resultSummary?.batch?.rootResults ?? []).map((root) => {
|
|
8607
8566
|
const rootState = taskRootState(root.state);
|
|
8608
8567
|
const rootName = props.controller.getPluginOverview(root.pluginId)?.displayName
|
|
8609
8568
|
?? root.pluginId.split('/').at(-1);
|
|
8610
8569
|
return _jsx(ResultRow, { title: rootName, description: `${root.targets.length} 个使用位置`, meta: rootState.label, status: rootState.status, compact: true }, `batch-root:${root.pluginId}`);
|
|
8611
|
-
})
|
|
8570
|
+
});
|
|
8571
|
+
const batchLocationResultRows = prioritizedTargets.map((target) => {
|
|
8612
8572
|
const state = operationTargetStateLabel(target.state);
|
|
8613
8573
|
const relation = target.relation === 'requested' ? '你选择的插件'
|
|
8614
8574
|
: target.relation === 'direct' ? '直接依赖'
|
|
8615
8575
|
: target.relation === 'transitive' ? '间接依赖' : undefined;
|
|
8616
8576
|
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}`);
|
|
8617
|
-
})
|
|
8577
|
+
});
|
|
8618
8578
|
const taskResultLimit = Math.max(1, pageActionBudget(5) - resultActionViewport.limit);
|
|
8619
|
-
const
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
const taskResultOffset = Math.min(batchTargetOffset, Math.max(0,
|
|
8623
|
-
const
|
|
8579
|
+
const pluginResultRows = taskOperation ? taskSelectedPluginRows : batchPluginResultRows;
|
|
8580
|
+
const locationResultRows = taskOperation ? taskLocationRows : batchLocationResultRows;
|
|
8581
|
+
const combinedResultRows = [...pluginResultRows, ...locationResultRows];
|
|
8582
|
+
const taskResultOffset = Math.min(batchTargetOffset, Math.max(0, combinedResultRows.length - 1));
|
|
8583
|
+
const visiblePluginResultRows = taskResultOffset < pluginResultRows.length
|
|
8584
|
+
? pluginResultRows.slice(taskResultOffset, taskResultOffset + taskResultLimit) : [];
|
|
8585
|
+
const visibleLocationStart = Math.max(0, taskResultOffset - pluginResultRows.length);
|
|
8586
|
+
const remainingResultRows = taskResultLimit - visiblePluginResultRows.length;
|
|
8587
|
+
const visibleLocationResultRows = locationResultRows.slice(visibleLocationStart, visibleLocationStart + remainingResultRows);
|
|
8624
8588
|
if (taskOperation || batchOperation || resultTargets.length > resultTargetLimit
|
|
8625
8589
|
|| prioritizedPortablePairs.length > portableSkillLimit) {
|
|
8626
|
-
help = '
|
|
8590
|
+
help = '←→ 查看全部项目 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
8627
8591
|
}
|
|
8628
|
-
content = taskOperation || batchOperation ? _jsxs(Box, { flexDirection: "column", children: [
|
|
8629
|
-
|
|
8592
|
+
content = taskOperation || batchOperation ? _jsxs(Box, { flexDirection: "column", children: [taskResultOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${taskResultOffset} 项`, status: "muted", compact: true }) : null, visiblePluginResultRows.length > 0 ? _jsx(ResultCard, { status: resultStatus, title: "\u63D2\u4EF6\u7ED3\u679C", meta: `${pluginResultRows.length} 个插件`, children: visiblePluginResultRows }) : null, visibleLocationResultRows.length > 0 ? _jsx(ResultCard, { status: resultStatus, title: "\u4F7F\u7528\u4F4D\u7F6E\u4E0E\u6240\u9700\u63D2\u4EF6", meta: `${locationResultRows.length} 项`, children: visibleLocationResultRows }) : null, combinedResultRows.length > taskResultOffset + visiblePluginResultRows.length + visibleLocationResultRows.length
|
|
8593
|
+
? _jsx(StaticRow, { title: "\u2193 \u540E\u9762\u8FD8\u6709", meta: `${combinedResultRows.length - taskResultOffset - visiblePluginResultRows.length - visibleLocationResultRows.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: [portableRecheckBusy
|
|
8630
8594
|
? _jsx(ProgressRow, { title: "\u6B63\u5728\u91CD\u65B0\u68C0\u67E5\u7ED3\u679C", description: "\u53EA\u8BFB\u53D6 Skills CLI \u5F53\u524D\u72B6\u6001\uFF0C\u4E0D\u6267\u884C\u5B89\u88C5", state: "active", compact: policy.mode === 'compact' })
|
|
8631
8595
|
: null, portableResultDetailOpen ? _jsxs(_Fragment, { children: [safePortableResultDetailOffset > 0
|
|
8632
8596
|
? _jsx(StaticRow, { title: `↑ 前面还有 ${safePortableResultDetailOffset} 行`, status: "muted", compact: true }) : null, visiblePortableResultDetailLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${safePortableResultDetailOffset + index}:${line}`)), safePortableResultDetailOffset + visiblePortableResultDetailLines.length
|
|
@@ -8653,7 +8617,6 @@ export function ConsoleAppV4(props) {
|
|
|
8653
8617
|
? '这里显示已验证目录包声明的组成,不读取或展示完整操作指令。'
|
|
8654
8618
|
: '当前没有可安全读取的目录内容。';
|
|
8655
8619
|
const contentNavigationActions = [
|
|
8656
|
-
{ label: '返回插件详情', run: back },
|
|
8657
8620
|
{ label: '查看技术详情', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
|
|
8658
8621
|
];
|
|
8659
8622
|
const contentTypeLabels = {
|
|
@@ -8665,41 +8628,27 @@ export function ConsoleAppV4(props) {
|
|
|
8665
8628
|
const versionDiffers = contentAvailable
|
|
8666
8629
|
&& installedVersions.some((version) => version !== overview.pluginVersion);
|
|
8667
8630
|
const items = overview?.items ?? [];
|
|
8668
|
-
const
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
}
|
|
8688
|
-
const contentViewport = actionViewport(actions, true, focus, scrollOffset, pageActionBudget(Math.min(items.length, pageRows) * 5 + 2));
|
|
8689
|
-
content = _jsxs(Box, { flexDirection: "column", children: [contentAvailable ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u7248\u672C\u8BC1\u636E", meta: `插件 ${overview.pluginVersion} · 目录 ${overview.catalogVersion}`, compact: true }), _jsx(StaticRow, { title: installedVersions.length > 0
|
|
8690
|
-
? `已安装 ${installedVersions.join('、')}`
|
|
8691
|
-
: '当前没有已确认的安装版本', description: versionDiffers
|
|
8692
|
-
? '以下是目录版本内容,不代表旧安装版本的内容。'
|
|
8693
|
-
: '内容来自当前已验证的目录包。', status: versionDiffers ? 'warning' : 'info', compact: true })] }) : contentState === 'loading'
|
|
8631
|
+
const itemActions = items.map((item) => ({
|
|
8632
|
+
actionId: `content:${item.id}`,
|
|
8633
|
+
label: item.name,
|
|
8634
|
+
description: `${contentTypeLabels[item.type]} · ${item.tools.join('、') || '适用工具待确认'} · ${item.description ?? '目录没有提供简短描述'}`,
|
|
8635
|
+
status: item.description ? undefined : 'warning',
|
|
8636
|
+
}));
|
|
8637
|
+
actions = [...itemActions, ...contentNavigationActions];
|
|
8638
|
+
const fixedContentRows = contentAvailable ? 4 : 3;
|
|
8639
|
+
currentActionViewportLineBudget = pageActionBudget(fixedContentRows + (overview?.notices.length ?? 0));
|
|
8640
|
+
const contentViewport = actionViewport(actions, false, focus, scrollOffset, currentActionViewportLineBudget);
|
|
8641
|
+
if (items.length > contentViewport.limit) {
|
|
8642
|
+
context = `${items.length} 项内容 · 上下键滚动`;
|
|
8643
|
+
help = '↑↓ 阅读全部内容 · Enter 选择 · Esc 返回';
|
|
8644
|
+
}
|
|
8645
|
+
content = _jsxs(Box, { flexDirection: "column", children: [contentAvailable ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: "\u76EE\u5F55\u7248\u672C", meta: `${overview.pluginVersion} · Catalog ${overview.catalogVersion}`, compact: true }), _jsx(StaticRow, { title: installedVersions.length > 0
|
|
8646
|
+
? `安装版本 ${installedVersions.join('、')}`
|
|
8647
|
+
: '安装版本尚未确认', description: versionDiffers
|
|
8648
|
+
? '以下清单属于目录版本,不代表旧安装版本的内容。'
|
|
8649
|
+
: '以下清单来自当前已验证的目录包。', status: versionDiffers ? 'warning' : 'info', compact: true })] }) : contentState === 'loading'
|
|
8694
8650
|
? _jsx(Callout, { status: "info", title: "\u6B63\u5728\u8BFB\u53D6\u5185\u5BB9", description: "\u5B8C\u6210\u540E\u5C06\u5728\u5F53\u524D\u9875\u9762\u663E\u793A\u3002" })
|
|
8695
|
-
: _jsx(Callout, { status: "warning", title: "\u5185\u5BB9\u6682\u4E0D\u53EF\u7528", description: "\u53EF\u4EE5\u67E5\u770B\u6280\u672F\u8BE6\u60C5\u6216\u6062\u590D\u63D2\u4EF6\u6765\u6E90\u540E\u91CD\u8BD5\u3002" }),
|
|
8696
|
-
}
|
|
8697
|
-
else if (route.name === 'pluginAbout' && plugin) {
|
|
8698
|
-
breadcrumb = `${plugin.displayName} / 关于`;
|
|
8699
|
-
title = plugin.displayName;
|
|
8700
|
-
description = plugin.description;
|
|
8701
|
-
actions = [{ label: '查看技术详情', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) }, { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) }];
|
|
8702
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(SectionCard, { children: [_jsx(StaticRow, { title: "\u7248\u672C", meta: plugin.version ?? '未知' }), _jsx(StaticRow, { title: "\u6765\u6E90", meta: plugin.sourceLabel }), _jsx(StaticRow, { title: "\u53EF\u7528\u76EE\u6807\u5E73\u53F0", meta: plugin.supportedPlatforms.map(platformLabel).join('、') }), _jsx(StaticRow, { title: "\u914D\u7F6E", meta: `${plugin.configFieldCount} 个字段` })] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact' })] });
|
|
8651
|
+
: _jsx(Callout, { status: "warning", title: "\u5185\u5BB9\u6682\u4E0D\u53EF\u7528", description: "\u53EF\u4EE5\u67E5\u770B\u6280\u672F\u8BE6\u60C5\u6216\u6062\u590D\u63D2\u4EF6\u6765\u6E90\u540E\u91CD\u8BD5\u3002" }), overview?.notices.map((notice, index) => _jsx(StaticRow, { title: notice, status: "warning", compact: true }, `${index}:${notice}`)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: false, offset: contentViewport.offset, limit: contentViewport.limit })] });
|
|
8703
8652
|
}
|
|
8704
8653
|
else if (route.name === 'pluginTechnical' && plugin) {
|
|
8705
8654
|
breadcrumb = `${plugin.displayName} / 技术详情`;
|
|
@@ -8708,32 +8657,35 @@ export function ConsoleAppV4(props) {
|
|
|
8708
8657
|
description = plugin.capabilityUnsupported
|
|
8709
8658
|
? '当前 AW CLI 不支持此插件所需 capability。'
|
|
8710
8659
|
: '默认页面不会展示这些内部信息。';
|
|
8711
|
-
actions = [{ label: '返回插件', run: back }];
|
|
8660
|
+
actions = [{ role: 'navigation', label: '返回插件', run: back }];
|
|
8712
8661
|
const prioritizedComponents = [...plugin.technicalComponents].sort((left, right) => {
|
|
8713
8662
|
const priority = (state) => state === 'error' ? 0
|
|
8714
8663
|
: state === 'partial' ? 1 : state === 'absent' ? 2 : 3;
|
|
8715
8664
|
return priority(left.state) - priority(right.state);
|
|
8716
8665
|
});
|
|
8717
|
-
const
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
?
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8666
|
+
const technicalFields = [
|
|
8667
|
+
`canonical pluginId · ${plugin.pluginId}`,
|
|
8668
|
+
`来源 · ${plugin.sourceId}`,
|
|
8669
|
+
...prioritizedComponents.map((component) => `组件 · ${component.state === 'absent' ? '缺失' : component.state}${component.errorCode ? ` · ${component.errorCode}` : ''} · ${component.key} · ${platformLabel(component.platform)} · ${component.shared ? '平台共享' : scopeLabel(component.scope)}`),
|
|
8670
|
+
...(plugin.capabilityUnsupported ? [
|
|
8671
|
+
'failure code · awCapabilityUnsupported',
|
|
8672
|
+
'capability 状态 · 当前 AW CLI 不支持',
|
|
8673
|
+
`缺少 capability · ${plugin.missingCapabilities?.join('、') ?? 'Status 未提供具体 capability'}`,
|
|
8674
|
+
`恢复方向 · ${plugin.capabilityRecovery ?? '升级 AW CLI 到支持所需 capability 的版本后重试。'}`,
|
|
8675
|
+
] : []),
|
|
8676
|
+
...plugin.installedPlacements.map((placement) => `${platformLabel(placement.platform)} target · ${placementStatus(placement).value}`),
|
|
8677
|
+
];
|
|
8678
|
+
readingText = technicalFields.join('\n');
|
|
8679
|
+
const technicalLines = technicalFields.flatMap((field) => wrapDisplayText(field, size.columns - 6));
|
|
8680
|
+
const technicalPageRows = Math.max(1, pageActionBudget(7));
|
|
8681
|
+
const technicalOffset = Math.min(batchTargetOffset, Math.max(0, technicalLines.length - 1));
|
|
8682
|
+
const visibleTechnicalLines = technicalLines.slice(technicalOffset, technicalOffset + technicalPageRows);
|
|
8683
|
+
if (technicalLines.length > technicalPageRows) {
|
|
8684
|
+
referenceDetailPagination = { total: technicalLines.length, pageRows: technicalPageRows };
|
|
8685
|
+
help = '←→ 或 PgUp/PgDn 阅读技术字段 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
8735
8686
|
}
|
|
8736
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [
|
|
8687
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(TechnicalDetails, { children: [technicalOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${technicalOffset} 行`, status: "muted", compact: true }) : null, visibleTechnicalLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${technicalOffset + index}:${line}`)), technicalOffset + visibleTechnicalLines.length < technicalLines.length
|
|
8688
|
+
? _jsx(StaticRow, { title: `↓ 后面还有 ${technicalLines.length - technicalOffset - visibleTechnicalLines.length} 行`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
8737
8689
|
}
|
|
8738
8690
|
else {
|
|
8739
8691
|
const missingPlugin = definition.entity === 'plugin' && plugin === undefined;
|
|
@@ -8759,13 +8711,12 @@ export function ConsoleAppV4(props) {
|
|
|
8759
8711
|
case 'pluginContent':
|
|
8760
8712
|
case 'pluginMore':
|
|
8761
8713
|
case 'platform':
|
|
8762
|
-
case 'pluginAbout':
|
|
8763
8714
|
case 'pluginTechnical':
|
|
8764
8715
|
case 'configLayer':
|
|
8765
8716
|
unavailable = missingPlugin ? 'plugin' : 'flow';
|
|
8766
8717
|
break;
|
|
8718
|
+
case 'portablePreparing':
|
|
8767
8719
|
case 'portableSkillSelect':
|
|
8768
|
-
case 'portableSkillSummary':
|
|
8769
8720
|
case 'portableDependencies':
|
|
8770
8721
|
case 'portableAgentMode':
|
|
8771
8722
|
case 'portableAgentInput':
|
|
@@ -8833,6 +8784,51 @@ export function ConsoleAppV4(props) {
|
|
|
8833
8784
|
: 'h/H 完成后导航'} · Esc 撤销等待 · ${help}`
|
|
8834
8785
|
: `h 首页 · H 重载 · ${help}`;
|
|
8835
8786
|
}
|
|
8787
|
+
const helpParts = help.split(' · ');
|
|
8788
|
+
const isRoutineKey = (part) => /^(↑|Enter|Space|Esc|Ctrl-C|←→)/u.test(part);
|
|
8789
|
+
help = [...helpParts.filter(isRoutineKey), ...helpParts.filter((part) => !isRoutineKey(part))].join(' · ');
|
|
8790
|
+
if (searchFocus)
|
|
8791
|
+
help = '←→ 光标 · ↑↓ 移动结果 · Enter/Esc 完成输入';
|
|
8792
|
+
else if (bottomActions.length > 0 && actions.length > 8)
|
|
8793
|
+
help += ' · a 下方操作';
|
|
8794
|
+
useEffect(() => {
|
|
8795
|
+
if ((bottomActions.length > 0 || route.name === 'issueList' || route.name === 'operationHistory')
|
|
8796
|
+
&& focus >= actions.length && actions.length > 0)
|
|
8797
|
+
setFocus(actions.length - 1);
|
|
8798
|
+
else if (actions[focus]?.role === 'navigation')
|
|
8799
|
+
setFocus(moveFocus(actions, focus, 1));
|
|
8800
|
+
}, [routeKey, focus, actions.length, actions.map((action) => action.role ?? '').join(',')]);
|
|
8801
|
+
const listCandidates = route.name === 'pluginList'
|
|
8802
|
+
|| route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8803
|
+
? actions.filter((action) => action.pluginId !== undefined) : [];
|
|
8804
|
+
const listCandidateSignature = listCandidates.map((action) => action.pluginId).join('\0');
|
|
8805
|
+
useEffect(() => {
|
|
8806
|
+
const pluginId = listCandidates[focus]?.pluginId;
|
|
8807
|
+
if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8808
|
+
&& !batchDraft.showSelectionTools && pluginId) {
|
|
8809
|
+
batchSelectionAnchor.current = {
|
|
8810
|
+
frameId: snapshot.router.currentFrameId, pluginId, index: focus, scrollOffset,
|
|
8811
|
+
};
|
|
8812
|
+
}
|
|
8813
|
+
}, [snapshot.router.currentFrameId, route.name, batchDraft?.stage,
|
|
8814
|
+
batchDraft?.showSelectionTools, listCandidateSignature, focus, scrollOffset]);
|
|
8815
|
+
useEffect(() => {
|
|
8816
|
+
if (route.name !== 'pluginList' && !(route.name === 'batchSelection' && batchDraft?.stage === 'select')) {
|
|
8817
|
+
listFocusAnchor.current = undefined;
|
|
8818
|
+
return;
|
|
8819
|
+
}
|
|
8820
|
+
const previous = listFocusAnchor.current;
|
|
8821
|
+
let nextFocus = focus;
|
|
8822
|
+
if (previous?.frameId === snapshot.router.currentFrameId && previous.ids !== listCandidateSignature
|
|
8823
|
+
&& previous.entityId) {
|
|
8824
|
+
const retained = listCandidates.findIndex((action) => action.pluginId === previous.entityId);
|
|
8825
|
+
nextFocus = retained >= 0 ? retained : Math.min(focus, Math.max(0, listCandidates.length - 1));
|
|
8826
|
+
if (nextFocus !== focus)
|
|
8827
|
+
setFocus(nextFocus);
|
|
8828
|
+
}
|
|
8829
|
+
listFocusAnchor.current = { frameId: snapshot.router.currentFrameId,
|
|
8830
|
+
ids: listCandidateSignature, entityId: listCandidates[nextFocus]?.pluginId };
|
|
8831
|
+
}, [snapshot.router.currentFrameId, route.name, batchDraft?.stage, listCandidateSignature, focus]);
|
|
8836
8832
|
const targetActionSignature = route.name === 'installTargets' && route.view !== 'blocked'
|
|
8837
8833
|
? actions.map((action) => String(Boolean(action.disabled))).join(',')
|
|
8838
8834
|
: '';
|
|
@@ -8895,7 +8891,7 @@ export function ConsoleAppV4(props) {
|
|
|
8895
8891
|
};
|
|
8896
8892
|
}, [dynamicActionSignature, routeKey]);
|
|
8897
8893
|
const runAction = (action) => {
|
|
8898
|
-
if (!action || action.disabled)
|
|
8894
|
+
if (!action || action.disabled || action.role === 'navigation')
|
|
8899
8895
|
return;
|
|
8900
8896
|
try {
|
|
8901
8897
|
const pending = action.run?.();
|
|
@@ -9009,10 +9005,19 @@ export function ConsoleAppV4(props) {
|
|
|
9009
9005
|
}
|
|
9010
9006
|
if (busy) {
|
|
9011
9007
|
const sessionNavigation = resolveTuiSessionNavigationKey({ input, key }, { characterInput: false });
|
|
9012
|
-
if ((key.pageUp || key.pageDown
|
|
9008
|
+
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9009
|
+
&& route.name === 'progress' && !overlay) {
|
|
9013
9010
|
const total = snapshot.progressTargets.length;
|
|
9014
9011
|
const pageRows = 1;
|
|
9015
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
9012
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp || key.leftArrow ? -pageRows : pageRows))));
|
|
9013
|
+
}
|
|
9014
|
+
else if (!overlay && input.toLowerCase() === 'y' && readingText !== undefined) {
|
|
9015
|
+
const currentRouteKey = routeKey;
|
|
9016
|
+
void copyToClipboard(readingText).then(() => {
|
|
9017
|
+
setCopyFeedback({ routeKey: currentRouteKey, message: '全文已复制' });
|
|
9018
|
+
}).catch(() => {
|
|
9019
|
+
setCopyFeedback({ routeKey: currentRouteKey, message: '复制失败,请重试' });
|
|
9020
|
+
});
|
|
9016
9021
|
}
|
|
9017
9022
|
else if (overlay === 'help') {
|
|
9018
9023
|
if (key.escape || key.return || input.toLowerCase() === 'b' || input === '?') {
|
|
@@ -9118,31 +9123,31 @@ export function ConsoleAppV4(props) {
|
|
|
9118
9123
|
closeConfirmation();
|
|
9119
9124
|
requestSessionNavigation(navigation);
|
|
9120
9125
|
}
|
|
9121
|
-
else if ((
|
|
9122
|
-
|
|
9126
|
+
else if (input.toLowerCase() === 'y' && confirmationText !== undefined) {
|
|
9127
|
+
const currentRouteKey = `${routeKey}:confirmation`;
|
|
9128
|
+
void copyToClipboard(confirmationText).then(() => {
|
|
9129
|
+
setCopyFeedback({ routeKey: currentRouteKey, message: '全文已复制' });
|
|
9130
|
+
}).catch(() => {
|
|
9131
|
+
setCopyFeedback({ routeKey: currentRouteKey, message: '复制失败,请重试' });
|
|
9132
|
+
});
|
|
9133
|
+
}
|
|
9134
|
+
else if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9123
9135
|
&& confirmationPagination) {
|
|
9124
9136
|
const warningCount = confirmationPagination.total;
|
|
9125
9137
|
const warningLimit = confirmationPagination.pageRows;
|
|
9126
9138
|
setOperationConfirmation((current) => current ? {
|
|
9127
9139
|
...current,
|
|
9128
9140
|
warningOffset: Math.min(Math.max(0, warningCount - 1), Math.max(0, current.warningOffset
|
|
9129
|
-
+ (key.pageUp ||
|
|
9141
|
+
+ (key.pageUp || key.leftArrow ? -warningLimit : warningLimit))),
|
|
9130
9142
|
} : current);
|
|
9131
9143
|
}
|
|
9132
9144
|
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
9133
|
-
setOverlayFocus((value) => moveIndex(
|
|
9145
|
+
setOverlayFocus((value) => moveIndex(2, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
9134
9146
|
}
|
|
9135
9147
|
else if (key.return) {
|
|
9136
9148
|
if (overlayFocus === 0) {
|
|
9137
9149
|
closeConfirmation();
|
|
9138
9150
|
}
|
|
9139
|
-
else if (confirmationPagination && (overlayFocus === 2 || overlayFocus === 3)) {
|
|
9140
|
-
const direction = overlayFocus === 2 ? -1 : 1;
|
|
9141
|
-
setOperationConfirmation((current) => current ? {
|
|
9142
|
-
...current,
|
|
9143
|
-
warningOffset: Math.min(Math.max(0, confirmationPagination.total - 1), Math.max(0, current.warningOffset + direction * confirmationPagination.pageRows)),
|
|
9144
|
-
} : current);
|
|
9145
|
-
}
|
|
9146
9151
|
else if (activeReview?.reviewToken !== operationConfirmation.reviewToken) {
|
|
9147
9152
|
closeConfirmation();
|
|
9148
9153
|
setActionError('确认内容已变化。请重新检查操作汇总。');
|
|
@@ -9194,35 +9199,6 @@ export function ConsoleAppV4(props) {
|
|
|
9194
9199
|
}
|
|
9195
9200
|
return;
|
|
9196
9201
|
}
|
|
9197
|
-
if (pluginListChooser) {
|
|
9198
|
-
const sourceIds = ['all', ...props.controller.listSources().map((source) => source.id)];
|
|
9199
|
-
const itemCount = pluginListChooser.kind === 'source' ? sourceIds.length : PLUGIN_FILTERS.length;
|
|
9200
|
-
if (key.escape) {
|
|
9201
|
-
setPluginListChooser(undefined);
|
|
9202
|
-
}
|
|
9203
|
-
else if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
9204
|
-
const direction = key.upArrow || key.leftArrow ? -1 : 1;
|
|
9205
|
-
setPluginListChooser((current) => current ? {
|
|
9206
|
-
...current,
|
|
9207
|
-
focus: moveIndex(itemCount, current.focus, direction),
|
|
9208
|
-
} : current);
|
|
9209
|
-
}
|
|
9210
|
-
else if (key.return) {
|
|
9211
|
-
const selectedIndex = Math.min(pluginListChooser.focus, Math.max(0, itemCount - 1));
|
|
9212
|
-
if (pluginListChooser.kind === 'source') {
|
|
9213
|
-
const selectedSourceId = sourceIds[selectedIndex];
|
|
9214
|
-
if (selectedSourceId)
|
|
9215
|
-
choosePluginListSource?.(selectedSourceId);
|
|
9216
|
-
}
|
|
9217
|
-
else {
|
|
9218
|
-
const selectedFilter = PLUGIN_FILTERS[selectedIndex];
|
|
9219
|
-
if (selectedFilter)
|
|
9220
|
-
choosePluginListFilter?.(selectedFilter);
|
|
9221
|
-
}
|
|
9222
|
-
setPluginListChooser(undefined);
|
|
9223
|
-
}
|
|
9224
|
-
return;
|
|
9225
|
-
}
|
|
9226
9202
|
if (overlay) {
|
|
9227
9203
|
if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
9228
9204
|
if (overlay === 'sessionNavigation')
|
|
@@ -9341,57 +9317,63 @@ export function ConsoleAppV4(props) {
|
|
|
9341
9317
|
setReportSaveError(undefined);
|
|
9342
9318
|
return;
|
|
9343
9319
|
}
|
|
9344
|
-
if (
|
|
9320
|
+
if (input.toLowerCase() === 'y' && readingText !== undefined) {
|
|
9321
|
+
const currentRouteKey = routeKey;
|
|
9322
|
+
void copyToClipboard(readingText).then(() => {
|
|
9323
|
+
setCopyFeedback({ routeKey: currentRouteKey, message: '全文已复制' });
|
|
9324
|
+
}).catch(() => {
|
|
9325
|
+
setCopyFeedback({ routeKey: currentRouteKey, message: '复制失败,请重试' });
|
|
9326
|
+
});
|
|
9327
|
+
return;
|
|
9328
|
+
}
|
|
9329
|
+
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9345
9330
|
&& route.name === 'result' && portableResultDetailOpen
|
|
9346
9331
|
&& portableResultDetailPagination) {
|
|
9347
9332
|
const { total, pageRows } = portableResultDetailPagination;
|
|
9348
|
-
const direction = key.pageUp ||
|
|
9333
|
+
const direction = key.pageUp || key.leftArrow ? -1 : 1;
|
|
9349
9334
|
setPortableResultDetailOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + direction * pageRows)));
|
|
9350
9335
|
return;
|
|
9351
9336
|
}
|
|
9352
|
-
if ((key.pageUp || key.pageDown ||
|
|
9337
|
+
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9338
|
+
&& route.name === 'portableDependencies' && plugin) {
|
|
9339
|
+
const total = dependencyOverview(plugin)?.dependencies.length ?? 0;
|
|
9340
|
+
const pageRows = showTechnical ? 1 : responsiveBudget(2, 3);
|
|
9341
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp || key.leftArrow ? -pageRows : pageRows))));
|
|
9342
|
+
return;
|
|
9343
|
+
}
|
|
9344
|
+
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9353
9345
|
&& (route.name === 'pluginContent'
|
|
9354
9346
|
|| route.name === 'pluginTechnical'
|
|
9355
9347
|
|| route.name === 'projectStatus' && showTechnical
|
|
9356
9348
|
|| route.name === 'issueDetail' && showTechnical
|
|
9349
|
+
|| route.name === 'portableBlocker' && showTechnical
|
|
9350
|
+
|| route.name === 'configUnavailable' && showTechnical
|
|
9357
9351
|
|| route.name === 'sourceRemoveBlocked' && sourceDependenciesExpanded
|
|
9358
9352
|
|| route.name === 'pluginDependencies' && route.view === 'removeBlocked' && !showTechnical
|
|
9359
9353
|
|| route.name === 'pluginDependencyNode'
|
|
9360
|
-
&& (route.view === 'references' || route.view === 'details')
|
|
9361
9354
|
|| route.name === 'pluginDetail'
|
|
9362
9355
|
|| route.name === 'configSave' && route.view === 'changes'
|
|
9356
|
+
|| route.name === 'configFields' && route.view === 'values'
|
|
9363
9357
|
|| route.name === 'sourceSave' && route.view === 'changes'
|
|
9364
9358
|
|| route.name === 'review'
|
|
9365
9359
|
|| route.name === 'result')
|
|
9366
9360
|
&& referenceDetailPagination) {
|
|
9367
9361
|
const { total, pageRows } = referenceDetailPagination;
|
|
9368
|
-
moveBatchTargetPage(total, pageRows, key.pageUp ||
|
|
9362
|
+
moveBatchTargetPage(total, pageRows, key.pageUp || key.leftArrow ? -1 : 1);
|
|
9369
9363
|
return;
|
|
9370
9364
|
}
|
|
9371
|
-
if ((key.pageUp || key.pageDown)
|
|
9365
|
+
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9372
9366
|
&& route.name === 'pluginWriteTask') {
|
|
9373
9367
|
const total = props.controller.listPluginWriteTaskCandidates(route.goal)
|
|
9374
9368
|
.filter((candidate) => candidate.target.pluginId === route.pluginId
|
|
9375
9369
|
&& (!route.targetKeys || route.targetKeys.includes(pluginWriteTaskTargetKey(candidate)))).length;
|
|
9376
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -1 : 1))));
|
|
9370
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp || key.leftArrow ? -1 : 1))));
|
|
9377
9371
|
return;
|
|
9378
9372
|
}
|
|
9379
|
-
if ((key.pageUp || key.pageDown
|
|
9380
|
-
const total = plugin.technicalComponents.length;
|
|
9381
|
-
const pageRows = 1;
|
|
9382
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - 1), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
9383
|
-
return;
|
|
9384
|
-
}
|
|
9385
|
-
if ((key.pageUp || key.pageDown) && route.name === 'issueDetail' && issue && showTechnical) {
|
|
9386
|
-
const total = diagnosticDetailRowCount(issue.diagnostic);
|
|
9387
|
-
const pageRows = responsiveBudget(5, 10);
|
|
9388
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp ? -pageRows : pageRows))));
|
|
9389
|
-
return;
|
|
9390
|
-
}
|
|
9391
|
-
if ((key.pageUp || key.pageDown || input.toLowerCase() === 'p' || input.toLowerCase() === 'n')
|
|
9373
|
+
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9392
9374
|
&& route.name === 'sourceTechnical') {
|
|
9393
9375
|
const total = diagnosticDetailLines(resolveSourceTechnicalDiagnostic(route.sourceId), size.columns - 6).length;
|
|
9394
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - sourceTechnicalPageRows), Math.max(0, value + (key.pageUp ||
|
|
9376
|
+
setBatchTargetOffset((value) => Math.min(Math.max(0, total - sourceTechnicalPageRows), Math.max(0, value + (key.pageUp || key.leftArrow
|
|
9395
9377
|
? -sourceTechnicalPageRows : sourceTechnicalPageRows))));
|
|
9396
9378
|
return;
|
|
9397
9379
|
}
|
|
@@ -9415,18 +9397,16 @@ export function ConsoleAppV4(props) {
|
|
|
9415
9397
|
return;
|
|
9416
9398
|
}
|
|
9417
9399
|
if ((route.name === 'operationDiagnostic' || route.name === 'operationReportPreview')
|
|
9418
|
-
&& (key.pageUp || key.pageDown ||
|
|
9400
|
+
&& (key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)) {
|
|
9419
9401
|
const detail = route.name === 'operationDiagnostic'
|
|
9420
9402
|
? props.controller.getOperationDiagnostic(route.recordId) : undefined;
|
|
9421
9403
|
const preview = route.name === 'operationReportPreview'
|
|
9422
9404
|
? props.controller.previewOperationReport(route.recordId) : undefined;
|
|
9423
|
-
const total = detail ? operationDiagnosticLineCount(detail)
|
|
9424
|
-
: preview ? operationReportLineCount(preview) : 0;
|
|
9425
|
-
const
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
: visibleRows;
|
|
9429
|
-
setScrollOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp || input.toLowerCase() === 'p' ? -pageRows : pageRows))));
|
|
9405
|
+
const total = detail ? operationDiagnosticLineCount(detail, size.columns)
|
|
9406
|
+
: preview ? operationReportLineCount(preview, size.columns) : 0;
|
|
9407
|
+
const pageRows = route.name === 'operationReportPreview'
|
|
9408
|
+
? operationReportPageRows : operationDetailPageRows;
|
|
9409
|
+
setScrollOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp || key.leftArrow ? -pageRows : pageRows))));
|
|
9430
9410
|
return;
|
|
9431
9411
|
}
|
|
9432
9412
|
if (route.name === 'portableAgentInput') {
|
|
@@ -9575,9 +9555,18 @@ export function ConsoleAppV4(props) {
|
|
|
9575
9555
|
setSourceInputError(undefined);
|
|
9576
9556
|
return;
|
|
9577
9557
|
}
|
|
9558
|
+
if (route.name === 'configFieldEditor' && activeField && key.ctrl && input.toLowerCase() === 'r') {
|
|
9559
|
+
const canUnset = ['project', 'user'].some((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
9560
|
+
.some((field) => field.key === activeField.key && configFieldCanUnset(field)));
|
|
9561
|
+
if (canUnset)
|
|
9562
|
+
removeDraft(activeField.key);
|
|
9563
|
+
return;
|
|
9564
|
+
}
|
|
9578
9565
|
if (route.name === 'configFieldEditor'
|
|
9579
9566
|
&& (route.editor === 'string' || route.editor === 'number')
|
|
9580
|
-
&& ((activeField?.choices?.length ?? 0) === 0 || route.mode === 'custom')
|
|
9567
|
+
&& ((activeField?.choices?.length ?? 0) === 0 || route.mode === 'custom')
|
|
9568
|
+
&& ['project', 'user'].some((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
9569
|
+
.some((field) => field.key === activeField?.key && configFieldCanSet(field)))) {
|
|
9581
9570
|
if (key.escape) {
|
|
9582
9571
|
setConfigCandidate(undefined);
|
|
9583
9572
|
back();
|
|
@@ -9595,7 +9584,11 @@ export function ConsoleAppV4(props) {
|
|
|
9595
9584
|
}
|
|
9596
9585
|
let candidate = inputValue;
|
|
9597
9586
|
if (route.editor === 'number') {
|
|
9598
|
-
|
|
9587
|
+
if (parseConfigNumberInput(inputValue) === undefined) {
|
|
9588
|
+
setInputError('请输入数字;空输入不会保存为 0。');
|
|
9589
|
+
return;
|
|
9590
|
+
}
|
|
9591
|
+
candidate = parseConfigNumberInput(inputValue);
|
|
9599
9592
|
}
|
|
9600
9593
|
const issues = validatePluginConfigValue(activeField.definition, candidate, activeField.key);
|
|
9601
9594
|
if (issues.length > 0) {
|
|
@@ -9668,9 +9661,12 @@ export function ConsoleAppV4(props) {
|
|
|
9668
9661
|
back();
|
|
9669
9662
|
return;
|
|
9670
9663
|
}
|
|
9664
|
+
if (activePropertyDefinition.type === 'number' && parseConfigNumberInput(inputValue) === undefined) {
|
|
9665
|
+
setInputError('请输入数字;空输入不会保存为 0。');
|
|
9666
|
+
return;
|
|
9667
|
+
}
|
|
9671
9668
|
const candidate = activePropertyDefinition.type === 'number'
|
|
9672
|
-
?
|
|
9673
|
-
: inputValue;
|
|
9669
|
+
? parseConfigNumberInput(inputValue) : inputValue;
|
|
9674
9670
|
if (updateObjectProperty(route.propertyKey, candidate))
|
|
9675
9671
|
back();
|
|
9676
9672
|
return;
|
|
@@ -9683,197 +9679,82 @@ export function ConsoleAppV4(props) {
|
|
|
9683
9679
|
return;
|
|
9684
9680
|
}
|
|
9685
9681
|
if (searchFocus && (route.name === 'pluginList' || route.name === 'batchSelection')) {
|
|
9686
|
-
|
|
9682
|
+
const currentQuery = route.name === 'pluginList' ? query : batchDraft?.query ?? '';
|
|
9683
|
+
const parts = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(currentQuery)]
|
|
9684
|
+
.map((part) => part.segment);
|
|
9685
|
+
const cursor = Math.min(searchCursor, parts.length);
|
|
9686
|
+
if (key.escape || key.return) {
|
|
9687
9687
|
setSearchFocus(false);
|
|
9688
9688
|
return;
|
|
9689
9689
|
}
|
|
9690
|
-
if (key.return) {
|
|
9691
|
-
setSearchFocus(false);
|
|
9692
|
-
if (route.name === 'pluginList') {
|
|
9693
|
-
const row = pluginRows[focus];
|
|
9694
|
-
if (row?.menuAction)
|
|
9695
|
-
row.menuAction();
|
|
9696
|
-
else if (row)
|
|
9697
|
-
go({ name: 'pluginDetail', pluginId: row.pluginId });
|
|
9698
|
-
}
|
|
9699
|
-
return;
|
|
9700
|
-
}
|
|
9701
9690
|
if (key.upArrow || key.downArrow) {
|
|
9702
9691
|
setSearchFocus(false);
|
|
9703
|
-
|
|
9692
|
+
const candidateCount = route.name === 'batchSelection' && batchDraft
|
|
9693
|
+
? batchCandidates(batchDraft).length : pluginRows.length;
|
|
9694
|
+
setFocus((value) => moveIndex(candidateCount, Math.min(value, Math.max(0, candidateCount - 1)), key.upArrow ? -1 : 1));
|
|
9704
9695
|
return;
|
|
9705
9696
|
}
|
|
9706
9697
|
if (key.leftArrow || key.rightArrow) {
|
|
9707
|
-
|
|
9708
|
-
const tabs = ['all', ...props.controller.listSources().map((source) => source.id)];
|
|
9709
|
-
const current = route.name === 'pluginList' ? route.sourceId ?? 'all' : batchDraft?.sourceId ?? 'all';
|
|
9710
|
-
const direction = key.leftArrow ? -1 : 1;
|
|
9711
|
-
const next = tabs[(tabs.indexOf(current) + direction + tabs.length) % tabs.length];
|
|
9712
|
-
if (route.name === 'pluginList') {
|
|
9713
|
-
props.controller.replace({ name: 'pluginList', mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}) }, { ...snapshot.router.current.context, query, installFilter, sourceTabId: next });
|
|
9714
|
-
}
|
|
9715
|
-
else {
|
|
9716
|
-
setBatchDraft((draft) => draft ? {
|
|
9717
|
-
...draft,
|
|
9718
|
-
...(next === 'all' ? { sourceId: undefined } : { sourceId: next }),
|
|
9719
|
-
showSelected: false,
|
|
9720
|
-
} : draft);
|
|
9721
|
-
}
|
|
9722
|
-
setFocus(0);
|
|
9723
|
-
setScrollOffset(0);
|
|
9698
|
+
setSearchCursor(Math.min(parts.length, Math.max(0, cursor + (key.leftArrow ? -1 : 1))));
|
|
9724
9699
|
return;
|
|
9725
9700
|
}
|
|
9701
|
+
let nextCursor = cursor;
|
|
9702
|
+
if (input === '\u0015' || input.toLowerCase() === 'u' && key.ctrl) {
|
|
9703
|
+
parts.splice(0);
|
|
9704
|
+
nextCursor = 0;
|
|
9705
|
+
}
|
|
9706
|
+
else if (key.backspace || key.delete) {
|
|
9707
|
+
if (cursor > 0)
|
|
9708
|
+
parts.splice(--nextCursor, 1);
|
|
9709
|
+
}
|
|
9710
|
+
else if (input && !key.ctrl && !key.meta && input >= ' ') {
|
|
9711
|
+
const inserted = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(input)]
|
|
9712
|
+
.map((part) => part.segment);
|
|
9713
|
+
parts.splice(cursor, 0, ...inserted);
|
|
9714
|
+
nextCursor += inserted.length;
|
|
9715
|
+
}
|
|
9716
|
+
const nextQuery = parts.join('');
|
|
9717
|
+
setSearchCursor(nextCursor);
|
|
9726
9718
|
if (route.name === 'pluginList') {
|
|
9727
|
-
const nextQuery = input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)
|
|
9728
|
-
? ''
|
|
9729
|
-
: key.backspace || key.delete
|
|
9730
|
-
? removeLastGrapheme(query)
|
|
9731
|
-
: input && !key.ctrl && !key.meta && input >= ' '
|
|
9732
|
-
? query + input : query;
|
|
9733
9719
|
setQuery(nextQuery);
|
|
9734
|
-
|
|
9735
|
-
mode: route.mode,
|
|
9736
|
-
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
9737
|
-
...(nextQuery ? { query: nextQuery } : {}),
|
|
9738
|
-
installFilter,
|
|
9739
|
-
});
|
|
9740
|
-
const matchingRows = pluginListRowIds(route.mode, matchingItems, nextQuery);
|
|
9741
|
-
setFocus(matchingItems[0] ? matchingRows.indexOf(matchingItems[0].pluginId) : 0);
|
|
9720
|
+
props.controller.updatePageContext({ query: nextQuery });
|
|
9742
9721
|
}
|
|
9743
9722
|
else {
|
|
9744
|
-
setBatchDraft((draft) => draft ? {
|
|
9745
|
-
...draft,
|
|
9746
|
-
query: input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)
|
|
9747
|
-
? ''
|
|
9748
|
-
: key.backspace || key.delete
|
|
9749
|
-
? removeLastGrapheme(draft.query)
|
|
9750
|
-
: input && !key.ctrl && !key.meta && input >= ' '
|
|
9751
|
-
? draft.query + input : draft.query,
|
|
9752
|
-
showSelected: false,
|
|
9753
|
-
} : draft);
|
|
9723
|
+
setBatchDraft((draft) => draft ? { ...draft, query: nextQuery, showSelected: false } : draft);
|
|
9754
9724
|
}
|
|
9755
|
-
|
|
9756
|
-
setFocus(0);
|
|
9725
|
+
setFocus(0);
|
|
9757
9726
|
setScrollOffset(0);
|
|
9758
9727
|
return;
|
|
9759
9728
|
}
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
setConfigGroupChooserOpen(false);
|
|
9763
|
-
setFocusZone('actions');
|
|
9764
|
-
setPinnedActionFocus(1);
|
|
9765
|
-
}
|
|
9766
|
-
else if (key.upArrow || key.downArrow) {
|
|
9767
|
-
setConfigGroupChoiceFocus((value) => moveIndex(visibleConfigTabs.length, value, key.upArrow ? -1 : 1));
|
|
9768
|
-
}
|
|
9769
|
-
else if (key.return) {
|
|
9770
|
-
const selected = visibleConfigTabs[configGroupChoiceFocus];
|
|
9771
|
-
if (selected) {
|
|
9772
|
-
props.controller.updatePageContext({
|
|
9773
|
-
configGroupId: selected.id,
|
|
9774
|
-
focusIndex: 0,
|
|
9775
|
-
scrollOffset: 0,
|
|
9776
|
-
});
|
|
9777
|
-
setConfigGroupChooserOpen(false);
|
|
9778
|
-
setFocusZone('content');
|
|
9779
|
-
setFocus(0);
|
|
9780
|
-
setScrollOffset(0);
|
|
9781
|
-
}
|
|
9782
|
-
}
|
|
9783
|
-
return;
|
|
9784
|
-
}
|
|
9785
|
-
if ((route.name === 'pluginList' || route.name === 'sourceList' || route.name === 'projectStatus'
|
|
9786
|
-
|| route.name === 'batchInstallPlatforms' || route.name === 'batchInstallScope'
|
|
9787
|
-
|| route.name === 'batchPlacement' && pinnedActions.length > 0
|
|
9788
|
-
|| route.name === 'portableSkillSelect'
|
|
9789
|
-
|| route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9790
|
-
|| route.name === 'installTargets' && route.view !== 'blocked') && key.tab) {
|
|
9791
|
-
setFocusZone((current) => current === 'content' ? 'actions' : 'content');
|
|
9729
|
+
// Paste and composed text are data, never multiple background commands.
|
|
9730
|
+
if (input.length > 1 && !key.ctrl && !key.meta)
|
|
9792
9731
|
return;
|
|
9793
|
-
|
|
9794
|
-
if (route.name === 'configFields' && key.tab) {
|
|
9795
|
-
setFocusZone((current) => current === 'content' ? 'actions' : 'content');
|
|
9732
|
+
if (key.tab)
|
|
9796
9733
|
return;
|
|
9797
|
-
}
|
|
9798
|
-
if (route.name === 'pluginList' && input.toLowerCase() === 'm') {
|
|
9799
|
-
openBatchSelection();
|
|
9800
|
-
return;
|
|
9801
|
-
}
|
|
9802
9734
|
if (route.name === 'pluginList' && input === '/') {
|
|
9803
|
-
|
|
9804
|
-
return;
|
|
9805
|
-
}
|
|
9806
|
-
if (route.name === 'pluginList' && focusZone === 'actions'
|
|
9807
|
-
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9808
|
-
const direction = key.upArrow || key.leftArrow ? -1 : 1;
|
|
9809
|
-
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, direction));
|
|
9810
|
-
return;
|
|
9811
|
-
}
|
|
9812
|
-
if (route.name === 'pluginList' && focusZone === 'actions' && key.return) {
|
|
9813
|
-
const actionIndex = Math.min(pinnedActionFocus, Math.max(0, pinnedActions.length - 1));
|
|
9814
|
-
runAction(pinnedActions[actionIndex]);
|
|
9815
|
-
return;
|
|
9816
|
-
}
|
|
9817
|
-
if (route.name === 'sourceList' && focusZone === 'actions'
|
|
9818
|
-
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9819
|
-
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
9820
|
-
return;
|
|
9821
|
-
}
|
|
9822
|
-
if (route.name === 'sourceList' && focusZone === 'actions' && key.return) {
|
|
9823
|
-
runAction(pinnedActions[pinnedActionFocus]);
|
|
9824
|
-
return;
|
|
9825
|
-
}
|
|
9826
|
-
if (route.name === 'projectStatus' && focusZone === 'actions'
|
|
9827
|
-
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9828
|
-
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
9829
|
-
return;
|
|
9830
|
-
}
|
|
9831
|
-
if (route.name === 'projectStatus' && focusZone === 'actions' && key.return) {
|
|
9832
|
-
runAction(pinnedActions[pinnedActionFocus]);
|
|
9735
|
+
runAction(listQueryActions.find((action) => action.actionId === 'plugin-list-search'));
|
|
9833
9736
|
return;
|
|
9834
9737
|
}
|
|
9835
|
-
if (route.name === '
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
return;
|
|
9856
|
-
}
|
|
9857
|
-
if ((route.name === 'batchInstallPlatforms' || route.name === 'batchInstallScope'
|
|
9858
|
-
|| route.name === 'batchPlacement' && pinnedActions.length > 0)
|
|
9859
|
-
&& focusZone === 'actions'
|
|
9860
|
-
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9861
|
-
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
9862
|
-
return;
|
|
9863
|
-
}
|
|
9864
|
-
if ((route.name === 'batchInstallPlatforms' || route.name === 'batchInstallScope'
|
|
9865
|
-
|| route.name === 'batchPlacement' && pinnedActions.length > 0)
|
|
9866
|
-
&& focusZone === 'actions' && key.return) {
|
|
9867
|
-
runAction(pinnedActions[pinnedActionFocus]);
|
|
9868
|
-
return;
|
|
9869
|
-
}
|
|
9870
|
-
if (route.name === 'configFields' && focusZone === 'actions'
|
|
9871
|
-
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9872
|
-
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
9873
|
-
return;
|
|
9874
|
-
}
|
|
9875
|
-
if (route.name === 'configFields' && focusZone === 'actions' && key.return) {
|
|
9876
|
-
runAction(pinnedActions[pinnedActionFocus]);
|
|
9738
|
+
if (route.name === 'pluginList' && input.toLowerCase() === 'm') {
|
|
9739
|
+
runAction(bottomActions.find((action) => action.actionId === 'plugin-list-multi'));
|
|
9740
|
+
return;
|
|
9741
|
+
}
|
|
9742
|
+
if (input.toLowerCase() === 'a' && bottomActions.length > 0) {
|
|
9743
|
+
const firstAction = actions.findIndex((action) => bottomActions.includes(action));
|
|
9744
|
+
if (firstAction >= 0) {
|
|
9745
|
+
const anchor = actionJumpAnchor.current;
|
|
9746
|
+
if (anchor?.routeKey === routeKey && focus >= firstAction) {
|
|
9747
|
+
const entityIndex = anchor.entityId
|
|
9748
|
+
? actions.findIndex((action) => action.pluginId === anchor.entityId) : -1;
|
|
9749
|
+
setFocus(entityIndex >= 0 ? entityIndex : Math.min(anchor.index, Math.max(0, firstAction - 1)));
|
|
9750
|
+
actionJumpAnchor.current = undefined;
|
|
9751
|
+
}
|
|
9752
|
+
else {
|
|
9753
|
+
actionJumpAnchor.current = { routeKey, index: focus, entityId: actions[focus]?.pluginId };
|
|
9754
|
+
setFocus(firstAction);
|
|
9755
|
+
}
|
|
9756
|
+
setScrollOffset(0);
|
|
9757
|
+
}
|
|
9877
9758
|
return;
|
|
9878
9759
|
}
|
|
9879
9760
|
if (key.tab && (route.name === 'configLayer'
|
|
@@ -9950,13 +9831,14 @@ export function ConsoleAppV4(props) {
|
|
|
9950
9831
|
}
|
|
9951
9832
|
else if (globalAction === 'back' && route.name === 'batchSelection'
|
|
9952
9833
|
&& batchDraft?.stage === 'select' && batchDraft.showSelectionTools) {
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9834
|
+
closeBatchSelectionTools();
|
|
9835
|
+
}
|
|
9836
|
+
else if (globalAction === 'back' && route.name === 'installPlacement'
|
|
9837
|
+
&& returnToPreviousInstallPlatform) {
|
|
9838
|
+
returnToPreviousInstallPlatform();
|
|
9956
9839
|
}
|
|
9957
9840
|
else if (globalAction === 'back'
|
|
9958
|
-
&& (route.name === 'configSave' || route.name === 'sourceSave')
|
|
9959
|
-
&& route.view === 'changes') {
|
|
9841
|
+
&& (route.name === 'configSave' || route.name === 'sourceSave' && route.view === 'changes')) {
|
|
9960
9842
|
back();
|
|
9961
9843
|
}
|
|
9962
9844
|
else if (globalAction === 'back' && route.name === 'configLayer') {
|
|
@@ -10016,7 +9898,7 @@ export function ConsoleAppV4(props) {
|
|
|
10016
9898
|
back();
|
|
10017
9899
|
}
|
|
10018
9900
|
else if (route.name === 'pluginList' && input.toLowerCase() === 'f') {
|
|
10019
|
-
|
|
9901
|
+
runAction(listQueryActions.find((action) => action.actionId === 'plugin-list-filter'));
|
|
10020
9902
|
}
|
|
10021
9903
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
10022
9904
|
&& input.toLowerCase() === 'f') {
|
|
@@ -10030,26 +9912,12 @@ export function ConsoleAppV4(props) {
|
|
|
10030
9912
|
setFocus(0);
|
|
10031
9913
|
setScrollOffset(0);
|
|
10032
9914
|
}
|
|
10033
|
-
else if (route.name === 'pluginList' && route.mode === 'installed' && input.toLowerCase() === 'a') {
|
|
10034
|
-
prepareCurrentProjectPlugins();
|
|
10035
|
-
}
|
|
10036
9915
|
else if (route.name === 'projectStatus' && input.toLowerCase() === 'c') {
|
|
10037
|
-
runAction(
|
|
10038
|
-
}
|
|
10039
|
-
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select' && input.toLowerCase() === 'a') {
|
|
10040
|
-
const candidates = batchCandidates(batchDraft)
|
|
10041
|
-
.filter((candidate) => batchCandidateBlockReason(batchDraft, candidate) === undefined);
|
|
10042
|
-
setBatchDraft((current) => current ? {
|
|
10043
|
-
...current,
|
|
10044
|
-
selectedPluginIds: [...new Set([
|
|
10045
|
-
...current.selectedPluginIds,
|
|
10046
|
-
...candidates.map((candidate) => candidate.pluginId),
|
|
10047
|
-
])],
|
|
10048
|
-
} : current);
|
|
9916
|
+
runAction(bottomActions.find((action) => action.actionId === 'project-sync-actionable'));
|
|
10049
9917
|
}
|
|
10050
9918
|
else if (route.name === 'installTargets' && route.view !== 'blocked'
|
|
10051
9919
|
&& input.toLowerCase() === 'c') {
|
|
10052
|
-
runAction(
|
|
9920
|
+
runAction(bottomActions.find((action) => action.actionId === 'confirm-install-platforms'));
|
|
10053
9921
|
}
|
|
10054
9922
|
else if (route.name === 'batchInstallPlatforms' && input.toLowerCase() === 'c') {
|
|
10055
9923
|
runAction(actions.find((action) => action.actionId === 'batch-install-platforms-confirm'));
|
|
@@ -10057,15 +9925,15 @@ export function ConsoleAppV4(props) {
|
|
|
10057
9925
|
else if (route.name === 'batchInstallScope' && input.toLowerCase() === 'c') {
|
|
10058
9926
|
runAction(actions.find((action) => action.actionId === 'batch-placement-confirm'));
|
|
10059
9927
|
}
|
|
10060
|
-
else if (route.name === 'batchPlacement' &&
|
|
9928
|
+
else if (route.name === 'batchPlacement' && bottomActions.length > 0
|
|
10061
9929
|
&& input.toLowerCase() === 'c') {
|
|
10062
9930
|
runAction(actions.find((action) => action.actionId === 'batch-placement-confirm'));
|
|
10063
9931
|
}
|
|
10064
9932
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select' && input.toLowerCase() === 'c') {
|
|
10065
|
-
runAction(
|
|
9933
|
+
runAction(bottomActions.find((action) => action.actionId === 'batch-selection-continue'));
|
|
10066
9934
|
}
|
|
10067
9935
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
10068
|
-
&&
|
|
9936
|
+
&& input === ' ') {
|
|
10069
9937
|
const action = actions[normalizedEffectiveFocus];
|
|
10070
9938
|
if (action?.pluginId
|
|
10071
9939
|
&& (batchDraft.selectedPluginIds.includes(action.pluginId) || action.canSelectNew)) {
|
|
@@ -10073,12 +9941,20 @@ export function ConsoleAppV4(props) {
|
|
|
10073
9941
|
}
|
|
10074
9942
|
}
|
|
10075
9943
|
else if (route.name === 'installTargets' && route.view !== 'blocked'
|
|
10076
|
-
&&
|
|
9944
|
+
&& input === ' ') {
|
|
10077
9945
|
const action = actions[focus];
|
|
10078
9946
|
if (action?.actionId?.startsWith('install-platform-'))
|
|
10079
9947
|
runAction(action);
|
|
10080
9948
|
}
|
|
9949
|
+
else if (route.name === 'batchInstallPlatforms'
|
|
9950
|
+
&& input === ' ') {
|
|
9951
|
+
const action = actions[focus];
|
|
9952
|
+
if (action?.actionId === 'batch-install-platform-codex'
|
|
9953
|
+
|| action?.actionId === 'batch-install-platform-claude')
|
|
9954
|
+
runAction(action);
|
|
9955
|
+
}
|
|
10081
9956
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select' && input === '/') {
|
|
9957
|
+
setSearchCursor([...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(batchDraft.query)].length);
|
|
10082
9958
|
setSearchFocus(true);
|
|
10083
9959
|
}
|
|
10084
9960
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
@@ -10096,7 +9972,7 @@ export function ConsoleAppV4(props) {
|
|
|
10096
9972
|
setScrollOffset(0);
|
|
10097
9973
|
}
|
|
10098
9974
|
else if (route.name === 'configFields' && input.toLowerCase() === 's') {
|
|
10099
|
-
runAction(
|
|
9975
|
+
runAction(bottomActions[0]);
|
|
10100
9976
|
}
|
|
10101
9977
|
else if (route.name === 'configFields' && input.toLowerCase() === 'u') {
|
|
10102
9978
|
const key = actions[focus]?.configKey;
|
|
@@ -10123,20 +9999,17 @@ export function ConsoleAppV4(props) {
|
|
|
10123
9999
|
}
|
|
10124
10000
|
else if ((input.toLowerCase() === 'r' || input.toLowerCase() === 'c')
|
|
10125
10001
|
&& route.name === 'sourceList') {
|
|
10126
|
-
|
|
10002
|
+
runAction(bottomActions.find((action) => action.actionId === 'source-check-all'));
|
|
10127
10003
|
}
|
|
10128
10004
|
else if (route.name === 'pluginList' && (key.leftArrow || key.rightArrow || input === '[' || input === ']')) {
|
|
10129
10005
|
const direction = key.leftArrow || input === '[' ? -1 : 1;
|
|
10130
10006
|
movePluginListSource?.(direction);
|
|
10131
10007
|
}
|
|
10132
10008
|
else if (input.toLowerCase() === 'c' && route.name === 'portableSkillSelect') {
|
|
10133
|
-
runAction(
|
|
10134
|
-
}
|
|
10135
|
-
else if (input.toLowerCase() === 'a' && route.name === 'portableSkillSelect') {
|
|
10136
|
-
runAction(selectAllPortableCandidates);
|
|
10009
|
+
runAction(bottomActions.find((action) => action.actionId === 'portable-selection-continue'));
|
|
10137
10010
|
}
|
|
10138
10011
|
else if (input === ' ' && route.name === 'portableSkillSelect'
|
|
10139
|
-
&&
|
|
10012
|
+
&& !portableToolsOpen) {
|
|
10140
10013
|
const candidate = portableVisibleCandidates[focus];
|
|
10141
10014
|
if (candidate)
|
|
10142
10015
|
props.controller.togglePortableSkill(candidate.name);
|
|
@@ -10148,9 +10021,8 @@ export function ConsoleAppV4(props) {
|
|
|
10148
10021
|
setFocus((value) => {
|
|
10149
10022
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
10150
10023
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
10151
|
-
const next =
|
|
10152
|
-
:
|
|
10153
|
-
: moveFocus(actions, current, -1);
|
|
10024
|
+
const next = route.name === 'sourceDetail' ? moveIndex(actions.length, current, -1)
|
|
10025
|
+
: moveFocus(actions, current, -1);
|
|
10154
10026
|
if (dynamicActionIds && dynamicActionSignature !== undefined) {
|
|
10155
10027
|
dynamicMenuFocus.current = {
|
|
10156
10028
|
routeKey,
|
|
@@ -10158,17 +10030,7 @@ export function ConsoleAppV4(props) {
|
|
|
10158
10030
|
focusedActionId: dynamicActionIds[next],
|
|
10159
10031
|
};
|
|
10160
10032
|
}
|
|
10161
|
-
const viewport =
|
|
10162
|
-
? batchDraft.showSelectionTools
|
|
10163
|
-
? actionViewport(actions, policy.mode === 'compact', next, scrollOffset, Math.max(1, pageActionBudget(4)))
|
|
10164
|
-
: objectCardActionViewport(actions, Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, Math.max(1, pageActionBudget(4)))
|
|
10165
|
-
: route.name === 'portableSkillSelect' && snapshot.portableDraft && !portableToolsOpen
|
|
10166
|
-
? portableSkillCandidateViewport(actions.slice(0, portableVisibleCandidates.length), Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, pageActionBudget(actions.length - portableVisibleCandidates.length
|
|
10167
|
-
+ (portableVariantCandidates.length > 0 ? 3 : 0)))
|
|
10168
|
-
: route.name === 'installTargets' && route.view !== 'blocked'
|
|
10169
|
-
? objectCardActionViewport(actions, Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, Math.max(1, pageActionBudget((pinnedActions[0]?.disabled ? 2 : 1)
|
|
10170
|
-
+ (policy.mode === 'compact' ? 4 : 0))))
|
|
10171
|
-
: actionViewport(actions, route.name === 'pluginDetail' || policy.mode === 'compact', next, scrollOffset, currentActionViewportLineBudget);
|
|
10033
|
+
const viewport = actionViewport(actions, policy.mode === 'compact', next, scrollOffset, currentActionViewportLineBudget);
|
|
10172
10034
|
if (route.name === 'pluginDependencies' && route.view !== 'removeBlocked') {
|
|
10173
10035
|
const projection = buildUnifiedDependencyGraphProjection({
|
|
10174
10036
|
overviews: props.controller.listPluginDependencyOverviews(),
|
|
@@ -10180,7 +10042,8 @@ export function ConsoleAppV4(props) {
|
|
|
10180
10042
|
...projection.dependents.defaultExpandedPluginIds,
|
|
10181
10043
|
]), next, viewport.offset);
|
|
10182
10044
|
}
|
|
10183
|
-
|
|
10045
|
+
if (bottomActions.length === 0)
|
|
10046
|
+
setScrollOffset(viewport.offset);
|
|
10184
10047
|
return next;
|
|
10185
10048
|
});
|
|
10186
10049
|
}
|
|
@@ -10191,9 +10054,8 @@ export function ConsoleAppV4(props) {
|
|
|
10191
10054
|
setFocus((value) => {
|
|
10192
10055
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
10193
10056
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
10194
|
-
const next =
|
|
10195
|
-
:
|
|
10196
|
-
: moveFocus(actions, current, 1);
|
|
10057
|
+
const next = route.name === 'sourceDetail' ? moveIndex(actions.length, current, 1)
|
|
10058
|
+
: moveFocus(actions, current, 1);
|
|
10197
10059
|
if (dynamicActionIds && dynamicActionSignature !== undefined) {
|
|
10198
10060
|
dynamicMenuFocus.current = {
|
|
10199
10061
|
routeKey,
|
|
@@ -10201,17 +10063,7 @@ export function ConsoleAppV4(props) {
|
|
|
10201
10063
|
focusedActionId: dynamicActionIds[next],
|
|
10202
10064
|
};
|
|
10203
10065
|
}
|
|
10204
|
-
const viewport =
|
|
10205
|
-
? batchDraft.showSelectionTools
|
|
10206
|
-
? actionViewport(actions, policy.mode === 'compact', next, scrollOffset, Math.max(1, pageActionBudget(4)))
|
|
10207
|
-
: objectCardActionViewport(actions, Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, Math.max(1, pageActionBudget(4)))
|
|
10208
|
-
: route.name === 'portableSkillSelect' && snapshot.portableDraft && !portableToolsOpen
|
|
10209
|
-
? portableSkillCandidateViewport(actions.slice(0, portableVisibleCandidates.length), Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, pageActionBudget(actions.length - portableVisibleCandidates.length
|
|
10210
|
-
+ (portableVariantCandidates.length > 0 ? 3 : 0)))
|
|
10211
|
-
: route.name === 'installTargets' && route.view !== 'blocked'
|
|
10212
|
-
? objectCardActionViewport(actions, Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, Math.max(1, pageActionBudget((pinnedActions[0]?.disabled ? 2 : 1)
|
|
10213
|
-
+ (policy.mode === 'compact' ? 4 : 0))))
|
|
10214
|
-
: actionViewport(actions, route.name === 'pluginDetail' || policy.mode === 'compact', next, scrollOffset, currentActionViewportLineBudget);
|
|
10066
|
+
const viewport = actionViewport(actions, policy.mode === 'compact', next, scrollOffset, currentActionViewportLineBudget);
|
|
10215
10067
|
if (route.name === 'pluginDependencies' && route.view !== 'removeBlocked') {
|
|
10216
10068
|
const projection = buildUnifiedDependencyGraphProjection({
|
|
10217
10069
|
overviews: props.controller.listPluginDependencyOverviews(),
|
|
@@ -10223,17 +10075,11 @@ export function ConsoleAppV4(props) {
|
|
|
10223
10075
|
...projection.dependents.defaultExpandedPluginIds,
|
|
10224
10076
|
]), next, viewport.offset);
|
|
10225
10077
|
}
|
|
10226
|
-
|
|
10078
|
+
if (bottomActions.length === 0)
|
|
10079
|
+
setScrollOffset(viewport.offset);
|
|
10227
10080
|
return next;
|
|
10228
10081
|
});
|
|
10229
10082
|
}
|
|
10230
|
-
else if (key.return && route.name === 'pluginList' && focusZone === 'content' && pluginRows[focus]) {
|
|
10231
|
-
const row = pluginRows[focus];
|
|
10232
|
-
if (row.menuAction)
|
|
10233
|
-
row.menuAction();
|
|
10234
|
-
else
|
|
10235
|
-
go({ name: 'pluginDetail', pluginId: row.pluginId });
|
|
10236
|
-
}
|
|
10237
10083
|
else if (key.return) {
|
|
10238
10084
|
runAction(actions[normalizedEffectiveFocus]);
|
|
10239
10085
|
}
|
|
@@ -10241,6 +10087,19 @@ export function ConsoleAppV4(props) {
|
|
|
10241
10087
|
if (tooSmall && !overlay) {
|
|
10242
10088
|
return (_jsx(ScreenFrame, { title: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", breadcrumb: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", context: `${size.columns} × ${size.rows}`, help: "q \u9000\u51FA", size: size, children: _jsx(Callout, { status: "warning", title: "\u2194 \u8BF7\u5C06\u7EC8\u7AEF\u8C03\u6574\u5230\u81F3\u5C11 60 \u00D7 18", description: "\u8C03\u6574\u540E\u4F1A\u8FD4\u56DE\u539F\u9875\u9762\uFF0C\u4E0D\u4F1A\u4E22\u5931\u5F53\u524D\u9009\u62E9\u3002" }) }));
|
|
10243
10089
|
}
|
|
10090
|
+
if (readingText !== undefined && route.name !== 'progress') {
|
|
10091
|
+
const paginated = referenceDetailPagination !== undefined
|
|
10092
|
+
|| portableResultDetailPagination !== undefined
|
|
10093
|
+
|| route.name === 'sourceTechnical'
|
|
10094
|
+
|| route.name === 'operationDiagnostic'
|
|
10095
|
+
|| route.name === 'operationReportPreview'
|
|
10096
|
+
|| route.name === 'portableDependencies';
|
|
10097
|
+
help = `${paginated ? '←→ 翻页 · ' : ''}[y] 复制全文 · Esc 返回${supportsTechnicalDetails
|
|
10098
|
+
? ' · d 技术详情' : ''} · ${size.columns < 80
|
|
10099
|
+
? '↑↓/Enter 菜单' : '↑↓ 移动 · Enter 选择'}`;
|
|
10100
|
+
if (copyFeedback?.routeKey === routeKey)
|
|
10101
|
+
description = copyFeedback.message;
|
|
10102
|
+
}
|
|
10244
10103
|
if (actionError)
|
|
10245
10104
|
description = actionError;
|
|
10246
10105
|
const base = (_jsx(ScreenFrame, { title: projectReminderOpen ? '' : title, description: projectReminderOpen ? undefined : description, breadcrumb: breadcrumb, context: context, help: portableEmptySelectionOpen ? 'Enter 返回选择 · Esc 返回' : help, size: size, children: projectReminderOpen || sourceTransportOverlay ? null : content }));
|
|
@@ -10296,12 +10155,13 @@ export function ConsoleAppV4(props) {
|
|
|
10296
10155
|
const visibleWarnings = shortConfirmation
|
|
10297
10156
|
? warningLines : warningLines.slice(warningOffset, warningOffset + warningLimit);
|
|
10298
10157
|
const warningPaginated = !shortConfirmation && warningLines.length > warningLimit;
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10158
|
+
confirmationText = portableUserConfirmation
|
|
10159
|
+
? '! 本次在所有项目范围安装选定 Skills。\n具体对象和范围以刚才的汇总为准。'
|
|
10160
|
+
: [
|
|
10161
|
+
`操作:${review.title} · ${review.subject.name}`,
|
|
10162
|
+
...(review.confirmationDetails ?? []),
|
|
10163
|
+
...operationRiskWarnings(review).map((warning) => `! ${warning}`),
|
|
10164
|
+
].join('\n');
|
|
10305
10165
|
if (warningPaginated) {
|
|
10306
10166
|
confirmationPagination = { total: warningLines.length, pageRows: warningLimit };
|
|
10307
10167
|
}
|
|
@@ -10311,14 +10171,9 @@ export function ConsoleAppV4(props) {
|
|
|
10311
10171
|
label: portableUserConfirmation ? '确认并执行' : `我已了解影响,${review.primaryLabel}`,
|
|
10312
10172
|
status: 'error',
|
|
10313
10173
|
},
|
|
10314
|
-
...(warningPaginated ? paginationActions({
|
|
10315
|
-
offset: warningOffset,
|
|
10316
|
-
total: warningLines.length,
|
|
10317
|
-
pageRows: warningLimit,
|
|
10318
|
-
move: moveWarningPage,
|
|
10319
|
-
}) : []),
|
|
10320
10174
|
];
|
|
10321
|
-
const confirmationCard = (_jsxs(ReviewCard, { title: portableUserConfirmation ? '确认本次影响' : '请确认本次风险', description:
|
|
10175
|
+
const confirmationCard = (_jsxs(ReviewCard, { title: portableUserConfirmation ? '确认本次影响' : '请确认本次风险', description: `${copyFeedback?.routeKey === `${routeKey}:confirmation`
|
|
10176
|
+
? `${copyFeedback.message} · ` : ''}确认只对当前操作汇总有效;[y] 复制全文。`, children: [!shortConfirmation && warningOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${warningOffset} 行`, status: "muted", compact: true }) : null, visibleWarnings.length > 0
|
|
10322
10177
|
? visibleWarnings.map((warning, index) => _jsx(StaticRow, { title: warning, status: warning.startsWith('! ') ? 'warning' : 'info', compact: true }, `${warningOffset + index}:${warning}`))
|
|
10323
10178
|
: _jsx(StaticRow, { title: "! \u6B64\u64CD\u4F5C\u9700\u8981\u660E\u786E\u6388\u6743", status: "warning", compact: true }), !shortConfirmation && warningLines.length - warningOffset - visibleWarnings.length > 0
|
|
10324
10179
|
? _jsx(StaticRow, { title: `↓ 后面还有 ${warningLines.length - warningOffset - visibleWarnings.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: confirmationActions, focus: overlayFocus, kind: "action", compact: true })] }));
|
|
@@ -10327,7 +10182,7 @@ export function ConsoleAppV4(props) {
|
|
|
10327
10182
|
}
|
|
10328
10183
|
return _jsx(ScreenFrame, { title: review.operationKind === 'catalogStorePrune'
|
|
10329
10184
|
? `确认${review.title}?`
|
|
10330
|
-
: `确认${review.title} ${review.subject.name}?`, breadcrumb: `${review.subject.name} / 风险确认`, context: "\u72EC\u7ACB\u6388\u6743", help: "PgUp/PgDn \
|
|
10185
|
+
: `确认${review.title} ${review.subject.name}?`, breadcrumb: `${review.subject.name} / 风险确认`, context: "\u72EC\u7ACB\u6388\u6743", help: "\u2190\u2192/PgUp/PgDn \u7FFB\u9875 \u00B7 [y] \u590D\u5236\u5168\u6587 \u00B7 Esc \u8FD4\u56DE", size: size, children: confirmationCard });
|
|
10331
10186
|
}
|
|
10332
10187
|
if (sourceTransportOverlay) {
|
|
10333
10188
|
const configuredSource = route.name === 'sourceEdit'
|
|
@@ -10345,33 +10200,6 @@ export function ConsoleAppV4(props) {
|
|
|
10345
10200
|
];
|
|
10346
10201
|
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 4, marginLeft: 2, width: size.columns - 4, flexDirection: "column", children: _jsxs(ReviewCard, { title: "\u9009\u62E9\u4E0B\u8F7D\u65B9\u5F0F", description: `当前草稿:${currentTransport ?? '未识别'};仅修改草稿,保存前可查看汇总。`, children: [_jsx(SemanticRows, { actions: transportActions, focus: overlayFocus, kind: "choice", compact: false }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u2191\u2193 \u9009\u62E9 \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u53D6\u6D88" })] }) })] });
|
|
10347
10202
|
}
|
|
10348
|
-
if (pluginListChooser && route.name === 'pluginList') {
|
|
10349
|
-
const sourceId = route.sourceId ?? 'all';
|
|
10350
|
-
const chooserActions = pluginListChooser.kind === 'source'
|
|
10351
|
-
? [
|
|
10352
|
-
{ label: '全部来源', selected: sourceId === 'all' },
|
|
10353
|
-
...props.controller.listSources().map((source) => ({
|
|
10354
|
-
label: source.label,
|
|
10355
|
-
meta: source.id === source.label ? undefined : source.id,
|
|
10356
|
-
selected: source.id === sourceId,
|
|
10357
|
-
})),
|
|
10358
|
-
]
|
|
10359
|
-
: PLUGIN_FILTERS.map((filter) => ({
|
|
10360
|
-
label: pluginFilterLabel(filter),
|
|
10361
|
-
selected: filter === installFilter,
|
|
10362
|
-
}));
|
|
10363
|
-
const chooserFocus = Math.min(pluginListChooser.focus, Math.max(0, chooserActions.length - 1));
|
|
10364
|
-
const chooserViewport = rowViewport({
|
|
10365
|
-
rowHeights: chooserActions.map(() => 1),
|
|
10366
|
-
focus: chooserFocus,
|
|
10367
|
-
requestedOffset: 0,
|
|
10368
|
-
rowBudget: responsiveBudget(6, 9),
|
|
10369
|
-
});
|
|
10370
|
-
const chooserWidth = Math.max(40, Math.floor(size.columns * 0.8));
|
|
10371
|
-
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: policy.mode === 'compact' ? 2 : 5, marginLeft: Math.max(2, Math.floor((size.columns - chooserWidth) / 2)), width: chooserWidth, flexDirection: "column", children: _jsxs(ReviewCard, { title: pluginListChooser.kind === 'source' ? '选择插件来源' : '筛选插件', description: pluginListChooser.kind === 'status' ? '只改变列表,不改变插件详情。' : undefined, children: [_jsx(SemanticRows, { actions: chooserActions, focus: chooserFocus, kind: "choice", compact: true, offset: chooserViewport.offset, limit: chooserViewport.limit }), chooserActions.length > chooserViewport.limit
|
|
10372
|
-
? _jsx(StaticRow, { title: `${chooserViewport.position} / ${chooserViewport.total}`, status: "muted", compact: true })
|
|
10373
|
-
: null, _jsx(StaticRow, { title: "\u2191\u2193 \u9009\u62E9 \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u53D6\u6D88", status: "muted", compact: true })] }) })] });
|
|
10374
|
-
}
|
|
10375
10203
|
if (!overlay)
|
|
10376
10204
|
return base;
|
|
10377
10205
|
const helpRows = helpRowsForRoute(route, actions.length, supportsTechnicalDetails, busy, props.onSessionNavigation !== undefined);
|