@lesliechan721/aw 0.1.0-beta.34 → 0.1.0-beta.35
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/scripts/test-plugin-scripts.js +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +1 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/portable/materializer.js +19 -3
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/review/review-profile.js +19 -2
- package/dist/src/review/review-profile.js.map +1 -1
- package/dist/src/tui/components.d.ts +20 -0
- package/dist/src/tui/components.js +13 -1
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-array-draft.d.ts +29 -0
- package/dist/src/tui/config-array-draft.js +48 -0
- package/dist/src/tui/config-array-draft.js.map +1 -0
- package/dist/src/tui/config-value-presentation.d.ts +1 -0
- package/dist/src/tui/config-value-presentation.js +17 -1
- package/dist/src/tui/config-value-presentation.js.map +1 -1
- package/dist/src/tui/console-app-v4.js +504 -288
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +27 -2
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +15 -0
- package/dist/src/tui/controller.js +111 -4
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/model.d.ts +9 -0
- package/dist/src/tui/model.js +10 -2
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/project-inventory.d.ts +68 -0
- package/dist/src/tui/project-inventory.js +203 -0
- package/dist/src/tui/project-inventory.js.map +1 -0
- package/dist/src/tui/router.d.ts +16 -3
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +7 -1
- package/dist/src/tui/screen-registry.js +1 -1
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/dist/src/tui/theme.d.ts +12 -0
- package/dist/src/tui/theme.js +12 -0
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/catalog-source-references.js +2 -2
- package/dist/src/utils/plugin-config-write.js +3 -1
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +5 -5
- package/dist/src/utils/plugin-config.js +47 -23
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { arrayDraftChanged, arrayDraftValue, createConfigArrayDraft, editConfigArrayDraft } from './config-array-draft.js';
|
|
3
|
+
import { PLUGIN_CONFIG_ARRAY_MAX_ITEMS } from '@lesliechan721/aw-plugin-core';
|
|
2
4
|
import { presentConfigValue, safeConfigText } from './config-value-presentation.js';
|
|
3
5
|
import { Box, Text, useInput, useWindowSize } from 'ink';
|
|
4
|
-
import { useEffect, useRef, useState } from 'react';
|
|
6
|
+
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
5
7
|
import path from 'node:path';
|
|
6
8
|
import stringWidth from 'string-width';
|
|
7
9
|
import { validatePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
@@ -12,7 +14,7 @@ import { normalizePortableAgents, SKILLS_CLI_SUPPORTED_VERSION_LABEL, } from '..
|
|
|
12
14
|
import { selectLifecycleRetryAction, } from '../commands/operation-planner.js';
|
|
13
15
|
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, SCRIPT_TIMEOUT_MAX_MS, SCRIPT_TIMEOUT_MIN_MS, } from '../commands/options.js';
|
|
14
16
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
15
|
-
import { ActionRow, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
17
|
+
import { ActionRow, ConfigTile, configTileHeight, HomeNavTile, KeyCap, ObjectCardRow, objectCardRowLayout, pluginListRowLayout, StatusBadge, diagnosticDetailLines, diagnosticDetailText, Callout, ChoiceRow, ContentViewport, DiagnosticSummary, InputCard, PluginListRow, ProgressRow, ResultCard, ResultRow, ReviewCard, ReviewChangeRow, ScreenFrame, SectionCard, SectionRow, StaticRow, StatusCard, TaskRow, TechnicalDetails, ToggleRow, } from './components.js';
|
|
16
18
|
import { safeDiagnosticCode, sanitizeDiagnosticText, } from './diagnostics.js';
|
|
17
19
|
import { extractPortableError, TuiConfigInvalidStoreError, userIssueKey, } from './controller.js';
|
|
18
20
|
import { pluginWriteTaskVersionEvidence } from './plugin-write-task.js';
|
|
@@ -31,7 +33,7 @@ import { contentRowBudget, rowViewport, wrapDisplayText } from './viewport.js';
|
|
|
31
33
|
import { resolveRouteScreenId, routeDefinition, TUI_SCREEN_IDS, unreachableTuiRoute, } from './screen-registry.js';
|
|
32
34
|
export const CONSOLE_V4_SCREEN_IDS = TUI_SCREEN_IDS;
|
|
33
35
|
const PLUGIN_FILTERS = [
|
|
34
|
-
'all', 'installed', 'uninstalled', 'updateAvailable', 'attention',
|
|
36
|
+
'all', 'installed', 'uninstalled', 'updateAvailable', 'attention', 'project', 'user',
|
|
35
37
|
];
|
|
36
38
|
function pluginFilterLabel(filter) {
|
|
37
39
|
return {
|
|
@@ -40,6 +42,8 @@ function pluginFilterLabel(filter) {
|
|
|
40
42
|
uninstalled: '未安装',
|
|
41
43
|
updateAvailable: '可更新',
|
|
42
44
|
attention: '需处理',
|
|
45
|
+
project: '当前项目',
|
|
46
|
+
user: '所有项目',
|
|
43
47
|
}[filter];
|
|
44
48
|
}
|
|
45
49
|
function sourceOperationBlockLabel(reason) {
|
|
@@ -198,6 +202,9 @@ function helpRowsForRoute(route, actionCount, supportsTechnicalDetails, busy, se
|
|
|
198
202
|
rows.push(['? / o', '关闭帮助 / 查看操作记录']);
|
|
199
203
|
return rows;
|
|
200
204
|
}
|
|
205
|
+
function projectDependencyLines(root) {
|
|
206
|
+
return root?.dependencies.flatMap((dependency) => dependency.targets.map((target) => `依赖 ${dependency.displayName} · ${dependency.pluginId} · ${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} · 期望 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'}${target.reasonCodes.length ? ` · ${target.reasonCodes.map((code) => safeDiagnosticCode(code, 'projectTargetUnknown')).join('、')}` : ''}`)) ?? [];
|
|
207
|
+
}
|
|
201
208
|
function configOwnerKey(route) {
|
|
202
209
|
return route.pluginId;
|
|
203
210
|
}
|
|
@@ -401,25 +408,7 @@ function dedupeActions(actions) {
|
|
|
401
408
|
});
|
|
402
409
|
}
|
|
403
410
|
function wrapConsoleText(value, columns) {
|
|
404
|
-
|
|
405
|
-
const graphemes = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(value)]
|
|
406
|
-
.map((part) => part.segment);
|
|
407
|
-
const lines = [];
|
|
408
|
-
let line = '';
|
|
409
|
-
let lineWidth = 0;
|
|
410
|
-
for (const grapheme of graphemes) {
|
|
411
|
-
const graphemeWidth = stringWidth(grapheme);
|
|
412
|
-
if (line && lineWidth + graphemeWidth > width) {
|
|
413
|
-
lines.push(line);
|
|
414
|
-
line = '';
|
|
415
|
-
lineWidth = 0;
|
|
416
|
-
}
|
|
417
|
-
line += grapheme;
|
|
418
|
-
lineWidth += graphemeWidth;
|
|
419
|
-
}
|
|
420
|
-
if (line || lines.length === 0)
|
|
421
|
-
lines.push(line);
|
|
422
|
-
return lines;
|
|
411
|
+
return wrapDisplayText(value, columns);
|
|
423
412
|
}
|
|
424
413
|
function sourceRefSummary(value) {
|
|
425
414
|
if (!value)
|
|
@@ -859,7 +848,8 @@ function SemanticRows(props) {
|
|
|
859
848
|
return null;
|
|
860
849
|
const index = visibleIndex + offset;
|
|
861
850
|
const rowProps = {
|
|
862
|
-
title: action.label,
|
|
851
|
+
title: _jsx(Text, { children: action.label.split(/(\[[a-z/?]\])/u).map((part, index) => /^\[[a-z/?]\]$/u.test(part)
|
|
852
|
+
? _jsx(KeyCap, { children: part.slice(1, -1) }, index) : part) }),
|
|
863
853
|
description: action.description,
|
|
864
854
|
meta: action.meta ?? (props.compact ? action.disabled : undefined),
|
|
865
855
|
status: action.status,
|
|
@@ -889,11 +879,13 @@ function SequenceRows(props) {
|
|
|
889
879
|
];
|
|
890
880
|
const viewport = rowViewport({
|
|
891
881
|
rowHeights: props.actions.map((action, index) => action.role === 'navigation' ? 0 : index < props.itemCount
|
|
892
|
-
?
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
882
|
+
? action.configTile ? configTileHeight({ width: props.columns, ...action.configTile })
|
|
883
|
+
: props.skillRows ? 2 + (action.exception ? 1 : 0)
|
|
884
|
+
: props.wrapValues ? configLines(action).length : props.cards ? objectCardRowLayout({
|
|
885
|
+
width: props.columns, description: action.disabled ?? action.description ?? '',
|
|
886
|
+
meta: action.meta, sourceLabel: action.sourceLabel, maxDescriptionRows: action.maxDescriptionRows,
|
|
887
|
+
hasTags: action.tags !== undefined, compact: props.compact,
|
|
888
|
+
}).height : action.description ? 2 : 1
|
|
897
889
|
: 1),
|
|
898
890
|
focus: props.focus, requestedOffset: props.scrollOffset, rowBudget: props.rowBudget,
|
|
899
891
|
});
|
|
@@ -902,6 +894,10 @@ function SequenceRows(props) {
|
|
|
902
894
|
return null;
|
|
903
895
|
const index = viewport.offset + visibleIndex;
|
|
904
896
|
const focused = !props.inactive && index === props.focus;
|
|
897
|
+
if (index < props.itemCount && action.configTile)
|
|
898
|
+
return _jsx(ConfigTile, { width: props.columns, title: action.label, focused: focused, ...action.configTile }, action.configKey);
|
|
899
|
+
if (index < props.itemCount && props.skillRows)
|
|
900
|
+
return _jsxs(Box, { flexDirection: "column", backgroundColor: focused ? tuiTheme.colors.selectionBackground : undefined, children: [_jsxs(Text, { bold: focused, color: tuiTheme.roles.primaryValue, wrap: "truncate-end", children: [focused ? '›' : ' ', " [", action.selected ? 'x' : ' ', "] ", action.label] }), _jsxs(Text, { color: tuiTheme.roles.secondary, wrap: "truncate-end", children: [" ", action.description || '暂无简介'] }), action.exception ? _jsxs(Text, { color: tuiTheme.roles.warning, wrap: "truncate-end", children: [" ! ", action.exception] }) : null] }, action.actionId ?? action.label);
|
|
905
901
|
if (index < props.itemCount && props.wrapValues)
|
|
906
902
|
return _jsx(Box, { flexDirection: "column", children: configLines(action).map((line, lineIndex) => _jsxs(Text, { bold: focused && lineIndex === 0, color: action.status === 'error' ? 'red' : undefined, children: [lineIndex === 0 ? focused ? '› ' : ' ' : ' ', line] }, lineIndex)) }, action.configKey ?? action.label);
|
|
907
903
|
return index < props.itemCount && props.cards
|
|
@@ -1161,6 +1157,9 @@ export function ConsoleAppV4(props) {
|
|
|
1161
1157
|
const [configLayerPages, setConfigLayerPages] = useState({});
|
|
1162
1158
|
const [configDrafts, setConfigDrafts] = useState({});
|
|
1163
1159
|
const [configCandidate, setConfigCandidate] = useState();
|
|
1160
|
+
const [arrayDrafts, setArrayDrafts] = useState({});
|
|
1161
|
+
const [arrayToolsOpen, setArrayToolsOpen] = useState(false);
|
|
1162
|
+
const [arrayToolFocus, setArrayToolFocus] = useState(0);
|
|
1164
1163
|
const [objectDrafts, setObjectDrafts] = useState({});
|
|
1165
1164
|
const [changedObjectDraftKeys, setChangedObjectDraftKeys] = useState([]);
|
|
1166
1165
|
const [objectError, setObjectError] = useState();
|
|
@@ -1208,7 +1207,7 @@ export function ConsoleAppV4(props) {
|
|
|
1208
1207
|
const selectedDependencyReferenceIndex = useRef(0);
|
|
1209
1208
|
const externalDraftRevision = useRef({ config: 0, source: 0, batch: 0 });
|
|
1210
1209
|
const previousExternalDraftValues = useRef({
|
|
1211
|
-
configDrafts, configCandidate, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
1210
|
+
configDrafts, configCandidate, arrayDrafts, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
1212
1211
|
});
|
|
1213
1212
|
const externalDraftFlowIds = useRef(new Map());
|
|
1214
1213
|
const configOverviewAnchors = useRef(new Map());
|
|
@@ -1354,11 +1353,16 @@ export function ConsoleAppV4(props) {
|
|
|
1354
1353
|
const savedScopes = new Set(snapshot.result.summary.configGroups
|
|
1355
1354
|
?.filter((group) => group.configSaved).map((group) => group.scope) ?? []);
|
|
1356
1355
|
const nextDrafts = Object.fromEntries(Object.entries(configDrafts)
|
|
1357
|
-
.filter(([draftScope]) => !savedScopes.has(draftScope))
|
|
1356
|
+
.filter(([draftScope]) => !savedScopes.has(draftScope))
|
|
1357
|
+
.map(([scope, draft]) => [scope, draft.editEvidence ? { ...draft, editEvidence: Object.fromEntries(Object.entries(draft.editEvidence).map(([key, evidence]) => [key, {
|
|
1358
|
+
...evidence, storeDigests: Object.fromEntries(Object.entries(evidence.storeDigests)
|
|
1359
|
+
.filter(([savedScope]) => !savedScopes.has(savedScope))),
|
|
1360
|
+
}])) } : draft]));
|
|
1358
1361
|
setConfigDrafts(nextDrafts);
|
|
1359
1362
|
setDirty(Object.keys(nextDrafts).length > 0);
|
|
1360
1363
|
setConfigCandidate(undefined);
|
|
1361
1364
|
setObjectDrafts({});
|
|
1365
|
+
setArrayDrafts({});
|
|
1362
1366
|
setChangedObjectDraftKeys([]);
|
|
1363
1367
|
setObjectError(undefined);
|
|
1364
1368
|
}
|
|
@@ -1374,6 +1378,7 @@ export function ConsoleAppV4(props) {
|
|
|
1374
1378
|
setDirty(Object.keys(nextDrafts).length > 0);
|
|
1375
1379
|
setConfigCandidate(undefined);
|
|
1376
1380
|
setObjectDrafts({});
|
|
1381
|
+
setArrayDrafts({});
|
|
1377
1382
|
setChangedObjectDraftKeys([]);
|
|
1378
1383
|
setObjectError(undefined);
|
|
1379
1384
|
}
|
|
@@ -1458,6 +1463,7 @@ export function ConsoleAppV4(props) {
|
|
|
1458
1463
|
const previous = previousExternalDraftValues.current;
|
|
1459
1464
|
if (previous.configDrafts !== configDrafts
|
|
1460
1465
|
|| previous.configCandidate !== configCandidate
|
|
1466
|
+
|| previous.arrayDrafts !== arrayDrafts
|
|
1461
1467
|
|| previous.objectDrafts !== objectDrafts
|
|
1462
1468
|
|| previous.changedObjectDraftKeys !== changedObjectDraftKeys) {
|
|
1463
1469
|
externalDraftRevision.current.config += 1;
|
|
@@ -1467,7 +1473,7 @@ export function ConsoleAppV4(props) {
|
|
|
1467
1473
|
if (previous.batchDraft !== batchDraft)
|
|
1468
1474
|
externalDraftRevision.current.batch += 1;
|
|
1469
1475
|
previousExternalDraftValues.current = {
|
|
1470
|
-
configDrafts, configCandidate, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
1476
|
+
configDrafts, configCandidate, arrayDrafts, objectDrafts, changedObjectDraftKeys, sourceDraft, batchDraft,
|
|
1471
1477
|
};
|
|
1472
1478
|
const currentFlowId = snapshot.router.currentFlowId;
|
|
1473
1479
|
if (currentFlowId && configRouteOwner) {
|
|
@@ -1476,7 +1482,7 @@ export function ConsoleAppV4(props) {
|
|
|
1476
1482
|
if (currentFlowId && sourceDraft)
|
|
1477
1483
|
externalDraftFlowIds.current.set(sourceDraft.owner, currentFlowId);
|
|
1478
1484
|
const drafts = [];
|
|
1479
|
-
if ((dirty || configCandidate || changedObjectDraftKeys.length > 0) && draftOwner) {
|
|
1485
|
+
if ((dirty || configCandidate || Object.values(arrayDrafts).some((draft) => draft.initial === undefined ? draft.rows.length > 0 : arrayDraftChanged(draft)) || changedObjectDraftKeys.length > 0) && draftOwner) {
|
|
1480
1486
|
const flowId = externalDraftFlowIds.current.get(draftOwner);
|
|
1481
1487
|
drafts.push({
|
|
1482
1488
|
kind: 'configDraft', owner: draftOwner, revision: externalDraftRevision.current.config,
|
|
@@ -1499,7 +1505,7 @@ export function ConsoleAppV4(props) {
|
|
|
1499
1505
|
props.controller.setExternalDraftState(drafts);
|
|
1500
1506
|
}, [
|
|
1501
1507
|
props.controller, batchDraft, changedObjectDraftKeys, configRouteOwner, dirty, draftOwner,
|
|
1502
|
-
configDrafts, configCandidate, objectDrafts, route.name, snapshot.router.currentFlowId, sourceDraft,
|
|
1508
|
+
configDrafts, configCandidate, arrayDrafts, objectDrafts, route.name, snapshot.router.currentFlowId, sourceDraft,
|
|
1503
1509
|
]);
|
|
1504
1510
|
useEffect(() => () => props.controller.setExternalDraftState(), [props.controller]);
|
|
1505
1511
|
const currentConfigPage = configPageMatches(configPage, configRoute) ? configPage : undefined;
|
|
@@ -1564,6 +1570,7 @@ export function ConsoleAppV4(props) {
|
|
|
1564
1570
|
setConfigDrafts({});
|
|
1565
1571
|
setConfigCandidate(undefined);
|
|
1566
1572
|
setObjectDrafts({});
|
|
1573
|
+
setArrayDrafts({});
|
|
1567
1574
|
setChangedObjectDraftKeys([]);
|
|
1568
1575
|
setObjectError(undefined);
|
|
1569
1576
|
setDirty(false);
|
|
@@ -1805,14 +1812,49 @@ export function ConsoleAppV4(props) {
|
|
|
1805
1812
|
setFocus(0);
|
|
1806
1813
|
}, [props.controller, route.name, route.name === 'pluginList' ? route.mode : '',
|
|
1807
1814
|
route.name === 'pluginList' ? route.sourceId : '', snapshot.phase]);
|
|
1808
|
-
|
|
1815
|
+
useLayoutEffect(() => {
|
|
1809
1816
|
setBatchTargetOffset(0);
|
|
1810
1817
|
}, [route.name, route.name === 'review' || route.name === 'result' ? route.operationId : '']);
|
|
1811
|
-
const
|
|
1818
|
+
const activeTopField = route.name === 'configFieldEditor'
|
|
1812
1819
|
|| route.name === 'configObjectPropertyEditor'
|
|
1813
1820
|
|| route.name === 'configFieldAction'
|
|
1814
1821
|
? currentConfigPage?.groups.flatMap((group) => group.fields).find((field) => field.key === route.fieldKey)
|
|
1815
1822
|
: undefined;
|
|
1823
|
+
const arrayItem = route.name === 'configFieldEditor' || route.name === 'configObjectPropertyEditor'
|
|
1824
|
+
? route.arrayItem : undefined;
|
|
1825
|
+
const activeArrayKey = activeTopField?.definition.type === 'array' && configRoute
|
|
1826
|
+
? `${configOwnerKey(configRoute)}\0${activeTopField.key}` : undefined;
|
|
1827
|
+
const activeArrayDraft = activeArrayKey ? arrayDrafts[activeArrayKey] : undefined;
|
|
1828
|
+
const itemValue = arrayItem && activeArrayDraft
|
|
1829
|
+
? activeArrayDraft.rows.find((row) => row.rowId === arrayItem.rowId)?.value : undefined;
|
|
1830
|
+
const activeField = arrayItem && activeTopField?.definition.type === 'array'
|
|
1831
|
+
? {
|
|
1832
|
+
...activeTopField,
|
|
1833
|
+
type: activeTopField.definition.items.type,
|
|
1834
|
+
definition: activeTopField.definition.items,
|
|
1835
|
+
title: arrayItem.rowId === undefined ? '新增项' : `第 ${(activeArrayDraft?.rows.findIndex((row) => row.rowId === arrayItem.rowId) ?? 0) + 1} 项`,
|
|
1836
|
+
description: activeTopField.definition.items.description,
|
|
1837
|
+
choices: activeTopField.definition.items.choices?.presets,
|
|
1838
|
+
allowCustom: activeTopField.definition.items.choices?.allowCustom ?? true,
|
|
1839
|
+
value: itemValue, draftValue: itemValue, configured: itemValue !== undefined,
|
|
1840
|
+
min: activeTopField.definition.items.type === 'number' ? activeTopField.definition.items.validation?.minimum : undefined,
|
|
1841
|
+
max: activeTopField.definition.items.type === 'number' ? activeTopField.definition.items.validation?.maximum : undefined,
|
|
1842
|
+
}
|
|
1843
|
+
: activeTopField;
|
|
1844
|
+
useEffect(() => {
|
|
1845
|
+
if (!activeArrayKey || activeArrayDraft || !activeTopField || route.name !== 'configFieldEditor')
|
|
1846
|
+
return;
|
|
1847
|
+
const value = draftUpdates[activeTopField.key] ?? activeTopField.draftValue ?? activeTopField.value;
|
|
1848
|
+
setArrayDrafts((current) => ({ ...current, [activeArrayKey]: { ...createConfigArrayDraft(Array.isArray(value) ? value : undefined), evidence: currentConfigPage?.editEvidence } }));
|
|
1849
|
+
}, [activeArrayKey, activeArrayDraft, activeTopField, route.name]);
|
|
1850
|
+
useEffect(() => {
|
|
1851
|
+
if (route.name !== 'configFieldEditor' || route.editor !== 'array' || !activeArrayKey || !activeArrayDraft)
|
|
1852
|
+
return;
|
|
1853
|
+
const row = activeArrayDraft.rows[focus];
|
|
1854
|
+
if (row && row.rowId !== activeArrayDraft.selectedRowId) {
|
|
1855
|
+
setArrayDrafts((current) => ({ ...current, [activeArrayKey]: { ...activeArrayDraft, selectedRowId: row.rowId } }));
|
|
1856
|
+
}
|
|
1857
|
+
}, [route.name, route.name === 'configFieldEditor' ? route.editor : '', activeArrayKey, activeArrayDraft, focus]);
|
|
1816
1858
|
useEffect(() => {
|
|
1817
1859
|
if (!configRoute
|
|
1818
1860
|
|| configRoute.name === 'configFields'
|
|
@@ -1832,13 +1874,13 @@ export function ConsoleAppV4(props) {
|
|
|
1832
1874
|
}
|
|
1833
1875
|
}, [props.controller, route, activeField]);
|
|
1834
1876
|
const activeObjectKey = activeField?.type === 'object' && configRoute
|
|
1835
|
-
? `${configOwnerKey(configRoute)}\0${activeField.key}`
|
|
1877
|
+
? `${configOwnerKey(configRoute)}\0${activeField.key}${arrayItem ? `\0${arrayItem.rowId ?? 'new'}` : ''}`
|
|
1836
1878
|
: undefined;
|
|
1837
|
-
const objectValueFromUpdate = activeField && Object.hasOwn(draftUpdates, activeField.key)
|
|
1879
|
+
const objectValueFromUpdate = !arrayItem && activeField && Object.hasOwn(draftUpdates, activeField.key)
|
|
1838
1880
|
&& typeof draftUpdates[activeField.key] === 'object'
|
|
1839
1881
|
? draftUpdates[activeField.key]
|
|
1840
1882
|
: undefined;
|
|
1841
|
-
const activeCandidateMutation = activeField ? configCandidate?.mutations[activeField.key] : undefined;
|
|
1883
|
+
const activeCandidateMutation = !arrayItem && activeField ? configCandidate?.mutations[activeField.key] : undefined;
|
|
1842
1884
|
const objectValueFromCandidate = activeCandidateMutation?.kind === 'set'
|
|
1843
1885
|
&& typeof activeCandidateMutation.value === 'object'
|
|
1844
1886
|
? activeCandidateMutation.value
|
|
@@ -1864,7 +1906,7 @@ export function ConsoleAppV4(props) {
|
|
|
1864
1906
|
if (route.name !== 'configFieldEditor' || !activeField)
|
|
1865
1907
|
return;
|
|
1866
1908
|
const candidate = configCandidate?.mutations[activeField.key];
|
|
1867
|
-
const draft = candidate?.kind === 'set' ? candidate.value : draftUpdates[activeField.key];
|
|
1909
|
+
const draft = arrayItem ? itemValue : candidate?.kind === 'set' ? candidate.value : draftUpdates[activeField.key];
|
|
1868
1910
|
setInputValue(activeField.sensitive
|
|
1869
1911
|
? draft === undefined ? '' : String(draft)
|
|
1870
1912
|
: typeof (draft ?? activeField.draftValue ?? activeField.value) === 'object'
|
|
@@ -1873,7 +1915,7 @@ export function ConsoleAppV4(props) {
|
|
|
1873
1915
|
setInputError(undefined);
|
|
1874
1916
|
}, [
|
|
1875
1917
|
route.name,
|
|
1876
|
-
route.name === 'configFieldEditor' ? `${route.fieldKey}:${route.mode ?? ''}` : '',
|
|
1918
|
+
route.name === 'configFieldEditor' ? `${route.fieldKey}:${route.mode ?? ''}:${route.arrayItem ? route.arrayItem.rowId ?? 'new' : 'field'}` : '',
|
|
1877
1919
|
activeField?.key,
|
|
1878
1920
|
configCandidate,
|
|
1879
1921
|
]);
|
|
@@ -2097,7 +2139,7 @@ export function ConsoleAppV4(props) {
|
|
|
2097
2139
|
sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined,
|
|
2098
2140
|
focusedEntityId: route.name === 'pluginList'
|
|
2099
2141
|
? pluginRows[focus]?.pluginId
|
|
2100
|
-
: route.name === 'batchSelection' ? actions[focus]?.pluginId
|
|
2142
|
+
: route.name === 'batchSelection' || route.name === 'projectStatus' ? actions[focus]?.pluginId
|
|
2101
2143
|
: route.name === 'sourceList' ? props.controller.listSources()[focus]?.id : undefined,
|
|
2102
2144
|
});
|
|
2103
2145
|
props.controller.push(next);
|
|
@@ -2155,6 +2197,12 @@ export function ConsoleAppV4(props) {
|
|
|
2155
2197
|
? restoredEntityIndex
|
|
2156
2198
|
: Math.min(restoredFocus, Math.max(0, rowIds.length - 1));
|
|
2157
2199
|
}
|
|
2200
|
+
else if (restoredFrame.route.name === 'projectStatus') {
|
|
2201
|
+
const roots = props.controller.listProjectInventoryRoots(restored.projectSection ?? 'declared').filter((root) => !restored.query || `${root.displayName} ${root.pluginId}`.toLocaleLowerCase().includes(restored.query.toLocaleLowerCase()));
|
|
2202
|
+
const index = roots.findIndex((root) => root.pluginId === restored.focusedEntityId);
|
|
2203
|
+
itemCount = roots.length + 2;
|
|
2204
|
+
restoredFocus = index >= 0 ? index : Math.min(restoredFocus, Math.max(0, itemCount - 1));
|
|
2205
|
+
}
|
|
2158
2206
|
else if (restoredFrame.route.name === 'batchSelection' && batchDraft) {
|
|
2159
2207
|
const candidates = batchCandidates(batchDraft);
|
|
2160
2208
|
const entityIndex = candidates.findIndex((candidate) => candidate.pluginId === restored.focusedEntityId);
|
|
@@ -2842,6 +2890,7 @@ export function ConsoleAppV4(props) {
|
|
|
2842
2890
|
configured,
|
|
2843
2891
|
privateValue: field.sensitive,
|
|
2844
2892
|
summary: field.summary,
|
|
2893
|
+
itemSummary: field.itemSummary,
|
|
2845
2894
|
full,
|
|
2846
2895
|
}).text;
|
|
2847
2896
|
const effectiveFieldDisplay = (field) => {
|
|
@@ -2895,13 +2944,74 @@ export function ConsoleAppV4(props) {
|
|
|
2895
2944
|
if (!configRoute || !currentConfigPage)
|
|
2896
2945
|
return;
|
|
2897
2946
|
setConfigActionError(undefined);
|
|
2947
|
+
if (arrayItem && activeArrayKey && activeArrayDraft && activeField) {
|
|
2948
|
+
const issues = validatePluginConfigValue(activeField.definition, value, key);
|
|
2949
|
+
if (issues.length) {
|
|
2950
|
+
setObjectError(issues.map(objectIssueText).join(';'));
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
const next = editConfigArrayDraft(activeArrayDraft, { kind: 'set', rowId: arrayItem.rowId, value: value });
|
|
2954
|
+
setArrayDrafts((current) => ({ ...current, [activeArrayKey]: { ...next, evidence: activeArrayDraft.evidence } }));
|
|
2955
|
+
if (activeObjectKey)
|
|
2956
|
+
setObjectDrafts((current) => Object.fromEntries(Object.entries(current).filter(([owner]) => owner !== activeObjectKey)));
|
|
2957
|
+
back();
|
|
2958
|
+
const index = next.rows.findIndex((row) => row.rowId === next.selectedRowId);
|
|
2959
|
+
props.controller.updatePageContext({ focusIndex: index, scrollOffset: index });
|
|
2960
|
+
setFocus(index);
|
|
2961
|
+
setScrollOffset(index);
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
if (activeField?.definition.type === 'array' && activeArrayKey && activeArrayDraft
|
|
2965
|
+
&& activeArrayDraft.initial !== undefined && configValuesEqual(activeArrayDraft.initial, value)) {
|
|
2966
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([owner]) => owner !== activeArrayKey)));
|
|
2967
|
+
back();
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2898
2970
|
openConfigTarget({
|
|
2899
2971
|
pluginId: configRoute.pluginId,
|
|
2900
2972
|
fieldKey: key,
|
|
2901
2973
|
groupId: snapshot.router.current.context.configGroupId,
|
|
2902
2974
|
mutations: { [key]: { kind: 'set', value } },
|
|
2975
|
+
...(activeArrayDraft?.evidence ? { editEvidence: activeArrayDraft.evidence } : {}),
|
|
2976
|
+
});
|
|
2977
|
+
};
|
|
2978
|
+
const editArray = (edit) => {
|
|
2979
|
+
if (!activeArrayKey || !activeArrayDraft)
|
|
2980
|
+
return;
|
|
2981
|
+
const next = editConfigArrayDraft(activeArrayDraft, edit);
|
|
2982
|
+
setArrayDrafts((current) => ({ ...current, [activeArrayKey]: { ...next, evidence: activeArrayDraft.evidence } }));
|
|
2983
|
+
setObjectError(undefined);
|
|
2984
|
+
const index = Math.max(0, next.rows.findIndex((row) => row.rowId === next.selectedRowId));
|
|
2985
|
+
setFocus(index);
|
|
2986
|
+
setScrollOffset(index);
|
|
2987
|
+
};
|
|
2988
|
+
const openArrayItem = (rowId) => {
|
|
2989
|
+
if (!activeField || activeField.definition.type !== 'array' || !configRoute)
|
|
2990
|
+
return;
|
|
2991
|
+
setObjectError(undefined);
|
|
2992
|
+
go({
|
|
2993
|
+
name: 'configFieldEditor', pluginId: configRoute.pluginId, layer: configRoute.layer,
|
|
2994
|
+
fieldKey: activeField.key, editor: activeField.definition.items.type,
|
|
2995
|
+
...(activeField.definition.items.choices ? { mode: 'choices' } : {}),
|
|
2996
|
+
arrayItem: { rowId },
|
|
2903
2997
|
});
|
|
2904
2998
|
};
|
|
2999
|
+
const commitArray = () => {
|
|
3000
|
+
if (!activeField || activeField.definition.type !== 'array' || !activeArrayDraft || !activeArrayKey)
|
|
3001
|
+
return;
|
|
3002
|
+
const value = arrayDraftValue(activeArrayDraft);
|
|
3003
|
+
const issues = validatePluginConfigValue(activeField.definition, value, activeField.key);
|
|
3004
|
+
if (issues.length) {
|
|
3005
|
+
setObjectError(issues.slice(0, 2).map(objectIssueText).join(';'));
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
if (!arrayDraftChanged(activeArrayDraft)) {
|
|
3009
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== activeArrayKey)));
|
|
3010
|
+
back();
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
updateDraft(activeField.key, value);
|
|
3014
|
+
};
|
|
2905
3015
|
const removeDraft = (key) => {
|
|
2906
3016
|
if (!configRoute)
|
|
2907
3017
|
return;
|
|
@@ -2924,7 +3034,14 @@ export function ConsoleAppV4(props) {
|
|
|
2924
3034
|
setObjectError(undefined);
|
|
2925
3035
|
setConfigActionError(undefined);
|
|
2926
3036
|
};
|
|
2927
|
-
const objectIssueText = (issue) =>
|
|
3037
|
+
const objectIssueText = (issue) => {
|
|
3038
|
+
const index = issue.path.segments?.find((part) => typeof part === 'number');
|
|
3039
|
+
const itemDefinition = activeTopField?.definition.type === 'array' ? activeTopField.definition.items : undefined;
|
|
3040
|
+
const property = issue.path.property;
|
|
3041
|
+
const label = property && itemDefinition?.type === 'object'
|
|
3042
|
+
? itemDefinition.properties?.[property]?.title ?? property : property ?? activeField?.title ?? issue.path.field;
|
|
3043
|
+
return `${index === undefined ? '' : `第 ${Number(index) + 1} 项/`}${label}:${issue.message}`;
|
|
3044
|
+
};
|
|
2928
3045
|
const commitObjectDraft = () => {
|
|
2929
3046
|
if (!activeField || activeField.definition.type !== 'object' || !activeObjectDraft)
|
|
2930
3047
|
return;
|
|
@@ -2957,6 +3074,7 @@ export function ConsoleAppV4(props) {
|
|
|
2957
3074
|
propertyKey,
|
|
2958
3075
|
dynamic,
|
|
2959
3076
|
stage: exists ? 'action' : objectPropertyStage(definition),
|
|
3077
|
+
...(arrayItem ? { arrayItem } : {}),
|
|
2960
3078
|
});
|
|
2961
3079
|
};
|
|
2962
3080
|
const updateObjectProperty = (propertyKey, value) => {
|
|
@@ -3031,12 +3149,18 @@ export function ConsoleAppV4(props) {
|
|
|
3031
3149
|
const previous = configDrafts[layer.scope] ?? { updates: {}, removes: [] };
|
|
3032
3150
|
const updates = { ...previous.updates };
|
|
3033
3151
|
const removes = new Set(previous.removes);
|
|
3152
|
+
const editEvidence = { ...previous.editEvidence };
|
|
3034
3153
|
for (const [key, mutation] of Object.entries(configCandidate.mutations)) {
|
|
3035
3154
|
const field = fields.get(key);
|
|
3155
|
+
if (configCandidate.editEvidence)
|
|
3156
|
+
editEvidence[key] = configCandidate.editEvidence;
|
|
3157
|
+
else
|
|
3158
|
+
delete editEvidence[key];
|
|
3036
3159
|
if (mutation.kind === 'set') {
|
|
3037
3160
|
removes.delete(key);
|
|
3038
3161
|
if (field.configured && configValuesEqual(field.draftValue ?? field.value, mutation.value)) {
|
|
3039
3162
|
delete updates[key];
|
|
3163
|
+
delete editEvidence[key];
|
|
3040
3164
|
}
|
|
3041
3165
|
else
|
|
3042
3166
|
updates[key] = mutation.value;
|
|
@@ -3049,7 +3173,7 @@ export function ConsoleAppV4(props) {
|
|
|
3049
3173
|
removes.delete(key);
|
|
3050
3174
|
}
|
|
3051
3175
|
}
|
|
3052
|
-
const next = { ...configDrafts, [layer.scope]: { updates, removes: [...removes] } };
|
|
3176
|
+
const next = { ...configDrafts, [layer.scope]: { updates, removes: [...removes], editEvidence } };
|
|
3053
3177
|
if (Object.keys(updates).length === 0 && removes.size === 0)
|
|
3054
3178
|
delete next[layer.scope];
|
|
3055
3179
|
const target = configCandidate.fieldKey
|
|
@@ -3063,22 +3187,44 @@ export function ConsoleAppV4(props) {
|
|
|
3063
3187
|
if (!configCandidate.flowId) {
|
|
3064
3188
|
props.controller.replace({ name: 'configFields', pluginId: route.pluginId, layer: { scope: 'effective' } }, target ? { configGroupId: target.groupId, focusIndex: target.focusIndex, scrollOffset: 0 } : undefined);
|
|
3065
3189
|
}
|
|
3190
|
+
if (configCandidate.fieldKey) {
|
|
3191
|
+
const owner = `${configCandidate.pluginId}\0${configCandidate.fieldKey}`;
|
|
3192
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== owner)));
|
|
3193
|
+
}
|
|
3066
3194
|
setConfigDrafts(next);
|
|
3067
3195
|
setDirty(Object.keys(next).length > 0);
|
|
3068
3196
|
setConfigCandidate(undefined);
|
|
3069
3197
|
setFocus(target?.focusIndex ?? 0);
|
|
3070
3198
|
setScrollOffset(0);
|
|
3071
3199
|
};
|
|
3200
|
+
useEffect(() => {
|
|
3201
|
+
if (route.name !== 'configLayer' || !configCandidate)
|
|
3202
|
+
return;
|
|
3203
|
+
const scopes = ['project', 'user'].filter((scope) => {
|
|
3204
|
+
const page = configLayerPages[scope];
|
|
3205
|
+
if (!page)
|
|
3206
|
+
return false;
|
|
3207
|
+
const fields = page.groups.flatMap((group) => group.fields);
|
|
3208
|
+
return Object.entries(configCandidate.mutations).every(([key, mutation]) => {
|
|
3209
|
+
const field = fields.find((entry) => entry.key === key);
|
|
3210
|
+
return field && (mutation.kind === 'set' ? configFieldCanSet(field) : configFieldCanUnset(field));
|
|
3211
|
+
});
|
|
3212
|
+
});
|
|
3213
|
+
if (scopes.length === 1) {
|
|
3214
|
+
openConfigLayerTarget({ scope: scopes[0] });
|
|
3215
|
+
setConfigActionError(`仅可保存到${configLayerLabel({ scope: scopes[0] })};已加入待保存修改。`);
|
|
3216
|
+
}
|
|
3217
|
+
}, [route.name, configCandidate, configLayerPages]);
|
|
3072
3218
|
const configChanges = (page, draft) => {
|
|
3073
3219
|
const fields = page.groups.flatMap((group) => group.fields);
|
|
3074
3220
|
return [...Object.keys(draft.updates), ...draft.removes.filter((key) => !Object.hasOwn(draft.updates, key))]
|
|
3075
3221
|
.map((key) => {
|
|
3076
3222
|
const field = fields.find((candidate) => candidate.key === key);
|
|
3077
3223
|
const beforeObject = field?.definition.type === 'object'
|
|
3078
|
-
&& typeof field.draftValue === 'object' ? field.draftValue : undefined;
|
|
3224
|
+
&& typeof field.draftValue === 'object' && !Array.isArray(field.draftValue) ? field.draftValue : undefined;
|
|
3079
3225
|
const updatedValue = draft.updates[key];
|
|
3080
3226
|
const afterObject = field?.definition.type === 'object'
|
|
3081
|
-
&& typeof updatedValue === 'object' ? updatedValue : undefined;
|
|
3227
|
+
&& typeof updatedValue === 'object' && !Array.isArray(updatedValue) ? updatedValue : undefined;
|
|
3082
3228
|
const childSummary = (property, value) => {
|
|
3083
3229
|
if (value === undefined)
|
|
3084
3230
|
return '未设置';
|
|
@@ -3135,7 +3281,9 @@ export function ConsoleAppV4(props) {
|
|
|
3135
3281
|
return {
|
|
3136
3282
|
key,
|
|
3137
3283
|
title: field?.title ?? key,
|
|
3138
|
-
before: field
|
|
3284
|
+
before: field?.definition.type === 'array' && !field.configured && field.effectiveValue !== undefined
|
|
3285
|
+
? `未设置(当前有效列表:${fieldValueText(field, field.effectiveValue, true)})`
|
|
3286
|
+
: field ? fieldValueText(field, field.draftValue ?? field.value, true, field.configured) : '未设置',
|
|
3139
3287
|
after,
|
|
3140
3288
|
...(children && children.length > 0 ? { children } : {}),
|
|
3141
3289
|
};
|
|
@@ -3154,7 +3302,10 @@ export function ConsoleAppV4(props) {
|
|
|
3154
3302
|
const updates = Object.fromEntries(Object.entries(draft.updates)
|
|
3155
3303
|
.filter(([key]) => !invalidForScope.has(key)));
|
|
3156
3304
|
const removes = draft.removes.filter((key) => !invalidForScope.has(key));
|
|
3157
|
-
return Object.keys(updates).length + removes.length > 0 ? [[scope, { updates, removes
|
|
3305
|
+
return Object.keys(updates).length + removes.length > 0 ? [[scope, { updates, removes,
|
|
3306
|
+
...(draft.editEvidence ? { editEvidence: Object.fromEntries(Object.entries(draft.editEvidence)
|
|
3307
|
+
.filter(([key]) => Object.hasOwn(updates, key))) } : {}),
|
|
3308
|
+
}]] : [];
|
|
3158
3309
|
}));
|
|
3159
3310
|
const owner = `${configOwnerKey(configRoute)}\0`;
|
|
3160
3311
|
const nextChangedObjectKeys = changedObjectDraftKeys.filter((key) => !key.startsWith(owner)
|
|
@@ -3180,6 +3331,7 @@ export function ConsoleAppV4(props) {
|
|
|
3180
3331
|
return [{
|
|
3181
3332
|
scope,
|
|
3182
3333
|
updates: draft.updates,
|
|
3334
|
+
...(draft.editEvidence ? { editEvidence: draft.editEvidence } : {}),
|
|
3183
3335
|
removeKeys: draft.removes,
|
|
3184
3336
|
sensitiveKeys: fields.filter((field) => field.sensitive).map((field) => field.key),
|
|
3185
3337
|
privateKeys: fields.filter((field) => configObjectHasSensitiveChild(field.definition))
|
|
@@ -3213,6 +3365,27 @@ export function ConsoleAppV4(props) {
|
|
|
3213
3365
|
setConfigActionError('设置值未通过验证。请重新检查修改。');
|
|
3214
3366
|
}
|
|
3215
3367
|
};
|
|
3368
|
+
useEffect(() => {
|
|
3369
|
+
if (route.name === 'configSave' && route.view !== 'changes' && configDraftCount > 0 && currentConfigPage) {
|
|
3370
|
+
props.controller.replace({ name: 'configFields', pluginId: route.pluginId, layer: route.layer });
|
|
3371
|
+
beginConfigSave(true);
|
|
3372
|
+
}
|
|
3373
|
+
}, [route.name, route.name === 'configSave' ? route.view : undefined, currentConfigPage, configDraftCount]);
|
|
3374
|
+
useEffect(() => {
|
|
3375
|
+
if (route.name !== 'sourceSave' || route.view === 'changes' || sourceDraft?.owner !== route.owner)
|
|
3376
|
+
return;
|
|
3377
|
+
props.controller.replace({ name: 'sourceEdit', sourceId: sourceDraft.sourceId, owner: sourceDraft.owner });
|
|
3378
|
+
beginSourceReview(sourceDraft, false);
|
|
3379
|
+
}, [route.name, route.name === 'sourceSave' ? route.view : undefined, sourceDraft]);
|
|
3380
|
+
useEffect(() => {
|
|
3381
|
+
const intent = pendingReviewIntentRef.current;
|
|
3382
|
+
if (route.name !== 'review' || intent?.kind !== 'configSaveBatch' || !intent.apply
|
|
3383
|
+
|| snapshot.activeReview?.configImpact?.state !== 'known'
|
|
3384
|
+
|| snapshot.activeReview.configImpact.affectedTargetCount !== 0)
|
|
3385
|
+
return;
|
|
3386
|
+
back();
|
|
3387
|
+
void beginReview({ ...intent, apply: false });
|
|
3388
|
+
}, [route.name, snapshot.activeReview?.reviewToken]);
|
|
3216
3389
|
useEffect(() => {
|
|
3217
3390
|
if (!operationConfirmation)
|
|
3218
3391
|
return;
|
|
@@ -3250,177 +3423,84 @@ export function ConsoleAppV4(props) {
|
|
|
3250
3423
|
}
|
|
3251
3424
|
else if (rendererFamily === 'home' && route.name === 'home') {
|
|
3252
3425
|
const home = snapshot.viewModel.home;
|
|
3253
|
-
const
|
|
3254
|
-
const
|
|
3255
|
-
const
|
|
3256
|
-
|
|
3257
|
-
|
|
3426
|
+
const inventory = snapshot.projectInventory;
|
|
3427
|
+
const plugins = props.controller.listPlugins({ mode: 'browse' });
|
|
3428
|
+
const attention = new Set(plugins.filter((entry) => entry.primaryAttention).map((entry) => entry.pluginId)).size;
|
|
3429
|
+
const unknown = new Set(plugins.filter((entry) => entry.supportedPlatforms.some((platform) => pluginPlatformUnknown(entry, platform))).map((entry) => entry.pluginId)).size;
|
|
3430
|
+
title = '';
|
|
3431
|
+
description = undefined;
|
|
3432
|
+
context = undefined;
|
|
3433
|
+
const pluginState = [home.pluginUpdateCount ? `↑ ${home.pluginUpdateCount} 可更新` : '',
|
|
3434
|
+
attention ? `! ${attention} 需处理` : '', unknown ? `? ${unknown} 未确认` : ''].filter(Boolean).join(' · ') || '✓ 当前可用';
|
|
3435
|
+
const projectState = !inventory ? '检查中' : inventory.declarationState === 'notProject' ? '当前不是项目'
|
|
3436
|
+
: inventory.declarationState === 'absent' ? '未配置项目插件'
|
|
3437
|
+
: inventory.declarationState !== 'ready' ? '! 项目声明需要检查'
|
|
3438
|
+
: [inventory.counts.blocked ? `! ${inventory.counts.blocked} 受阻` : '',
|
|
3439
|
+
inventory.counts.pending ? `↻ ${inventory.counts.pending} 待同步` : '',
|
|
3440
|
+
inventory.counts.unknown ? `? ${inventory.counts.unknown} 未确认` : ''].filter(Boolean).join(' · ') || '✓ 全部就绪';
|
|
3441
|
+
const sourceState = [home.catalogUpdateCount ? `↑ ${home.catalogUpdateCount} 有更新` : '',
|
|
3442
|
+
home.catalogRecoveryCount ? `! ${home.catalogRecoveryCount} 检查失败` : '',
|
|
3443
|
+
home.unavailableSourceCount ? `! ${home.unavailableSourceCount} 不可用` : '',
|
|
3444
|
+
home.catalogCheckingCount ? `${home.catalogCheckingCount} 检查中` : ''].filter(Boolean).join(' · ') || '✓ 当前可用';
|
|
3258
3445
|
actions = [
|
|
3259
|
-
{ label: '插件', description:
|
|
3260
|
-
{ label: '
|
|
3261
|
-
{
|
|
3262
|
-
label: '插件来源',
|
|
3263
|
-
description: '管理 Git 仓库和本地目录',
|
|
3264
|
-
meta: home.catalogUpdateCount > 0 ? `${home.catalogUpdateCount} 个更新`
|
|
3265
|
-
: home.catalogCheckingCount > 0 ? `${home.catalogCheckingCount} 个检查中`
|
|
3266
|
-
: home.catalogRecoveryCount > 0 ? `${home.catalogRecoveryCount} 个需处理` : String(home.sourceCount),
|
|
3267
|
-
status: home.catalogUpdateCount > 0 || home.catalogRecoveryCount > 0 ? 'warning' : undefined,
|
|
3268
|
-
run: () => go({ name: 'sourceList' }),
|
|
3269
|
-
},
|
|
3446
|
+
{ label: '插件', description: pluginState, run: () => go({ name: 'pluginList', mode: 'browse' }) },
|
|
3447
|
+
{ label: '项目插件', description: projectState, run: () => go({ name: 'projectStatus' }) },
|
|
3448
|
+
{ label: '插件来源', description: sourceState, run: () => go({ name: 'sourceList' }) },
|
|
3270
3449
|
];
|
|
3271
|
-
content =
|
|
3272
|
-
|
|
3273
|
-
? '? 部分项目插件状态无法确认' : projectPendingCount > 0
|
|
3274
|
-
? `! ${projectPendingCount} 个位置待处理`
|
|
3275
|
-
: snapshot.projectDeclarationAbsent
|
|
3276
|
-
? '当前目录没有共享插件声明;不会自动安装插件' : '没有待同步提醒' }), projectIntentIssue && showTechnical ? _jsxs(TechnicalDetails, { title: "\u9879\u76EE\u58F0\u660E\u8BCA\u65AD", compact: true, children: [_jsx(StaticRow, { title: "\u9519\u8BEF\u4EE3\u7801", meta: projectIntentIssue.code, compact: true }), _jsx(StaticRow, { title: "\u8BF4\u660E", meta: sanitizeDiagnosticText(projectIntentIssue.message), compact: true })] }) : null, _jsx(SemanticRows, { actions: actions, focus: projectReminderOpen ? -1 : focus, kind: "task", compact: false })] });
|
|
3450
|
+
content = _jsx(Box, { flexDirection: "column", children: actions.map((action, index) => _jsx(Box, { marginBottom: 1, children: _jsx(HomeNavTile, { width: size.columns - 2, title: action.label, focused: !projectReminderOpen && focus === index, children: _jsx(Text, { wrap: "wrap", children: action.description?.split(' · ').map((state, stateIndex) => _jsxs(Text, { color: state.startsWith('↑') ? tuiTheme.roles.change : state.startsWith('!') || state.startsWith('↻') ? tuiTheme.roles.warning
|
|
3451
|
+
: state.startsWith('?') ? tuiTheme.roles.unknown : state.startsWith('✓') ? tuiTheme.roles.success : tuiTheme.roles.secondary, children: [stateIndex > 0 ? ' ' : '', "[", state, "]"] }, stateIndex)) }) }) }, action.label)) });
|
|
3277
3452
|
}
|
|
3278
3453
|
else if (route.name === 'projectStatus') {
|
|
3279
|
-
const
|
|
3280
|
-
const
|
|
3281
|
-
const
|
|
3282
|
-
const
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
if (isSourceBlocker(reasonCodes))
|
|
3303
|
-
return '来源当前不可用';
|
|
3304
|
-
if (reasonCodes.includes('awCapabilityUnsupported'))
|
|
3305
|
-
return '当前 AW 不支持所需能力';
|
|
3306
|
-
if (reasonCodes.includes('codexExtensionScopeConflict'))
|
|
3307
|
-
return '使用位置冲突';
|
|
3308
|
-
if (reasonCodes.includes('artifactCollision'))
|
|
3309
|
-
return '输出内容冲突';
|
|
3310
|
-
if (reasonCodes.includes('codexSharedNativeForeign'))
|
|
3311
|
-
return '共享位置存在非本插件内容';
|
|
3312
|
-
if (reasonCodes.some((code) => code === 'codexSharedNativeUnobserved'
|
|
3313
|
-
|| code === 'codexNativeObservationFailed'))
|
|
3314
|
-
return '平台状态未确认';
|
|
3315
|
-
if (state === 'actionable')
|
|
3316
|
-
return '已确认需要同步';
|
|
3317
|
-
if (state === 'blocked')
|
|
3318
|
-
return '当前目标受阻';
|
|
3319
|
-
return '当前状态未确认';
|
|
3320
|
-
};
|
|
3321
|
-
breadcrumb = '需要处理 / 本项目';
|
|
3322
|
-
title = projectIntentIssue || unconfirmed.length > 0
|
|
3323
|
-
? '尚未确认项目插件状态'
|
|
3324
|
-
: blocked.length > 0 ? '部分项目插件需要先处理'
|
|
3325
|
-
: allTargets.length === 0 ? '项目插件状态已检查' : '项目共享插件';
|
|
3326
|
-
description = projectIntentIssue
|
|
3327
|
-
? projectIntentIssue.state === 'invalid'
|
|
3328
|
-
? '本项目插件声明无效。修复声明后重新检查。'
|
|
3329
|
-
: '读取本项目插件声明失败。当前不会执行安装或恢复。'
|
|
3330
|
-
: allTargets.length === 0
|
|
3331
|
-
? '当前没有待同步或待检查的项目插件位置。'
|
|
3332
|
-
: `${pluginCount} 个插件涉及 ${allTargets.length} 个位置:`
|
|
3333
|
-
+ `${actionable.length} 个可同步 · ${blocked.length} 个受阻 · ${unconfirmed.length} 个未确认`;
|
|
3334
|
-
context = allTargets.length > 0 ? `${pluginCount} 个插件 · ${allTargets.length} 个位置` : '当前项目';
|
|
3335
|
-
actions = allTargets.map(({ target, state }) => {
|
|
3336
|
-
const overview = props.controller.getPluginOverview(target.pluginId);
|
|
3337
|
-
const status = state === 'actionable' ? 'warning'
|
|
3338
|
-
: state === 'blocked' ? 'error' : 'muted';
|
|
3339
|
-
const symbol = state === 'actionable' ? '!' : state === 'blocked' ? '!' : '?';
|
|
3340
|
-
return {
|
|
3341
|
-
actionId: `project-target:${target.pluginId}:${target.platform}:${target.scope}`,
|
|
3342
|
-
label: overview?.displayName ?? target.pluginId.split('/').at(-1) ?? target.pluginId,
|
|
3343
|
-
description: `${platformLabel(target.platform)} · 当前项目 · ${reasonLabel(target.reasonCodes, state)}`,
|
|
3344
|
-
meta: `来源 ${sourceLabel(target.pluginId)} · 项目共享声明`,
|
|
3345
|
-
tags: _jsx(StatusBadge, { items: [{
|
|
3346
|
-
key: state,
|
|
3347
|
-
label: state === 'actionable' ? '待同步' : state === 'blocked' ? '需处理' : '未确认',
|
|
3348
|
-
symbol,
|
|
3349
|
-
status,
|
|
3350
|
-
}] }),
|
|
3351
|
-
status,
|
|
3352
|
-
run: state === 'blocked' && isSourceBlocker(target.reasonCodes)
|
|
3353
|
-
? () => {
|
|
3354
|
-
const sourceId = sourceLabel(target.pluginId);
|
|
3355
|
-
go(props.controller.getSourceOverview(sourceId).source
|
|
3356
|
-
? { name: 'sourceDetail', sourceId } : { name: 'sourceList' });
|
|
3357
|
-
}
|
|
3358
|
-
: state === 'unconfirmed' || !overview ? () => setShowTechnical(true)
|
|
3359
|
-
: () => go({ name: 'pluginDetail', pluginId: target.pluginId }),
|
|
3360
|
-
};
|
|
3361
|
-
});
|
|
3454
|
+
const inventory = snapshot.projectInventory;
|
|
3455
|
+
const section = snapshot.router.current.context.projectSection ?? 'declared';
|
|
3456
|
+
const stateLabel = { blocked: '受阻', pending: '待同步', unknown: '未确认', ready: '就绪' };
|
|
3457
|
+
const roots = props.controller.listProjectInventoryRoots(section).filter((root) => !query || `${root.displayName} ${root.pluginId}`.toLocaleLowerCase().includes(query.toLocaleLowerCase()));
|
|
3458
|
+
breadcrumb = '项目插件';
|
|
3459
|
+
title = showTechnical ? '项目状态安全详情' : inventory?.projectName ?? '项目插件';
|
|
3460
|
+
context = undefined;
|
|
3461
|
+
description = inventory?.declarationState === 'notProject' ? '当前不是项目;没有项目同步目标。'
|
|
3462
|
+
: inventory?.declarationState === 'absent' ? '未配置项目插件。'
|
|
3463
|
+
: snapshot.projectInventoryRefresh.state === 'error' ? '项目检查失败;可重新检查。'
|
|
3464
|
+
: inventory?.declarationState === 'invalid' || inventory?.declarationState === 'unavailable' ? '项目声明需要检查。' : undefined;
|
|
3465
|
+
actions = roots.map((root) => ({
|
|
3466
|
+
actionId: `project-root:${root.pluginId}`, pluginId: root.pluginId,
|
|
3467
|
+
label: root.displayName,
|
|
3468
|
+
description: [root.targets.map((target) => `${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} · ${stateLabel[target.state]}`).join(';'),
|
|
3469
|
+
...(root.dependencies.some((dependency) => dependency.targets.some((target) => target.location === 'sharedUser'))
|
|
3470
|
+
? [`${root.dependencies.filter((dependency) => dependency.targets.some((target) => target.location === 'sharedUser')).length} 个依赖共用个人安装`] : [])].join('\n'),
|
|
3471
|
+
maxDescriptionRows: 2,
|
|
3472
|
+
sourceLabel: root.sourceId,
|
|
3473
|
+
run: () => go({ name: 'pluginDetail', pluginId: root.pluginId }),
|
|
3474
|
+
}));
|
|
3475
|
+
const syncTargets = inventory?.syncTargets ?? [];
|
|
3476
|
+
const syncCount = new Set(syncTargets.map((target) => target.pluginId)).size;
|
|
3362
3477
|
bottomActions = [
|
|
3363
|
-
...(
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
}] : []),
|
|
3371
|
-
...(sourceBlockedCount > 0 ? [{
|
|
3372
|
-
actionId: 'project-open-sources',
|
|
3373
|
-
label: '处理插件来源',
|
|
3374
|
-
description: `${sourceBlockedCount} 个位置需要先处理来源问题`,
|
|
3375
|
-
run: () => go({ name: 'sourceList' }),
|
|
3376
|
-
}] : []),
|
|
3377
|
-
{
|
|
3378
|
-
actionId: 'project-recheck',
|
|
3379
|
-
label: '重新检查',
|
|
3380
|
-
description: '重新读取项目声明和平台状态;不会自动安装或恢复',
|
|
3381
|
-
run: () => requestSessionNavigation('reload'),
|
|
3382
|
-
},
|
|
3383
|
-
...(projectIntentIssue || blocked.length > 0 || unconfirmed.length > 0 ? [{
|
|
3384
|
-
actionId: 'project-error-details',
|
|
3385
|
-
label: '错误详情',
|
|
3386
|
-
description: '查看现有安全错误代码和诊断,不执行写操作',
|
|
3387
|
-
run: () => setShowTechnical((value) => !value),
|
|
3388
|
-
}] : []),
|
|
3478
|
+
...(inventory?.projectRoot && syncCount > 0 ? [{ actionId: 'project-sync-actionable',
|
|
3479
|
+
label: `同步待就绪插件(${syncCount}) [c]`, run: () => prepareCurrentProjectTargets(syncTargets) }] : []),
|
|
3480
|
+
...(inventory?.counts.blocked ? [{ actionId: 'project-open-sources', label: '处理插件来源', run: () => go({ name: 'sourceList' }) }] : []),
|
|
3481
|
+
...(!showTechnical && (inventory?.counts.blocked || inventory?.counts.unknown || snapshot.projectIntentIssue) ? [{ actionId: 'project-error-details', label: '错误详情', run: () => { setShowTechnical(true); setBatchTargetOffset(0); } }] : []),
|
|
3482
|
+
{ actionId: 'project-recheck', label: snapshot.projectInventoryRefresh.state === 'checking' ? '正在检查项目' : '重新检查项目 [r]',
|
|
3483
|
+
disabled: snapshot.projectInventoryRefresh.state === 'checking' ? '检查中' : undefined,
|
|
3484
|
+
run: async () => { await props.controller.refreshProjectInventory(); } },
|
|
3389
3485
|
];
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3486
|
+
const technicalText = [snapshot.projectIntentIssue ? `${safeDiagnosticCode(snapshot.projectIntentIssue.code, 'projectIntentUnknown')} · ${sanitizeDiagnosticText(snapshot.projectIntentIssue.message)}` : '',
|
|
3487
|
+
...roots.flatMap((root) => [...projectDependencyLines(root), ...root.targets.flatMap((target) => [
|
|
3488
|
+
`${root.pluginId} · ${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
|
|
3489
|
+
`期望 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'} · ${stateLabel[target.state]}`,
|
|
3490
|
+
...target.reasonCodes.map((code) => `原因 · ${safeDiagnosticCode(code, 'projectTargetUnknown')}`)
|
|
3491
|
+
])])].filter(Boolean).join('\n');
|
|
3492
|
+
const itemCount = showTechnical ? 0 : actions.length;
|
|
3394
3493
|
actions = [...(showTechnical ? [] : actions), ...bottomActions];
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
: state === 'blocked' ? '原因 · blocked' : '原因 · unconfirmed']),
|
|
3406
|
-
]),
|
|
3407
|
-
];
|
|
3408
|
-
const technicalLines = technicalFields.flatMap((field) => wrapConsoleText(field, Math.max(20, size.columns - 8)));
|
|
3409
|
-
if (showTechnical)
|
|
3410
|
-
readingText = technicalFields.join('\n');
|
|
3411
|
-
if (showTechnical)
|
|
3412
|
-
description = undefined;
|
|
3413
|
-
const technicalPageRows = responsiveBudget(4, 8);
|
|
3414
|
-
const technicalOffset = Math.min(batchTargetOffset, Math.max(0, technicalLines.length - 1));
|
|
3415
|
-
const visibleTechnicalLines = technicalLines.slice(technicalOffset, technicalOffset + technicalPageRows);
|
|
3416
|
-
referenceDetailPagination = showTechnical && technicalLines.length > 0
|
|
3417
|
-
? { total: technicalLines.length, pageRows: technicalPageRows }
|
|
3418
|
-
: undefined;
|
|
3419
|
-
content = _jsxs(Box, { flexDirection: "column", children: [!showTechnical && (projectIntentIssue || unconfirmed.length > 0) ? _jsx(StaticRow, { status: "warning", title: "? \u672A\u77E5\u72B6\u6001\u4E0D\u7B49\u4E8E\u672A\u5B89\u88C5\uFF1B\u6CA1\u6709\u81EA\u52A8\u6267\u884C\u5B89\u88C5\u6216\u6062\u590D\u3002", compact: true }) : null, !showTechnical && actionable.length > 0 && blocked.length + unconfirmed.length > 0
|
|
3420
|
-
? _jsx(StaticRow, { status: "info", title: `只同步明确项;保留 ${blocked.length} 个受阻位置和 ${unconfirmed.length} 个未确认位置。`, compact: true })
|
|
3421
|
-
: null, showTechnical ? _jsxs(TechnicalDetails, { title: "\u9879\u76EE\u72B6\u6001\u5B89\u5168\u8BE6\u60C5", compact: true, children: [technicalOffset > 0
|
|
3422
|
-
? _jsx(StaticRow, { title: `↑ 前面还有 ${technicalOffset} 行`, status: "muted", compact: true }) : null, visibleTechnicalLines.map((line, index) => _jsx(StaticRow, { title: line, compact: true }, `${technicalOffset + index}:${line}`)), technicalOffset + visibleTechnicalLines.length < technicalLines.length
|
|
3423
|
-
? _jsx(StaticRow, { title: `↓ 后面还有 ${technicalLines.length - technicalOffset - visibleTechnicalLines.length} 行`, status: "muted", compact: true }) : null] }) : _jsx(SequenceRows, { actions: actions, itemCount: projectItemCount, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2), compact: policy.mode === 'compact', cards: true }), showTechnical ? _jsx(SemanticRows, { actions: actions, offset: projectItemCount, focus: focus, kind: "action", compact: true }) : null] });
|
|
3494
|
+
help = '↑↓ 移动 · Enter 查看 · Esc 返回';
|
|
3495
|
+
if (showTechnical) {
|
|
3496
|
+
readingText = technicalText;
|
|
3497
|
+
const lines = wrapConsoleText(technicalText, size.columns - 2);
|
|
3498
|
+
const pageRows = pageActionBudget(3);
|
|
3499
|
+
referenceDetailPagination = { total: lines.length, pageRows };
|
|
3500
|
+
content = _jsxs(Box, { flexDirection: "column", children: [lines.slice(batchTargetOffset, batchTargetOffset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
3501
|
+
}
|
|
3502
|
+
else
|
|
3503
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(KeyCap, { children: "f" }), " ", section === 'declared' ? '项目声明' : '额外安装', " \u00B7 ", roots.length] }), _jsxs(Text, { children: ["\u641C\u7D22 ", _jsx(KeyCap, { children: "/" }), " ", searchFocus ? _jsx(InputDisplay, { value: query, cursor: searchCursor, columns: size.columns - 14 }) : query || '名称或来源'] }), roots.length === 0 ? _jsx(Text, { color: tuiTheme.roles.secondary, children: "\u672C\u6761\u4EF6\u65E0\u5339\u914D" }) : null, _jsx(SequenceRows, { actions: actions, itemCount: itemCount, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2 + (roots.length === 0 ? 1 : 0)), compact: policy.mode === 'compact', cards: true, inactive: searchFocus })] });
|
|
3424
3504
|
}
|
|
3425
3505
|
else if (route.name === 'pluginList') {
|
|
3426
3506
|
const sourceId = route.sourceId ?? 'all';
|
|
@@ -3463,9 +3543,10 @@ export function ConsoleAppV4(props) {
|
|
|
3463
3543
|
setFocus(retained >= 0 ? retained : Math.min(focus, Math.max(0, nextRowIds.length - 1)));
|
|
3464
3544
|
setScrollOffset(0);
|
|
3465
3545
|
};
|
|
3546
|
+
const availableFilters = PLUGIN_FILTERS.filter((filter) => filter === 'all' || filterCounts[filter] > 0 || filter === installFilter);
|
|
3466
3547
|
togglePluginListFilter = () => {
|
|
3467
|
-
const nextFilter =
|
|
3468
|
-
%
|
|
3548
|
+
const nextFilter = availableFilters[(availableFilters.indexOf(installFilter) + 1)
|
|
3549
|
+
% availableFilters.length];
|
|
3469
3550
|
choosePluginListFilter?.(nextFilter);
|
|
3470
3551
|
};
|
|
3471
3552
|
choosePluginListSource = (next) => {
|
|
@@ -3501,6 +3582,8 @@ export function ConsoleAppV4(props) {
|
|
|
3501
3582
|
.filter((candidate) => candidate.target.scope === 'project');
|
|
3502
3583
|
const projectIntentRoots = props.controller.listCurrentProjectIntentRoots();
|
|
3503
3584
|
const menuActions = [
|
|
3585
|
+
...(props.controller.listUserIssues().length > 0 ? [{ actionId: 'plugin-list-issues',
|
|
3586
|
+
label: '需要处理', run: () => go({ name: 'issueList' }) }] : []),
|
|
3504
3587
|
...(recommendedUpdatePluginIds.length > 0 ? [{
|
|
3505
3588
|
actionId: 'plugin-list-update',
|
|
3506
3589
|
label: `更新 ${recommendedUpdatePluginIds.length} 个`,
|
|
@@ -3562,10 +3645,9 @@ export function ConsoleAppV4(props) {
|
|
|
3562
3645
|
.flatMap((rowId) => pluginRowsById.get(rowId) ?? []);
|
|
3563
3646
|
if (pluginRows[focus])
|
|
3564
3647
|
pluginListFocusId.current = pluginRows[focus].pluginId;
|
|
3565
|
-
if (installFilter
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
description = '当前筛选范围内没有经过版本证据确认的更新。';
|
|
3648
|
+
if (installFilter !== 'all' && items.length === 0 && !sourceError) {
|
|
3649
|
+
title = '本条件无匹配';
|
|
3650
|
+
description = '保留当前筛选;可切换筛选或修改搜索。';
|
|
3569
3651
|
actions = [{
|
|
3570
3652
|
label: '查看全部我的插件',
|
|
3571
3653
|
description: '按 f 返回完整列表',
|
|
@@ -3630,9 +3712,9 @@ export function ConsoleAppV4(props) {
|
|
|
3630
3712
|
}).height : 1),
|
|
3631
3713
|
focus, requestedOffset: scrollOffset, rowBudget: pageActionBudget(3),
|
|
3632
3714
|
});
|
|
3633
|
-
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(SourceTabs, { controller: props.controller, current: sourceId, compact: policy.mode === 'compact', columns: size.columns - 2, mode: route.mode, query: query || undefined, installFilter: installFilter }), _jsxs(Text, { color: searchFocus ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["\u641C\u7D22
|
|
3715
|
+
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(SourceTabs, { controller: props.controller, current: sourceId, compact: policy.mode === 'compact', columns: size.columns - 2, mode: route.mode, query: query || undefined, installFilter: installFilter }), _jsxs(Text, { color: searchFocus ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["\u641C\u7D22 ", _jsx(KeyCap, { children: "/" }), " ", searchFocus
|
|
3634
3716
|
? _jsx(InputDisplay, { value: query, cursor: searchCursor, placeholder: "\u8F93\u5165\u641C\u7D22\u5185\u5BB9", columns: Math.max(8, size.columns - 14) })
|
|
3635
|
-
: _jsxs(_Fragment, { children: [query || '输入名称或用途', query ? ' / 编辑' : ''] })] }), _jsxs(Text, { wrap: "truncate-end", color: tuiTheme.colors.muted, children: ["\u7B5B\u9009 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", pluginFilterLabel(installFilter), " ", filterCounts[installFilter], "]"] }), "
|
|
3717
|
+
: _jsxs(_Fragment, { children: [query || '输入名称或用途', query ? ' / 编辑' : ''] })] }), _jsxs(Text, { wrap: "truncate-end", color: tuiTheme.colors.muted, children: ["\u7B5B\u9009 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", pluginFilterLabel(installFilter), " ", filterCounts[installFilter], "]"] }), " ", _jsx(KeyCap, { children: "f" }), " \u5207\u6362"] }), sourceError
|
|
3636
3718
|
? _jsx(Callout, { status: "error", title: `${sourceId} 暂时无法提供插件`, description: "\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })
|
|
3637
3719
|
: route.mode === 'installed' && items.length === 0 && !query && sourceId === 'all'
|
|
3638
3720
|
? installFilter === 'updateAvailable'
|
|
@@ -4161,7 +4243,7 @@ export function ConsoleAppV4(props) {
|
|
|
4161
4243
|
breadcrumb = `${plugin.displayName} / 通用 Skills`;
|
|
4162
4244
|
context = `${draft.selectedCount} / ${draft.totalCount} 已选择`;
|
|
4163
4245
|
title = '选择要安装的 Skills';
|
|
4164
|
-
description =
|
|
4246
|
+
description = undefined;
|
|
4165
4247
|
const visibleCandidates = portableSelectionView === 'selected'
|
|
4166
4248
|
? draft.candidates.filter((candidate) => candidate.selected)
|
|
4167
4249
|
: draft.candidates;
|
|
@@ -4173,12 +4255,13 @@ export function ConsoleAppV4(props) {
|
|
|
4173
4255
|
go({ name: 'portableAgentMode', pluginId: plugin.pluginId });
|
|
4174
4256
|
};
|
|
4175
4257
|
const candidateActions = visibleCandidates.map((candidate) => ({
|
|
4176
|
-
label: candidate.name
|
|
4258
|
+
label: draft.candidates.filter((entry) => entry.name === candidate.name).length > 1
|
|
4259
|
+
? `${candidate.name} · ${candidate.providerDisplayName}` : candidate.name,
|
|
4177
4260
|
selected: candidate.selected,
|
|
4178
|
-
description:
|
|
4179
|
-
|
|
4180
|
-
: candidate
|
|
4181
|
-
|
|
4261
|
+
description: candidate.shortDescription ?? candidate.description.split(/[。!?\n]/u)[0]?.slice(0, 160),
|
|
4262
|
+
exception: candidate.availableScopes.length === 0 ? '安装范围待确认'
|
|
4263
|
+
: portableScopeVariantsDiffer(candidate) ? '范围内容不同,继续后确认'
|
|
4264
|
+
: candidate.availableScopes.length === 1 ? `仅${scopeLabel(candidate.availableScopes[0])}` : undefined,
|
|
4182
4265
|
run: () => props.controller.togglePortableSkill(candidate.name),
|
|
4183
4266
|
}));
|
|
4184
4267
|
selectAllPortableCandidates = { actionId: 'portable-selection-select-all', label: '全选当前结果', run: () => {
|
|
@@ -4225,7 +4308,7 @@ export function ConsoleAppV4(props) {
|
|
|
4225
4308
|
help = portableToolsOpen ? '↑↓ 移动 · Enter 选择 · Esc 返回'
|
|
4226
4309
|
: '↑↓ 移动 · Space/Enter 切换 · Esc 返回';
|
|
4227
4310
|
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ContentViewport, { children: [_jsx(SequenceRows, { actions: actions, itemCount: portableToolsOpen ? 0 : candidateActions.length, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget((variantCandidates.length > 0 ? 3 : 0) + (!draft.canContinue ? 1 : 0)
|
|
4228
|
-
+ (showTechnical && technicalVariant ? 5 : 0)), compact: policy.mode === 'compact',
|
|
4311
|
+
+ (showTechnical && technicalVariant ? 5 : 0)), compact: policy.mode === 'compact', skillRows: true, inactive: portableEmptySelectionOpen }), !portableToolsOpen && variantCandidates.length > 0
|
|
4229
4312
|
? showTechnical
|
|
4230
4313
|
? _jsx(StaticRow, { title: `! ${variantCandidates.length} 个 Skill 存在范围变体`, status: "warning", compact: true })
|
|
4231
4314
|
: _jsx(Callout, { status: "warning", title: `! ${variantCandidates.length} 个 Skill 在不同安装范围会生成不同内容。选择范围后会重新确认最终内容。` })
|
|
@@ -4245,25 +4328,23 @@ export function ConsoleAppV4(props) {
|
|
|
4245
4328
|
}
|
|
4246
4329
|
else if (route.name === 'portableDependencies' && plugin && snapshot.portableDraft) {
|
|
4247
4330
|
const overview = dependencyOverview(plugin);
|
|
4248
|
-
breadcrumb = `${plugin.displayName} / 通用 Skills / 依赖`;
|
|
4249
|
-
context = `${(overview?.summary.totalCount ?? 0) + 1} 个插件`;
|
|
4250
|
-
title = '生成这些候选 Skills 需要的插件';
|
|
4251
|
-
description = '插件依赖用于生成,不能在这里取消。';
|
|
4252
|
-
actions = [
|
|
4253
|
-
{ role: 'navigation', label: '返回 Skill 选择', run: back },
|
|
4254
|
-
{ label: showTechnical ? '隐藏技术详情' : '技术详情', run: () => toggleTechnical() },
|
|
4255
|
-
];
|
|
4256
4331
|
const dependencies = overview?.dependencies ?? [];
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
const
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4332
|
+
breadcrumb = `${plugin.displayName} / 通用 Skills / 依赖`;
|
|
4333
|
+
context = `${dependencies.length + 1} 个插件`;
|
|
4334
|
+
title = '本次生成依赖';
|
|
4335
|
+
description = '这些插件用于生成所选 Skills;安装位置在最终汇总中确认。';
|
|
4336
|
+
const fields = [plugin.displayName, ...dependencies.flatMap((entry) => [
|
|
4337
|
+
`${entry.displayName} · ${entry.pluginId}`,
|
|
4338
|
+
`${entry.relation === 'direct' ? '直接依赖' : '间接依赖'} · ${entry.requiredBy.map((parent) => parent.displayName).join('、')} 需要它`,
|
|
4339
|
+
`提供 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.providerPluginId === entry.pluginId).length} 个 Skills`,
|
|
4340
|
+
])];
|
|
4341
|
+
readingText = fields.join('\n');
|
|
4342
|
+
const lines = fields.flatMap((field) => wrapConsoleText(field, size.columns - 2));
|
|
4343
|
+
const pageRows = pageActionBudget(2);
|
|
4344
|
+
referenceDetailPagination = { total: lines.length, pageRows };
|
|
4345
|
+
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
4346
|
+
actions = [{ label: '返回 Skills 选择', run: back }];
|
|
4347
|
+
content = _jsxs(Box, { flexDirection: "column", children: [lines.slice(offset, offset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsxs(Text, { color: tuiTheme.roles.secondary, children: [offset + 1, "\u2013", Math.min(lines.length, offset + pageRows), " / ", lines.length] }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
4267
4348
|
}
|
|
4268
4349
|
else if (route.name === 'portableAgentMode' && plugin && snapshot.portableDraft) {
|
|
4269
4350
|
breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
|
|
@@ -5212,6 +5293,20 @@ export function ConsoleAppV4(props) {
|
|
|
5212
5293
|
? '可以先处理可执行项,或进入下方对应入口处理受阻位置。'
|
|
5213
5294
|
: '请进入下方对应入口处理;返回后会重新读取当前证据。', compact: policy.mode === 'compact' }) : null, missingTargetKeys.length > 0 ? _jsx(Callout, { status: "warning", title: `${missingTargetKeys.length} 个本次安装位置已失效或无需处理`, description: "\u4E0D\u4F1A\u628A\u8FD9\u4E9B\u4F4D\u7F6E\u5F53\u4F5C\u5DF2\u5B8C\u6210\uFF0C\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u7F29\u5C0F\u672C\u6B21\u8303\u56F4\u3002\u8BF7\u8FD4\u56DE\u8C03\u6574\u9009\u62E9\u540E\u91CD\u65B0\u5F00\u59CB\u3002", compact: policy.mode === 'compact' }) : null, addedTargetKeys.length > 0 ? _jsx(Callout, { status: "warning", title: `${addedTargetKeys.length} 个新位置需要重新确认`, description: "\u4E0D\u4F1A\u628A\u5237\u65B0\u540E\u65B0\u589E\u7684\u4F4D\u7F6E\u81EA\u52A8\u52A0\u5165\u672C\u6B21\u64CD\u4F5C\u3002\u8BF7\u8FD4\u56DE\u8C03\u6574\u9009\u62E9\u540E\u91CD\u65B0\u5F00\u59CB\u3002", compact: policy.mode === 'compact' }) : null, versionEvidenceChanged ? _jsx(Callout, { status: "warning", title: "\u63D2\u4EF6\u76EE\u6807\u7248\u672C\u5DF2\u53D8\u5316", description: "\u4E0D\u4F1A\u6CBF\u7528\u6765\u6E90\u542F\u7528\u524D\u7684\u7248\u672C\u8BC1\u636E\uFF0C\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u6267\u884C\u65B0\u7248\u672C\u3002", compact: policy.mode === 'compact' }) : null, _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true, offset: taskViewport.offset, limit: taskViewport.limit })] });
|
|
5214
5295
|
}
|
|
5296
|
+
else if (route.name === 'pluginDetail' && !plugin && props.controller.getProjectInventoryRoot(route.pluginId)) {
|
|
5297
|
+
const root = props.controller.getProjectInventoryRoot(route.pluginId);
|
|
5298
|
+
breadcrumb = '项目插件';
|
|
5299
|
+
title = root.displayName;
|
|
5300
|
+
description = '来源当前没有此插件;项目声明仍保留。';
|
|
5301
|
+
const fields = [root.pluginId, `来源:${root.sourceId}`, ...projectDependencyLines(root), ...root.targets.map((target) => `${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '当前项目'} · 期望 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'} · ${target.reasonCodes.join('、')}`)];
|
|
5302
|
+
readingText = fields.join('\n');
|
|
5303
|
+
const lines = fields.flatMap((field) => wrapConsoleText(field, size.columns - 2));
|
|
5304
|
+
const pageRows = pageActionBudget(3);
|
|
5305
|
+
referenceDetailPagination = { total: lines.length, pageRows };
|
|
5306
|
+
actions = [{ label: '查看插件来源', run: () => go(props.controller.getSourceOverview(root.sourceId).source
|
|
5307
|
+
? { name: 'sourceDetail', sourceId: root.sourceId } : { name: 'sourceList' }) }];
|
|
5308
|
+
content = _jsxs(Box, { flexDirection: "column", children: [lines.slice(batchTargetOffset, batchTargetOffset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
5309
|
+
}
|
|
5215
5310
|
else if (route.name === 'pluginDetail' && plugin) {
|
|
5216
5311
|
breadcrumb = `插件 / ${plugin.displayName}`;
|
|
5217
5312
|
context = `${plugin.sourceLabel}${plugin.version ? ` · ${plugin.version}` : ''}`;
|
|
@@ -5409,6 +5504,8 @@ export function ConsoleAppV4(props) {
|
|
|
5409
5504
|
plugin.description,
|
|
5410
5505
|
`版本 · ${versionSummary}`,
|
|
5411
5506
|
`来源 · ${plugin.sourceLabel}`,
|
|
5507
|
+
...(props.controller.getProjectInventoryRoot(plugin.pluginId)?.targets.map((target) => `${platformLabel(target.platform)} · ${target.location === 'sharedUser' ? '共用个人安装' : '项目声明'} · 期望 ${target.expectedVersion ?? '未知'} · 实际 ${target.installedVersion ?? '未知'}`) ?? []),
|
|
5508
|
+
...projectDependencyLines(props.controller.getProjectInventoryRoot(plugin.pluginId)),
|
|
5412
5509
|
...(detailPositions.length > 0 ? detailPositions : [platformSummary]),
|
|
5413
5510
|
...(plugin.portableSkills ? ['[Skills 可选]'] : []),
|
|
5414
5511
|
...(plugin.configOnly ? ['[已保存设置]'] : []),
|
|
@@ -5769,7 +5866,7 @@ export function ConsoleAppV4(props) {
|
|
|
5769
5866
|
const readonly = false;
|
|
5770
5867
|
breadcrumb = `${plugin.displayName} / 设置`;
|
|
5771
5868
|
title = '插件设置';
|
|
5772
|
-
description =
|
|
5869
|
+
description = undefined;
|
|
5773
5870
|
if (configActionError)
|
|
5774
5871
|
description = configActionError;
|
|
5775
5872
|
help = '↑↓ 移动 · Enter 修改· ←→ 分组 · s 查看保存汇总 · u 恢复继承 · Esc 返回';
|
|
@@ -5785,7 +5882,7 @@ export function ConsoleAppV4(props) {
|
|
|
5785
5882
|
? currentConfigPage.groups.find((group) => group.id === activeTab.id)
|
|
5786
5883
|
: undefined;
|
|
5787
5884
|
if (configDraftIssues.length > 0) {
|
|
5788
|
-
description
|
|
5885
|
+
description = `${description ?? ''} 有 ${configDraftIssues.length} 项未保存修改已不适用于当前设置。`;
|
|
5789
5886
|
}
|
|
5790
5887
|
const mutableLayer = { scope: 'effective' };
|
|
5791
5888
|
const tabActions = activeTab?.kind === 'presets'
|
|
@@ -5798,19 +5895,21 @@ export function ConsoleAppV4(props) {
|
|
|
5798
5895
|
}))
|
|
5799
5896
|
: (activeGroup?.fields ?? []).filter((field) => readonly || configFieldCanSet(field) || configFieldCanUnset(field)).map((field) => ({
|
|
5800
5897
|
label: field.title,
|
|
5801
|
-
description: field.invalidOverrideIssue ??
|
|
5802
|
-
|
|
5803
|
-
|| field.effectiveSourceLabel === '当前项目' && pendingFieldDisplay(field, 'project') === undefined).map(({ scope, value }) => `待保存到${configLayerLabel({ scope })}:${value}`),
|
|
5804
|
-
pendingFieldDisplay(field, 'user') !== undefined
|
|
5805
|
-
&& field.effectiveSourceLabel === '当前项目'
|
|
5806
|
-
&& pendingFieldDisplay(field, 'project') === undefined
|
|
5807
|
-
? '个人默认待修改,当前项目不受此值影响' : undefined,
|
|
5808
|
-
].filter(Boolean).join(' · ') : undefined),
|
|
5809
|
-
meta: (() => {
|
|
5898
|
+
description: field.invalidOverrideIssue ?? field.description,
|
|
5899
|
+
configTile: (() => {
|
|
5810
5900
|
const project = pendingFieldDisplay(field, 'project');
|
|
5811
5901
|
const user = pendingFieldDisplay(field, 'user');
|
|
5812
|
-
const
|
|
5813
|
-
|
|
5902
|
+
const covered = user !== undefined && field.effectiveSourceLabel === '当前项目' && project === undefined;
|
|
5903
|
+
const pending = project ?? (covered ? undefined : user);
|
|
5904
|
+
const targets = pendingFieldTargets(field);
|
|
5905
|
+
return {
|
|
5906
|
+
value: pending ?? effectiveFieldDisplay(field),
|
|
5907
|
+
source: field.effectiveSourceLabel,
|
|
5908
|
+
invalid: Boolean(field.invalidOverrideIssue),
|
|
5909
|
+
...(targets.length ? { pending: covered
|
|
5910
|
+
? '待保存到个人;个人修改不会影响本项目'
|
|
5911
|
+
: `待保存到${targets.map(({ scope }) => configLayerLabel({ scope })).join('、')}` } : {}),
|
|
5912
|
+
};
|
|
5814
5913
|
})(),
|
|
5815
5914
|
configKey: field.key,
|
|
5816
5915
|
run: () => go({
|
|
@@ -5852,13 +5951,13 @@ export function ConsoleAppV4(props) {
|
|
|
5852
5951
|
actionId: 'config-save-summary',
|
|
5853
5952
|
label: configDraftCount > 0 ? `保存设置(${configDraftCount}) [s]` : '保存设置 [s]',
|
|
5854
5953
|
description: '确认全部设置层的变化,再选择只保存或保存并应用',
|
|
5855
|
-
status: configDraftCount > 0 ? '
|
|
5954
|
+
status: configDraftCount > 0 ? 'info' : 'muted',
|
|
5856
5955
|
disabled: configDraftCount === 0 ? '没有待保存设置'
|
|
5857
5956
|
: configDraftIssues.length > 0 ? '请先放弃失效草稿' : undefined,
|
|
5858
5957
|
run: () => {
|
|
5859
5958
|
setFocus(0);
|
|
5860
5959
|
setScrollOffset(0);
|
|
5861
|
-
|
|
5960
|
+
beginConfigSave(true);
|
|
5862
5961
|
},
|
|
5863
5962
|
};
|
|
5864
5963
|
bottomActions = [configSaveAction, {
|
|
@@ -5905,7 +6004,7 @@ export function ConsoleAppV4(props) {
|
|
|
5905
6004
|
actions = [{ label: '返回设置', run: back }];
|
|
5906
6005
|
help = '←→ 或 PgUp/PgDn 阅读 · Esc 返回';
|
|
5907
6006
|
return _jsxs(Box, { flexDirection: "column", children: [lines.slice(offset, offset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
5908
|
-
})() : _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab ? _jsx(ConfigGroupTabs, { tabs: visibleConfigTabs, current: activeTab.id, compact: policy.mode === 'compact', columns: size.columns - 2 }) : null, _jsx(SequenceRows, { actions: actions, itemCount: tabActions.length,
|
|
6007
|
+
})() : _jsxs(Box, { flexDirection: "column", children: [!readonly && configDraftIssues.length > 0 ? _jsx(Callout, { status: "warning", title: "\u6709\u672A\u4FDD\u5B58\u4FEE\u6539\u65E0\u6CD5\u7EE7\u7EED\u4F7F\u7528", description: "\u63D2\u4EF6\u6765\u6E90\u6062\u590D\u540E\uFF0C\u8BBE\u7F6E\u5185\u5BB9\u6216\u53EF\u7528\u4F4D\u7F6E\u5DF2\u53D8\u5316\u3002\u8BF7\u653E\u5F03\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u6216\u8FD4\u56DE\u68C0\u67E5\u63D2\u4EF6\u6765\u6E90\uFF1B\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u3002" }) : null, activeTab ? _jsx(ConfigGroupTabs, { tabs: visibleConfigTabs, current: activeTab.id, compact: policy.mode === 'compact', columns: size.columns - 2 }) : null, _jsx(SequenceRows, { actions: actions, itemCount: tabActions.length, focus: effectiveConfigFocus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2 + (configDraftIssues.length > 0 ? 3 : 0)), compact: policy.mode === 'compact' }), focusedConfigField && (focusedConfigField.invalidOverrideIssue || focusedConfigField.description) ? _jsx(Text, { color: focusedConfigField.invalidOverrideIssue ? tuiTheme.roles.error : tuiTheme.roles.secondary, wrap: "truncate-end", children: focusedConfigField.invalidOverrideIssue ?? focusedConfigField.description }) : null] });
|
|
5909
6008
|
}
|
|
5910
6009
|
}
|
|
5911
6010
|
else if (route.name === 'configSave' && plugin) {
|
|
@@ -5977,12 +6076,12 @@ export function ConsoleAppV4(props) {
|
|
|
5977
6076
|
const showChoices = (activeField.choices?.length ?? 0) > 0 && route.mode !== 'custom';
|
|
5978
6077
|
const targetFields = ['project', 'user'].flatMap((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields).filter((field) => field.key === activeField.key) ?? []);
|
|
5979
6078
|
const canSet = targetFields.some(configFieldCanSet);
|
|
5980
|
-
const canUnset = targetFields.some(configFieldCanUnset);
|
|
6079
|
+
const canUnset = !arrayItem && targetFields.some(configFieldCanUnset);
|
|
5981
6080
|
if (!canSet) {
|
|
5982
6081
|
title = activeField.title;
|
|
5983
6082
|
description = activeField.invalidOverrideIssue ?? '此旧值不允许修改;可以删除覆盖并恢复继承。';
|
|
5984
|
-
actions = [];
|
|
5985
|
-
content = _jsx(StatusCard, { status: "warning", title: "\u4EC5\u53EF\u5220\u9664\u65E7\u503C", description: description });
|
|
6083
|
+
actions = canUnset ? [{ label: '删除此覆盖并恢复继承', run: () => removeDraft(activeField.key) }] : [];
|
|
6084
|
+
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(StatusCard, { status: "warning", title: "\u4EC5\u53EF\u5220\u9664\u65E7\u503C", description: description }), _jsx(SemanticRows, { actions: actions, focus: focus, kind: "action", compact: true })] });
|
|
5986
6085
|
}
|
|
5987
6086
|
else if (showChoices) {
|
|
5988
6087
|
title = `选择${activeField.title}`;
|
|
@@ -5992,7 +6091,7 @@ export function ConsoleAppV4(props) {
|
|
|
5992
6091
|
help = '↑↓ 移动 · Enter 使用 · Esc 取消';
|
|
5993
6092
|
const current = activeCandidateMutation?.kind === 'set'
|
|
5994
6093
|
? activeCandidateMutation.value
|
|
5995
|
-
: draftUpdates[activeField.key] ?? activeField.draftValue ?? activeField.value;
|
|
6094
|
+
: (arrayItem ? itemValue : draftUpdates[activeField.key]) ?? activeField.draftValue ?? activeField.value;
|
|
5996
6095
|
const booleanCustomValues = activeField.type === 'boolean' && activeField.allowCustom
|
|
5997
6096
|
? [true, false].filter((value) => !activeField.choices?.some((preset) => preset.value === value))
|
|
5998
6097
|
: [];
|
|
@@ -6022,20 +6121,42 @@ export function ConsoleAppV4(props) {
|
|
|
6022
6121
|
];
|
|
6023
6122
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' });
|
|
6024
6123
|
}
|
|
6124
|
+
else if (route.editor === 'array' && activeField.definition.type === 'array') {
|
|
6125
|
+
title = activeField.title;
|
|
6126
|
+
description = objectError ?? `${activeArrayDraft?.rows.length ?? 0} 项 · 顺序将按当前列表保存`;
|
|
6127
|
+
help = '↑↓ 移动 · Enter 编辑 · c 使用此列表 · Esc 取消';
|
|
6128
|
+
const rows = activeArrayDraft?.rows ?? [];
|
|
6129
|
+
const definition = activeField.definition;
|
|
6130
|
+
const errors = activeArrayDraft ? validatePluginConfigValue(definition, arrayDraftValue(activeArrayDraft), activeField.key) : [];
|
|
6131
|
+
const itemActions = rows.map((row, index) => ({
|
|
6132
|
+
actionId: `array:${row.rowId}`,
|
|
6133
|
+
label: `${String(index + 1).padStart(2, '0')} ${presentConfigValue({ definition: definition.items, value: row.value, summary: activeField.itemSummary }).text}`,
|
|
6134
|
+
run: () => openArrayItem(row.rowId),
|
|
6135
|
+
}));
|
|
6136
|
+
bottomActions = [{
|
|
6137
|
+
label: '添加一项', run: () => openArrayItem(),
|
|
6138
|
+
disabled: rows.length >= (definition.validation?.maxItems ?? PLUGIN_CONFIG_ARRAY_MAX_ITEMS) ? '已达到列表最大项数' : undefined,
|
|
6139
|
+
}, {
|
|
6140
|
+
label: '调整当前项', disabled: rows.length === 0 ? '列表为空' : undefined,
|
|
6141
|
+
run: () => { setArrayToolFocus(0); setArrayToolsOpen(true); },
|
|
6142
|
+
}, { label: '使用此列表 [c]', run: commitArray }];
|
|
6143
|
+
actions = [...itemActions, ...bottomActions];
|
|
6144
|
+
content = _jsxs(Box, { flexDirection: "column", children: [rows.length === 0 ? _jsx(Text, { children: "\u7A7A\u5217\u8868\uFF1B\u786E\u8BA4\u4F7F\u7528\u540E\u8986\u76D6\u539F\u503C\u3002" }) : null, errors.length ? _jsx(Text, { color: tuiTheme.colors.warning, children: errors.slice(0, 2).map(objectIssueText).join(';') }) : null, _jsx(SequenceRows, { actions: actions, itemCount: itemActions.length, focus: focus, scrollOffset: scrollOffset, compact: true, columns: size.columns, rowBudget: pageActionBudget(errors.length ? 5 : 3) })] });
|
|
6145
|
+
}
|
|
6025
6146
|
else if (route.editor === 'boolean') {
|
|
6026
6147
|
title = `${activeField.title}?`;
|
|
6027
6148
|
description = activeField.description ?? '选择开启或关闭。';
|
|
6028
6149
|
help = '↑↓ 移动 · Enter 使用 · Esc 取消';
|
|
6029
6150
|
const current = activeCandidateMutation?.kind === 'set'
|
|
6030
6151
|
? activeCandidateMutation.value
|
|
6031
|
-
: draftUpdates[activeField.key] ?? activeField.value;
|
|
6152
|
+
: (arrayItem ? itemValue : draftUpdates[activeField.key]) ?? activeField.value;
|
|
6032
6153
|
actions = [{ label: '开启', selected: !activeField.sensitive && current === true, run: () => updateDraft(activeField.key, true) }, { label: '关闭', selected: !activeField.sensitive && current === false, run: () => updateDraft(activeField.key, false) }];
|
|
6033
6154
|
content = _jsx(SemanticRows, { actions: actions, focus: focus, kind: "choice", compact: policy.mode === 'compact' });
|
|
6034
6155
|
}
|
|
6035
6156
|
else if (route.editor === 'object' && activeField.definition.type === 'object' && activeObjectDraft) {
|
|
6036
6157
|
const objectDefinition = activeField.definition;
|
|
6037
6158
|
title = `编辑${activeField.title}`;
|
|
6038
|
-
description = activeField.configured
|
|
6159
|
+
description = arrayItem ? '确认后更新当前列表项;列表尚未保存。' : activeField.configured
|
|
6039
6160
|
? '保存时会提交这个顶层字段的完整对象。'
|
|
6040
6161
|
: '草稿来自当前完整有效对象;保存后会在此层写入完整对象并覆盖继承对象。';
|
|
6041
6162
|
if (objectError)
|
|
@@ -6077,11 +6198,12 @@ export function ConsoleAppV4(props) {
|
|
|
6077
6198
|
fieldKey: activeField.key,
|
|
6078
6199
|
dynamic: true,
|
|
6079
6200
|
stage: 'key',
|
|
6201
|
+
...(arrayItem ? { arrayItem } : {}),
|
|
6080
6202
|
}),
|
|
6081
6203
|
}] : []),
|
|
6082
6204
|
{
|
|
6083
|
-
label: '使用此完整对象',
|
|
6084
|
-
description: `${Object.keys(activeObjectDraft).length} 个属性将作为单个顶层值保存`,
|
|
6205
|
+
label: arrayItem ? '使用这一项 [c]' : '使用此完整对象',
|
|
6206
|
+
description: arrayItem ? '确认完整对象后返回列表' : `${Object.keys(activeObjectDraft).length} 个属性将作为单个顶层值保存`,
|
|
6085
6207
|
run: commitObjectDraft,
|
|
6086
6208
|
},
|
|
6087
6209
|
];
|
|
@@ -6094,6 +6216,13 @@ export function ConsoleAppV4(props) {
|
|
|
6094
6216
|
: 0;
|
|
6095
6217
|
const objectViewport = actionViewport(propertyActions, policy.mode === 'compact', propertyFocus, scrollOffset, pageActionBudget(3 + objectOperationActions.length));
|
|
6096
6218
|
content = _jsxs(Box, { flexDirection: "column", children: [propertyActions.length > 0 ? _jsx(SectionCard, { title: "\u5BF9\u8C61\u5C5E\u6027", children: _jsx(SemanticRows, { actions: propertyActions, focus: effectiveObjectFocus, kind: "section", compact: policy.mode === 'compact', offset: objectViewport.offset, limit: objectViewport.limit }) }) : null, _jsx(SemanticRows, { actions: actions, focus: effectiveObjectFocus, kind: "action", compact: policy.mode === 'compact', offset: propertyActions.length, limit: objectOperationActions.length })] });
|
|
6219
|
+
if (arrayItem) {
|
|
6220
|
+
for (const action of propertyActions) {
|
|
6221
|
+
action.configKey = action.label;
|
|
6222
|
+
action.configTile = { value: action.meta ?? '未设置', source: '' };
|
|
6223
|
+
}
|
|
6224
|
+
content = _jsx(SequenceRows, { actions: actions, itemCount: propertyActions.length, focus: focus, scrollOffset: scrollOffset, columns: size.columns - 2, rowBudget: pageActionBudget(2), compact: true });
|
|
6225
|
+
}
|
|
6097
6226
|
}
|
|
6098
6227
|
else {
|
|
6099
6228
|
title = activeField.sensitive ? '输入敏感值' : `输入${activeField.title}`;
|
|
@@ -6839,7 +6968,7 @@ export function ConsoleAppV4(props) {
|
|
|
6839
6968
|
if (!draft)
|
|
6840
6969
|
return;
|
|
6841
6970
|
setFocus(0);
|
|
6842
|
-
|
|
6971
|
+
beginSourceReview(draft, false);
|
|
6843
6972
|
} },
|
|
6844
6973
|
];
|
|
6845
6974
|
content = _jsxs(Box, { flexDirection: "column", children: [_jsx(SectionCard, { title: "\u5F53\u524D\u8BBE\u7F6E", compact: policy.mode === 'compact', children: _jsx(StaticRow, { title: "\u6765\u6E90\u7C7B\u578B", meta: (draft?.type ?? detail?.type) === 'git' ? 'Git 仓库' : '本地目录', compact: true }) }), _jsx(SemanticRows, { actions: actions, focus: sourceTransportOverlay ? -1 : focus, kind: "section", compact: policy.mode === 'compact' })] });
|
|
@@ -7311,7 +7440,7 @@ export function ConsoleAppV4(props) {
|
|
|
7311
7440
|
const projectPreApplyConfirmation = review.changes.some((change) => change.kind === 'warning' && change.title.endsWith('当前项目同步目标将运行 preApply'));
|
|
7312
7441
|
breadcrumb = `${review.subject.name} / ${review.title}`;
|
|
7313
7442
|
title = `${snapshot.result?.state === 'stale' ? '状态已变化 · ' : ''}${review.operationKind === 'portableInstall' || taskReview
|
|
7314
|
-
? review.title : `确认${review.title}`}${review.operationKind === 'catalogStorePrune' ? '' : ` ${review.subject.name}`}`;
|
|
7443
|
+
? review.title : review.title.startsWith('确认') ? review.title : `确认${review.title}`}${review.operationKind === 'catalogStorePrune' ? '' : ` ${review.subject.name}`}`;
|
|
7315
7444
|
description = snapshot.result?.state === 'stale'
|
|
7316
7445
|
? '当前状态已变化。请检查新的变化后再次确认。'
|
|
7317
7446
|
: review.description;
|
|
@@ -7323,8 +7452,11 @@ export function ConsoleAppV4(props) {
|
|
|
7323
7452
|
context = `${review.batch?.requestedPluginIds.length ?? 0} 个根插件 · ${review.batch?.groupCount ?? 0} 组`;
|
|
7324
7453
|
help = 'PgUp/PgDn 查看全部根插件和依赖 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7325
7454
|
}
|
|
7455
|
+
const configApplyBlocked = pendingReviewIntent?.kind === 'configSaveBatch' && pendingReviewIntent.apply
|
|
7456
|
+
&& (review.configImpact?.state === 'unknown' || review.configImpact?.affectedTargetCount === 0);
|
|
7326
7457
|
actions = [
|
|
7327
|
-
{ label: review.primaryLabel,
|
|
7458
|
+
{ label: review.primaryLabel, disabled: configApplyBlocked ? review.configImpact?.state === 'unknown'
|
|
7459
|
+
? '应用影响未确认;请选择只保存或重新检查' : '没有应用目标;请选择只保存' : undefined, description: requiresIndependentConfirmation
|
|
7328
7460
|
? '打开独立风险确认;执行时再次检查当前状态'
|
|
7329
7461
|
: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, run: () => {
|
|
7330
7462
|
if (requiresIndependentConfirmation) {
|
|
@@ -7347,6 +7479,28 @@ export function ConsoleAppV4(props) {
|
|
|
7347
7479
|
operationBusy.current = false;
|
|
7348
7480
|
});
|
|
7349
7481
|
} },
|
|
7482
|
+
...(sourceDraft && (pendingReviewIntent?.kind === 'sourceMutation' || pendingReviewIntent?.kind === 'catalogGeneration')
|
|
7483
|
+
&& pendingReviewIntent.sourceId === sourceDraft.sourceId ? [{
|
|
7484
|
+
label: pendingReviewIntent.kind === 'catalogGeneration' ? '改为只保存' : '改为保存并启用',
|
|
7485
|
+
run: () => {
|
|
7486
|
+
const activate = pendingReviewIntent.kind !== 'catalogGeneration';
|
|
7487
|
+
back();
|
|
7488
|
+
beginSourceReview(sourceDraft, activate);
|
|
7489
|
+
},
|
|
7490
|
+
}] : []),
|
|
7491
|
+
...(pendingReviewIntent?.kind === 'configSaveBatch' && (pendingReviewIntent.apply
|
|
7492
|
+
|| review.configImpact?.state === 'known' && review.configImpact.affectedTargetCount > 0) ? [{
|
|
7493
|
+
label: pendingReviewIntent.apply ? '改为只保存,稍后应用' : '改为保存并生效',
|
|
7494
|
+
description: '重新检查影响;此选择不写入配置',
|
|
7495
|
+
run: () => {
|
|
7496
|
+
const intent = { ...pendingReviewIntent, apply: !pendingReviewIntent.apply };
|
|
7497
|
+
back();
|
|
7498
|
+
beginReview(intent);
|
|
7499
|
+
},
|
|
7500
|
+
}] : []),
|
|
7501
|
+
...(pendingReviewIntent?.kind === 'configSaveBatch' && review.configImpact?.state === 'unknown' ? [{
|
|
7502
|
+
label: '重新检查应用影响', run: () => { const intent = pendingReviewIntent; back(); beginReview(intent); },
|
|
7503
|
+
}] : []),
|
|
7350
7504
|
...(!showTechnical && catalogReferenceDetailOffset >= 0 ? [{
|
|
7351
7505
|
label: '查看完整引用影响',
|
|
7352
7506
|
description: '逐项查看安装、设置、生命周期和 Marketplace 引用',
|
|
@@ -7615,6 +7769,25 @@ export function ConsoleAppV4(props) {
|
|
|
7615
7769
|
help = 'PgUp/PgDn 查看全部确认信息 · ↑↓ 移动 · Enter 选择 · Esc 返回';
|
|
7616
7770
|
}
|
|
7617
7771
|
content = _jsx(Box, { flexDirection: "column", children: _jsxs(ReviewCard, { title: reviewRootSummary ? `本次插件 · ${reviewRootSummary}` : review.subject.name, meta: reviewRootSummary ? `版本 ${reviewVersionSummary} · 范围 ${reviewRangeSummary || '未知'}` : `${review.subject.sourceLabel ?? ''} ${review.subject.version ?? ''}`, description: showEquivalentCommand ? review.description : undefined, children: [showEquivalentCommand ? _jsxs(Text, { children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] }) : null, portableReview ? _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true, offset: reviewActionViewport.offset, limit: reviewActionViewport.limit }) : null, showTechnical && review.catalogBinding ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `Catalog:${review.catalogBinding.sourceId} ${review.catalogBinding.catalogVersion} · 当前使用`, meta: `generation ${review.catalogBinding.generationDigestShort}`, status: "success", compact: true }), _jsx(StaticRow, { title: "\u64CD\u4F5C\u671F\u95F4\u4E0D\u68C0\u67E5\u8FDC\u7AEF Catalog", status: "success", compact: true })] }) : null, installDependencyPluginIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx(StaticRow, { title: `需要 ${installDependencyPluginIds.size} 个额外插件;${reusedDependencyPluginIds.size} 个复用、${newDependencyCount} 个新增`, status: "info", compact: true }), _jsx(StaticRow, { title: widenedDependencyCount > 0 ? `${widenedDependencyCount} 个依赖会扩大到所有项目` : '没有依赖会扩大到所有项目', status: widenedDependencyCount > 0 ? 'warning' : 'success', compact: true })] }) : null, review.requiresConfirmation ? _jsx(StaticRow, { title: review.operationKind === 'catalogStorePrune' ? '! 需要确认删除本次预览的可回收项目' : projectPreApplyConfirmation ? '! 需要确认当前项目同步脚本' : '! 需要确认会影响其他项目的内容', meta: review.sharedImpact.pluginIds.length > 0 ? `${review.sharedImpact.pluginIds.length} 个插件` : undefined, description: review.sharedImpact.warning, status: "warning", compact: true }) : null, showTechnical ? technicalChanges : changeGroups, 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] }) });
|
|
7772
|
+
if (pendingReviewIntent?.kind === 'configSaveBatch' || sourceDraft && (pendingReviewIntent?.kind === 'sourceMutation' || pendingReviewIntent?.kind === 'catalogGeneration') && pendingReviewIntent.sourceId === sourceDraft.sourceId) {
|
|
7773
|
+
const changesText = pendingReviewIntent.kind === 'configSaveBatch' ? pendingReviewIntent.groups.map((group) => `${configLayerLabel({ scope: group.scope })}\n${(group.changes ?? []).flatMap((change) => [
|
|
7774
|
+
`${change.title} · ${change.before ?? '未设置'} → ${change.after}${change.children?.length ? ` · ${change.children.length} 个子属性变化` : ''}`,
|
|
7775
|
+
...(change.children ?? []).map((child) => `${child.property} · ${child.before} → ${child.after}`),
|
|
7776
|
+
]).join('\n')}`).join('\n') : changeDetailText(sourceDraftChanges(sourceDraft));
|
|
7777
|
+
const impact = review.changes.filter((change) => change.group !== 'configChanges')
|
|
7778
|
+
.map((change) => `${reviewSymbol(change)} ${change.title}${change.description ? ` · ${change.description}` : ''}`);
|
|
7779
|
+
readingText = pendingReviewIntent.kind === 'configSaveBatch'
|
|
7780
|
+
? [changesText, ...new Set([...impact, ...(review.technicalDetails ?? []), ...(review.confirmationDetails ?? []),
|
|
7781
|
+
...(review.sharedImpact.warning ? [review.sharedImpact.warning] : [])])].join('\n')
|
|
7782
|
+
: `${changesText}\n\n${readingText}`;
|
|
7783
|
+
if (pendingReviewIntent.kind === 'configSaveBatch')
|
|
7784
|
+
description = pendingReviewIntent.apply ? '保存并生效;每层独立提交,应用结果单独报告。' : '只保存;每层独立提交,稍后应用。';
|
|
7785
|
+
const lines = wrapConsoleText(readingText, size.columns - 2);
|
|
7786
|
+
const pageRows = Math.max(1, pageActionBudget(actions.length + 1));
|
|
7787
|
+
referenceDetailPagination = { total: lines.length, pageRows };
|
|
7788
|
+
const offset = Math.min(batchTargetOffset, Math.max(0, lines.length - 1));
|
|
7789
|
+
content = _jsxs(Box, { flexDirection: "column", children: [lines.slice(offset, offset + pageRows).map((line, index) => _jsx(Text, { children: line }, index)), _jsxs(Text, { color: tuiTheme.roles.secondary, children: [offset + 1, "\u2013", Math.min(lines.length, offset + pageRows), " / ", lines.length] }), _jsx(SemanticRows, { actions: actions, focus: operationConfirmation ? -1 : focus, kind: "action", compact: true })] });
|
|
7790
|
+
}
|
|
7618
7791
|
}
|
|
7619
7792
|
else if (route.name === 'progress') {
|
|
7620
7793
|
const taskOperation = snapshot.activeReview?.operationKind === 'pluginWriteTask';
|
|
@@ -8798,7 +8971,7 @@ export function ConsoleAppV4(props) {
|
|
|
8798
8971
|
else if (actions[focus]?.role === 'navigation')
|
|
8799
8972
|
setFocus(moveFocus(actions, focus, 1));
|
|
8800
8973
|
}, [routeKey, focus, actions.length, actions.map((action) => action.role ?? '').join(',')]);
|
|
8801
|
-
const listCandidates = route.name === 'pluginList'
|
|
8974
|
+
const listCandidates = route.name === 'pluginList' || route.name === 'projectStatus'
|
|
8802
8975
|
|| route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
8803
8976
|
? actions.filter((action) => action.pluginId !== undefined) : [];
|
|
8804
8977
|
const listCandidateSignature = listCandidates.map((action) => action.pluginId).join('\0');
|
|
@@ -8813,7 +8986,7 @@ export function ConsoleAppV4(props) {
|
|
|
8813
8986
|
}, [snapshot.router.currentFrameId, route.name, batchDraft?.stage,
|
|
8814
8987
|
batchDraft?.showSelectionTools, listCandidateSignature, focus, scrollOffset]);
|
|
8815
8988
|
useEffect(() => {
|
|
8816
|
-
if (route.name !== 'pluginList' && !(route.name === 'batchSelection' && batchDraft?.stage === 'select')) {
|
|
8989
|
+
if (route.name !== 'pluginList' && route.name !== 'projectStatus' && !(route.name === 'batchSelection' && batchDraft?.stage === 'select')) {
|
|
8817
8990
|
listFocusAnchor.current = undefined;
|
|
8818
8991
|
return;
|
|
8819
8992
|
}
|
|
@@ -8961,6 +9134,7 @@ export function ConsoleAppV4(props) {
|
|
|
8961
9134
|
setConfigDrafts({});
|
|
8962
9135
|
setConfigCandidate(undefined);
|
|
8963
9136
|
setObjectDrafts({});
|
|
9137
|
+
setArrayDrafts({});
|
|
8964
9138
|
setChangedObjectDraftKeys([]);
|
|
8965
9139
|
setObjectError(undefined);
|
|
8966
9140
|
setSourceDraft(undefined);
|
|
@@ -9057,6 +9231,21 @@ export function ConsoleAppV4(props) {
|
|
|
9057
9231
|
}
|
|
9058
9232
|
return;
|
|
9059
9233
|
}
|
|
9234
|
+
if (arrayToolsOpen && activeArrayDraft) {
|
|
9235
|
+
if (key.escape)
|
|
9236
|
+
setArrayToolsOpen(false);
|
|
9237
|
+
else if (key.upArrow)
|
|
9238
|
+
setArrayToolFocus((value) => Math.max(0, value - 1));
|
|
9239
|
+
else if (key.downArrow)
|
|
9240
|
+
setArrayToolFocus((value) => Math.min(2, value + 1));
|
|
9241
|
+
else if (key.return && activeArrayDraft.selectedRowId) {
|
|
9242
|
+
editArray(arrayToolFocus === 2
|
|
9243
|
+
? { kind: 'remove', rowId: activeArrayDraft.selectedRowId }
|
|
9244
|
+
: { kind: 'move', rowId: activeArrayDraft.selectedRowId, offset: arrayToolFocus === 0 ? -1 : 1 });
|
|
9245
|
+
setArrayToolsOpen(false);
|
|
9246
|
+
}
|
|
9247
|
+
return;
|
|
9248
|
+
}
|
|
9060
9249
|
if (portableEmptySelectionOpen) {
|
|
9061
9250
|
const closeEmptySelection = () => setPortableEmptySelectionOpen(false);
|
|
9062
9251
|
if (key.escape || key.return || input.toLowerCase() === 'b') {
|
|
@@ -9252,6 +9441,7 @@ export function ConsoleAppV4(props) {
|
|
|
9252
9441
|
setConfigDrafts({});
|
|
9253
9442
|
setConfigCandidate(undefined);
|
|
9254
9443
|
setObjectDrafts({});
|
|
9444
|
+
setArrayDrafts({});
|
|
9255
9445
|
setChangedObjectDraftKeys([]);
|
|
9256
9446
|
const discardedSourceDraft = sourceDraft;
|
|
9257
9447
|
setSourceDraft(undefined);
|
|
@@ -9335,14 +9525,7 @@ export function ConsoleAppV4(props) {
|
|
|
9335
9525
|
return;
|
|
9336
9526
|
}
|
|
9337
9527
|
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9338
|
-
&& route.name === 'portableDependencies'
|
|
9339
|
-
const total = dependencyOverview(plugin)?.dependencies.length ?? 0;
|
|
9340
|
-
const pageRows = showTechnical ? 1 : responsiveBudget(2, 3);
|
|
9341
|
-
setBatchTargetOffset((value) => Math.min(Math.max(0, total - pageRows), Math.max(0, value + (key.pageUp || key.leftArrow ? -pageRows : pageRows))));
|
|
9342
|
-
return;
|
|
9343
|
-
}
|
|
9344
|
-
if ((key.pageUp || key.pageDown || key.leftArrow || key.rightArrow)
|
|
9345
|
-
&& (route.name === 'pluginContent'
|
|
9528
|
+
&& (route.name === 'portableDependencies' || route.name === 'pluginContent'
|
|
9346
9529
|
|| route.name === 'pluginTechnical'
|
|
9347
9530
|
|| route.name === 'projectStatus' && showTechnical
|
|
9348
9531
|
|| route.name === 'issueDetail' && showTechnical
|
|
@@ -9555,7 +9738,15 @@ export function ConsoleAppV4(props) {
|
|
|
9555
9738
|
setSourceInputError(undefined);
|
|
9556
9739
|
return;
|
|
9557
9740
|
}
|
|
9558
|
-
if (route.name === 'configFieldEditor' &&
|
|
9741
|
+
if (route.name === 'configFieldEditor' && route.mode !== 'choices' && input === 'c'
|
|
9742
|
+
&& (route.editor === 'array' || route.editor === 'object')) {
|
|
9743
|
+
if (route.editor === 'array')
|
|
9744
|
+
commitArray();
|
|
9745
|
+
else
|
|
9746
|
+
commitObjectDraft();
|
|
9747
|
+
return;
|
|
9748
|
+
}
|
|
9749
|
+
if (route.name === 'configFieldEditor' && !arrayItem && activeField && key.ctrl && input.toLowerCase() === 'r') {
|
|
9559
9750
|
const canUnset = ['project', 'user'].some((scope) => configLayerPages[scope]?.groups.flatMap((group) => group.fields)
|
|
9560
9751
|
.some((field) => field.key === activeField.key && configFieldCanUnset(field)));
|
|
9561
9752
|
if (canUnset)
|
|
@@ -9678,8 +9869,8 @@ export function ConsoleAppV4(props) {
|
|
|
9678
9869
|
setInputError(undefined);
|
|
9679
9870
|
return;
|
|
9680
9871
|
}
|
|
9681
|
-
if (searchFocus && (route.name === 'pluginList' || route.name === 'batchSelection')) {
|
|
9682
|
-
const currentQuery = route.name
|
|
9872
|
+
if (searchFocus && (route.name === 'pluginList' || route.name === 'projectStatus' || route.name === 'batchSelection')) {
|
|
9873
|
+
const currentQuery = route.name !== 'batchSelection' ? query : batchDraft?.query ?? '';
|
|
9683
9874
|
const parts = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(currentQuery)]
|
|
9684
9875
|
.map((part) => part.segment);
|
|
9685
9876
|
const cursor = Math.min(searchCursor, parts.length);
|
|
@@ -9690,7 +9881,7 @@ export function ConsoleAppV4(props) {
|
|
|
9690
9881
|
if (key.upArrow || key.downArrow) {
|
|
9691
9882
|
setSearchFocus(false);
|
|
9692
9883
|
const candidateCount = route.name === 'batchSelection' && batchDraft
|
|
9693
|
-
? batchCandidates(batchDraft).length : pluginRows.length;
|
|
9884
|
+
? batchCandidates(batchDraft).length : route.name === 'projectStatus' ? actions.filter((action) => action.pluginId).length : pluginRows.length;
|
|
9694
9885
|
setFocus((value) => moveIndex(candidateCount, Math.min(value, Math.max(0, candidateCount - 1)), key.upArrow ? -1 : 1));
|
|
9695
9886
|
return;
|
|
9696
9887
|
}
|
|
@@ -9715,7 +9906,7 @@ export function ConsoleAppV4(props) {
|
|
|
9715
9906
|
}
|
|
9716
9907
|
const nextQuery = parts.join('');
|
|
9717
9908
|
setSearchCursor(nextCursor);
|
|
9718
|
-
if (route.name
|
|
9909
|
+
if (route.name !== 'batchSelection') {
|
|
9719
9910
|
setQuery(nextQuery);
|
|
9720
9911
|
props.controller.updatePageContext({ query: nextQuery });
|
|
9721
9912
|
}
|
|
@@ -9731,6 +9922,11 @@ export function ConsoleAppV4(props) {
|
|
|
9731
9922
|
return;
|
|
9732
9923
|
if (key.tab)
|
|
9733
9924
|
return;
|
|
9925
|
+
if (route.name === 'projectStatus' && input === '/' && !showTechnical) {
|
|
9926
|
+
setSearchCursor([...query].length);
|
|
9927
|
+
setSearchFocus(true);
|
|
9928
|
+
return;
|
|
9929
|
+
}
|
|
9734
9930
|
if (route.name === 'pluginList' && input === '/') {
|
|
9735
9931
|
runAction(listQueryActions.find((action) => action.actionId === 'plugin-list-search'));
|
|
9736
9932
|
return;
|
|
@@ -9851,6 +10047,10 @@ export function ConsoleAppV4(props) {
|
|
|
9851
10047
|
else if (globalAction === 'back'
|
|
9852
10048
|
&& (route.name === 'configFieldEditor' || route.name === 'configFieldAction')) {
|
|
9853
10049
|
setConfigCandidate(undefined);
|
|
10050
|
+
if (activeArrayKey && !arrayItem) {
|
|
10051
|
+
setArrayDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== activeArrayKey)));
|
|
10052
|
+
setObjectError(undefined);
|
|
10053
|
+
}
|
|
9854
10054
|
if (activeObjectKey) {
|
|
9855
10055
|
setObjectDrafts((current) => Object.fromEntries(Object.entries(current)
|
|
9856
10056
|
.filter(([key]) => key !== activeObjectKey)));
|
|
@@ -9902,8 +10102,9 @@ export function ConsoleAppV4(props) {
|
|
|
9902
10102
|
}
|
|
9903
10103
|
else if (route.name === 'batchSelection' && batchDraft?.stage === 'select'
|
|
9904
10104
|
&& input.toLowerCase() === 'f') {
|
|
9905
|
-
const
|
|
9906
|
-
|
|
10105
|
+
const filters = PLUGIN_FILTERS.filter((filter) => filter === 'all' || filter === batchDraft.installFilter
|
|
10106
|
+
|| batchCandidates({ ...batchDraft, installFilter: filter }).length > 0);
|
|
10107
|
+
const nextFilter = filters[(filters.indexOf(batchDraft.installFilter) + 1) % filters.length];
|
|
9907
10108
|
setBatchDraft((current) => current ? {
|
|
9908
10109
|
...current,
|
|
9909
10110
|
installFilter: nextFilter,
|
|
@@ -9912,6 +10113,14 @@ export function ConsoleAppV4(props) {
|
|
|
9912
10113
|
setFocus(0);
|
|
9913
10114
|
setScrollOffset(0);
|
|
9914
10115
|
}
|
|
10116
|
+
else if (route.name === 'projectStatus' && input.toLowerCase() === 'f' && !showTechnical) {
|
|
10117
|
+
props.controller.updatePageContext({ projectSection: snapshot.router.current.context.projectSection === 'extraInstalled' ? 'declared' : 'extraInstalled' });
|
|
10118
|
+
setFocus(0);
|
|
10119
|
+
setScrollOffset(0);
|
|
10120
|
+
}
|
|
10121
|
+
else if (route.name === 'projectStatus' && input.toLowerCase() === 'r') {
|
|
10122
|
+
runAction(bottomActions.find((action) => action.actionId === 'project-recheck'));
|
|
10123
|
+
}
|
|
9915
10124
|
else if (route.name === 'projectStatus' && input.toLowerCase() === 'c') {
|
|
9916
10125
|
runAction(bottomActions.find((action) => action.actionId === 'project-sync-actionable'));
|
|
9917
10126
|
}
|
|
@@ -10107,6 +10316,13 @@ export function ConsoleAppV4(props) {
|
|
|
10107
10316
|
const modalWidth = Math.max(40, Math.floor(size.columns * 0.6));
|
|
10108
10317
|
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 }) }) })] });
|
|
10109
10318
|
}
|
|
10319
|
+
if (arrayToolsOpen && activeArrayDraft && activeField?.definition.type === 'array') {
|
|
10320
|
+
const index = activeArrayDraft.rows.findIndex((row) => row.rowId === activeArrayDraft.selectedRowId);
|
|
10321
|
+
const row = activeArrayDraft.rows[index];
|
|
10322
|
+
return _jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsx(Box, { position: "absolute", marginTop: 3, marginLeft: 3, width: size.columns - 6, flexDirection: "column", children: _jsxs(ReviewCard, { title: `调整第 ${index + 1} 项`, description: row ? presentConfigValue({
|
|
10323
|
+
definition: activeField.definition.items, value: row.value, summary: activeField.itemSummary,
|
|
10324
|
+
}).text : '', children: [_jsx(SemanticRows, { actions: [{ label: '上移' }, { label: '下移' }, { label: '删除这一项', status: 'error' }], focus: arrayToolFocus, kind: "action", compact: true }), _jsx(Text, { children: "\u2191\u2193 \u9009\u62E9 \u00B7 Enter \u786E\u8BA4 \u00B7 Esc \u8FD4\u56DE" })] }) })] });
|
|
10325
|
+
}
|
|
10110
10326
|
if (projectReminderOpen && snapshot.projectIntentReminder) {
|
|
10111
10327
|
const reminder = snapshot.projectIntentReminder;
|
|
10112
10328
|
const pluginCount = new Set([
|