@lesliechan721/aw 0.1.0-beta.29 → 0.1.0-beta.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/tui/components.d.ts +41 -3
- package/dist/src/tui/components.js +70 -3
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +1 -1
- package/dist/src/tui/console-app-v4.js +990 -376
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller.d.ts +4 -1
- package/dist/src/tui/controller.js +24 -3
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/operation-review-planner.js +68 -2
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/router.d.ts +5 -0
- package/dist/src/tui/router.js +85 -1
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +14 -4
- package/dist/src/tui/screen-registry.js +7 -1
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/package.json +3 -3
|
@@ -11,7 +11,7 @@ import { normalizePortableAgents, SKILLS_CLI_SUPPORTED_VERSION_LABEL, } from '..
|
|
|
11
11
|
import { selectLifecycleRetryAction, } from '../commands/operation-planner.js';
|
|
12
12
|
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, SCRIPT_TIMEOUT_MAX_MS, SCRIPT_TIMEOUT_MIN_MS, } from '../commands/options.js';
|
|
13
13
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
14
|
-
import { ActionRow, Callout, ChoiceRow, ContentViewport, diagnosticDetailRowCount, DiagnosticSummary, InputCard, PluginListRow, PinnedActionBar, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
14
|
+
import { ActionRow, ObjectCardRow, objectCardRowLayout, StatusBadge, diagnosticDetailLines, Callout, ChoiceRow, ContentViewport, diagnosticDetailRowCount, DiagnosticSummary, InputCard, PluginListRow, PinnedActionBar, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
15
15
|
import { safeDiagnosticCode, sanitizeDiagnosticText, } from './diagnostics.js';
|
|
16
16
|
import { extractPortableError, TuiConfigInvalidStoreError, userIssueKey, } from './controller.js';
|
|
17
17
|
import { pluginWriteTaskVersionEvidence } from './plugin-write-task.js';
|
|
@@ -184,7 +184,7 @@ function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy, se
|
|
|
184
184
|
}
|
|
185
185
|
if (route.name === 'batchSelection') {
|
|
186
186
|
rows.push(['Space', '切换当前插件']);
|
|
187
|
-
rows.push(['a', '
|
|
187
|
+
rows.push(['a', '全选当前筛选结果']);
|
|
188
188
|
rows.push(['c', '继续选择位置和平台']);
|
|
189
189
|
}
|
|
190
190
|
if (route.name === 'configFields' && route.layer.scope !== 'effective') {
|
|
@@ -198,13 +198,16 @@ function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy, se
|
|
|
198
198
|
rows.push(['d', route.name === 'progress' ? '打开技术进度' : '打开技术详情']);
|
|
199
199
|
}
|
|
200
200
|
if (sessionNavigation && !busy && !isCharacterInputMode(route, false)) {
|
|
201
|
-
rows.push(['h', '返回首页']);
|
|
202
|
-
rows.push(['H', '重新读取本地状态']);
|
|
201
|
+
rows.push(['h / H', '返回首页 / 重新读取本地状态']);
|
|
203
202
|
}
|
|
204
203
|
if (sessionNavigation && busy) {
|
|
205
204
|
rows.push(['h / H', '操作成功后返回首页或重新读取']);
|
|
206
205
|
rows.push(['Esc', '撤销等待中的页面跳转']);
|
|
207
206
|
}
|
|
207
|
+
if (route.name === 'pluginList' || route.name === 'pluginDetail'
|
|
208
|
+
|| route.name === 'batchSelection' || route.name === 'installTargets') {
|
|
209
|
+
rows.push(['状态', '✓已装 ○未装 !异常 ↑更新 ?未知']);
|
|
210
|
+
}
|
|
208
211
|
rows.push(['q / Ctrl-C', busy ? '操作完成后退出' : '退出控制台']);
|
|
209
212
|
rows.push(['? / o', '关闭帮助 / 查看操作记录']);
|
|
210
213
|
return rows;
|
|
@@ -302,34 +305,49 @@ function pluginUpdateDescription(plugin) {
|
|
|
302
305
|
}
|
|
303
306
|
return '当前启用目录已验证;可直接进入更新确认。';
|
|
304
307
|
}
|
|
305
|
-
function
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
function pluginPlatformBadges(plugin) {
|
|
309
|
+
const items = plugin.supportedPlatforms.map((platform) => {
|
|
310
|
+
const placements = plugin.installedPlacements.filter((entry) => entry.platform === platform);
|
|
311
|
+
const installed = placements.some((entry) => entry.state === 'installed' || entry.state === 'scopeConflict');
|
|
312
|
+
const unknown = plugin.sourceUnavailable || placements.length === 0
|
|
313
|
+
|| placements.some((entry) => entry.state === 'installed' && entry.sharedNative?.state === 'unobserved');
|
|
314
|
+
const attention = placements.some((entry) => entry.state === 'blocked' || entry.state === 'scopeConflict'
|
|
315
|
+
|| entry.state === 'installed' && (entry.health !== 'ready' || entry.blockingReason !== undefined));
|
|
316
|
+
const update = plugin.update.targets.some((target) => target.platform === platform
|
|
317
|
+
&& target.availableVersion !== undefined);
|
|
318
|
+
return {
|
|
319
|
+
key: platform,
|
|
320
|
+
label: `${platformLabel(platform)}${attention && (installed || unknown) ? ' !' : ''}${update ? ' ↑' : ''}${unknown && installed ? ' ?' : ''}`,
|
|
321
|
+
symbol: installed ? '✓' : unknown ? '?' : attention ? '!' : '○',
|
|
322
|
+
status: attention ? 'warning' : unknown ? 'muted'
|
|
323
|
+
: update ? 'info' : installed ? 'success' : 'muted',
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
return _jsxs(Box, { children: [_jsx(StatusBadge, { items: items }), plugin.portableSkills ? _jsx(Text, { color: tuiTheme.colors.accent, children: " [Skills \u53EF\u9009]" }) : null, plugin.configOnly ? _jsx(Text, { color: tuiTheme.colors.muted, children: "[\u5DF2\u4FDD\u5B58\u8BBE\u7F6E]" }) : null] });
|
|
327
|
+
}
|
|
328
|
+
function installedScopeStates(plugin, platform) {
|
|
329
|
+
const states = new Map();
|
|
330
|
+
const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
|
|
331
|
+
if (!placement || placement.state === 'notInstalled' || placement.state === 'blocked')
|
|
332
|
+
return states;
|
|
333
|
+
const scoped = placement.state === 'scopeConflict'
|
|
334
|
+
? placement.placements
|
|
335
|
+
: placement.scopedPlacements ?? [{
|
|
336
|
+
scope: placement.scope,
|
|
337
|
+
health: placement.health,
|
|
338
|
+
revision: placement.revision,
|
|
339
|
+
actualState: placement.actualState,
|
|
340
|
+
}];
|
|
341
|
+
for (const entry of scoped) {
|
|
342
|
+
const actualState = 'actualState' in entry ? entry.actualState : undefined;
|
|
343
|
+
const healthy = entry.health === 'ready' && actualState !== 'blocked'
|
|
344
|
+
&& actualState !== 'needsSync';
|
|
345
|
+
states.set(entry.scope, healthy
|
|
346
|
+
? { symbol: '✓', status: 'success' }
|
|
347
|
+
: { symbol: '!', status: actualState === 'blocked' || entry.health === 'error'
|
|
348
|
+
? 'error' : 'warning' });
|
|
310
349
|
}
|
|
311
|
-
return
|
|
312
|
-
const label = platformLabel(platform);
|
|
313
|
-
const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
|
|
314
|
-
if (!placement || placement.state === 'notInstalled')
|
|
315
|
-
return `[○ ${label} 未安装]`;
|
|
316
|
-
if (placement.state === 'blocked')
|
|
317
|
-
return `[× ${label} 输出冲突]`;
|
|
318
|
-
if (placement.state === 'scopeConflict')
|
|
319
|
-
return `[! ${label} 位置冲突]`;
|
|
320
|
-
if (placement.blockingReason === 'artifactCollision') {
|
|
321
|
-
return `[! ${label}] ${scopeLabel(placement.scope)} · 输出冲突`;
|
|
322
|
-
}
|
|
323
|
-
const health = placement.actualState === 'needsSync' ? '需要同步'
|
|
324
|
-
: placement.actualState === 'blocked' ? '已阻断'
|
|
325
|
-
: placement.health === 'ready' ? '可用'
|
|
326
|
-
: placement.health === 'incomplete' ? '需要处理' : '不可用';
|
|
327
|
-
const desired = placement.desiredScopes?.map(scopeLabel).join('+') || '未保存';
|
|
328
|
-
const actual = placement.actualScopes?.map(scopeLabel).join('+') || '未启用';
|
|
329
|
-
return placement.desiredScopes?.length
|
|
330
|
-
? `[${health === '可用' ? '✓' : '!'} ${label}] 已保存 ${desired} · 当前 ${actual} · ${health}`
|
|
331
|
-
: `[${health === '可用' ? '✓' : '!'} ${label}] 当前 ${actual} · ${health}`;
|
|
332
|
-
}).join(' ') || '没有可用平台';
|
|
350
|
+
return states;
|
|
333
351
|
}
|
|
334
352
|
function issueMatchesTarget(issue, pluginId, platform, scope) {
|
|
335
353
|
if (!platform && !scope && issue.pluginId === pluginId)
|
|
@@ -452,51 +470,22 @@ function actionViewport(actions, compact, focus, requestedOffset, lineBudget) {
|
|
|
452
470
|
rowBudget: lineBudget,
|
|
453
471
|
});
|
|
454
472
|
}
|
|
455
|
-
function
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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,
|
|
462
483
|
requestedOffset,
|
|
463
|
-
rowBudget,
|
|
484
|
+
rowBudget: lineBudget,
|
|
464
485
|
});
|
|
465
486
|
}
|
|
466
|
-
function
|
|
467
|
-
|
|
468
|
-
if (total === 0) {
|
|
469
|
-
return { offset: 0, limit: 0, hiddenBefore: 0, hiddenAfter: 0, position: 0, total: 0 };
|
|
470
|
-
}
|
|
471
|
-
const focus = Math.min(Math.max(0, options.focus), total - 1);
|
|
472
|
-
const height = (start, end) => {
|
|
473
|
-
let rows = 0;
|
|
474
|
-
let previousGroup;
|
|
475
|
-
for (let index = start; index < end; index += 1) {
|
|
476
|
-
const group = options.groupKeys[index];
|
|
477
|
-
if (group !== undefined && group !== previousGroup)
|
|
478
|
-
rows += options.groupOverhead;
|
|
479
|
-
rows += options.rowHeights[index] ?? 1;
|
|
480
|
-
previousGroup = group;
|
|
481
|
-
}
|
|
482
|
-
return rows;
|
|
483
|
-
};
|
|
484
|
-
let offset = Math.min(Math.max(0, options.requestedOffset), focus);
|
|
485
|
-
while (offset < focus && height(offset, focus + 1) > options.rowBudget)
|
|
486
|
-
offset += 1;
|
|
487
|
-
let end = focus + 1;
|
|
488
|
-
while (end < total && height(offset, end + 1) <= options.rowBudget)
|
|
489
|
-
end += 1;
|
|
490
|
-
while (offset > 0 && height(offset - 1, end) <= options.rowBudget)
|
|
491
|
-
offset -= 1;
|
|
492
|
-
return {
|
|
493
|
-
offset,
|
|
494
|
-
limit: end - offset,
|
|
495
|
-
hiddenBefore: offset,
|
|
496
|
-
hiddenAfter: total - end,
|
|
497
|
-
position: focus + 1,
|
|
498
|
-
total,
|
|
499
|
-
};
|
|
487
|
+
function portableSkillCandidateViewport(actions, width, compact, focus, requestedOffset, rowBudget) {
|
|
488
|
+
return objectCardActionViewport(actions, width, compact, focus, requestedOffset, rowBudget);
|
|
500
489
|
}
|
|
501
490
|
function prefixRowLimit(rowHeights, rowBudget) {
|
|
502
491
|
let used = 0;
|
|
@@ -547,41 +536,59 @@ function sourceLocationSummary(type, value) {
|
|
|
547
536
|
}
|
|
548
537
|
}
|
|
549
538
|
function portableBlockerDiagnostic(route) {
|
|
550
|
-
const
|
|
551
|
-
|
|
539
|
+
const cliMissing = route.code === 'portableSkillsCliMissing';
|
|
540
|
+
const cliIncompatible = route.code === 'portableSkillsCliIncompatible';
|
|
541
|
+
const cliUnavailable = cliMissing || cliIncompatible;
|
|
552
542
|
const sensitiveOutput = route.diagnosticCode === 'portableSensitiveOutput';
|
|
553
543
|
const invalidRuntimeImport = route.diagnosticCode === 'portableRuntimeImportMissing';
|
|
544
|
+
const skillCollision = route.diagnosticCode === 'portableSkillCollision';
|
|
545
|
+
const distributionUnavailable = route.diagnosticCode === 'portableDistributionUnavailable';
|
|
554
546
|
const userMessage = route.code === 'requiredConfigMissing'
|
|
555
547
|
? '插件缺少生成内容所需的设置。'
|
|
556
548
|
: route.code === 'portableScopeUnavailable'
|
|
557
549
|
? '至少一个所选 Skill 不适用于这个范围。'
|
|
558
|
-
:
|
|
559
|
-
? '
|
|
560
|
-
:
|
|
561
|
-
?
|
|
562
|
-
:
|
|
563
|
-
? '
|
|
564
|
-
:
|
|
550
|
+
: cliMissing
|
|
551
|
+
? '没有找到已安装的 Skills CLI。'
|
|
552
|
+
: cliIncompatible
|
|
553
|
+
? `已安装 Skills CLI,但版本不在支持范围 ${SKILLS_CLI_SUPPORTED_VERSION_LABEL}。`
|
|
554
|
+
: sensitiveOutput
|
|
555
|
+
? '生成的 Skill 内容包含敏感配置值或未解析模板。'
|
|
556
|
+
: invalidRuntimeImport
|
|
557
|
+
? '所需运行时引用未解析、缺失或无效。'
|
|
558
|
+
: skillCollision
|
|
559
|
+
? '两个来源提供同名 Skill,无法安全确定写入内容。'
|
|
560
|
+
: distributionUnavailable
|
|
561
|
+
? '插件没有可信的通用 Skills 分发定义。'
|
|
562
|
+
: '候选 Skill 没有通过 Portable 安全校验。';
|
|
565
563
|
const blocked = route.code === 'requiredConfigMissing'
|
|
566
564
|
? '缺少生成 Skill 所需的设置;没有调用 Skills CLI。'
|
|
567
|
-
:
|
|
568
|
-
? '
|
|
569
|
-
:
|
|
570
|
-
? '
|
|
571
|
-
:
|
|
572
|
-
? '
|
|
573
|
-
:
|
|
565
|
+
: cliMissing
|
|
566
|
+
? 'Skills CLI 尚未安装;当前不能开始安装。'
|
|
567
|
+
: cliIncompatible
|
|
568
|
+
? '已安装的 Skills CLI 版本不兼容;当前不能开始安装。'
|
|
569
|
+
: route.code === 'portableScopeUnavailable'
|
|
570
|
+
? '所选内容不能在这个范围完整生成;没有开始写入。'
|
|
571
|
+
: sensitiveOutput || invalidRuntimeImport || skillCollision || distributionUnavailable
|
|
572
|
+
? '没有开始写入,也没有调用 Skills CLI。'
|
|
573
|
+
: '候选内容没有通过安全校验;没有开始写入。';
|
|
574
574
|
const nextStep = route.code === 'requiredConfigMissing'
|
|
575
575
|
? '完成插件设置,然后重新准备。'
|
|
576
|
-
:
|
|
577
|
-
? '
|
|
578
|
-
:
|
|
579
|
-
? '
|
|
580
|
-
: '
|
|
576
|
+
: cliMissing
|
|
577
|
+
? '安装兼容的 Skills CLI,然后重新检查。'
|
|
578
|
+
: cliIncompatible
|
|
579
|
+
? '升级或切换到兼容的 Skills CLI,然后重新检查。'
|
|
580
|
+
: route.code === 'portableScopeUnavailable'
|
|
581
|
+
? '返回 Skill 选择或更改范围,然后重新检查。'
|
|
582
|
+
: skillCollision
|
|
583
|
+
? '返回修改 Skills,移除同名冲突后重新检查。'
|
|
584
|
+
: distributionUnavailable
|
|
585
|
+
? '修复插件的通用 Skills 分发定义,然后重新检查。'
|
|
586
|
+
: '修复来源内容或修改选择,然后重新检查。';
|
|
581
587
|
return {
|
|
582
588
|
severity: 'error',
|
|
583
589
|
title: route.code === 'portableScopeUnavailable' ? '所选范围不可用'
|
|
584
|
-
:
|
|
590
|
+
: cliMissing ? '未安装 Skills CLI'
|
|
591
|
+
: cliIncompatible ? 'Skills CLI 版本不兼容' : '无法准备通用 Skills',
|
|
585
592
|
userMessage,
|
|
586
593
|
impact: [
|
|
587
594
|
{ state: 'blocked', message: blocked },
|
|
@@ -816,6 +823,8 @@ function SemanticRows(props) {
|
|
|
816
823
|
focused: index === props.focus,
|
|
817
824
|
compact: props.compact,
|
|
818
825
|
};
|
|
826
|
+
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}`);
|
|
819
828
|
if (props.kind === 'section')
|
|
820
829
|
return _jsx(SectionRow, { ...rowProps }, `${action.label}:${index}`);
|
|
821
830
|
if (props.kind === 'choice') {
|
|
@@ -831,7 +840,7 @@ function SemanticRows(props) {
|
|
|
831
840
|
}
|
|
832
841
|
function PluginRows(props) {
|
|
833
842
|
return (_jsx(Box, { flexDirection: "column", children: props.rows.slice(props.viewport.offset, props.viewport.offset + props.viewport.limit)
|
|
834
|
-
.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, 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))) }));
|
|
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))) }));
|
|
835
844
|
}
|
|
836
845
|
function IssueTechnicalDetails(props) {
|
|
837
846
|
const visible = props.details.slice(0, props.compact ? 1 : 2);
|
|
@@ -874,7 +883,7 @@ function SourceTabs(props) {
|
|
|
874
883
|
}
|
|
875
884
|
function configTabs(page) {
|
|
876
885
|
return [
|
|
877
|
-
...page.groups.map((group) => ({
|
|
886
|
+
...page.groups.filter((group) => group.fields.length > 0).map((group) => ({
|
|
878
887
|
id: group.id,
|
|
879
888
|
label: group.title,
|
|
880
889
|
count: group.fields.length,
|
|
@@ -1050,11 +1059,12 @@ export function ConsoleAppV4(props) {
|
|
|
1050
1059
|
const windowSize = useWindowSize();
|
|
1051
1060
|
const size = props.size ?? windowSize;
|
|
1052
1061
|
const policy = resolveTuiSizePolicy(size.columns, size.rows);
|
|
1053
|
-
const [snapshot, setSnapshot] = useState(props.controller.snapshot);
|
|
1062
|
+
const [snapshot, setSnapshot] = useState(() => props.controller.snapshot);
|
|
1054
1063
|
const [overlay, setOverlay] = useState();
|
|
1055
1064
|
const [overlayFocus, setOverlayFocus] = useState(0);
|
|
1056
1065
|
const [operationConfirmation, setOperationConfirmation] = useState();
|
|
1057
1066
|
const [sourceTransportOverlay, setSourceTransportOverlay] = useState();
|
|
1067
|
+
const [pluginListChooser, setPluginListChooser] = useState();
|
|
1058
1068
|
const [projectReminderOpen, setProjectReminderOpen] = useState(false);
|
|
1059
1069
|
const [discardForExit, setDiscardForExit] = useState(false);
|
|
1060
1070
|
const [discardForSessionNavigation, setDiscardForSessionNavigation] = useState();
|
|
@@ -1064,10 +1074,10 @@ export function ConsoleAppV4(props) {
|
|
|
1064
1074
|
const [pinnedActionFocus, setPinnedActionFocus] = useState(0);
|
|
1065
1075
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
1066
1076
|
const [searchFocus, setSearchFocus] = useState(false);
|
|
1067
|
-
const [query, setQuery] = useState(
|
|
1068
|
-
const [installFilter, setInstallFilter] = useState(
|
|
1069
|
-
?? (
|
|
1070
|
-
&&
|
|
1077
|
+
const [query, setQuery] = useState(snapshot.router.current.context.query ?? '');
|
|
1078
|
+
const [installFilter, setInstallFilter] = useState(snapshot.router.current.context.installFilter
|
|
1079
|
+
?? (snapshot.router.current.route.name === 'pluginList'
|
|
1080
|
+
&& snapshot.router.current.route.mode === 'installed' ? 'installed' : 'all'));
|
|
1071
1081
|
const [scope, setScope] = useState('user');
|
|
1072
1082
|
const [selectedTargets, setSelectedTargets] = useState([]);
|
|
1073
1083
|
const [installPlatformScopes, setInstallPlatformScopes] = useState({});
|
|
@@ -1088,6 +1098,8 @@ export function ConsoleAppV4(props) {
|
|
|
1088
1098
|
const [inputValue, setInputValue] = useState('');
|
|
1089
1099
|
const [inputError, setInputError] = useState();
|
|
1090
1100
|
const [configActionError, setConfigActionError] = useState();
|
|
1101
|
+
const [configGroupChooserOpen, setConfigGroupChooserOpen] = useState(false);
|
|
1102
|
+
const [configGroupChoiceFocus, setConfigGroupChoiceFocus] = useState(0);
|
|
1091
1103
|
const [actionError, setActionError] = useState();
|
|
1092
1104
|
const [configResetAvailability, setConfigResetAvailability] = useState({});
|
|
1093
1105
|
const [sourceDraft, setSourceDraft] = useState();
|
|
@@ -1095,6 +1107,7 @@ export function ConsoleAppV4(props) {
|
|
|
1095
1107
|
const [batchTargetOffset, setBatchTargetOffset] = useState(0);
|
|
1096
1108
|
const [portableResultDetailOpen, setPortableResultDetailOpen] = useState(false);
|
|
1097
1109
|
const [portableResultDetailOffset, setPortableResultDetailOffset] = useState(0);
|
|
1110
|
+
const [portableRecheckBusy, setPortableRecheckBusy] = useState(false);
|
|
1098
1111
|
const portableResultDetailPairKey = useRef(undefined);
|
|
1099
1112
|
const portableResultDetailReturnFocus = useRef(0);
|
|
1100
1113
|
const [expandedDependencyPluginIds, setExpandedDependencyPluginIds] = useState();
|
|
@@ -1113,6 +1126,7 @@ export function ConsoleAppV4(props) {
|
|
|
1113
1126
|
const [portableAgentInput, setPortableAgentInput] = useState('');
|
|
1114
1127
|
const [portableSelectionView, setPortableSelectionView] = useState('all');
|
|
1115
1128
|
const [portableToolsOpen, setPortableToolsOpen] = useState(false);
|
|
1129
|
+
const [portableEmptySelectionOpen, setPortableEmptySelectionOpen] = useState(false);
|
|
1116
1130
|
const [reportSaveMode, setReportSaveMode] = useState(false);
|
|
1117
1131
|
const [reportSavePath, setReportSavePath] = useState('');
|
|
1118
1132
|
const [reportSaveResult, setReportSaveResult] = useState();
|
|
@@ -1127,9 +1141,16 @@ export function ConsoleAppV4(props) {
|
|
|
1127
1141
|
configDrafts, configCandidate, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
1128
1142
|
});
|
|
1129
1143
|
const externalDraftFlowIds = useRef(new Map());
|
|
1144
|
+
const configOverviewAnchors = useRef(new Map());
|
|
1130
1145
|
const exitSent = useRef(false);
|
|
1131
1146
|
const operationBusy = useRef(false);
|
|
1132
1147
|
const route = snapshot.router.current.route;
|
|
1148
|
+
if (route.name === 'configFields') {
|
|
1149
|
+
configOverviewAnchors.current.set(route.pluginId, {
|
|
1150
|
+
overviewFrameId: snapshot.router.currentFrameId,
|
|
1151
|
+
...(snapshot.router.currentFlowId ? { flowId: snapshot.router.currentFlowId } : {}),
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1133
1154
|
const configDraftCount = ['project', 'user'].reduce((count, draftScope) => {
|
|
1134
1155
|
const draft = configDrafts[draftScope];
|
|
1135
1156
|
return count + Object.keys(draft?.updates ?? {}).length + (draft?.removes.length ?? 0);
|
|
@@ -1357,6 +1378,10 @@ export function ConsoleAppV4(props) {
|
|
|
1357
1378
|
setInputValue('');
|
|
1358
1379
|
}
|
|
1359
1380
|
}, [route.name]);
|
|
1381
|
+
useEffect(() => {
|
|
1382
|
+
if (route.name !== 'configFields')
|
|
1383
|
+
setConfigGroupChooserOpen(false);
|
|
1384
|
+
}, [route.name]);
|
|
1360
1385
|
const configRoute = route.name === 'configFields'
|
|
1361
1386
|
|| route.name === 'configFieldEditor'
|
|
1362
1387
|
|| route.name === 'configObjectPropertyEditor'
|
|
@@ -1506,9 +1531,12 @@ export function ConsoleAppV4(props) {
|
|
|
1506
1531
|
: error instanceof Error
|
|
1507
1532
|
? safeDiagnosticCode(error.message.split(':', 1)[0], 'configUnavailable')
|
|
1508
1533
|
: 'configUnavailable';
|
|
1534
|
+
const overviewAnchor = configOverviewAnchors.current.get(expectedPluginId);
|
|
1509
1535
|
props.controller.replace({
|
|
1510
1536
|
name: 'configUnavailable',
|
|
1511
1537
|
pluginId: expectedPluginId,
|
|
1538
|
+
...(overviewAnchor && overviewAnchor.flowId === snapshot.router.currentFlowId
|
|
1539
|
+
? { overviewFrameId: overviewAnchor.overviewFrameId } : {}),
|
|
1512
1540
|
diagnosticCode: code,
|
|
1513
1541
|
diagnosticStage: 'read-config',
|
|
1514
1542
|
...(invalidStores ? { invalidStores } : {}),
|
|
@@ -1869,6 +1897,10 @@ export function ConsoleAppV4(props) {
|
|
|
1869
1897
|
if (route.name !== 'portableSkillSelect')
|
|
1870
1898
|
setPortableToolsOpen(false);
|
|
1871
1899
|
}, [route.name]);
|
|
1900
|
+
useEffect(() => {
|
|
1901
|
+
if (route.name !== 'portableSkillSelect')
|
|
1902
|
+
setPortableEmptySelectionOpen(false);
|
|
1903
|
+
}, [route.name]);
|
|
1872
1904
|
const screenId = resolveConsoleV4ScreenId(snapshot, props.controller, overlay, tooSmall, searchFocus);
|
|
1873
1905
|
const portableVariantCandidates = route.name === 'portableSkillSelect' && snapshot.portableDraft
|
|
1874
1906
|
? snapshot.portableDraft.candidates.filter(portableScopeVariantsDiffer)
|
|
@@ -1880,6 +1912,7 @@ export function ConsoleAppV4(props) {
|
|
|
1880
1912
|
: [];
|
|
1881
1913
|
const supportsTechnicalDetails = snapshot.phase === 'error'
|
|
1882
1914
|
|| Boolean(snapshot.projectIntentIssue)
|
|
1915
|
+
|| route.name === 'projectStatus'
|
|
1883
1916
|
|| Boolean(route.name === 'reviewPreparing' && snapshot.reviewPreparation?.code)
|
|
1884
1917
|
|| Boolean(route.name === 'progress' && snapshot.progress.messages.length > 0)
|
|
1885
1918
|
|| Boolean(route.name === 'review' && snapshot.activeReview?.technicalDetails?.length)
|
|
@@ -1915,6 +1948,8 @@ export function ConsoleAppV4(props) {
|
|
|
1915
1948
|
let selectAllPortableCandidates;
|
|
1916
1949
|
let togglePluginListFilter;
|
|
1917
1950
|
let movePluginListSource;
|
|
1951
|
+
let choosePluginListFilter;
|
|
1952
|
+
let choosePluginListSource;
|
|
1918
1953
|
let visibleConfigTabs = [];
|
|
1919
1954
|
let activeConfigTabId;
|
|
1920
1955
|
const dynamicMenu = plugin && route.name === 'platform'
|
|
@@ -1953,8 +1988,9 @@ export function ConsoleAppV4(props) {
|
|
|
1953
1988
|
const base = policy.mode === 'compact' ? compactRows : standardRows;
|
|
1954
1989
|
return base + Math.max(0, size.rows - baseline);
|
|
1955
1990
|
};
|
|
1956
|
-
const sourceTechnicalPageRows =
|
|
1957
|
-
|
|
1991
|
+
const sourceTechnicalPageRows = Math.max(1, contentRowBudget({
|
|
1992
|
+
terminalRows: size.rows, description: false, compact: policy.mode === 'compact', staticRows: 6,
|
|
1993
|
+
}));
|
|
1958
1994
|
const resolveSourceTechnicalDiagnostic = (sourceId) => {
|
|
1959
1995
|
const overview = props.controller.getSourceOverview(sourceId);
|
|
1960
1996
|
return sourceDiagnostic(sourceId, overview.health?.state ?? overview.source?.state, overview.detail, props.controller.getCatalogGeneration(sourceId), props.controller.getCatalogFreshness(sourceId)) ?? {
|
|
@@ -2201,7 +2237,7 @@ export function ConsoleAppV4(props) {
|
|
|
2201
2237
|
showSelected: false,
|
|
2202
2238
|
});
|
|
2203
2239
|
setDirty(true);
|
|
2204
|
-
setFocus(
|
|
2240
|
+
setFocus(0);
|
|
2205
2241
|
setScrollOffset(0);
|
|
2206
2242
|
setSearchFocus(false);
|
|
2207
2243
|
};
|
|
@@ -2742,12 +2778,39 @@ export function ConsoleAppV4(props) {
|
|
|
2742
2778
|
}
|
|
2743
2779
|
return `${field.effectiveDisplayValue}${field.unit && field.effectiveDisplayValue !== '未设置' ? ` ${field.unit}` : ''}`;
|
|
2744
2780
|
};
|
|
2781
|
+
const pendingFieldDisplay = (field, scope) => {
|
|
2782
|
+
const draft = configDrafts[scope];
|
|
2783
|
+
if (!draft)
|
|
2784
|
+
return undefined;
|
|
2785
|
+
if (draft.removes.includes(field.key))
|
|
2786
|
+
return '将删除此位置的覆盖并恢复继承';
|
|
2787
|
+
if (!Object.hasOwn(draft.updates, field.key))
|
|
2788
|
+
return undefined;
|
|
2789
|
+
const value = draft.updates[field.key];
|
|
2790
|
+
if (field.sensitive)
|
|
2791
|
+
return '将更新';
|
|
2792
|
+
return typeof value === 'object' ? objectSummary(field, value) : scalarSummary(field, value);
|
|
2793
|
+
};
|
|
2794
|
+
const pendingFieldTargets = (field) => ['project', 'user'].flatMap((scope) => {
|
|
2795
|
+
const value = pendingFieldDisplay(field, scope);
|
|
2796
|
+
return value ? [{ scope, value }] : [];
|
|
2797
|
+
});
|
|
2745
2798
|
const fieldEditor = (field) => {
|
|
2746
2799
|
return field.type;
|
|
2747
2800
|
};
|
|
2748
2801
|
const fieldEditorMode = (field) => (field.choices?.length ?? 0) > 0 ? { mode: 'choices' } : {};
|
|
2749
2802
|
const openConfigTarget = (candidate) => {
|
|
2750
|
-
|
|
2803
|
+
const currentFlowId = snapshot.router.currentFlowId;
|
|
2804
|
+
const storedAnchor = configOverviewAnchors.current.get(candidate.pluginId);
|
|
2805
|
+
const storedOverviewFrameId = storedAnchor && storedAnchor.flowId === currentFlowId
|
|
2806
|
+
? storedAnchor.overviewFrameId : undefined;
|
|
2807
|
+
const overviewFrameId = storedOverviewFrameId
|
|
2808
|
+
?? props.controller.configOverviewFrameId(currentFlowId, candidate.pluginId);
|
|
2809
|
+
setConfigCandidate({
|
|
2810
|
+
...candidate,
|
|
2811
|
+
...(currentFlowId ? { flowId: currentFlowId } : {}),
|
|
2812
|
+
...(overviewFrameId ? { overviewFrameId } : {}),
|
|
2813
|
+
});
|
|
2751
2814
|
go({
|
|
2752
2815
|
name: 'configLayer',
|
|
2753
2816
|
pluginId: candidate.pluginId,
|
|
@@ -2917,17 +2980,21 @@ export function ConsoleAppV4(props) {
|
|
|
2917
2980
|
const next = { ...configDrafts, [layer.scope]: { updates, removes: [...removes] } };
|
|
2918
2981
|
if (Object.keys(updates).length === 0 && removes.size === 0)
|
|
2919
2982
|
delete next[layer.scope];
|
|
2920
|
-
setConfigDrafts(next);
|
|
2921
|
-
setDirty(Object.keys(next).length > 0);
|
|
2922
2983
|
const target = configCandidate.fieldKey
|
|
2923
2984
|
? configFieldTarget(configPage ?? page, configCandidate.fieldKey)
|
|
2924
2985
|
: configCandidate.groupId ? { groupId: configCandidate.groupId, focusIndex: 0 } : undefined;
|
|
2986
|
+
const returned = props.controller.returnToConfigOverview(configCandidate.flowId, configCandidate.overviewFrameId, route.pluginId, target ? { configGroupId: target.groupId, focusIndex: target.focusIndex, scrollOffset: 0 } : undefined);
|
|
2987
|
+
if (!returned) {
|
|
2988
|
+
setConfigActionError('设置总览已变化。请返回插件详情后重新打开设置。');
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
if (!configCandidate.flowId) {
|
|
2992
|
+
props.controller.replace({ name: 'configFields', pluginId: route.pluginId, layer: { scope: 'effective' } }, target ? { configGroupId: target.groupId, focusIndex: target.focusIndex, scrollOffset: 0 } : undefined);
|
|
2993
|
+
}
|
|
2994
|
+
setConfigDrafts(next);
|
|
2995
|
+
setDirty(Object.keys(next).length > 0);
|
|
2925
2996
|
setConfigCandidate(undefined);
|
|
2926
|
-
|
|
2927
|
-
props.controller.back();
|
|
2928
|
-
props.controller.replace({ name: 'configFields', pluginId: route.pluginId, layer: { scope: 'effective' } }, target
|
|
2929
|
-
? { configGroupId: target.groupId, focusIndex: target.focusIndex, scrollOffset: 0 } : undefined);
|
|
2930
|
-
setFocus(0);
|
|
2997
|
+
setFocus(target?.focusIndex ?? 0);
|
|
2931
2998
|
setScrollOffset(0);
|
|
2932
2999
|
};
|
|
2933
3000
|
const configChanges = (page, draft) => {
|
|
@@ -3117,6 +3184,7 @@ export function ConsoleAppV4(props) {
|
|
|
3117
3184
|
const projectIntentIssue = snapshot.projectIntentIssue;
|
|
3118
3185
|
const projectPendingCount = (projectReminder?.actionableTargets.length ?? 0)
|
|
3119
3186
|
+ (projectReminder?.blockedTargets.length ?? 0);
|
|
3187
|
+
const projectTargetCount = projectPendingCount + (projectReminder?.unconfirmedTargets.length ?? 0);
|
|
3120
3188
|
context = '当前项目';
|
|
3121
3189
|
actions = [
|
|
3122
3190
|
...(projectIntentIssue ? [{
|
|
@@ -3128,19 +3196,21 @@ export function ConsoleAppV4(props) {
|
|
|
3128
3196
|
? '本项目插件声明无效;修复后重新加载'
|
|
3129
3197
|
: '无法读取本项目插件声明;检查项目位置或权限后重新加载',
|
|
3130
3198
|
status: 'warning',
|
|
3131
|
-
run: () =>
|
|
3199
|
+
run: () => go({ name: 'projectStatus' }),
|
|
3132
3200
|
}] : []),
|
|
3133
|
-
...(
|
|
3201
|
+
...(projectTargetCount > 0 ? [{
|
|
3134
3202
|
label: '项目插件',
|
|
3135
3203
|
description: projectReminder.actionableTargets.length > 0
|
|
3136
|
-
? `${projectReminder.actionableTargets.length}
|
|
3137
|
-
+ `${projectReminder.blockedTargets.length}
|
|
3138
|
-
|
|
3139
|
-
|
|
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),
|
|
3140
3212
|
status: 'warning',
|
|
3141
|
-
run: () =>
|
|
3142
|
-
? prepareCurrentProjectTargets(projectReminder.actionableTargets)
|
|
3143
|
-
: prepareCurrentProjectPlugins(),
|
|
3213
|
+
run: () => go({ name: 'projectStatus' }),
|
|
3144
3214
|
}] : []),
|
|
3145
3215
|
{ label: '插件', description: '浏览、安装和管理所有插件', meta: `${home.installedPlugins} 已安装`, status: home.pluginUpdateCount > 0 ? 'warning' : undefined, run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
3146
3216
|
{ label: '需要处理', description: '解决安装、来源或设置问题', meta: String(home.issueCount), status: 'warning', run: () => go({ name: 'issueList' }) },
|
|
@@ -3154,16 +3224,180 @@ export function ConsoleAppV4(props) {
|
|
|
3154
3224
|
run: () => go({ name: 'sourceList' }),
|
|
3155
3225
|
},
|
|
3156
3226
|
];
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
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
|
+
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
|
+
: projectReminder && projectReminder.unconfirmedTargets.length > 0
|
|
3235
|
+
? '? 部分项目插件状态无法确认' : projectPendingCount > 0
|
|
3236
|
+
? `! ${projectPendingCount} 个位置待处理`
|
|
3237
|
+
: 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", cards: !showTechnical, columns: size.columns - 2, compact: policy.mode === 'compact', offset: homeViewport.offset, limit: homeViewport.limit })] });
|
|
3239
|
+
}
|
|
3240
|
+
else if (route.name === 'projectStatus') {
|
|
3241
|
+
const reminder = snapshot.projectIntentReminder;
|
|
3242
|
+
const projectIntentIssue = snapshot.projectIntentIssue;
|
|
3243
|
+
const actionable = reminder?.actionableTargets ?? [];
|
|
3244
|
+
const blocked = reminder?.blockedTargets ?? [];
|
|
3245
|
+
const unconfirmed = reminder?.unconfirmedTargets ?? [];
|
|
3246
|
+
const allTargets = [
|
|
3247
|
+
...actionable.map((target) => ({ target, state: 'actionable' })),
|
|
3248
|
+
...blocked.map((target) => ({ target, state: 'blocked' })),
|
|
3249
|
+
...unconfirmed.map((target) => ({ target, state: 'unconfirmed' })),
|
|
3250
|
+
];
|
|
3251
|
+
const pluginCount = new Set(allTargets.map(({ target }) => target.pluginId)).size;
|
|
3252
|
+
const sourceLabel = (pluginId) => pluginId.split('/')[0] ?? pluginId;
|
|
3253
|
+
const isSourceBlocker = (reasonCodes) => reasonCodes.some((code) => code === 'catalogSourceUnavailable' || code === 'sourceUnavailable'
|
|
3254
|
+
|| code === 'catalogSourceMissing' || code === 'sourceMissing' || code === 'bundleUnavailable'
|
|
3255
|
+
|| code === 'codexIntentSourceUnavailable' || code === 'sourceChanged');
|
|
3256
|
+
const sourceBlockedCount = blocked.filter((target) => isSourceBlocker(target.reasonCodes)).length;
|
|
3257
|
+
const reasonLabel = (reasonCodes, state) => {
|
|
3258
|
+
if (reasonCodes.includes('sourceChanged'))
|
|
3259
|
+
return '来源身份已变化';
|
|
3260
|
+
if (reasonCodes.includes('sourceMissing'))
|
|
3261
|
+
return '来源已移除';
|
|
3262
|
+
if (reasonCodes.includes('bundleUnavailable'))
|
|
3263
|
+
return '当前来源目录没有此插件';
|
|
3264
|
+
if (isSourceBlocker(reasonCodes))
|
|
3265
|
+
return '来源当前不可用';
|
|
3266
|
+
if (reasonCodes.includes('awCapabilityUnsupported'))
|
|
3267
|
+
return '当前 AW 不支持所需能力';
|
|
3268
|
+
if (reasonCodes.includes('codexExtensionScopeConflict'))
|
|
3269
|
+
return '使用位置冲突';
|
|
3270
|
+
if (reasonCodes.includes('artifactCollision'))
|
|
3271
|
+
return '输出内容冲突';
|
|
3272
|
+
if (reasonCodes.includes('codexSharedNativeForeign'))
|
|
3273
|
+
return '共享位置存在非本插件内容';
|
|
3274
|
+
if (reasonCodes.some((code) => code === 'codexSharedNativeUnobserved'
|
|
3275
|
+
|| code === 'codexNativeObservationFailed'))
|
|
3276
|
+
return '平台状态未确认';
|
|
3277
|
+
if (state === 'actionable')
|
|
3278
|
+
return '已确认需要同步';
|
|
3279
|
+
if (state === 'blocked')
|
|
3280
|
+
return '当前目标受阻';
|
|
3281
|
+
return '当前状态未确认';
|
|
3282
|
+
};
|
|
3283
|
+
breadcrumb = '需要处理 / 本项目';
|
|
3284
|
+
title = projectIntentIssue || unconfirmed.length > 0
|
|
3285
|
+
? '尚未确认项目插件状态'
|
|
3286
|
+
: blocked.length > 0 ? '部分项目插件需要先处理'
|
|
3287
|
+
: allTargets.length === 0 ? '项目插件状态已检查' : '项目共享插件';
|
|
3288
|
+
description = projectIntentIssue
|
|
3289
|
+
? projectIntentIssue.state === 'invalid'
|
|
3290
|
+
? '本项目插件声明无效。修复声明后重新检查。'
|
|
3291
|
+
: '读取本项目插件声明失败。当前不会执行安装或恢复。'
|
|
3292
|
+
: allTargets.length === 0
|
|
3293
|
+
? '当前没有待同步或待检查的项目插件位置。'
|
|
3294
|
+
: `${pluginCount} 个插件涉及 ${allTargets.length} 个位置:`
|
|
3295
|
+
+ `${actionable.length} 个可同步 · ${blocked.length} 个受阻 · ${unconfirmed.length} 个未确认`;
|
|
3296
|
+
context = allTargets.length > 0 ? `${pluginCount} 个插件 · ${allTargets.length} 个位置` : '当前项目';
|
|
3297
|
+
actions = allTargets.map(({ target, state }) => {
|
|
3298
|
+
const overview = props.controller.getPluginOverview(target.pluginId);
|
|
3299
|
+
const status = state === 'actionable' ? 'warning'
|
|
3300
|
+
: state === 'blocked' ? 'error' : 'muted';
|
|
3301
|
+
const symbol = state === 'actionable' ? '!' : state === 'blocked' ? '!' : '?';
|
|
3302
|
+
return {
|
|
3303
|
+
actionId: `project-target:${target.pluginId}:${target.platform}:${target.scope}`,
|
|
3304
|
+
label: overview?.displayName ?? target.pluginId.split('/').at(-1) ?? target.pluginId,
|
|
3305
|
+
description: `${platformLabel(target.platform)} · 当前项目 · ${reasonLabel(target.reasonCodes, state)}`,
|
|
3306
|
+
meta: `来源 ${sourceLabel(target.pluginId)} · 项目共享声明`,
|
|
3307
|
+
tags: _jsx(StatusBadge, { items: [{
|
|
3308
|
+
key: state,
|
|
3309
|
+
label: state === 'actionable' ? '待同步' : state === 'blocked' ? '需处理' : '未确认',
|
|
3310
|
+
symbol,
|
|
3311
|
+
status,
|
|
3312
|
+
}] }),
|
|
3313
|
+
status,
|
|
3314
|
+
run: state === 'blocked' && isSourceBlocker(target.reasonCodes)
|
|
3315
|
+
? () => {
|
|
3316
|
+
const sourceId = sourceLabel(target.pluginId);
|
|
3317
|
+
go(props.controller.getSourceOverview(sourceId).source
|
|
3318
|
+
? { name: 'sourceDetail', sourceId } : { name: 'sourceList' });
|
|
3319
|
+
}
|
|
3320
|
+
: state === 'unconfirmed' || !overview ? () => setShowTechnical(true)
|
|
3321
|
+
: () => go({ name: 'pluginDetail', pluginId: target.pluginId }),
|
|
3322
|
+
};
|
|
3323
|
+
});
|
|
3324
|
+
pinnedActions = [
|
|
3325
|
+
...(actionable.length > 0 ? [{
|
|
3326
|
+
actionId: 'project-sync-actionable',
|
|
3327
|
+
label: `同步明确的 ${actionable.length} 个位置`,
|
|
3328
|
+
description: blocked.length + unconfirmed.length > 0
|
|
3329
|
+
? `保留 ${blocked.length} 个受阻位置和 ${unconfirmed.length} 个未确认位置;进入现有操作汇总后再确认`
|
|
3330
|
+
: '进入现有操作汇总后再确认',
|
|
3331
|
+
run: () => prepareCurrentProjectTargets(actionable),
|
|
3332
|
+
}] : []),
|
|
3333
|
+
...(sourceBlockedCount > 0 ? [{
|
|
3334
|
+
actionId: 'project-open-sources',
|
|
3335
|
+
label: '处理插件来源',
|
|
3336
|
+
description: `${sourceBlockedCount} 个位置需要先处理来源问题`,
|
|
3337
|
+
run: () => go({ name: 'sourceList' }),
|
|
3338
|
+
}] : []),
|
|
3339
|
+
{
|
|
3340
|
+
actionId: 'project-recheck',
|
|
3341
|
+
label: '重新检查',
|
|
3342
|
+
description: '重新读取项目声明和平台状态;不会自动安装或恢复',
|
|
3343
|
+
run: () => requestSessionNavigation('reload'),
|
|
3344
|
+
},
|
|
3345
|
+
...(projectIntentIssue || blocked.length > 0 || unconfirmed.length > 0 ? [{
|
|
3346
|
+
actionId: 'project-error-details',
|
|
3347
|
+
label: '错误详情',
|
|
3348
|
+
description: '查看现有安全错误代码和诊断,不执行写操作',
|
|
3349
|
+
run: () => setShowTechnical((value) => !value),
|
|
3350
|
+
}] : []),
|
|
3351
|
+
{
|
|
3352
|
+
actionId: 'project-back-home',
|
|
3353
|
+
label: '返回首页',
|
|
3354
|
+
run: back,
|
|
3355
|
+
},
|
|
3356
|
+
];
|
|
3357
|
+
help = showTechnical
|
|
3358
|
+
? 'p/n 或 PgUp/PgDn 阅读 · d 返回项目位置 · Tab 固定操作 · Esc 返回'
|
|
3359
|
+
: focusZone === 'actions'
|
|
3360
|
+
? '←→ 移动固定操作 · Enter 选择 · Tab 返回项目位置 · Esc 返回'
|
|
3361
|
+
: '↑↓ 移动 · Enter 查看 · Tab 固定操作 · Esc 返回';
|
|
3362
|
+
const viewport = rowViewport({
|
|
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 = [
|
|
3374
|
+
...(projectIntentIssue ? [
|
|
3375
|
+
`项目声明 · ${safeDiagnosticCode(projectIntentIssue.code, 'projectIntentUnavailable')}`,
|
|
3376
|
+
...wrapConsoleText(`说明 · ${sanitizeDiagnosticText(projectIntentIssue.message)}`, Math.max(20, size.columns - 8)),
|
|
3377
|
+
] : []),
|
|
3378
|
+
...allTargets.flatMap(({ target, state }) => [
|
|
3379
|
+
...wrapConsoleText(`${target.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`, Math.max(20, size.columns - 8)),
|
|
3380
|
+
...(target.reasonCodes.length > 0
|
|
3381
|
+
? target.reasonCodes.flatMap((code) => wrapConsoleText(`原因 · ${safeDiagnosticCode(code, 'projectTargetUnknown')}`, Math.max(20, size.columns - 8)))
|
|
3382
|
+
: [state === 'actionable' ? '原因 · confirmedDifference'
|
|
3383
|
+
: state === 'blocked' ? '原因 · blocked' : '原因 · unconfirmed']),
|
|
3384
|
+
]),
|
|
3385
|
+
];
|
|
3386
|
+
const technicalPageRows = responsiveBudget(4, 8);
|
|
3387
|
+
const technicalOffset = Math.min(batchTargetOffset, Math.max(0, technicalLines.length - 1));
|
|
3388
|
+
const visibleTechnicalLines = technicalLines.slice(technicalOffset, technicalOffset + technicalPageRows);
|
|
3389
|
+
referenceDetailPagination = showTechnical && technicalLines.length > 0
|
|
3390
|
+
? { total: technicalLines.length, pageRows: technicalPageRows }
|
|
3391
|
+
: undefined;
|
|
3392
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action, index) => ({
|
|
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
|
|
3397
|
+
? _jsx(StaticRow, { status: "info", title: `只同步明确项;保留 ${blocked.length} 个受阻位置和 ${unconfirmed.length} 个未确认位置。`, compact: true })
|
|
3398
|
+
: null, showTechnical ? _jsxs(TechnicalDetails, { title: "\u9879\u76EE\u72B6\u6001\u5B89\u5168\u8BE6\u60C5", compact: true, children: [technicalOffset > 0
|
|
3399
|
+
? _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(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 }) })] });
|
|
3167
3401
|
}
|
|
3168
3402
|
else if (route.name === 'pluginList') {
|
|
3169
3403
|
const sourceId = route.sourceId ?? 'all';
|
|
@@ -3193,10 +3427,8 @@ export function ConsoleAppV4(props) {
|
|
|
3193
3427
|
actions = sourceError
|
|
3194
3428
|
? [{ label: '查看来源问题', description: '当前不能浏览这个来源', run: () => go({ name: 'issueDetail', issueId: `source:${sourceId}:unavailable` }) }]
|
|
3195
3429
|
: [];
|
|
3196
|
-
|
|
3430
|
+
choosePluginListFilter = (nextFilter) => {
|
|
3197
3431
|
const selectedPluginId = pluginRows[focus]?.pluginId ?? pluginListFocusId.current;
|
|
3198
|
-
const nextFilter = PLUGIN_FILTERS[(PLUGIN_FILTERS.indexOf(installFilter) + 1)
|
|
3199
|
-
% PLUGIN_FILTERS.length];
|
|
3200
3432
|
const nextItems = props.controller.listPlugins({
|
|
3201
3433
|
mode: route.mode,
|
|
3202
3434
|
...(route.sourceId ? { sourceId: route.sourceId } : {}),
|
|
@@ -3210,11 +3442,13 @@ export function ConsoleAppV4(props) {
|
|
|
3210
3442
|
setFocus(retained >= 0 ? retained : 0);
|
|
3211
3443
|
setScrollOffset(0);
|
|
3212
3444
|
};
|
|
3213
|
-
|
|
3214
|
-
const
|
|
3445
|
+
togglePluginListFilter = () => {
|
|
3446
|
+
const nextFilter = PLUGIN_FILTERS[(PLUGIN_FILTERS.indexOf(installFilter) + 1)
|
|
3447
|
+
% PLUGIN_FILTERS.length];
|
|
3448
|
+
choosePluginListFilter?.(nextFilter);
|
|
3449
|
+
};
|
|
3450
|
+
choosePluginListSource = (next) => {
|
|
3215
3451
|
const selectedPluginId = pluginRows[focus]?.pluginId;
|
|
3216
|
-
const index = tabs.indexOf(sourceId);
|
|
3217
|
-
const next = tabs[(index + direction + tabs.length) % tabs.length];
|
|
3218
3452
|
const nextItems = props.controller.listPlugins({
|
|
3219
3453
|
mode: route.mode,
|
|
3220
3454
|
...(next !== 'all' ? { sourceId: next } : {}),
|
|
@@ -3232,6 +3466,11 @@ export function ConsoleAppV4(props) {
|
|
|
3232
3466
|
setFocus(retainedFocus);
|
|
3233
3467
|
setScrollOffset(retainedFocus >= 4 ? retainedFocus - 3 : 0);
|
|
3234
3468
|
};
|
|
3469
|
+
movePluginListSource = (direction) => {
|
|
3470
|
+
const tabs = ['all', ...props.controller.listSources().map((source) => source.id)];
|
|
3471
|
+
const index = tabs.indexOf(sourceId);
|
|
3472
|
+
choosePluginListSource?.(tabs[(index + direction + tabs.length) % tabs.length]);
|
|
3473
|
+
};
|
|
3235
3474
|
const listedPluginIds = new Set(items.map((item) => item.pluginId));
|
|
3236
3475
|
const recommendedUpdateCandidates = props.controller.listPluginWriteTaskCandidates('update')
|
|
3237
3476
|
.filter((candidate) => candidate.eligible && listedPluginIds.has(candidate.target.pluginId));
|
|
@@ -3275,13 +3514,20 @@ export function ConsoleAppV4(props) {
|
|
|
3275
3514
|
{
|
|
3276
3515
|
actionId: 'plugin-list-filter',
|
|
3277
3516
|
label: `筛选:${pluginFilterLabel(installFilter)}`,
|
|
3278
|
-
run:
|
|
3517
|
+
run: () => setPluginListChooser({
|
|
3518
|
+
kind: 'status',
|
|
3519
|
+
focus: Math.max(0, PLUGIN_FILTERS.indexOf(installFilter)),
|
|
3520
|
+
}),
|
|
3279
3521
|
},
|
|
3280
3522
|
{ actionId: 'plugin-list-search', label: '/ 搜索', run: () => setSearchFocus(true) },
|
|
3281
3523
|
{
|
|
3282
3524
|
actionId: 'plugin-list-source',
|
|
3283
3525
|
label: `来源:${sourceId === 'all' ? '全部' : sourceId}`,
|
|
3284
|
-
run: () =>
|
|
3526
|
+
run: () => setPluginListChooser({
|
|
3527
|
+
kind: 'source',
|
|
3528
|
+
focus: Math.max(0, ['all', ...props.controller.listSources().map((source) => source.id)]
|
|
3529
|
+
.indexOf(sourceId)),
|
|
3530
|
+
}),
|
|
3285
3531
|
},
|
|
3286
3532
|
];
|
|
3287
3533
|
const pluginRowsById = new Map([
|
|
@@ -3293,7 +3539,8 @@ export function ConsoleAppV4(props) {
|
|
|
3293
3539
|
? '来源当前没有这个插件;可以查看已保存设置或定点重置设置层。'
|
|
3294
3540
|
: item.description,
|
|
3295
3541
|
...(item.primaryAttention ? { attention: item.primaryAttention.message } : {}),
|
|
3296
|
-
placementSummary:
|
|
3542
|
+
placementSummary: '',
|
|
3543
|
+
tags: pluginPlatformBadges(item),
|
|
3297
3544
|
statusLabel: statusText(item),
|
|
3298
3545
|
status: pluginRowStatus(item),
|
|
3299
3546
|
}]),
|
|
@@ -3374,7 +3621,7 @@ export function ConsoleAppV4(props) {
|
|
|
3374
3621
|
id: action.actionId ?? `${action.label}:${index}`,
|
|
3375
3622
|
label: action.label,
|
|
3376
3623
|
disabled: Boolean(action.disabled),
|
|
3377
|
-
})), active: 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
|
|
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
|
|
3378
3625
|
? _jsx(InputDisplay, { value: query, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3379
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
|
|
3380
3627
|
? _jsx(Callout, { status: "error", title: `${sourceId} 暂时无法提供插件`, description: "\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })
|
|
@@ -3383,8 +3630,8 @@ export function ConsoleAppV4(props) {
|
|
|
3383
3630
|
? _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" })
|
|
3384
3631
|
: _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" })
|
|
3385
3632
|
: null, _jsx(ContentViewport, { children: pluginRows.length > 0
|
|
3386
|
-
? _jsx(PluginRows, { rows: pluginRows, focus: focus, viewport: viewport, active: focusZone === 'content' })
|
|
3387
|
-
: _jsx(SemanticRows, { actions: actions, focus: focusZone === 'content' ? focus : -1, kind: "action", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit }) })] }));
|
|
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 }) })] }));
|
|
3388
3635
|
}
|
|
3389
3636
|
else if (route.name === 'batchSelection') {
|
|
3390
3637
|
breadcrumb = '插件 / 选择多个';
|
|
@@ -3419,6 +3666,7 @@ export function ConsoleAppV4(props) {
|
|
|
3419
3666
|
: statusText(plugin)}`
|
|
3420
3667
|
: `${isSelected ? '可取消选择' : '不可选择'}:${blockReason}`,
|
|
3421
3668
|
meta: plugin ? batchPluginVersionSummary(plugin) : '版本待确认',
|
|
3669
|
+
tags: plugin ? pluginPlatformBadges(plugin) : undefined,
|
|
3422
3670
|
selected: isSelected,
|
|
3423
3671
|
pluginId: candidate.pluginId,
|
|
3424
3672
|
canSelectNew: blockReason === undefined,
|
|
@@ -3467,9 +3715,10 @@ export function ConsoleAppV4(props) {
|
|
|
3467
3715
|
} : current);
|
|
3468
3716
|
setFocus(0);
|
|
3469
3717
|
setScrollOffset(0);
|
|
3718
|
+
setFocusZone('content');
|
|
3470
3719
|
},
|
|
3471
3720
|
};
|
|
3472
|
-
const
|
|
3721
|
+
const selectionToolActions = [
|
|
3473
3722
|
{
|
|
3474
3723
|
label: '返回插件选择',
|
|
3475
3724
|
description: '保留当前已选插件',
|
|
@@ -3477,6 +3726,7 @@ export function ConsoleAppV4(props) {
|
|
|
3477
3726
|
setBatchDraft((current) => current ? { ...current, showSelectionTools: false } : current);
|
|
3478
3727
|
setFocus(0);
|
|
3479
3728
|
setScrollOffset(0);
|
|
3729
|
+
setFocusZone('content');
|
|
3480
3730
|
},
|
|
3481
3731
|
},
|
|
3482
3732
|
{
|
|
@@ -3499,40 +3749,41 @@ export function ConsoleAppV4(props) {
|
|
|
3499
3749
|
label: '清空全部已选',
|
|
3500
3750
|
run: () => setBatchDraft((current) => current ? { ...current, selectedPluginIds: [] } : current),
|
|
3501
3751
|
},
|
|
3502
|
-
] : [
|
|
3503
|
-
selectedSetAction,
|
|
3504
|
-
{
|
|
3505
|
-
label: '选择工具',
|
|
3506
|
-
description: '全选、取消当前结果或清空全部选择',
|
|
3507
|
-
run: () => {
|
|
3508
|
-
setBatchDraft((current) => current ? { ...current, showSelectionTools: true } : current);
|
|
3509
|
-
setFocus(candidateActions.length + 1);
|
|
3510
|
-
setScrollOffset(Math.max(0, candidateActions.length));
|
|
3511
|
-
},
|
|
3512
|
-
},
|
|
3513
3752
|
];
|
|
3514
|
-
|
|
3515
|
-
|
|
3753
|
+
const selectionToolsAction = {
|
|
3754
|
+
actionId: 'batch-selection-tools',
|
|
3755
|
+
label: batchDraft.showSelectionTools ? '返回插件选择' : '选择工具',
|
|
3756
|
+
description: '全选、取消当前结果或清空全部选择',
|
|
3757
|
+
run: () => {
|
|
3758
|
+
setBatchDraft((current) => current ? {
|
|
3759
|
+
...current,
|
|
3760
|
+
showSelectionTools: !current.showSelectionTools,
|
|
3761
|
+
} : current);
|
|
3762
|
+
setFocus(0);
|
|
3763
|
+
setScrollOffset(0);
|
|
3764
|
+
setFocusZone('content');
|
|
3765
|
+
},
|
|
3766
|
+
};
|
|
3767
|
+
actions = batchDraft.showSelectionTools ? selectionToolActions : candidateActions;
|
|
3768
|
+
pinnedActions = [continueAction, selectedSetAction, selectionToolsAction];
|
|
3769
|
+
const batchFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
3516
3770
|
context = `已选 ${batchDraft.selectedPluginIds.length} 个 · 显示 ${candidates.length} 个${hiddenSelected > 0 ? ` · 隐藏 ${hiddenSelected} 个` : ''}`;
|
|
3517
|
-
help =
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
const candidateLimit = Math.max(0, Math.min(candidateActions.length, viewport.offset + viewport.limit) - candidateOffset);
|
|
3530
|
-
const utilityOffset = Math.max(0, viewport.offset - candidateActions.length);
|
|
3531
|
-
const utilityLimit = Math.max(0, Math.min(utilityActions.length, viewport.offset + viewport.limit - candidateActions.length)
|
|
3532
|
-
- utilityOffset);
|
|
3533
|
-
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
|
|
3771
|
+
help = batchDraft.showSelectionTools
|
|
3772
|
+
? '↑↓ 移动 · Enter 执行 · Tab 操作栏 · Esc 返回选择'
|
|
3773
|
+
: '↑↓ 移动 · Space 选择 · Enter 查看 · Tab 操作栏 · c 查看确认 · ←→ 来源 · / 搜索 · f 状态 · Esc 返回';
|
|
3774
|
+
const cardWidth = Math.max(20, size.columns - 2);
|
|
3775
|
+
const viewport = batchDraft.showSelectionTools
|
|
3776
|
+
? actionViewport(actions, policy.mode === 'compact', batchFocus, scrollOffset, Math.max(1, pageActionBudget(4)))
|
|
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
|
|
3534
3783
|
? _jsx(InputDisplay, { value: batchDraft.query, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3535
|
-
: _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(
|
|
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))) }) })] });
|
|
3536
3787
|
}
|
|
3537
3788
|
else {
|
|
3538
3789
|
const selectedIds = batchDraft.selectedPluginIds;
|
|
@@ -3929,13 +4180,19 @@ export function ConsoleAppV4(props) {
|
|
|
3929
4180
|
? draft.candidates.filter((candidate) => candidate.selected)
|
|
3930
4181
|
: draft.candidates;
|
|
3931
4182
|
const continuePortableSelection = () => {
|
|
3932
|
-
if (draft.canContinue)
|
|
3933
|
-
|
|
4183
|
+
if (!draft.canContinue) {
|
|
4184
|
+
setPortableEmptySelectionOpen(true);
|
|
4185
|
+
return;
|
|
4186
|
+
}
|
|
4187
|
+
go({ name: 'portableAgentMode', pluginId: plugin.pluginId });
|
|
3934
4188
|
};
|
|
3935
4189
|
const candidateActions = visibleCandidates.map((candidate) => ({
|
|
3936
4190
|
label: candidate.name,
|
|
3937
4191
|
selected: candidate.selected,
|
|
3938
4192
|
description: `${candidate.description}${candidate.dynamic ? ' · 按设置生成' : ''}`,
|
|
4193
|
+
meta: `${candidate.providerDisplayName} · ${candidate.relation === 'requested' ? '当前插件'
|
|
4194
|
+
: candidate.relation === 'direct' ? '直接依赖插件' : '间接依赖插件'}`,
|
|
4195
|
+
tags: _jsxs(Text, { color: tuiTheme.colors.muted, children: ["[", candidate.availableScopes.map(scopeLabel).join('、') || '范围待确认', "]", candidate.dynamic ? ' [按设置生成]' : ''] }),
|
|
3939
4196
|
run: () => go({
|
|
3940
4197
|
name: 'portableSkillSummary',
|
|
3941
4198
|
pluginId: plugin.pluginId,
|
|
@@ -3970,7 +4227,7 @@ export function ConsoleAppV4(props) {
|
|
|
3970
4227
|
setScrollOffset(0);
|
|
3971
4228
|
} },
|
|
3972
4229
|
];
|
|
3973
|
-
const continueAction = { actionId: 'portable-selection-continue', label: `继续(${draft.selectedCount})`,
|
|
4230
|
+
const continueAction = { actionId: 'portable-selection-continue', label: `继续(${draft.selectedCount})`, run: continuePortableSelection };
|
|
3974
4231
|
const openToolsAction = { actionId: 'portable-selection-tools', label: '选择工具', run: () => {
|
|
3975
4232
|
setPortableToolsOpen(true);
|
|
3976
4233
|
setFocusZone('content');
|
|
@@ -3979,17 +4236,13 @@ export function ConsoleAppV4(props) {
|
|
|
3979
4236
|
} };
|
|
3980
4237
|
pinnedActions = [continueAction, openToolsAction];
|
|
3981
4238
|
actions = portableToolsOpen ? selectionToolActions : candidateActions;
|
|
3982
|
-
const candidateViewport = portableSkillCandidateViewport(candidateActions,
|
|
4239
|
+
const candidateViewport = portableSkillCandidateViewport(candidateActions, Math.max(20, size.columns - 2), policy.mode === 'compact', focus, scrollOffset, pageActionBudget(2 + (portableVariantCandidates.length > 0 ? 3 : 0)));
|
|
3983
4240
|
const viewport = showTechnical
|
|
3984
4241
|
? { offset: focus < visibleCandidates.length ? focus : candidateViewport.offset, limit: 1 }
|
|
3985
4242
|
: candidateViewport;
|
|
3986
4243
|
const visibleCandidateEntries = visibleCandidates
|
|
3987
4244
|
.map((candidate, index) => ({ candidate, action: candidateActions[index], index }))
|
|
3988
4245
|
.slice(viewport.offset, viewport.offset + viewport.limit);
|
|
3989
|
-
const providerGroups = [...new Map(visibleCandidateEntries.map((entry) => [
|
|
3990
|
-
entry.candidate.providerPluginId,
|
|
3991
|
-
visibleCandidateEntries.filter((candidate) => candidate.candidate.providerPluginId === entry.candidate.providerPluginId),
|
|
3992
|
-
])).values()];
|
|
3993
4246
|
const variantCandidates = portableVariantCandidates;
|
|
3994
4247
|
const focusedVariant = variantCandidates.find((candidate) => candidate.name === visibleCandidates[focus]?.name);
|
|
3995
4248
|
const visibleVariant = visibleCandidateEntries.find((entry) => portableScopeVariantsDiffer(entry.candidate))?.candidate;
|
|
@@ -4001,14 +4254,9 @@ export function ConsoleAppV4(props) {
|
|
|
4001
4254
|
: '↑↓ 移动 · Space 切换 · Enter 查看摘要 · Tab 固定栏 · c 继续 · Esc 返回';
|
|
4002
4255
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action) => ({
|
|
4003
4256
|
id: action.actionId, label: action.label, disabled: Boolean(action.disabled),
|
|
4004
|
-
})), active: focusZone === 'actions', focus: pinnedActionFocus }), _jsxs(ContentViewport, { children: [portableToolsOpen
|
|
4257
|
+
})), active: !portableEmptySelectionOpen && focusZone === 'actions', focus: pinnedActionFocus }), _jsxs(ContentViewport, { children: [portableToolsOpen
|
|
4005
4258
|
? _jsx(SemanticRows, { actions: selectionToolActions, focus: focusZone === 'content' ? focus : -1, kind: "action", compact: policy.mode === 'compact' })
|
|
4006
|
-
:
|
|
4007
|
-
const provider = entries[0].candidate;
|
|
4008
|
-
const relation = provider.relation === 'requested' ? '当前插件'
|
|
4009
|
-
: provider.relation === 'direct' ? '直接依赖插件' : '间接依赖插件';
|
|
4010
|
-
return _jsx(SectionCard, { title: provider.providerDisplayName, meta: relation, compact: policy.mode === 'compact', children: entries.map(({ action, index }) => _jsx(ToggleRow, { title: action.label, description: action.description, selected: Boolean(action.selected), focused: focusZone === 'content' && focus === index, compact: policy.mode === 'compact' }, String(action.label))) }, provider.providerPluginId);
|
|
4011
|
-
}), !portableToolsOpen && variantCandidates.length > 0
|
|
4259
|
+
: visibleCandidateEntries.map(({ action, index }) => _jsx(ObjectCardRow, { width: Math.max(20, size.columns - 2), title: `${action.selected ? '[x]' : '[ ]'} ${action.label}`, description: action.description ?? '', meta: action.meta, tags: action.tags, focused: !portableEmptySelectionOpen && focusZone === 'content' && focus === index, anchored: !portableEmptySelectionOpen && focusZone !== 'content' && focus === index, muted: portableEmptySelectionOpen, compact: policy.mode === 'compact' }, String(action.label))), !portableToolsOpen && variantCandidates.length > 0
|
|
4012
4260
|
? showTechnical
|
|
4013
4261
|
? _jsx(StaticRow, { title: `! ${variantCandidates.length} 个 Skill 存在范围变体`, status: "warning", compact: true })
|
|
4014
4262
|
: _jsx(Callout, { status: "warning", title: `! ${variantCandidates.length} 个 Skill 在不同安装范围会生成不同内容。选择范围后会重新确认最终内容。` })
|
|
@@ -4079,16 +4327,29 @@ export function ConsoleAppV4(props) {
|
|
|
4079
4327
|
else if (route.name === 'portableBlocker' && plugin) {
|
|
4080
4328
|
breadcrumb = `${plugin.displayName} / 通用 Skills`;
|
|
4081
4329
|
const diagnostic = portableBlockerDiagnostic(route);
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4330
|
+
const cliUnavailable = route.code === 'portableSkillsCliMissing'
|
|
4331
|
+
|| route.code === 'portableSkillsCliIncompatible';
|
|
4332
|
+
const showPortableRequirements = cliUnavailable && showTechnical;
|
|
4333
|
+
if (showPortableRequirements) {
|
|
4334
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills / 环境`;
|
|
4335
|
+
context = '第 1 / 1 页';
|
|
4336
|
+
title = '准备安装工具';
|
|
4337
|
+
description = '完成环境准备后返回问题摘要并重新检查。';
|
|
4338
|
+
}
|
|
4339
|
+
else {
|
|
4340
|
+
title = diagnostic.title;
|
|
4341
|
+
description = '';
|
|
4342
|
+
}
|
|
4343
|
+
actions = showPortableRequirements ? [
|
|
4344
|
+
{ label: '返回问题摘要', run: () => toggleTechnical() },
|
|
4345
|
+
] : [
|
|
4085
4346
|
...(route.code === 'requiredConfigMissing' ? [{ label: '完成插件设置', run: () => {
|
|
4086
4347
|
props.controller.replace({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'effective' } });
|
|
4087
4348
|
setFocus(0);
|
|
4088
4349
|
setScrollOffset(0);
|
|
4089
4350
|
} }] : []),
|
|
4090
|
-
...(
|
|
4091
|
-
? [{ label:
|
|
4351
|
+
...(cliUnavailable
|
|
4352
|
+
? [{ label: '查看准备要求', run: () => toggleTechnical() }]
|
|
4092
4353
|
: []),
|
|
4093
4354
|
...(route.code === 'portableScopeUnavailable' ? [{ label: '返回 Skill 选择', run: () => {
|
|
4094
4355
|
props.controller.resetPortableFlowToPreview(plugin.pluginId, { name: 'portableSkillSelect', pluginId: plugin.pluginId }, true);
|
|
@@ -4097,12 +4358,12 @@ export function ConsoleAppV4(props) {
|
|
|
4097
4358
|
} }] : []),
|
|
4098
4359
|
{ label: '重新检查', run: () => beginPortable(plugin.pluginId) },
|
|
4099
4360
|
{ label: snapshot.installPlacementDraft?.pluginId === plugin.pluginId ? '返回目标平台' : '返回插件详情', run: () => props.controller.resetPortableFlowToPreview(plugin.pluginId) },
|
|
4100
|
-
...(
|
|
4361
|
+
...(cliUnavailable
|
|
4101
4362
|
? []
|
|
4102
4363
|
: [{ label: showTechnical ? '返回问题摘要' : '技术详情', run: () => toggleTechnical() }]),
|
|
4103
4364
|
];
|
|
4104
4365
|
const blockerActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 4));
|
|
4105
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(DiagnosticSummary, { diagnostic: diagnostic, technical: showTechnical, compact: policy.mode === 'compact' }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: blockerActionViewport.offset, limit: blockerActionViewport.limit })] });
|
|
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 })] });
|
|
4106
4367
|
}
|
|
4107
4368
|
else if (route.name === 'pluginDependencies' && plugin) {
|
|
4108
4369
|
const overview = dependencyOverview(plugin);
|
|
@@ -4446,24 +4707,52 @@ export function ConsoleAppV4(props) {
|
|
|
4446
4707
|
{ label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
|
|
4447
4708
|
{ label: '返回插件详情', run: back },
|
|
4448
4709
|
];
|
|
4449
|
-
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)
|
|
4710
|
+
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' })] });
|
|
4450
4711
|
}
|
|
4451
4712
|
else {
|
|
4452
4713
|
const confirmNativePlatforms = () => {
|
|
4714
|
+
if (validSelectedTargets.length === 0) {
|
|
4715
|
+
setActionError('请至少选择一个目标平台。');
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4453
4718
|
setDirty(false);
|
|
4454
4719
|
props.controller.setInstallPlatforms(validSelectedTargets);
|
|
4455
4720
|
go({ name: 'installPlacement', pluginId: plugin.pluginId });
|
|
4456
4721
|
};
|
|
4457
|
-
const
|
|
4458
|
-
|
|
4722
|
+
const nativeActions = choices.map((choice) => {
|
|
4723
|
+
const installed = installedScopeStates(plugin, choice.platform);
|
|
4724
|
+
const configuredScopes = plugin.supportedScopes?.[choice.platform];
|
|
4725
|
+
const supportedScopes = ['project', 'user'].filter((candidateScope) => configuredScopes?.includes(candidateScope)
|
|
4726
|
+
|| installed.has(candidateScope) || choice.scopes.includes(candidateScope));
|
|
4727
|
+
const locationItems = supportedScopes.map((candidateScope) => {
|
|
4728
|
+
const state = installed.get(candidateScope);
|
|
4729
|
+
if (state)
|
|
4730
|
+
return {
|
|
4731
|
+
key: `${choice.platform}:${candidateScope}`,
|
|
4732
|
+
label: scopeLabel(candidateScope),
|
|
4733
|
+
symbol: state.symbol,
|
|
4734
|
+
status: state.status,
|
|
4735
|
+
};
|
|
4736
|
+
return choice.scopes.includes(candidateScope)
|
|
4737
|
+
? { key: `${choice.platform}:${candidateScope}`, label: scopeLabel(candidateScope),
|
|
4738
|
+
symbol: '○', status: 'muted' }
|
|
4739
|
+
: { key: `${choice.platform}:${candidateScope}`, label: scopeLabel(candidateScope),
|
|
4740
|
+
symbol: '?', status: 'muted' };
|
|
4741
|
+
});
|
|
4742
|
+
const missingLabels = choice.scopes.filter((candidateScope) => !installed.has(candidateScope))
|
|
4743
|
+
.map(scopeLabel);
|
|
4744
|
+
return {
|
|
4745
|
+
actionId: `install-platform-${choice.platform}`,
|
|
4459
4746
|
label: platformLabel(choice.platform),
|
|
4460
4747
|
selected: validSelectedTargets.includes(choice.platform),
|
|
4461
|
-
description: choice.disabled
|
|
4462
|
-
|
|
4463
|
-
|
|
4748
|
+
description: choice.disabled ? `不可选择:${choice.disabled}` : (missingLabels.length > 0
|
|
4749
|
+
? `可添加到:${missingLabels.join('、')}` : '所有支持位置已经安装'),
|
|
4750
|
+
status: choice.disabled ? 'muted' : undefined,
|
|
4751
|
+
tags: locationItems.length > 0 ? _jsx(StatusBadge, { items: locationItems }) : undefined,
|
|
4464
4752
|
run: () => {
|
|
4465
4753
|
if (!choice.compatible)
|
|
4466
4754
|
return;
|
|
4755
|
+
setActionError(undefined);
|
|
4467
4756
|
setSelectedTargets((current) => {
|
|
4468
4757
|
const next = current.includes(choice.platform)
|
|
4469
4758
|
? current.filter((platform) => platform !== choice.platform)
|
|
@@ -4472,30 +4761,46 @@ export function ConsoleAppV4(props) {
|
|
|
4472
4761
|
return next;
|
|
4473
4762
|
});
|
|
4474
4763
|
},
|
|
4475
|
-
}
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
description: validSelectedTargets.length > 0 ? `已选择 ${validSelectedTargets.length} 个平台` : undefined,
|
|
4492
|
-
disabled: validSelectedTargets.length === 0 ? '至少选择一个目标平台' : undefined,
|
|
4493
|
-
run: confirmNativePlatforms,
|
|
4764
|
+
};
|
|
4765
|
+
});
|
|
4766
|
+
const portableAction = {
|
|
4767
|
+
actionId: 'portable-agent-skills',
|
|
4768
|
+
label: '通用 Agent Skills →',
|
|
4769
|
+
description: plugin.portableSkills
|
|
4770
|
+
? '通过外部 Skills CLI 安装到支持的 Agent;只安装选定 Skills'
|
|
4771
|
+
: '这个插件不提供通用 Agent Skills',
|
|
4772
|
+
status: plugin.portableSkills ? undefined : 'muted',
|
|
4773
|
+
tags: plugin.portableSkills
|
|
4774
|
+
? _jsx(Text, { color: tuiTheme.colors.accent, children: "[Skills \u53EF\u9009]" })
|
|
4775
|
+
: undefined,
|
|
4776
|
+
run: () => {
|
|
4777
|
+
if (!plugin.portableSkills)
|
|
4778
|
+
return;
|
|
4779
|
+
beginPortable(plugin.pluginId);
|
|
4494
4780
|
},
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4781
|
+
};
|
|
4782
|
+
const confirmAction = {
|
|
4783
|
+
actionId: 'confirm-install-platforms',
|
|
4784
|
+
label: '确定所选平台',
|
|
4785
|
+
disabled: validSelectedTargets.length === 0 ? '至少选择一个目标平台' : undefined,
|
|
4786
|
+
run: confirmNativePlatforms,
|
|
4787
|
+
};
|
|
4788
|
+
const backAction = {
|
|
4789
|
+
actionId: 'install-platforms-back', label: '返回插件详情', run: back,
|
|
4790
|
+
};
|
|
4791
|
+
actions = [...nativeActions, portableAction];
|
|
4792
|
+
pinnedActions = [confirmAction, backAction];
|
|
4793
|
+
const cardWidth = Math.max(20, size.columns - 2);
|
|
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)) })] });
|
|
4499
4804
|
}
|
|
4500
4805
|
}
|
|
4501
4806
|
else if (route.name === 'installPlacement' && plugin) {
|
|
@@ -4561,7 +4866,7 @@ export function ConsoleAppV4(props) {
|
|
|
4561
4866
|
const scopeChoiceCount = commonScopes.length > 0 ? actions.length : Math.max(0, actions.length - 2);
|
|
4562
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
|
|
4563
4868
|
? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: policy.mode === 'compact', offset: scopeChoiceCount })
|
|
4564
|
-
: null, hasDependencies ? _jsx(Callout, { title: "\u4F9D\u8D56\u8BA1\u5212", description: "\
|
|
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] });
|
|
4565
4870
|
help = '↑↓ 移动 · Enter 选择/继续 · Esc 返回';
|
|
4566
4871
|
}
|
|
4567
4872
|
else if (route.name === 'uninstallTargets' && plugin) {
|
|
@@ -4928,7 +5233,7 @@ export function ConsoleAppV4(props) {
|
|
|
4928
5233
|
breadcrumb = `插件 / ${plugin.displayName}`;
|
|
4929
5234
|
context = `${plugin.sourceLabel}${plugin.version ? ` · ${plugin.version}` : ''}`;
|
|
4930
5235
|
title = plugin.displayName;
|
|
4931
|
-
description =
|
|
5236
|
+
description = '';
|
|
4932
5237
|
const updateDescription = pluginUpdateDescription(plugin);
|
|
4933
5238
|
const installedVersions = [...new Set((plugin.update?.targets ?? [])
|
|
4934
5239
|
.flatMap((target) => target.installedVersion ? [target.installedVersion] : []))];
|
|
@@ -5040,7 +5345,7 @@ export function ConsoleAppV4(props) {
|
|
|
5040
5345
|
: plugin.capabilityUnsupported
|
|
5041
5346
|
? '当前 AW CLI 不支持这个插件'
|
|
5042
5347
|
: !hasInstallationOption
|
|
5043
|
-
? '
|
|
5348
|
+
? '当前工具和插件没有可用的安装方式'
|
|
5044
5349
|
: '选择工具和使用范围',
|
|
5045
5350
|
...(plugin.sourceUnavailable || plugin.capabilityUnsupported || !hasInstallationOption
|
|
5046
5351
|
? { disabled: plugin.sourceUnavailable ? '插件来源不可用'
|
|
@@ -5104,35 +5409,34 @@ export function ConsoleAppV4(props) {
|
|
|
5104
5409
|
&& dependencyOverviewForDetail.summary.totalCount > 0
|
|
5105
5410
|
? `需要 ${dependencyOverviewForDetail.summary.totalCount} 个插件;当前可用关系按当前范围检查,未显示的关系尚未检查通过。`
|
|
5106
5411
|
: undefined;
|
|
5107
|
-
const
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5412
|
+
const detailPositions = plugin.installedPlacements
|
|
5413
|
+
.filter((placement) => placement.state !== 'notInstalled')
|
|
5414
|
+
.map((placement) => `${platformLabel(placement.platform)}:${pluginPlacementConclusions(placement).map((entry) => entry.text).join(' · ')}`);
|
|
5415
|
+
const detailSummary = [`${plugin.description}`, `版本 ${plugin.version ?? '未知'} · ${plugin.sourceLabel}`,
|
|
5416
|
+
...detailPositions].join('\n');
|
|
5417
|
+
const detailSummaryLines = objectCardRowLayout({
|
|
5418
|
+
description: detailSummary, width: size.columns - 2, hasTags: true,
|
|
5419
|
+
}).descriptionLines;
|
|
5420
|
+
const detailExtraRows = (updateDescription ? 1 : 0) + (dependencyDetailText ? 1 : 0)
|
|
5421
|
+
+ (snapshot.completionNotice?.pluginId === plugin.pluginId ? 4 : 0);
|
|
5422
|
+
const detailPageRows = Math.max(1, pageActionBudget(detailExtraRows + 4 + 3));
|
|
5423
|
+
const detailOffset = Math.min(batchTargetOffset, Math.max(0, detailSummaryLines.length - 1));
|
|
5424
|
+
const visibleDetailLines = detailSummaryLines.slice(detailOffset, detailOffset + detailPageRows);
|
|
5425
|
+
if (detailSummaryLines.length > detailPageRows) {
|
|
5426
|
+
context += ` · 摘要 ${detailOffset + 1}–${detailOffset + visibleDetailLines.length}/${detailSummaryLines.length}`;
|
|
5427
|
+
actions.push(...paginationActions({
|
|
5428
|
+
offset: detailOffset, total: detailSummaryLines.length, pageRows: detailPageRows,
|
|
5429
|
+
move: (direction) => moveBatchTargetPage(detailSummaryLines.length, detailPageRows, direction),
|
|
5430
|
+
}));
|
|
5431
|
+
referenceDetailPagination = { total: detailSummaryLines.length, pageRows: detailPageRows };
|
|
5432
|
+
}
|
|
5433
|
+
currentActionViewportLineBudget = pageActionBudget(detailExtraRows + 4 + visibleDetailLines.length);
|
|
5434
|
+
const detailViewport = actionViewport(actions, true, focus, scrollOffset, currentActionViewportLineBudget);
|
|
5125
5435
|
content = _jsxs(Box, { flexDirection: "column", children: [snapshot.completionNotice?.pluginId === plugin.pluginId
|
|
5126
5436
|
? _jsx(Callout, { status: "success", title: snapshot.completionNotice.title, description: snapshot.completionNotice.description })
|
|
5127
|
-
: null, _jsx(
|
|
5128
|
-
: hasUncertainPlacement ? 'warning'
|
|
5129
|
-
: confirmedAvailablePlacements.length > 0 ? 'success' : 'info', title: hasUnavailablePlacement ? '部分使用位置暂时不可用'
|
|
5130
|
-
: hasUncertainPlacement ? '部分使用位置需要处理'
|
|
5131
|
-
: confirmedAvailablePlacements.length > 0 ? '已确认的使用位置可用' : '尚未确认使用位置', children: visiblePlacementConclusions.map((placement) => _jsx(StaticRow, { title: platformLabel(placement.platform), meta: placement.text, status: placement.status, compact: true }, placement.key)) }), updateDescription
|
|
5437
|
+
: null, _jsx(ObjectCardRow, { width: size.columns - 2, title: plugin.displayName, description: visibleDetailLines.join('\n'), tags: pluginPlatformBadges(plugin) }), updateDescription
|
|
5132
5438
|
? _jsx(StaticRow, { title: "\u6709\u53EF\u7528\u66F4\u65B0", meta: updateDescription, status: "warning", compact: true })
|
|
5133
|
-
: null, dependencyDetailText ? _jsx(StaticRow, { title: dependencyDetailText, status: "muted", compact: true }) : null,
|
|
5134
|
-
? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: detailViewport.offset, limit: detailViewport.limit })
|
|
5135
|
-
: _jsx(SectionCard, { title: "\u63D2\u4EF6\u64CD\u4F5C", children: _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: detailViewport.offset, limit: detailViewport.limit }) })] });
|
|
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 })] });
|
|
5136
5440
|
}
|
|
5137
5441
|
else if (route.name === 'pluginMore' && plugin) {
|
|
5138
5442
|
breadcrumb = `插件 / ${plugin.displayName} / 更多操作`;
|
|
@@ -5480,6 +5784,24 @@ export function ConsoleAppV4(props) {
|
|
|
5480
5784
|
const selectedPreset = route.presetId && configLayerDefinitionPage
|
|
5481
5785
|
? configLayerDefinitionPage.presets.find((preset) => preset.id === route.presetId)
|
|
5482
5786
|
: undefined;
|
|
5787
|
+
const selectedMutation = selectedField && configCandidate
|
|
5788
|
+
? configCandidate.mutations[selectedField.key]
|
|
5789
|
+
: undefined;
|
|
5790
|
+
const candidateSummary = selectedField && selectedMutation
|
|
5791
|
+
? selectedMutation.kind === 'unset' ? '恢复继承'
|
|
5792
|
+
: selectedField.sensitive ? '已更新'
|
|
5793
|
+
: typeof selectedMutation.value === 'object'
|
|
5794
|
+
? objectSummary(selectedField, selectedMutation.value)
|
|
5795
|
+
: scalarSummary(selectedField, selectedMutation.value)
|
|
5796
|
+
: selectedPreset && configCandidate
|
|
5797
|
+
? `方案“${selectedPreset.title}” · ${Object.keys(configCandidate.mutations).length} 项修改`
|
|
5798
|
+
: undefined;
|
|
5799
|
+
const candidateSummaryLines = candidateSummary
|
|
5800
|
+
? wrapConsoleText(candidateSummary, Math.max(12, size.columns - 14))
|
|
5801
|
+
: [];
|
|
5802
|
+
const candidateSummaryDisplay = candidateSummaryLines.length > 1
|
|
5803
|
+
? `${candidateSummaryLines[0]}…`
|
|
5804
|
+
: candidateSummaryLines[0];
|
|
5483
5805
|
title = selectedField ? `“${selectedField.title}”保存到哪里?`
|
|
5484
5806
|
: selectedPreset ? `“${selectedPreset.title}”保存到哪里?` : '这个值保存到哪里?';
|
|
5485
5807
|
description = selectedField
|
|
@@ -5513,19 +5835,19 @@ export function ConsoleAppV4(props) {
|
|
|
5513
5835
|
}] : []),
|
|
5514
5836
|
];
|
|
5515
5837
|
content = actions.length > 0
|
|
5516
|
-
? _jsx(SemanticRows, { actions: actions, focus: focus, kind: "section", compact: policy.mode === 'compact' })
|
|
5838
|
+
? _jsxs(Box, { flexDirection: "column", children: [candidateSummaryDisplay ? _jsx(StatusCard, { status: "info", title: "\u5DF2\u786E\u5B9A\u65B0\u503C", description: `新值:${candidateSummaryDisplay}`, compact: true }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "section", compact: policy.mode === 'compact' })] })
|
|
5517
5839
|
: _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u68C0\u67E5\u53EF\u7528\u4FDD\u5B58\u4F4D\u7F6E" });
|
|
5518
5840
|
}
|
|
5519
5841
|
else if (route.name === 'configFields' && plugin) {
|
|
5520
5842
|
const readonly = false;
|
|
5521
5843
|
breadcrumb = `${plugin.displayName} / 设置`;
|
|
5522
5844
|
title = '插件设置';
|
|
5523
|
-
description = '
|
|
5845
|
+
description = '先编辑值,再选择保存位置。Tab 可选择完整分组。';
|
|
5524
5846
|
if (configActionError)
|
|
5525
5847
|
description = configActionError;
|
|
5526
|
-
help = '↑↓ 移动 · Enter 修改 · ←→ 分组 · s 查看保存汇总 · u 恢复继承 · Esc 返回';
|
|
5848
|
+
help = '↑↓ 移动 · Enter 修改 · Tab 固定操作 · ←→ 分组 · s 查看保存汇总 · u 恢复继承 · Esc 返回';
|
|
5527
5849
|
if (!currentConfigPage) {
|
|
5528
|
-
content = _jsx(StatusCard, { status: "info", title: "\u6B63\u5728\u8BFB\u53D6\
|
|
5850
|
+
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" }) });
|
|
5529
5851
|
}
|
|
5530
5852
|
else {
|
|
5531
5853
|
visibleConfigTabs = configTabs(currentConfigPage);
|
|
@@ -5536,7 +5858,7 @@ export function ConsoleAppV4(props) {
|
|
|
5536
5858
|
? currentConfigPage.groups.find((group) => group.id === activeTab.id)
|
|
5537
5859
|
: undefined;
|
|
5538
5860
|
if (configDraftIssues.length > 0) {
|
|
5539
|
-
description +=
|
|
5861
|
+
description += ` 有 ${configDraftIssues.length} 项未保存修改已不适用于当前设置。`;
|
|
5540
5862
|
}
|
|
5541
5863
|
const mutableLayer = { scope: 'effective' };
|
|
5542
5864
|
const tabActions = activeTab?.kind === 'presets'
|
|
@@ -5549,13 +5871,21 @@ export function ConsoleAppV4(props) {
|
|
|
5549
5871
|
}))
|
|
5550
5872
|
: (activeGroup?.fields ?? []).filter((field) => readonly || configFieldCanSet(field) || configFieldCanUnset(field)).map((field) => ({
|
|
5551
5873
|
label: field.title,
|
|
5552
|
-
description: field.invalidOverrideIssue ??
|
|
5874
|
+
description: field.invalidOverrideIssue ?? [
|
|
5875
|
+
`当前生效:${effectiveFieldDisplay(field)}(${field.effectiveSourceLabel})`,
|
|
5876
|
+
...pendingFieldTargets(field).map(({ scope: draftScope, value }) => `待保存到${draftScope === 'project' ? '当前项目' : '个人默认'}:${value}`),
|
|
5877
|
+
field.description,
|
|
5878
|
+
].filter((value) => Boolean(value)).join(' · '),
|
|
5553
5879
|
meta: (() => {
|
|
5554
|
-
const targets =
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5880
|
+
const targets = pendingFieldTargets(field);
|
|
5881
|
+
if (targets.length === 0) {
|
|
5882
|
+
return `${effectiveFieldDisplay(field)} · ${field.effectiveSourceLabel}`;
|
|
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(' · ');
|
|
5559
5889
|
})(),
|
|
5560
5890
|
configKey: field.key,
|
|
5561
5891
|
run: () => go(['project', 'user'].some((draftScope) => configLayerPages[draftScope]?.groups.flatMap((group) => group.fields)
|
|
@@ -5575,8 +5905,8 @@ export function ConsoleAppV4(props) {
|
|
|
5575
5905
|
}));
|
|
5576
5906
|
const operationActions = [
|
|
5577
5907
|
...(configDraftIssues.length > 0 ? [{
|
|
5578
|
-
label: '
|
|
5579
|
-
description: `${configDraftIssues.length}
|
|
5908
|
+
label: '放弃无法保存的修改',
|
|
5909
|
+
description: `${configDraftIssues.length} 项未保存修改已不再适用于当前插件设置;放弃后不会修改已保存设置。`,
|
|
5580
5910
|
status: 'warning',
|
|
5581
5911
|
run: discardInvalidConfigDraftChanges,
|
|
5582
5912
|
}] : []),
|
|
@@ -5616,7 +5946,19 @@ export function ConsoleAppV4(props) {
|
|
|
5616
5946
|
go({ name: 'configSave', pluginId: route.pluginId, layer: { scope: 'effective' } });
|
|
5617
5947
|
},
|
|
5618
5948
|
};
|
|
5619
|
-
|
|
5949
|
+
const configGroupAction = {
|
|
5950
|
+
actionId: 'config-select-group',
|
|
5951
|
+
label: '选择分组',
|
|
5952
|
+
description: '查看全部设置分组和快速方案',
|
|
5953
|
+
disabled: visibleConfigTabs.length === 0 ? '没有可用设置分组' : undefined,
|
|
5954
|
+
run: () => {
|
|
5955
|
+
const current = Math.max(0, visibleConfigTabs.findIndex((tab) => tab.id === activeTab?.id));
|
|
5956
|
+
setConfigGroupChoiceFocus(current);
|
|
5957
|
+
setConfigGroupChooserOpen(true);
|
|
5958
|
+
setFocusZone('content');
|
|
5959
|
+
},
|
|
5960
|
+
};
|
|
5961
|
+
pinnedActions = [configSaveAction, configGroupAction];
|
|
5620
5962
|
actions = [...tabActions, ...operationActions];
|
|
5621
5963
|
const effectiveConfigFocus = Math.min(focus, Math.max(0, actions.length - 1));
|
|
5622
5964
|
const focusedConfigField = effectiveConfigFocus < (activeGroup?.fields.length ?? 0)
|
|
@@ -5628,7 +5970,7 @@ export function ConsoleAppV4(props) {
|
|
|
5628
5970
|
.some((field) => field.key === focusedConfigField.key && configFieldCanUnset(field)))
|
|
5629
5971
|
: false;
|
|
5630
5972
|
help = focusZone === 'actions'
|
|
5631
|
-
? 'Enter
|
|
5973
|
+
? '↑↓ 移动 · Enter 选择固定操作 · Tab 返回设置字段 · s 查看保存汇总 · Esc 返回'
|
|
5632
5974
|
: canUnsetFocused
|
|
5633
5975
|
? '↑↓ 移动 · Enter 修改 · ←→ 分组 · Tab 保存栏 · s 查看汇总 · u 恢复继承 · Esc 返回'
|
|
5634
5976
|
: '↑↓ 移动 · Enter 修改 · ←→ 分组 · Tab 保存栏 · s 查看汇总 · Esc 返回';
|
|
@@ -5637,19 +5979,33 @@ export function ConsoleAppV4(props) {
|
|
|
5637
5979
|
? effectiveConfigFocus < tabActions.length ? effectiveConfigFocus : Math.min(scrollOffset, tabActions.length - 1)
|
|
5638
5980
|
: 0;
|
|
5639
5981
|
const operationRows = operationActions.reduce((rows, action) => rows + (policy.mode === 'compact' || !action.description ? 1 : 2), 0);
|
|
5640
|
-
const viewport = actionViewport(tabActions,
|
|
5982
|
+
const viewport = actionViewport(tabActions, false, fieldFocus, scrollOffset, pageActionBudget(2 + operationRows + 3 + (configDraftIssues.length > 0 ? 3 : 0)));
|
|
5641
5983
|
const totalFields = currentConfigPage.groups.reduce((count, group) => count + group.fields.length, 0);
|
|
5642
5984
|
const countLabel = `${totalFields} 项${currentConfigPage.presets.length > 0 ? ` · ${currentConfigPage.presets.length} 个方案` : ''}`;
|
|
5643
5985
|
context = configDraftCount > 0 ? `${configDraftCount} 项待保存` : countLabel;
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5986
|
+
const groupChoiceActions = visibleConfigTabs.map((tab) => ({
|
|
5987
|
+
label: tab.label,
|
|
5988
|
+
description: tab.kind === 'presets' ? `${tab.count} 个快速方案` : `${tab.count} 个设置字段`,
|
|
5989
|
+
}));
|
|
5990
|
+
const groupChoiceViewport = actionViewport(groupChoiceActions, false, configGroupChoiceFocus, 0, Math.max(2, pageActionBudget(5)));
|
|
5991
|
+
if (configGroupChooserOpen) {
|
|
5992
|
+
title = '选择设置分组';
|
|
5993
|
+
description = '选择后回到同一设置总览;未保存草稿保持不变。';
|
|
5994
|
+
help = '↑↓ 移动 · Enter 选择 · Esc 取消';
|
|
5995
|
+
context = `${visibleConfigTabs.length} 个入口`;
|
|
5996
|
+
}
|
|
5997
|
+
content = configGroupChooserOpen
|
|
5998
|
+
? _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["\u5B8C\u6574\u540D\u79F0\uFF1A", visibleConfigTabs[configGroupChoiceFocus]?.label] }), _jsx(SectionCard, { title: "\u5168\u90E8\u5206\u7EC4\u548C\u5FEB\u901F\u65B9\u6848", meta: groupChoiceViewport.total > groupChoiceViewport.limit
|
|
5999
|
+
? `${groupChoiceViewport.position}/${groupChoiceViewport.total}` : undefined, children: _jsx(SemanticRows, { actions: groupChoiceActions, focus: configGroupChoiceFocus, kind: "section", compact: false, offset: groupChoiceViewport.offset, limit: groupChoiceViewport.limit }) })] })
|
|
6000
|
+
: _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action) => ({
|
|
6001
|
+
id: action.actionId,
|
|
6002
|
+
label: action.label,
|
|
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] });
|
|
5653
6009
|
}
|
|
5654
6010
|
}
|
|
5655
6011
|
else if (route.name === 'configSave' && plugin) {
|
|
@@ -6061,10 +6417,16 @@ export function ConsoleAppV4(props) {
|
|
|
6061
6417
|
? 'SSH' : 'HTTPS';
|
|
6062
6418
|
const version = `${generation?.active?.catalogVersion ?? '未启用'}${freshness?.candidate?.catalogVersion
|
|
6063
6419
|
? `→${freshness.candidate.catalogVersion}` : ''}`;
|
|
6420
|
+
const available = generation?.state === 'ready' || health === 'ready';
|
|
6421
|
+
const unavailable = health === 'unavailable' || health === 'error' || health === 'recovery';
|
|
6064
6422
|
return {
|
|
6065
|
-
label:
|
|
6423
|
+
label: source.id,
|
|
6066
6424
|
description: sourceLocationSummary(overview.detail?.type ?? 'git', overview.detail?.locationSummary),
|
|
6067
|
-
meta:
|
|
6425
|
+
meta: `当前 ${version} · ${source.count === undefined ? '插件数未知' : `${source.count} 个插件`}`,
|
|
6426
|
+
tags: _jsxs(Box, { children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["[", source.primary ? '主来源' : transport, "] "] }), _jsx(StatusBadge, { items: [{ key: 'health', label: available ? '可用' : unavailable ? '不可用' : '未确认',
|
|
6427
|
+
symbol: available ? '✓' : unavailable ? '!' : '?', status: available ? 'success' : unavailable ? 'warning' : 'muted' }] }), freshness && freshness.state !== 'current' && freshness.state !== 'fixed'
|
|
6428
|
+
? _jsxs(Text, { color: tuiTheme.colors.accent, children: [" [", freshness.state === 'updateAvailable' ? '↑ ' : '', presentation.label, "]"] })
|
|
6429
|
+
: null] }),
|
|
6068
6430
|
status: presentation.status,
|
|
6069
6431
|
run: () => go({ name: 'sourceDetail', sourceId: source.id }),
|
|
6070
6432
|
};
|
|
@@ -6081,12 +6443,17 @@ export function ConsoleAppV4(props) {
|
|
|
6081
6443
|
help = focusZone === 'actions'
|
|
6082
6444
|
? '←→ 移动固定操作 · Enter 选择 · Tab 返回来源卡片 · Esc 返回'
|
|
6083
6445
|
: '↑↓ 移动 · Enter 打开 · Tab 固定操作 · r/c 检查全部 · Esc 返回';
|
|
6084
|
-
const viewport =
|
|
6446
|
+
const viewport = rowViewport({
|
|
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
|
+
});
|
|
6085
6452
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PinnedActionBar, { actions: pinnedActions.map((action, index) => ({
|
|
6086
6453
|
id: action.actionId ?? `${action.label}:${index}`,
|
|
6087
6454
|
label: action.label,
|
|
6088
6455
|
disabled: Boolean(action.disabled),
|
|
6089
|
-
})), 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", compact: policy.mode === 'compact', offset: viewport.offset, limit: viewport.limit }) })] });
|
|
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 }) })] });
|
|
6090
6457
|
}
|
|
6091
6458
|
else if (route.name === 'catalogStoreOverview') {
|
|
6092
6459
|
breadcrumb = '插件来源 / 本地存储';
|
|
@@ -6327,7 +6694,6 @@ export function ConsoleAppV4(props) {
|
|
|
6327
6694
|
run: () => go({ name: 'sourceMore', sourceId: route.sourceId }),
|
|
6328
6695
|
},
|
|
6329
6696
|
];
|
|
6330
|
-
const sourceActionViewport = actionViewport(actions, policy.mode === 'compact', focus, scrollOffset, responsiveBudget(2, 5));
|
|
6331
6697
|
const freshnessLabel = freshness?.state === 'checkFailed' && generationState !== 'ready'
|
|
6332
6698
|
? '检查失败' : catalogSourceFreshnessLabel(freshness);
|
|
6333
6699
|
const sourceStateLabel = generationState === 'ready' ? '来源可用'
|
|
@@ -6336,12 +6702,24 @@ export function ConsoleAppV4(props) {
|
|
|
6336
6702
|
: generationState === 'configMismatch' ? '来源设置与当前目录不一致'
|
|
6337
6703
|
: generationState === 'stateInvalid' ? '来源状态需要恢复'
|
|
6338
6704
|
: '来源尚未启用';
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6705
|
+
const sourceSummary = [
|
|
6706
|
+
`${transport} · ${sourceLocationSummary(detail?.type ?? 'git', detail?.locationSummary)}`,
|
|
6707
|
+
`当前 ${activeVersion ?? '未启用'}${candidateVersion ? ` → ${candidateVersion}` : ''} · ${sourceCount === undefined ? '插件数未知' : `${sourceCount} 个插件`}`,
|
|
6708
|
+
`版本策略:${versionPolicy}`,
|
|
6709
|
+
`检查:${freshnessLabel} · ${freshness?.checkedAt ?? '尚未检查'}`,
|
|
6710
|
+
].join('\n');
|
|
6711
|
+
const sourceCardHeight = objectCardRowLayout({
|
|
6712
|
+
description: sourceSummary, width: size.columns - 2, hasTags: true,
|
|
6713
|
+
}).height;
|
|
6714
|
+
const focusedSourceDescription = actions[focus]?.disabled ?? actions[focus]?.description;
|
|
6715
|
+
const sourceDescriptionLines = focusedSourceDescription
|
|
6716
|
+
? wrapConsoleText(focusedSourceDescription, size.columns - 2) : [];
|
|
6717
|
+
const sourceActionDescriptionLines = sourceDescriptionLines.length <= pageActionBudget(sourceCardHeight + 1)
|
|
6718
|
+
? sourceDescriptionLines : [];
|
|
6719
|
+
const detailMenuViewport = actionViewport(actions, true, focus, scrollOffset, pageActionBudget(sourceCardHeight + sourceActionDescriptionLines.length));
|
|
6720
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ObjectCardRow, { width: size.columns - 2, title: route.sourceId, description: sourceSummary, tags: _jsxs(Box, { children: [detail?.primary ? _jsx(Text, { color: tuiTheme.colors.accent, children: "[\u4E3B\u6765\u6E90] " }) : null, _jsx(StatusBadge, { items: [{ key: 'source-state', label: sourceStateLabel,
|
|
6721
|
+
symbol: generationState === 'ready' ? '✓' : '!',
|
|
6722
|
+
status: generationState === 'ready' ? 'success' : 'warning' }] })] }) }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: detailMenuViewport.offset, limit: detailMenuViewport.limit }), sourceActionDescriptionLines.map((line, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: line }, index))] });
|
|
6345
6723
|
}
|
|
6346
6724
|
else if (route.name === 'sourceMore') {
|
|
6347
6725
|
const overview = props.controller.getSourceOverview(route.sourceId);
|
|
@@ -6426,17 +6804,19 @@ export function ConsoleAppV4(props) {
|
|
|
6426
6804
|
else if (route.name === 'sourceTechnical') {
|
|
6427
6805
|
const diagnostic = resolveSourceTechnicalDiagnostic(route.sourceId);
|
|
6428
6806
|
breadcrumb = `插件来源 / ${route.sourceId} / 技术详情`;
|
|
6429
|
-
|
|
6807
|
+
const lines = diagnosticDetailLines(diagnostic, size.columns - 6);
|
|
6808
|
+
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - sourceTechnicalPageRows));
|
|
6809
|
+
context = `${offset + 1}–${Math.min(lines.length, offset + sourceTechnicalPageRows)} / ${lines.length} 行`;
|
|
6430
6810
|
title = '来源技术诊断';
|
|
6431
6811
|
description = '';
|
|
6432
|
-
help = '
|
|
6433
|
-
const sourceTechnicalLastOffset = Math.max(0,
|
|
6812
|
+
help = '↑↓ 选择翻页 · Enter 执行 · p/n 翻页 · Esc 返回';
|
|
6813
|
+
const sourceTechnicalLastOffset = Math.max(0, lines.length - sourceTechnicalPageRows);
|
|
6434
6814
|
actions = [
|
|
6435
6815
|
{ label: 'p 上一页', run: () => setBatchTargetOffset((value) => Math.max(0, value - sourceTechnicalPageRows)) },
|
|
6436
6816
|
{ label: 'n 下一页', run: () => setBatchTargetOffset((value) => Math.min(sourceTechnicalLastOffset, value + sourceTechnicalPageRows)) },
|
|
6437
6817
|
{ label: '返回来源详情', run: back },
|
|
6438
6818
|
];
|
|
6439
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(
|
|
6819
|
+
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 })] });
|
|
6440
6820
|
}
|
|
6441
6821
|
else if (route.name === 'sourceAdd' && sourceDraft?.owner === route.owner) {
|
|
6442
6822
|
breadcrumb = sourceDraft.mode === 'add' ? '插件来源 / 添加' : `插件来源 / ${sourceDraft.sourceId} / 编辑`;
|
|
@@ -6460,7 +6840,7 @@ export function ConsoleAppV4(props) {
|
|
|
6460
6840
|
}
|
|
6461
6841
|
else if (route.step === 'refPolicy') {
|
|
6462
6842
|
title = '选择版本来源';
|
|
6463
|
-
description = '
|
|
6843
|
+
description = '选择稳定版、预览版,或指定一个版本。';
|
|
6464
6844
|
const useRefPreset = async (refPolicy, ref) => {
|
|
6465
6845
|
const next = { ...sourceDraft, refPolicy, ref };
|
|
6466
6846
|
setSourceDraft(next);
|
|
@@ -6471,17 +6851,17 @@ export function ConsoleAppV4(props) {
|
|
|
6471
6851
|
go({ name: 'sourceAdd', step: 'subdir', owner: route.owner });
|
|
6472
6852
|
};
|
|
6473
6853
|
actions = [
|
|
6474
|
-
{ label: '
|
|
6475
|
-
{ label: '
|
|
6476
|
-
{ label: '
|
|
6477
|
-
{ label: '
|
|
6854
|
+
{ label: '自动选择', description: `跟随当前 AW 使用${snapshot.cliChannel === 'beta' ? '预览版' : '稳定版'}目录`, selected: sourceDraft.refPolicy === 'cliChannel', run: () => { const policy = catalogRefPolicyForPreset('followCli'); return useRefPreset(policy.type, ''); } },
|
|
6855
|
+
{ label: '稳定版', description: '使用稳定发布的插件目录', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref === CATALOG_LATEST_REF, run: () => { const policy = catalogRefPolicyForPreset('release'); return useRefPreset(policy.type, policy.type === 'explicit' ? policy.ref : ''); } },
|
|
6856
|
+
{ label: '预览版', description: '使用预览发布的插件目录', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref === CATALOG_BETA_REF, run: () => { const policy = catalogRefPolicyForPreset('beta'); return useRefPreset(policy.type, policy.type === 'explicit' ? policy.ref : ''); } },
|
|
6857
|
+
{ label: '指定版本', description: '输入分支名、标签或完整提交标识', selected: sourceDraft.refPolicy === 'explicit' && sourceDraft.ref !== CATALOG_LATEST_REF && sourceDraft.ref !== CATALOG_BETA_REF, run: () => {
|
|
6478
6858
|
if (route.returnToDraft)
|
|
6479
6859
|
props.controller.replace({ name: 'sourceAdd', step: 'ref', owner: route.owner, returnToDraft: true });
|
|
6480
6860
|
else
|
|
6481
6861
|
go({ name: 'sourceAdd', step: 'ref', owner: route.owner });
|
|
6482
6862
|
} },
|
|
6483
6863
|
];
|
|
6484
|
-
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["\
|
|
6864
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u81EA\u52A8\u9009\u62E9\uFF1A\u5F53\u524D AW \u4F7F\u7528", snapshot.cliChannel === 'beta' ? '预览版' : '稳定版', "\u76EE\u5F55"] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' })] });
|
|
6485
6865
|
}
|
|
6486
6866
|
else {
|
|
6487
6867
|
title = route.step === 'id' ? '输入来源 ID' : route.step === 'location' ? `输入 ${sourceDraft.type === 'git' ? 'Git URL' : '绝对路径'}` : route.step === 'ref' ? '输入固定 Git 引用' : '输入 Catalog 子目录';
|
|
@@ -6953,7 +7333,11 @@ export function ConsoleAppV4(props) {
|
|
|
6953
7333
|
{ label: 'n 下一页', run: () => setScrollOffset((value) => Math.min(lastReportOffset, value + reportLineLimit)) },
|
|
6954
7334
|
{ label: '返回技术详情', run: back },
|
|
6955
7335
|
];
|
|
6956
|
-
|
|
7336
|
+
const reportFeedbackVisible = Boolean(reportSaveResult || reportSaveError);
|
|
7337
|
+
const compactReportActions = policy.mode === 'compact' || reportFeedbackVisible;
|
|
7338
|
+
const reportActionLimit = policy.mode === 'compact' || reportFeedbackVisible ? 2 : 3;
|
|
7339
|
+
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 })] });
|
|
6957
7341
|
}
|
|
6958
7342
|
else {
|
|
6959
7343
|
actions = [{ label: '返回技术详情', run: back }];
|
|
@@ -7011,15 +7395,28 @@ export function ConsoleAppV4(props) {
|
|
|
7011
7395
|
}
|
|
7012
7396
|
else if (route.name === 'review' && snapshot.activeReview) {
|
|
7013
7397
|
const review = snapshot.activeReview;
|
|
7398
|
+
const technicalDetails = (review.technicalDetails ?? []).flatMap((detail) => review.operationKind === 'catalogGeneration' && detail.startsWith('引用影响:')
|
|
7399
|
+
? wrapConsoleText(detail, Math.max(20, size.columns - 10))
|
|
7400
|
+
: [detail]);
|
|
7401
|
+
const catalogReferenceDetailOffset = review.operationKind === 'catalogGeneration'
|
|
7402
|
+
? technicalDetails.findIndex((detail) => detail.startsWith('引用影响:'))
|
|
7403
|
+
: -1;
|
|
7404
|
+
const portableUserConfirmation = review.operationKind === 'portableInstall'
|
|
7405
|
+
&& snapshot.portableDraft?.scope === 'user';
|
|
7406
|
+
const requiresIndependentConfirmation = review.requiresConfirmation || portableUserConfirmation;
|
|
7014
7407
|
const installReviewPluginId = pendingReviewIntent?.kind === 'pluginWrite'
|
|
7015
7408
|
&& pendingReviewIntent.action === 'install'
|
|
7016
7409
|
? pendingReviewIntent.pluginId
|
|
7017
7410
|
: review.pluginAction === 'install' ? snapshot.installPlacementDraft?.pluginId : undefined;
|
|
7018
7411
|
const batchReview = review.operationKind === 'pluginWriteBatch';
|
|
7019
7412
|
const taskReview = review.operationKind === 'pluginWriteTask';
|
|
7413
|
+
const exactProjectSyncReview = batchReview && review.pluginAction === 'sync'
|
|
7414
|
+
&& review.changes.some((change) => change.group === 'requested'
|
|
7415
|
+
&& change.description?.includes('当前项目'));
|
|
7416
|
+
const projectPreApplyConfirmation = review.changes.some((change) => change.kind === 'warning' && change.title.endsWith('当前项目同步目标将运行 preApply'));
|
|
7020
7417
|
breadcrumb = `${review.subject.name} / ${review.title}`;
|
|
7021
7418
|
title = `${snapshot.result?.state === 'stale' ? '状态已变化 · ' : ''}${review.operationKind === 'portableInstall' || taskReview
|
|
7022
|
-
? review.title : `确认${review.title}`} ${review.subject.name}`;
|
|
7419
|
+
? review.title : `确认${review.title}`}${review.operationKind === 'catalogStorePrune' ? '' : ` ${review.subject.name}`}`;
|
|
7023
7420
|
description = snapshot.result?.state === 'stale'
|
|
7024
7421
|
? '当前状态已变化。请检查新的变化后再次确认。'
|
|
7025
7422
|
: review.description;
|
|
@@ -7032,10 +7429,10 @@ export function ConsoleAppV4(props) {
|
|
|
7032
7429
|
help = 'PgUp/PgDn 查看全部根插件和依赖 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7033
7430
|
}
|
|
7034
7431
|
actions = [
|
|
7035
|
-
{ label: review.primaryLabel, description:
|
|
7432
|
+
{ label: review.primaryLabel, description: requiresIndependentConfirmation
|
|
7036
7433
|
? '打开独立风险确认;执行时再次检查当前状态'
|
|
7037
7434
|
: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, run: () => {
|
|
7038
|
-
if (
|
|
7435
|
+
if (requiresIndependentConfirmation) {
|
|
7039
7436
|
setOperationConfirmation({
|
|
7040
7437
|
reviewToken: review.reviewToken,
|
|
7041
7438
|
returnFocus: focus,
|
|
@@ -7055,6 +7452,16 @@ export function ConsoleAppV4(props) {
|
|
|
7055
7452
|
operationBusy.current = false;
|
|
7056
7453
|
});
|
|
7057
7454
|
} },
|
|
7455
|
+
...(!showTechnical && catalogReferenceDetailOffset >= 0 ? [{
|
|
7456
|
+
label: '查看完整引用影响',
|
|
7457
|
+
description: '逐项查看安装、设置、生命周期和 Marketplace 引用',
|
|
7458
|
+
run: () => {
|
|
7459
|
+
setShowTechnical(true);
|
|
7460
|
+
setBatchTargetOffset(catalogReferenceDetailOffset);
|
|
7461
|
+
setFocus(0);
|
|
7462
|
+
setScrollOffset(0);
|
|
7463
|
+
},
|
|
7464
|
+
}] : []),
|
|
7058
7465
|
...(taskReview
|
|
7059
7466
|
&& pendingReviewIntent?.kind === 'pluginWriteTask'
|
|
7060
7467
|
&& pendingReviewIntent.goal === 'update'
|
|
@@ -7193,7 +7600,7 @@ export function ConsoleAppV4(props) {
|
|
|
7193
7600
|
const headingLines = compressedReview ? 0 : visibleSpecs.filter((group) => group.label).length;
|
|
7194
7601
|
const staticRowBudget = Math.max(visibleSpecs.length, responsiveBudget(7, 12) - headingLines);
|
|
7195
7602
|
const reviewPaginationTotal = showTechnical
|
|
7196
|
-
? Math.max(review.changes.length,
|
|
7603
|
+
? Math.max(review.changes.length, technicalDetails.length)
|
|
7197
7604
|
: userReviewChanges.length;
|
|
7198
7605
|
const reviewPageRows = Math.max(1, responsiveBudget(6, 10) - actions.length - 2);
|
|
7199
7606
|
if (reviewPaginationTotal > reviewPageRows) {
|
|
@@ -7238,18 +7645,33 @@ export function ConsoleAppV4(props) {
|
|
|
7238
7645
|
return _jsx(ReviewChangeRow, { title: `${category}${reviewSymbol(change)} ${change.title}${description ? ` · ${description}` : ''}`, meta: count, status: changeStatus(change), compact: true }, `${change.title}:${index}`);
|
|
7239
7646
|
})] }, group.key);
|
|
7240
7647
|
});
|
|
7241
|
-
const
|
|
7648
|
+
const portableReview = review.operationKind === 'portableInstall';
|
|
7649
|
+
const batchChangeLimit = portableReview
|
|
7650
|
+
? responsiveBudget(3, 5)
|
|
7651
|
+
: policy.mode === 'compact' ? 1 : Math.max(1, responsiveBudget(6, 10) - reviewActionViewport.limit);
|
|
7242
7652
|
const batchChanges = userReviewChanges.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
7243
7653
|
const batchHiddenBefore = batchTargetOffset;
|
|
7244
|
-
const prioritizedReviewChanges = [
|
|
7654
|
+
const prioritizedReviewChanges = portableReview ? [
|
|
7655
|
+
...userReviewChanges.filter((change) => change.kind === 'warning'),
|
|
7656
|
+
...userReviewChanges.filter((change) => change.kind !== 'warning'
|
|
7657
|
+
&& change.group === 'portableTarget'),
|
|
7658
|
+
...userReviewChanges.filter((change) => change.kind !== 'warning'
|
|
7659
|
+
&& change.group !== 'portableTarget'),
|
|
7660
|
+
] : [
|
|
7245
7661
|
...userReviewChanges.filter((change) => change.kind === 'warning'),
|
|
7246
7662
|
...userReviewChanges.filter((change) => change.kind !== 'warning'),
|
|
7247
7663
|
];
|
|
7248
7664
|
const pagedReviewChanges = prioritizedReviewChanges.slice(batchTargetOffset, batchTargetOffset + batchChangeLimit);
|
|
7249
|
-
const changeGroups = batchReview || userReviewChanges.length > staticRowBudget
|
|
7250
|
-
? _jsxs(Box, { flexDirection: "column", children: [batchHiddenBefore > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${batchHiddenBefore} 项`, status: "muted", compact: true }) : null, (batchReview ? batchChanges : pagedReviewChanges).map((change, index) => _jsx(ReviewChangeRow, { title: `${
|
|
7665
|
+
const changeGroups = portableReview || batchReview || userReviewChanges.length > staticRowBudget
|
|
7666
|
+
? _jsxs(Box, { flexDirection: "column", children: [batchHiddenBefore > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${batchHiddenBefore} 项`, status: "muted", compact: true }) : null, (batchReview ? batchChanges : pagedReviewChanges).map((change, index) => _jsx(ReviewChangeRow, { title: `${[
|
|
7667
|
+
portableReview && change.group ? groupTitle[change.group] : undefined,
|
|
7668
|
+
change.sourceId,
|
|
7669
|
+
change.relation === 'requested' ? '根插件'
|
|
7670
|
+
: change.relation === 'direct' ? '直接依赖'
|
|
7671
|
+
: change.relation === 'transitive' ? '间接依赖' : undefined,
|
|
7672
|
+
].filter(Boolean).join(' · ')}${portableReview && change.group || change.sourceId || change.relation ? ' · ' : ''}${reviewSymbol(change)} ${change.title}`, description: (portableReview && change.kind !== 'warning') || exactProjectSyncReview
|
|
7673
|
+
? change.description : undefined, meta: portableReview || exactProjectSyncReview ? undefined : change.description, status: changeStatus(change), compact: !portableReview && !exactProjectSyncReview }, `${change.batchGroupId ?? ''}:${change.pluginId ?? change.title}:${index}`)), Math.max(0, userReviewChanges.length - batchTargetOffset - (batchReview ? batchChanges : pagedReviewChanges).length) > 0 ? _jsx(StaticRow, { title: `↓ 后面还有 ${Math.max(0, userReviewChanges.length - batchTargetOffset - (batchReview ? batchChanges : pagedReviewChanges).length)} 项`, status: "muted", compact: true }) : null] })
|
|
7251
7674
|
: standardChangeGroups;
|
|
7252
|
-
const technicalDetails = review.technicalDetails ?? [];
|
|
7253
7675
|
const technicalDetailLimit = responsiveBudget(1, 1);
|
|
7254
7676
|
const visibleTechnicalDetails = technicalDetails.slice(batchTargetOffset, batchTargetOffset + technicalDetailLimit);
|
|
7255
7677
|
const hiddenTechnicalDetails = Math.max(0, technicalDetails.length - batchTargetOffset - visibleTechnicalDetails.length);
|
|
@@ -7291,7 +7713,7 @@ export function ConsoleAppV4(props) {
|
|
|
7291
7713
|
&& (review.changes.length > technicalChangeBudget || technicalDetails.length > technicalDetailLimit)) {
|
|
7292
7714
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7293
7715
|
}
|
|
7294
|
-
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: review.subject.name, meta: `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, _jsx(SemanticRows, { actions: actions, focus: 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: index === visibleTechnicalDetails.length - 1 && hiddenTechnicalDetails > 0 ? `另有 ${hiddenTechnicalDetails} 项` : undefined, compact: true }, detail)) }) : null] }) });
|
|
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] }) });
|
|
7295
7717
|
}
|
|
7296
7718
|
else if (route.name === 'progress') {
|
|
7297
7719
|
const taskOperation = snapshot.activeReview?.operationKind === 'pluginWriteTask';
|
|
@@ -7332,7 +7754,10 @@ export function ConsoleAppV4(props) {
|
|
|
7332
7754
|
: catalogSourcesRecovery ? '正在备份损坏配置并恢复官方默认来源。'
|
|
7333
7755
|
: sourceOperation ? '只原子保存来源配置,不访问网络。'
|
|
7334
7756
|
: configOperation ? configApply ? '先保存所选层,再重新应用当前可见安装位置。' : '只保存所选设置层。'
|
|
7335
|
-
: '
|
|
7757
|
+
: snapshot.activeReview?.operationKind === 'pluginWriteBatch'
|
|
7758
|
+
&& snapshot.activeReview.pluginAction === 'sync'
|
|
7759
|
+
? '只同步确认页中的精确目标;不会自动升级插件来源。'
|
|
7760
|
+
: '请勿关闭当前终端。';
|
|
7336
7761
|
const batchProgress = snapshot.activeReview?.operationKind === 'pluginWriteBatch' || taskOperation;
|
|
7337
7762
|
const progress = snapshot.progress;
|
|
7338
7763
|
const elapsedSeconds = Math.max(0, Math.floor(((progress.elapsedMs
|
|
@@ -7540,16 +7965,17 @@ export function ConsoleAppV4(props) {
|
|
|
7540
7965
|
? blocked || partial ? '× 问题仍未解决' : '✓ 问题已处理'
|
|
7541
7966
|
: sourceOperation
|
|
7542
7967
|
? blocked ? '× 来源操作未完成'
|
|
7543
|
-
:
|
|
7544
|
-
:
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
: resultSummary?.catalogAction === '
|
|
7550
|
-
: resultSummary?.catalogAction === '
|
|
7551
|
-
:
|
|
7552
|
-
: '✓
|
|
7968
|
+
: partial ? '! 来源操作只完成了一部分'
|
|
7969
|
+
: operationKind === 'catalogSourcesRecovery' ? '✓ Catalog 来源配置已恢复'
|
|
7970
|
+
: resultSummary?.catalogAction === 'update'
|
|
7971
|
+
? resultSummary.catalogGeneration?.activeChanged
|
|
7972
|
+
&& resultSummary.catalogGeneration.outcome === 'activated'
|
|
7973
|
+
? '✓ Catalog 已更新' : '✓ Catalog 无需更新'
|
|
7974
|
+
: resultSummary?.catalogAction === 'repair' ? '✓ Catalog 已修复'
|
|
7975
|
+
: resultSummary?.catalogAction === 'replaceDamaged' ? '✓ 损坏的 Catalog 已替换'
|
|
7976
|
+
: resultSummary?.catalogAction === 'recoverState' ? '✓ Catalog 状态已恢复'
|
|
7977
|
+
: operationKind === 'catalogGeneration' ? '✓ Catalog 已启用'
|
|
7978
|
+
: '✓ 来源配置已保存'
|
|
7553
7979
|
: configOperation
|
|
7554
7980
|
? blocked
|
|
7555
7981
|
? '× 设置未确认保存'
|
|
@@ -7598,7 +8024,9 @@ export function ConsoleAppV4(props) {
|
|
|
7598
8024
|
? '来源配置可能已保存。请重新检查来源列表。'
|
|
7599
8025
|
: '来源操作未执行。草稿已保留,可返回修改。'
|
|
7600
8026
|
: partial ? resultSummary?.message ?? '来源配置已保存,但 Catalog 尚未启用。可立即重试。'
|
|
7601
|
-
:
|
|
8027
|
+
: result.sourceAction === 'remove'
|
|
8028
|
+
? '来源记录已移除;已安装插件和插件设置保留。'
|
|
8029
|
+
: resultSummary?.message ?? '来源配置已保存。'
|
|
7602
8030
|
: blocked ? result.message
|
|
7603
8031
|
: configOperation
|
|
7604
8032
|
? partial
|
|
@@ -7747,13 +8175,26 @@ export function ConsoleAppV4(props) {
|
|
|
7747
8175
|
: portableOperation
|
|
7748
8176
|
? blocked || partial ? [
|
|
7749
8177
|
...(portableReceipt?.verificationUnknown ? [{
|
|
7750
|
-
label: '重新检查结果',
|
|
7751
|
-
description:
|
|
8178
|
+
label: portableRecheckBusy ? '正在重新检查结果…' : '重新检查结果',
|
|
8179
|
+
description: portableRecheckBusy
|
|
8180
|
+
? '正在读取 Skills CLI 当前状态;不会执行安装'
|
|
8181
|
+
: '只读取 Skills CLI 当前状态,不执行安装',
|
|
8182
|
+
disabled: portableRecheckBusy ? '只读检查进行中' : undefined,
|
|
7752
8183
|
status: 'warning',
|
|
7753
8184
|
run: async () => {
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
8185
|
+
if (operationBusy.current)
|
|
8186
|
+
return;
|
|
8187
|
+
operationBusy.current = true;
|
|
8188
|
+
setPortableRecheckBusy(true);
|
|
8189
|
+
try {
|
|
8190
|
+
await props.controller.recheckPortableResult();
|
|
8191
|
+
setFocus(0);
|
|
8192
|
+
setScrollOffset(0);
|
|
8193
|
+
}
|
|
8194
|
+
finally {
|
|
8195
|
+
operationBusy.current = false;
|
|
8196
|
+
setPortableRecheckBusy(false);
|
|
8197
|
+
}
|
|
7757
8198
|
},
|
|
7758
8199
|
}] : []),
|
|
7759
8200
|
...(!portableReceipt?.verificationUnknown
|
|
@@ -8058,14 +8499,14 @@ export function ConsoleAppV4(props) {
|
|
|
8058
8499
|
? 3 + (portableReceipt.failureCode ? 1 : 0)
|
|
8059
8500
|
+ (policy.mode === 'compact' ? 0 : resultSummary?.elapsedMs !== undefined ? 1 : 0)
|
|
8060
8501
|
: 1;
|
|
8061
|
-
const portableFixedRows = showTechnical
|
|
8502
|
+
const portableFixedRows = (portableRecheckBusy ? 1 : 0) + (showTechnical
|
|
8062
8503
|
? 1 + portableSkillLimit + 1
|
|
8063
8504
|
+ (portableResultAgentMode === 'automatic' ? 1 : 0)
|
|
8064
8505
|
+ (portableReceipt?.recovery ? 1 : 0)
|
|
8065
8506
|
+ 1 + portableTechnicalFieldCount
|
|
8066
8507
|
: 3 + portableSkillLimit + 1
|
|
8067
8508
|
+ (portableResultAgentMode === 'automatic' ? 1 : 0)
|
|
8068
|
-
+ (portableReceipt?.recovery ? 1 : 0);
|
|
8509
|
+
+ (portableReceipt?.recovery ? 1 : 0));
|
|
8069
8510
|
currentActionViewportLineBudget = pageActionBudget(portableOperation
|
|
8070
8511
|
? portableResultDetailOpen
|
|
8071
8512
|
? visiblePortableResultDetailLines.length
|
|
@@ -8185,7 +8626,9 @@ export function ConsoleAppV4(props) {
|
|
|
8185
8626
|
help = 'p/n 查看全部项目 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
8186
8627
|
}
|
|
8187
8628
|
content = taskOperation || batchOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(ResultCard, { status: resultStatus, title: "\u6240\u9009\u63D2\u4EF6\u7ED3\u679C", children: [taskResultOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${taskResultOffset} 项`, status: "muted", compact: true }) : null, visibleTaskResultRows, rootResultRows.length > taskResultOffset + visibleTaskResultRows.length
|
|
8188
|
-
? _jsx(StaticRow, { title: "\u2193 \u540E\u9762\u8FD8\u6709", meta: `${rootResultRows.length - taskResultOffset - visibleTaskResultRows.length} 项`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] }) : portableOperation ? _jsxs(Box, { flexDirection: "column", children: [
|
|
8629
|
+
? _jsx(StaticRow, { title: "\u2193 \u540E\u9762\u8FD8\u6709", meta: `${rootResultRows.length - taskResultOffset - visibleTaskResultRows.length} 项`, status: "muted", compact: true }) : null] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: resultActionViewport.offset, limit: resultActionViewport.limit })] }) : portableOperation ? _jsxs(Box, { flexDirection: "column", children: [portableRecheckBusy
|
|
8630
|
+
? _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
|
+
: null, portableResultDetailOpen ? _jsxs(_Fragment, { children: [safePortableResultDetailOffset > 0
|
|
8189
8632
|
? _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
|
|
8190
8633
|
< portableResultDetailLines.length
|
|
8191
8634
|
? _jsx(StaticRow, { title: `↓ 后面还有 ${portableResultDetailLines.length
|
|
@@ -8376,7 +8819,8 @@ export function ConsoleAppV4(props) {
|
|
|
8376
8819
|
}
|
|
8377
8820
|
if (canOpenHelp && supportsTechnicalDetails) {
|
|
8378
8821
|
const technicalHelp = route.name === 'progress' ? 'd 技术进度' : 'd 技术详情';
|
|
8379
|
-
const remainingHelp = help.
|
|
8822
|
+
const remainingHelp = help.split(' · ')
|
|
8823
|
+
.filter((item) => item !== '? 帮助' && item !== technicalHelp).join(' · ');
|
|
8380
8824
|
help = `${technicalHelp} · ? 帮助 · ${remainingHelp}`;
|
|
8381
8825
|
}
|
|
8382
8826
|
else if (canOpenHelp && !help.includes('? 帮助')) {
|
|
@@ -8384,7 +8828,9 @@ export function ConsoleAppV4(props) {
|
|
|
8384
8828
|
}
|
|
8385
8829
|
if (props.onSessionNavigation && !isCharacterInputMode(route, searchFocus)) {
|
|
8386
8830
|
help = busy
|
|
8387
|
-
?
|
|
8831
|
+
? `${props.pendingSessionNavigation === 'reload' ? '已安排完成后重载'
|
|
8832
|
+
: props.pendingSessionNavigation === 'home' ? '已安排完成后回首页'
|
|
8833
|
+
: 'h/H 完成后导航'} · Esc 撤销等待 · ${help}`
|
|
8388
8834
|
: `h 首页 · H 重载 · ${help}`;
|
|
8389
8835
|
}
|
|
8390
8836
|
const targetActionSignature = route.name === 'installTargets' && route.view !== 'blocked'
|
|
@@ -8606,6 +9052,16 @@ export function ConsoleAppV4(props) {
|
|
|
8606
9052
|
}
|
|
8607
9053
|
return;
|
|
8608
9054
|
}
|
|
9055
|
+
if (portableEmptySelectionOpen) {
|
|
9056
|
+
const closeEmptySelection = () => setPortableEmptySelectionOpen(false);
|
|
9057
|
+
if (key.escape || key.return || input.toLowerCase() === 'b') {
|
|
9058
|
+
closeEmptySelection();
|
|
9059
|
+
}
|
|
9060
|
+
else if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
9061
|
+
requestConfirmedExit(closeEmptySelection);
|
|
9062
|
+
}
|
|
9063
|
+
return;
|
|
9064
|
+
}
|
|
8609
9065
|
if (projectReminderOpen) {
|
|
8610
9066
|
const closeProjectReminder = () => {
|
|
8611
9067
|
setProjectReminderOpen(false);
|
|
@@ -8638,7 +9094,7 @@ export function ConsoleAppV4(props) {
|
|
|
8638
9094
|
prepareCurrentProjectTargets(reminder.actionableTargets);
|
|
8639
9095
|
}
|
|
8640
9096
|
else {
|
|
8641
|
-
|
|
9097
|
+
go({ name: 'projectStatus' });
|
|
8642
9098
|
}
|
|
8643
9099
|
}
|
|
8644
9100
|
return;
|
|
@@ -8738,6 +9194,35 @@ export function ConsoleAppV4(props) {
|
|
|
8738
9194
|
}
|
|
8739
9195
|
return;
|
|
8740
9196
|
}
|
|
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
|
+
}
|
|
8741
9226
|
if (overlay) {
|
|
8742
9227
|
if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
|
|
8743
9228
|
if (overlay === 'sessionNavigation')
|
|
@@ -8867,11 +9352,13 @@ export function ConsoleAppV4(props) {
|
|
|
8867
9352
|
if ((key.pageUp || key.pageDown || input.toLowerCase() === 'p' || input.toLowerCase() === 'n')
|
|
8868
9353
|
&& (route.name === 'pluginContent'
|
|
8869
9354
|
|| route.name === 'pluginTechnical'
|
|
9355
|
+
|| route.name === 'projectStatus' && showTechnical
|
|
8870
9356
|
|| route.name === 'issueDetail' && showTechnical
|
|
8871
9357
|
|| route.name === 'sourceRemoveBlocked' && sourceDependenciesExpanded
|
|
8872
9358
|
|| route.name === 'pluginDependencies' && route.view === 'removeBlocked' && !showTechnical
|
|
8873
9359
|
|| route.name === 'pluginDependencyNode'
|
|
8874
9360
|
&& (route.view === 'references' || route.view === 'details')
|
|
9361
|
+
|| route.name === 'pluginDetail'
|
|
8875
9362
|
|| route.name === 'configSave' && route.view === 'changes'
|
|
8876
9363
|
|| route.name === 'sourceSave' && route.view === 'changes'
|
|
8877
9364
|
|| route.name === 'review'
|
|
@@ -8903,7 +9390,7 @@ export function ConsoleAppV4(props) {
|
|
|
8903
9390
|
}
|
|
8904
9391
|
if ((key.pageUp || key.pageDown || input.toLowerCase() === 'p' || input.toLowerCase() === 'n')
|
|
8905
9392
|
&& route.name === 'sourceTechnical') {
|
|
8906
|
-
const total =
|
|
9393
|
+
const total = diagnosticDetailLines(resolveSourceTechnicalDiagnostic(route.sourceId), size.columns - 6).length;
|
|
8907
9394
|
setBatchTargetOffset((value) => Math.min(Math.max(0, total - sourceTechnicalPageRows), Math.max(0, value + (key.pageUp || input.toLowerCase() === 'p'
|
|
8908
9395
|
? -sourceTechnicalPageRows : sourceTechnicalPageRows))));
|
|
8909
9396
|
return;
|
|
@@ -9035,7 +9522,7 @@ export function ConsoleAppV4(props) {
|
|
|
9035
9522
|
}
|
|
9036
9523
|
else if (route.step === 'ref') {
|
|
9037
9524
|
if (sourceInput.trim() === '' || sourceInput !== sourceInput.trim() || /[\s\u0000-\u001f\u007f]/u.test(sourceInput))
|
|
9038
|
-
error = '
|
|
9525
|
+
error = '请输入分支名、标签或提交标识,不含空白或控制字符。';
|
|
9039
9526
|
}
|
|
9040
9527
|
else if (sourceInput !== '') {
|
|
9041
9528
|
const normalized = path.posix.normalize(sourceInput);
|
|
@@ -9156,7 +9643,7 @@ export function ConsoleAppV4(props) {
|
|
|
9156
9643
|
: [];
|
|
9157
9644
|
const definition = activeField.definition.additionalProperties;
|
|
9158
9645
|
if (!definition) {
|
|
9159
|
-
setInputError('
|
|
9646
|
+
setInputError('这个设置不支持新增项目。');
|
|
9160
9647
|
return;
|
|
9161
9648
|
}
|
|
9162
9649
|
const countIssues = validatePluginConfigValue(activeField.definition, { ...activeObjectDraft, [propertyKey]: definition.type === 'number'
|
|
@@ -9266,14 +9753,41 @@ export function ConsoleAppV4(props) {
|
|
|
9266
9753
|
} : draft);
|
|
9267
9754
|
}
|
|
9268
9755
|
if (route.name === 'batchSelection')
|
|
9269
|
-
setFocus(
|
|
9756
|
+
setFocus(0);
|
|
9270
9757
|
setScrollOffset(0);
|
|
9271
9758
|
return;
|
|
9272
9759
|
}
|
|
9273
|
-
if (
|
|
9760
|
+
if (route.name === 'configFields' && configGroupChooserOpen) {
|
|
9761
|
+
if (key.escape || input.toLowerCase() === 'b') {
|
|
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'
|
|
9274
9786
|
|| route.name === 'batchInstallPlatforms' || route.name === 'batchInstallScope'
|
|
9275
9787
|
|| route.name === 'batchPlacement' && pinnedActions.length > 0
|
|
9276
|
-
|| route.name === 'portableSkillSelect'
|
|
9788
|
+
|| route.name === 'portableSkillSelect'
|
|
9789
|
+
|| route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9790
|
+
|| route.name === 'installTargets' && route.view !== 'blocked') && key.tab) {
|
|
9277
9791
|
setFocusZone((current) => current === 'content' ? 'actions' : 'content');
|
|
9278
9792
|
return;
|
|
9279
9793
|
}
|
|
@@ -9309,6 +9823,15 @@ export function ConsoleAppV4(props) {
|
|
|
9309
9823
|
runAction(pinnedActions[pinnedActionFocus]);
|
|
9310
9824
|
return;
|
|
9311
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]);
|
|
9833
|
+
return;
|
|
9834
|
+
}
|
|
9312
9835
|
if (route.name === 'portableSkillSelect' && focusZone === 'actions'
|
|
9313
9836
|
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9314
9837
|
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
@@ -9318,6 +9841,19 @@ export function ConsoleAppV4(props) {
|
|
|
9318
9841
|
runAction(pinnedActions[pinnedActionFocus]);
|
|
9319
9842
|
return;
|
|
9320
9843
|
}
|
|
9844
|
+
if ((route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9845
|
+
|| route.name === 'installTargets' && route.view !== 'blocked')
|
|
9846
|
+
&& focusZone === 'actions'
|
|
9847
|
+
&& (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow)) {
|
|
9848
|
+
setPinnedActionFocus((value) => moveFocus(pinnedActions, value, key.upArrow || key.leftArrow ? -1 : 1));
|
|
9849
|
+
return;
|
|
9850
|
+
}
|
|
9851
|
+
if ((route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9852
|
+
|| route.name === 'installTargets' && route.view !== 'blocked')
|
|
9853
|
+
&& focusZone === 'actions' && key.return) {
|
|
9854
|
+
runAction(pinnedActions[pinnedActionFocus]);
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9321
9857
|
if ((route.name === 'batchInstallPlatforms' || route.name === 'batchInstallScope'
|
|
9322
9858
|
|| route.name === 'batchPlacement' && pinnedActions.length > 0)
|
|
9323
9859
|
&& focusZone === 'actions'
|
|
@@ -9412,6 +9948,12 @@ export function ConsoleAppV4(props) {
|
|
|
9412
9948
|
setFocus(0);
|
|
9413
9949
|
setScrollOffset(0);
|
|
9414
9950
|
}
|
|
9951
|
+
else if (globalAction === 'back' && route.name === 'batchSelection'
|
|
9952
|
+
&& batchDraft?.stage === 'select' && batchDraft.showSelectionTools) {
|
|
9953
|
+
setBatchDraft((current) => current ? { ...current, showSelectionTools: false } : current);
|
|
9954
|
+
setFocus(0);
|
|
9955
|
+
setScrollOffset(0);
|
|
9956
|
+
}
|
|
9415
9957
|
else if (globalAction === 'back'
|
|
9416
9958
|
&& (route.name === 'configSave' || route.name === 'sourceSave')
|
|
9417
9959
|
&& route.view === 'changes') {
|
|
@@ -9485,12 +10027,15 @@ export function ConsoleAppV4(props) {
|
|
|
9485
10027
|
installFilter: nextFilter,
|
|
9486
10028
|
showSelected: false,
|
|
9487
10029
|
} : current);
|
|
9488
|
-
setFocus(
|
|
10030
|
+
setFocus(0);
|
|
9489
10031
|
setScrollOffset(0);
|
|
9490
10032
|
}
|
|
9491
10033
|
else if (route.name === 'pluginList' && route.mode === 'installed' && input.toLowerCase() === 'a') {
|
|
9492
10034
|
prepareCurrentProjectPlugins();
|
|
9493
10035
|
}
|
|
10036
|
+
else if (route.name === 'projectStatus' && input.toLowerCase() === 'c') {
|
|
10037
|
+
runAction(pinnedActions.find((action) => action.actionId === 'project-sync-actionable'));
|
|
10038
|
+
}
|
|
9494
10039
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select' && input.toLowerCase() === 'a') {
|
|
9495
10040
|
const candidates = batchCandidates(batchDraft)
|
|
9496
10041
|
.filter((candidate) => batchCandidateBlockReason(batchDraft, candidate) === undefined);
|
|
@@ -9504,7 +10049,7 @@ export function ConsoleAppV4(props) {
|
|
|
9504
10049
|
}
|
|
9505
10050
|
else if (route.name === 'installTargets' && route.view !== 'blocked'
|
|
9506
10051
|
&& input.toLowerCase() === 'c') {
|
|
9507
|
-
runAction(
|
|
10052
|
+
runAction(pinnedActions.find((action) => action.actionId === 'confirm-install-platforms'));
|
|
9508
10053
|
}
|
|
9509
10054
|
else if (route.name === 'batchInstallPlatforms' && input.toLowerCase() === 'c') {
|
|
9510
10055
|
runAction(actions.find((action) => action.actionId === 'batch-install-platforms-confirm'));
|
|
@@ -9517,15 +10062,22 @@ export function ConsoleAppV4(props) {
|
|
|
9517
10062
|
runAction(actions.find((action) => action.actionId === 'batch-placement-confirm'));
|
|
9518
10063
|
}
|
|
9519
10064
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select' && input.toLowerCase() === 'c') {
|
|
9520
|
-
runAction(
|
|
10065
|
+
runAction(pinnedActions.find((action) => action.actionId === 'batch-selection-continue'));
|
|
9521
10066
|
}
|
|
9522
|
-
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
10067
|
+
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
10068
|
+
&& focusZone === 'content' && input === ' ') {
|
|
9523
10069
|
const action = actions[normalizedEffectiveFocus];
|
|
9524
10070
|
if (action?.pluginId
|
|
9525
10071
|
&& (batchDraft.selectedPluginIds.includes(action.pluginId) || action.canSelectNew)) {
|
|
9526
10072
|
toggleBatchPlugin(action.pluginId);
|
|
9527
10073
|
}
|
|
9528
10074
|
}
|
|
10075
|
+
else if (route.name === 'installTargets' && route.view !== 'blocked'
|
|
10076
|
+
&& focusZone === 'content' && input === ' ') {
|
|
10077
|
+
const action = actions[focus];
|
|
10078
|
+
if (action?.actionId?.startsWith('install-platform-'))
|
|
10079
|
+
runAction(action);
|
|
10080
|
+
}
|
|
9529
10081
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select' && input === '/') {
|
|
9530
10082
|
setSearchFocus(true);
|
|
9531
10083
|
}
|
|
@@ -9540,7 +10092,7 @@ export function ConsoleAppV4(props) {
|
|
|
9540
10092
|
...(next === 'all' ? { sourceId: undefined } : { sourceId: next }),
|
|
9541
10093
|
showSelected: false,
|
|
9542
10094
|
} : draft);
|
|
9543
|
-
setFocus(
|
|
10095
|
+
setFocus(0);
|
|
9544
10096
|
setScrollOffset(0);
|
|
9545
10097
|
}
|
|
9546
10098
|
else if (route.name === 'configFields' && input.toLowerCase() === 's') {
|
|
@@ -9596,7 +10148,9 @@ export function ConsoleAppV4(props) {
|
|
|
9596
10148
|
setFocus((value) => {
|
|
9597
10149
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9598
10150
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
9599
|
-
const next = pluginRows.length > 0 ? moveIndex(pluginRows.length, current, -1)
|
|
10151
|
+
const next = pluginRows.length > 0 ? moveIndex(pluginRows.length, current, -1)
|
|
10152
|
+
: route.name === 'sourceDetail' ? moveIndex(actions.length, current, -1)
|
|
10153
|
+
: moveFocus(actions, current, -1);
|
|
9600
10154
|
if (dynamicActionIds && dynamicActionSignature !== undefined) {
|
|
9601
10155
|
dynamicMenuFocus.current = {
|
|
9602
10156
|
routeKey,
|
|
@@ -9605,11 +10159,16 @@ export function ConsoleAppV4(props) {
|
|
|
9605
10159
|
};
|
|
9606
10160
|
}
|
|
9607
10161
|
const viewport = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9608
|
-
?
|
|
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)))
|
|
9609
10165
|
: route.name === 'portableSkillSelect' && snapshot.portableDraft && !portableToolsOpen
|
|
9610
|
-
? portableSkillCandidateViewport(actions.slice(0, portableVisibleCandidates.length),
|
|
10166
|
+
? portableSkillCandidateViewport(actions.slice(0, portableVisibleCandidates.length), Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, pageActionBudget(actions.length - portableVisibleCandidates.length
|
|
9611
10167
|
+ (portableVariantCandidates.length > 0 ? 3 : 0)))
|
|
9612
|
-
:
|
|
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);
|
|
9613
10172
|
if (route.name === 'pluginDependencies' && route.view !== 'removeBlocked') {
|
|
9614
10173
|
const projection = buildUnifiedDependencyGraphProjection({
|
|
9615
10174
|
overviews: props.controller.listPluginDependencyOverviews(),
|
|
@@ -9621,9 +10180,7 @@ export function ConsoleAppV4(props) {
|
|
|
9621
10180
|
...projection.dependents.defaultExpandedPluginIds,
|
|
9622
10181
|
]), next, viewport.offset);
|
|
9623
10182
|
}
|
|
9624
|
-
setScrollOffset(
|
|
9625
|
-
? next === 0 ? 0 : viewport.offset + 1
|
|
9626
|
-
: viewport.offset);
|
|
10183
|
+
setScrollOffset(viewport.offset);
|
|
9627
10184
|
return next;
|
|
9628
10185
|
});
|
|
9629
10186
|
}
|
|
@@ -9634,7 +10191,9 @@ export function ConsoleAppV4(props) {
|
|
|
9634
10191
|
setFocus((value) => {
|
|
9635
10192
|
const current = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9636
10193
|
? normalizedEffectiveFocus : dynamicActionIds ? effectiveFocus : value;
|
|
9637
|
-
const next = pluginRows.length > 0 ? moveIndex(pluginRows.length, current, 1)
|
|
10194
|
+
const next = pluginRows.length > 0 ? moveIndex(pluginRows.length, current, 1)
|
|
10195
|
+
: route.name === 'sourceDetail' ? moveIndex(actions.length, current, 1)
|
|
10196
|
+
: moveFocus(actions, current, 1);
|
|
9638
10197
|
if (dynamicActionIds && dynamicActionSignature !== undefined) {
|
|
9639
10198
|
dynamicMenuFocus.current = {
|
|
9640
10199
|
routeKey,
|
|
@@ -9643,11 +10202,16 @@ export function ConsoleAppV4(props) {
|
|
|
9643
10202
|
};
|
|
9644
10203
|
}
|
|
9645
10204
|
const viewport = route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9646
|
-
?
|
|
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)))
|
|
9647
10208
|
: route.name === 'portableSkillSelect' && snapshot.portableDraft && !portableToolsOpen
|
|
9648
|
-
? portableSkillCandidateViewport(actions.slice(0, portableVisibleCandidates.length),
|
|
10209
|
+
? portableSkillCandidateViewport(actions.slice(0, portableVisibleCandidates.length), Math.max(20, size.columns - 2), policy.mode === 'compact', next, scrollOffset, pageActionBudget(actions.length - portableVisibleCandidates.length
|
|
9649
10210
|
+ (portableVariantCandidates.length > 0 ? 3 : 0)))
|
|
9650
|
-
:
|
|
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);
|
|
9651
10215
|
if (route.name === 'pluginDependencies' && route.view !== 'removeBlocked') {
|
|
9652
10216
|
const projection = buildUnifiedDependencyGraphProjection({
|
|
9653
10217
|
overviews: props.controller.listPluginDependencyOverviews(),
|
|
@@ -9659,9 +10223,7 @@ export function ConsoleAppV4(props) {
|
|
|
9659
10223
|
...projection.dependents.defaultExpandedPluginIds,
|
|
9660
10224
|
]), next, viewport.offset);
|
|
9661
10225
|
}
|
|
9662
|
-
setScrollOffset(
|
|
9663
|
-
? next === 0 ? 0 : viewport.offset + 1
|
|
9664
|
-
: viewport.offset);
|
|
10226
|
+
setScrollOffset(viewport.offset);
|
|
9665
10227
|
return next;
|
|
9666
10228
|
});
|
|
9667
10229
|
}
|
|
@@ -9681,34 +10243,54 @@ export function ConsoleAppV4(props) {
|
|
|
9681
10243
|
}
|
|
9682
10244
|
if (actionError)
|
|
9683
10245
|
description = actionError;
|
|
9684
|
-
const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, help: help, size: size, children: content }));
|
|
10246
|
+
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 }));
|
|
10247
|
+
if (portableEmptySelectionOpen) {
|
|
10248
|
+
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
10249
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 7, marginLeft: Math.max(2, Math.floor((size.columns - modalWidth) / 2)), width: modalWidth, flexDirection: "column", backgroundColor: tuiTheme.colors.panelBackground, children: _jsx(ReviewCard, { title: "\u8FD8\u6CA1\u6709\u9009\u62E9 Skill", description: "\u7528 Space \u52FE\u9009\uFF1B\u4E5F\u53EF\u4EE5\u6253\u5F00\u9009\u62E9\u5DE5\u5177\u3002", children: _jsx(SemanticRows, { actions: [{ label: '返回选择' }], focus: 0, kind: "action", compact: true }) }) })] });
|
|
10250
|
+
}
|
|
9685
10251
|
if (projectReminderOpen && snapshot.projectIntentReminder) {
|
|
9686
10252
|
const reminder = snapshot.projectIntentReminder;
|
|
9687
10253
|
const pluginCount = new Set([
|
|
9688
10254
|
...reminder.actionableTargets,
|
|
9689
10255
|
...reminder.blockedTargets,
|
|
10256
|
+
...reminder.unconfirmedTargets,
|
|
9690
10257
|
].map((target) => target.pluginId)).size;
|
|
9691
10258
|
const reminderActions = [
|
|
9692
10259
|
{ label: '稍后处理', description: '首页会保留项目插件入口' },
|
|
9693
10260
|
{
|
|
9694
|
-
label:
|
|
9695
|
-
description:
|
|
10261
|
+
label: `查看并同步 ${reminder.actionableTargets.length} 个位置`,
|
|
10262
|
+
description: `先显示完整操作汇总和脚本影响,再单独确认;保留 ${reminder.blockedTargets.length} 个受阻位置和 ${reminder.unconfirmedTargets.length} 个未确认位置`,
|
|
9696
10263
|
disabled: reminder.actionableTargets.length === 0 ? '当前没有已确认可同步的目标' : undefined,
|
|
9697
10264
|
},
|
|
9698
10265
|
{ label: '查看全部项目插件', description: '包含需要先处理和无法确认的项目目标' },
|
|
9699
10266
|
];
|
|
9700
|
-
|
|
9701
|
-
|
|
10267
|
+
const reminderWidth = Math.max(40, size.columns - (policy.mode === 'compact' ? 8 : 16));
|
|
10268
|
+
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 - reminderWidth) / 2)), width: reminderWidth, flexDirection: "column", children: _jsxs(ReviewCard, { title: `本项目有 ${pluginCount} 个插件尚未就绪`, description: `${reminder.actionableTargets.length} 个位置可同步;`
|
|
10269
|
+
+ `${reminder.blockedTargets.length} 个位置需要先处理;`
|
|
10270
|
+
+ `${reminder.unconfirmedTargets.length} 个位置未确认。`, children: [_jsx(StaticRow, { title: "\u6309\u9879\u76EE\u5171\u4EAB\u8BBE\u7F6E\u6062\u590D\uFF0C\u4E2A\u4EBA\u8BBE\u7F6E\u4E0D\u4F1A\u88AB\u6E05\u7A7A\u3002", compact: true }), _jsx(StaticRow, { title: "\u786E\u8BA4\u540C\u6B65\u524D\u53EF\u67E5\u770B\u811A\u672C\u548C\u5B8C\u6574\u5F71\u54CD\u3002", status: "warning", compact: true }), _jsx(SemanticRows, { actions: reminderActions, focus: overlayFocus, kind: "action", compact: true }), _jsx(StaticRow, { title: "\u2191\u2193 \u9009\u62E9 \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u7A0D\u540E\u5904\u7406", status: "muted", compact: true })] }) })] });
|
|
9702
10271
|
}
|
|
9703
10272
|
if (operationConfirmation && snapshot.activeReview) {
|
|
9704
10273
|
const review = snapshot.activeReview;
|
|
9705
|
-
const
|
|
9706
|
-
|
|
10274
|
+
const portableUserConfirmation = review.operationKind === 'portableInstall'
|
|
10275
|
+
&& snapshot.portableDraft?.scope === 'user';
|
|
10276
|
+
const confirmationRiskLines = (columns) => portableUserConfirmation
|
|
10277
|
+
? [
|
|
10278
|
+
'! 本次在所有项目范围安装选定 Skills。',
|
|
10279
|
+
'具体对象和范围以刚才的汇总为准。',
|
|
10280
|
+
].flatMap((line) => wrapConsoleText(line, Math.max(1, columns - 2)))
|
|
10281
|
+
: operationRiskLines(review, columns);
|
|
10282
|
+
const modalWidth = portableUserConfirmation
|
|
10283
|
+
? Math.max(40, size.columns - (policy.mode === 'compact' ? 8 : 16))
|
|
10284
|
+
: Math.max(40, Math.floor(size.columns * 0.6));
|
|
10285
|
+
const modalMarginLeft = portableUserConfirmation
|
|
10286
|
+
? Math.max(2, Math.floor((size.columns - modalWidth) / 2))
|
|
10287
|
+
: Math.max(2, Math.floor(size.columns / 5));
|
|
10288
|
+
const modalWarningLines = confirmationRiskLines(Math.max(12, modalWidth - 6));
|
|
9707
10289
|
const shortWarningLimit = policy.mode === 'compact' ? 4 : 6;
|
|
9708
10290
|
const shortConfirmation = modalWarningLines.length <= shortWarningLimit;
|
|
9709
10291
|
const warningLines = shortConfirmation
|
|
9710
10292
|
? modalWarningLines
|
|
9711
|
-
:
|
|
10293
|
+
: confirmationRiskLines(Math.max(12, size.columns - 8));
|
|
9712
10294
|
const warningLimit = Math.max(1, policy.mode === 'compact' ? 1 : 7);
|
|
9713
10295
|
const warningOffset = Math.min(operationConfirmation.warningOffset, Math.max(0, warningLines.length - 1));
|
|
9714
10296
|
const visibleWarnings = shortConfirmation
|
|
@@ -9725,7 +10307,10 @@ export function ConsoleAppV4(props) {
|
|
|
9725
10307
|
}
|
|
9726
10308
|
const confirmationActions = [
|
|
9727
10309
|
{ label: '返回检查', description: '回到完整操作汇总' },
|
|
9728
|
-
{
|
|
10310
|
+
{
|
|
10311
|
+
label: portableUserConfirmation ? '确认并执行' : `我已了解影响,${review.primaryLabel}`,
|
|
10312
|
+
status: 'error',
|
|
10313
|
+
},
|
|
9729
10314
|
...(warningPaginated ? paginationActions({
|
|
9730
10315
|
offset: warningOffset,
|
|
9731
10316
|
total: warningLines.length,
|
|
@@ -9733,14 +10318,16 @@ export function ConsoleAppV4(props) {
|
|
|
9733
10318
|
move: moveWarningPage,
|
|
9734
10319
|
}) : []),
|
|
9735
10320
|
];
|
|
9736
|
-
const confirmationCard = (_jsxs(ReviewCard, { title:
|
|
10321
|
+
const confirmationCard = (_jsxs(ReviewCard, { title: portableUserConfirmation ? '确认本次影响' : '请确认本次风险', description: "\u786E\u8BA4\u53EA\u5BF9\u5F53\u524D\u64CD\u4F5C\u6C47\u603B\u6709\u6548\uFF1B\u5185\u5BB9\u53D8\u5316\u540E\u5FC5\u987B\u91CD\u65B0\u68C0\u67E5\u3002", children: [!shortConfirmation && warningOffset > 0 ? _jsx(StaticRow, { title: `↑ 前面还有 ${warningOffset} 行`, status: "muted", compact: true }) : null, visibleWarnings.length > 0
|
|
9737
10322
|
? visibleWarnings.map((warning, index) => _jsx(StaticRow, { title: warning, status: warning.startsWith('! ') ? 'warning' : 'info', compact: true }, `${warningOffset + index}:${warning}`))
|
|
9738
10323
|
: _jsx(StaticRow, { title: "! \u6B64\u64CD\u4F5C\u9700\u8981\u660E\u786E\u6388\u6743", status: "warning", compact: true }), !shortConfirmation && warningLines.length - warningOffset - visibleWarnings.length > 0
|
|
9739
10324
|
? _jsx(StaticRow, { title: `↓ 后面还有 ${warningLines.length - warningOffset - visibleWarnings.length} 行`, status: "muted", compact: true }) : null, _jsx(SemanticRows, { actions: confirmationActions, focus: overlayFocus, kind: "action", compact: true })] }));
|
|
9740
10325
|
if (shortConfirmation) {
|
|
9741
|
-
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft:
|
|
10326
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: modalMarginLeft, width: modalWidth, flexDirection: "column", children: confirmationCard })] });
|
|
9742
10327
|
}
|
|
9743
|
-
return _jsx(ScreenFrame, { title:
|
|
10328
|
+
return _jsx(ScreenFrame, { title: review.operationKind === 'catalogStorePrune'
|
|
10329
|
+
? `确认${review.title}?`
|
|
10330
|
+
: `确认${review.title} ${review.subject.name}?`, breadcrumb: `${review.subject.name} / 风险确认`, context: "\u72EC\u7ACB\u6388\u6743", help: "PgUp/PgDn \u67E5\u770B\u5168\u90E8\u8B66\u544A \u00B7 \u2191\u2193 \u79FB\u52A8 \u00B7 Enter \u9009\u62E9 \u00B7 Esc \u8FD4\u56DE", size: size, children: confirmationCard });
|
|
9744
10331
|
}
|
|
9745
10332
|
if (sourceTransportOverlay) {
|
|
9746
10333
|
const configuredSource = route.name === 'sourceEdit'
|
|
@@ -9756,7 +10343,34 @@ export function ConsoleAppV4(props) {
|
|
|
9756
10343
|
{ label: 'HTTPS', description: '默认方式;无需预先配置 SSH 密钥', selected: currentTransport === 'HTTPS' },
|
|
9757
10344
|
{ label: 'SSH', description: '适合已配置 GitHub SSH 访问的用户', selected: currentTransport === 'SSH' },
|
|
9758
10345
|
];
|
|
9759
|
-
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop:
|
|
10346
|
+
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
|
+
}
|
|
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 })] }) })] });
|
|
9760
10374
|
}
|
|
9761
10375
|
if (!overlay)
|
|
9762
10376
|
return base;
|
|
@@ -9773,7 +10387,7 @@ export function ConsoleAppV4(props) {
|
|
|
9773
10387
|
{ label: overlay === 'discard' || overlay === 'sessionNavigation' ? '继续编辑' : '继续使用' },
|
|
9774
10388
|
{ label: overlay === 'discard' || overlay === 'sessionNavigation' ? '放弃修改' : '退出', status: 'error' },
|
|
9775
10389
|
];
|
|
9776
|
-
return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: overlay === 'help' ? null : base }), _jsx(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", children: _jsxs(ReviewCard, { title: overlay === 'help' ? '当前页快捷键'
|
|
10390
|
+
return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: overlay === 'help' ? null : base }), _jsx(Box, { position: "absolute", marginTop: overlay === 'help' ? 0 : 5, marginLeft: overlay === 'help' ? 1 : Math.max(2, Math.floor(size.columns / 5)), width: overlay === 'help' ? size.columns - 2 : Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", children: _jsxs(ReviewCard, { title: overlay === 'help' ? '当前页快捷键'
|
|
9777
10391
|
: overlay === 'discard' || overlay === 'sessionNavigation' ? '放弃未保存修改?'
|
|
9778
10392
|
: '退出控制台?', children: [overlay === 'help'
|
|
9779
10393
|
? _jsx(_Fragment, { children: helpRows.map(([key, value]) => _jsx(StaticRow, { title: `${key} ${value}`, compact: true }, key)) })
|