@contentful/experience-design-system-cli 2.17.1-dev-build-39c6683.0 → 2.17.1-dev-build-c40b4d2.0
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/package.json +1 -1
- package/dist/src/analyze/command.js +5 -9
- package/dist/src/import/tui/ai-flag.d.ts +0 -1
- package/dist/src/import/tui/ai-flag.js +1 -1
- package/dist/src/import/tui/steps/AtomicScopeGateStep.js +5 -4
- package/dist/src/import/tui/steps/ScopeGateStep.d.ts +0 -1
- package/dist/src/import/tui/steps/ScopeGateStep.js +3 -3
- package/dist/src/session/db.d.ts +0 -1
- package/dist/src/session/db.js +19 -27
- package/package.json +3 -3
- package/skills/generate-components.md +2 -3
package/dist/package.json
CHANGED
|
@@ -238,9 +238,9 @@ export function registerAnalyzeCommand(program) {
|
|
|
238
238
|
for (const { component, inspection } of inspectedComponents) {
|
|
239
239
|
const verdict = isNonAuthorableComponent(component);
|
|
240
240
|
const keepDespiteZeroSurface = verdict.skip && verdict.reason === 'component has no props and no slots' && inspection.keepDespiteZeroSurface;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
if (verdict.skip && !keepDespiteZeroSurface) {
|
|
242
|
+
filterWarnings.push(`Skipped non-authorable component: ${component.name} (${verdict.reason})`);
|
|
243
|
+
continue;
|
|
244
244
|
}
|
|
245
245
|
if (keepDespiteZeroSurface) {
|
|
246
246
|
filterWarnings.push(`${component.name}: retained despite 0 props/slots because the source renders visible or compositional UI`);
|
|
@@ -257,12 +257,9 @@ export function registerAnalyzeCommand(program) {
|
|
|
257
257
|
// from the Svelte parser) by merging them into the post-processing recompute.
|
|
258
258
|
// Without this, recomputing here clobbers the per-extractor signal.
|
|
259
259
|
const extractorReasons = component.reviewReasons ?? [];
|
|
260
|
-
const nonAuthorableReason = retainedForReview ? [`non-authorable:${verdict.reason}`] : [];
|
|
261
260
|
const { confidence, reasons } = computeExtractionScore(component, {
|
|
262
|
-
additionalIssueCount: wrapperConfidenceToIssueCount(inspection.wrapperConfidence) +
|
|
263
|
-
|
|
264
|
-
nonAuthorableReason.length,
|
|
265
|
-
additionalReasons: [...extractorReasons, ...inspection.reviewReasons, ...nonAuthorableReason],
|
|
261
|
+
additionalIssueCount: wrapperConfidenceToIssueCount(inspection.wrapperConfidence) + extractorReasons.length,
|
|
262
|
+
additionalReasons: [...extractorReasons, ...inspection.reviewReasons],
|
|
266
263
|
});
|
|
267
264
|
filteredComponents.push({
|
|
268
265
|
...component,
|
|
@@ -271,7 +268,6 @@ export function registerAnalyzeCommand(program) {
|
|
|
271
268
|
needsReview: deriveNeedsReview(confidence) ||
|
|
272
269
|
inspection.wrapperConfidence >= 4 ||
|
|
273
270
|
inspection.keepDespiteZeroSurface ||
|
|
274
|
-
retainedForReview ||
|
|
275
271
|
// An extractor-level type-resolution failure is a strong signal regardless
|
|
276
272
|
// of the otherwise-derived confidence threshold; force review.
|
|
277
273
|
extractorReasons.includes('props-type-unresolved') ||
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function isAiFlagged(row) {
|
|
2
|
-
return row.
|
|
2
|
+
return row.aiDecision === 'rejected' || row.aiDecision === 'failed';
|
|
3
3
|
}
|
|
4
4
|
export function isDefaultIncluded(row) {
|
|
5
5
|
return !isAiFlagged(row);
|
|
@@ -40,7 +40,8 @@ export function AtomicScopeGateStep({ components, onConfirm, onQuit, aiFilterSta
|
|
|
40
40
|
return false;
|
|
41
41
|
if (userUnExcluded.has(row.name))
|
|
42
42
|
return true;
|
|
43
|
-
|
|
43
|
+
// INTEG-4318: exclude on 'rejected' AND 'failed'.
|
|
44
|
+
return row.aiDecision !== 'rejected' && row.aiDecision !== 'failed';
|
|
44
45
|
};
|
|
45
46
|
const partition = () => {
|
|
46
47
|
const accepted = [];
|
|
@@ -188,7 +189,7 @@ export function AtomicScopeGateStep({ components, onConfirm, onQuit, aiFilterSta
|
|
|
188
189
|
groups: 0,
|
|
189
190
|
total,
|
|
190
191
|
};
|
|
191
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713 Extraction complete" }), _jsxs(Text, { dimColor: true, children: ["Found ", total, " component", total === 1 ? '' : 's', ". Pick which ones to import. Generation runs only on the included set."] }), _jsx(AutoFilterBanner, { status: aiFilterStatus, progress: aiFilterProgress, error: aiFilterError }), _jsx(CounterStrip, { counters: counters, totalWidth: totalWidth }), reasonPanelOpen && flatList[cursor] !== undefined && isAiFlagged(flatList[cursor]) && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: PALETTE.border, paddingX: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: `
|
|
192
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713 Extraction complete" }), _jsxs(Text, { dimColor: true, children: ["Found ", total, " component", total === 1 ? '' : 's', ". Pick which ones to import. Generation runs only on the included set."] }), _jsx(AutoFilterBanner, { status: aiFilterStatus, progress: aiFilterProgress, error: aiFilterError }), _jsx(CounterStrip, { counters: counters, totalWidth: totalWidth }), reasonPanelOpen && flatList[cursor] !== undefined && isAiFlagged(flatList[cursor]) && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: PALETTE.border, paddingX: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: `AI rejection reason: ${flatList[cursor].name}` }), _jsx(Text, { children: flatList[cursor].aiReason ?? '<no reason given>' }), _jsx(Text, { dimColor: true, children: "[s] close \u00B7 [Esc] close" })] })), allRejected ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: PALETTE.warning, children: "AI excluded all components \u2014 press [a] to override or [q] to quit" }) })) : (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [above > 0 && _jsxs(Text, { dimColor: true, children: ["\u2191 ", above, " above"] }), visible.map((c, vi) => {
|
|
192
193
|
const i = vi + scrollOffset;
|
|
193
194
|
const isCursor = i === cursor;
|
|
194
195
|
const included = isIncluded(c);
|
|
@@ -200,11 +201,11 @@ export function AtomicScopeGateStep({ components, onConfirm, onQuit, aiFilterSta
|
|
|
200
201
|
const inlineReason = !isCursor && aiFlagged ? ` ${truncateReason(c.aiReason)}` : '';
|
|
201
202
|
const showAiHeader = aiList.length > 0 && i === 0;
|
|
202
203
|
const showComponentsHeader = componentsList.length > 0 && i === aiList.length;
|
|
203
|
-
const header = showAiHeader ? (_jsx(Text, { bold: true, children: `
|
|
204
|
+
const header = showAiHeader ? (_jsx(Text, { bold: true, children: `AI recommended exclusions (${aiList.length})` }, `hdr-ai-${i}`)) : showComponentsHeader ? (_jsx(Text, { bold: true, children: `Components (${componentsList.length})` }, `hdr-comp-${i}`)) : null;
|
|
204
205
|
if (isCursor) {
|
|
205
206
|
const wrapReason = aiFlagged && c.aiReason !== null && c.aiReason !== undefined && c.aiReason.length > 0;
|
|
206
207
|
return (_jsxs(React.Fragment, { children: [header, _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: `${prefix} ` }), aiMarkerNode, _jsx(Text, { color: stateColor, children: stateGlyph }), _jsx(Text, { color: PALETTE.info, children: ` ${c.name}` })] }), wrapReason && _jsx(Text, { dimColor: true, children: `${REASON_WRAP_INDENT}${c.aiReason}` })] }, c.componentId));
|
|
207
208
|
}
|
|
208
209
|
return (_jsxs(React.Fragment, { children: [header, _jsxs(Text, { children: [_jsx(Text, { children: `${prefix} ` }), aiMarkerNode, _jsx(Text, { color: stateColor, children: stateGlyph }), _jsx(Text, { color: stateColor, children: ` ${c.name}` }), inlineReason !== '' && _jsx(Text, { dimColor: true, children: inlineReason })] })] }, c.componentId));
|
|
209
|
-
}), below > 0 && _jsxs(Text, { dimColor: true, children: ["\u2193 ", below, " below"] })] })), _jsxs(Box, { gap: 3, marginTop: 1, children: [includedCount > 0 ? (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.success, children: includedCount }), _jsxs(Text, { dimColor: true, children: ["/", total, " included"] })] })) : (_jsx(Text, { color: PALETTE.warning, children: "none included" })), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[j/k]" }), " ", _jsx(Text, { dimColor: true, children: "move" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[a/space]" }), " ", _jsx(Text, { dimColor: true, children: "toggle" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[A]" }), " ", _jsx(Text, { dimColor: true, children: "toggle all" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[f]" }), " ", _jsx(Text, { dimColor: true, children: "continue" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[q]" }), " ", _jsx(Text, { dimColor: true, children: "quit" })] }), hasAnyAi && (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[s]" }), " ", _jsx(Text, { dimColor: true, children: "
|
|
210
|
+
}), below > 0 && _jsxs(Text, { dimColor: true, children: ["\u2193 ", below, " below"] })] })), _jsxs(Box, { gap: 3, marginTop: 1, children: [includedCount > 0 ? (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.success, children: includedCount }), _jsxs(Text, { dimColor: true, children: ["/", total, " included"] })] })) : (_jsx(Text, { color: PALETTE.warning, children: "none included" })), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[j/k]" }), " ", _jsx(Text, { dimColor: true, children: "move" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[a/space]" }), " ", _jsx(Text, { dimColor: true, children: "toggle" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[A]" }), " ", _jsx(Text, { dimColor: true, children: "toggle all" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[f]" }), " ", _jsx(Text, { dimColor: true, children: "continue" })] }), _jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[q]" }), " ", _jsx(Text, { dimColor: true, children: "quit" })] }), hasAnyAi && (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "[s]" }), " ", _jsx(Text, { dimColor: true, children: "AI reason" })] })), hasAnyAi && (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: "*" }), " ", _jsx(Text, { dimColor: true, children: "originally excluded by AI" })] }))] })] }));
|
|
210
211
|
}
|
|
@@ -58,7 +58,7 @@ const HELP_SECTIONS = [
|
|
|
58
58
|
title: 'Panels',
|
|
59
59
|
entries: [
|
|
60
60
|
{ keys: 'c', label: 'Cycle list' },
|
|
61
|
-
{ keys: 'x', label: '
|
|
61
|
+
{ keys: 'x', label: 'AI exclusions' },
|
|
62
62
|
],
|
|
63
63
|
},
|
|
64
64
|
{
|
|
@@ -797,7 +797,7 @@ export function ScopeGateStep({ components, onConfirm, onQuit, aiFilterStatus =
|
|
|
797
797
|
if (showHelp) {
|
|
798
798
|
return _jsx(HelpOverlay, { sections: HELP_SECTIONS, onClose: () => setShowHelp(false) });
|
|
799
799
|
}
|
|
800
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713 Extraction complete" }), _jsxs(Text, { dimColor: true, children: ["Found ", totalComponents, " component", totalComponents === 1 ? '' : 's', ". Pick which ones to import. Generation runs only on the included set."] }), _jsx(AutoFilterBanner, { status: aiFilterStatus, progress: aiFilterProgress, error: aiFilterError }), hasAnyAi && (_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [`
|
|
800
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713 Extraction complete" }), _jsxs(Text, { dimColor: true, children: ["Found ", totalComponents, " component", totalComponents === 1 ? '' : 's', ". Pick which ones to import. Generation runs only on the included set."] }), _jsx(AutoFilterBanner, { status: aiFilterStatus, progress: aiFilterProgress, error: aiFilterError }), hasAnyAi && (_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [`AI recommended exclusions (${aiExcludedCount})`, aiRows.length > 0 && _jsx(Text, { color: PALETTE.info, children: ' — [x] review & jump' })] }) })), _jsx(CounterStrip, { counters: counters, totalWidth: totalWidth }), hasCycles && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "If you must have components with cycles, select them together into the generate step and then use the editor to fix them." }) })), nothingIncluded && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: PALETTE.warning, children: ["nothing selected \u2014 press ", _jsx(Text, { color: PALETTE.info, children: "[Y]" }), " to accept all non-flagged,", ' ', _jsx(Text, { color: PALETTE.info, children: "[A]" }), " to toggle all, or ", _jsx(Text, { color: PALETTE.info, children: "[a]" }), " to accept the highlighted row"] }) })), _jsxs(Box, { flexDirection: "row", children: [aiRationalePanel.isOpen ? (_jsx(GotoBanner, { title: `AI recommended exclusions (${aiExcludedCount})`, rows: aiRows, cursor: aiCursor, maxRows: panelMaxRows, width: sidebarWidth, footerHint: "[\u2191/\u2193] move \u00B7 [Enter] jump \u00B7 [x/Esc] close" })) : lineagePanel.isOpen && focusedComponent ? (_jsx(LineagePanel, { focusedComponentKey: focusedComponent.name, entries: lineageEntries, cursor: lineageCursor, jumpables: lineageJumpables, maxRows: panelMaxRows, width: sidebarWidth })) : (_jsx(GroupedSidebar, { items: groupedItems, cycleParticipants: cycleParticipants, selectedIdx: selectedItemIdx, selectedRowIdx: safeCursor, onSelect: () => { }, expandedGroups: expandedGroups, onToggleExpanded: toggleExpanded, width: sidebarWidth, focused: focusedColumn === 'main', scrollOffset: scrollOffset, visibleCount: visibleCount, showFlatTier: false, selectionStateByKey: selectionStateByKey, aiFlaggedByKey: aiFlaggedByKey, dimPredicate: dimPredicate, visibleRows: visibleRows, viewMode: columnOneView, graph: graph })), columnPlan.layout === 'three-column' && (_jsxs(_Fragment, { children: [_jsx(Box, { width: 2, flexShrink: 0 }), _jsx(AddedComponentsColumn, { width: columnPlan.added, entries: addedComponents, cursor: safeAddedComponentsCursor, focused: focusedColumn === 'added-components', aiFlaggedByKey: aiFlaggedByKey, visibleCount: visibleCount }), _jsx(Box, { width: 2, flexShrink: 0 }), _jsx(AddedGroupsColumn, { width: columnPlan.groups, entries: addedGroups, cursor: safeAddedGroupsCursor, focused: focusedColumn === 'added-groups', aiFlaggedByKey: aiFlaggedByKey, visibleCount: visibleCount })] }))] }), focusedComponent && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.info, children: focusedComponent.name }), _jsx(Text, { dimColor: true, children: ' — ' }), isIncluded(focusedComponent.name) ? (_jsx(Text, { color: PALETTE.success, children: "included" })) : (_jsx(Text, { color: PALETTE.error, children: "excluded" })), isAiFlagged(focusedComponent) && (_jsx(Text, { color: PALETTE.warning, bold: true, children: ' [×]' }))] }), isAiFlagged(focusedComponent) &&
|
|
801
801
|
focusedComponent.aiReason !== null &&
|
|
802
802
|
focusedComponent.aiReason !== undefined &&
|
|
803
803
|
focusedComponent.aiReason !== '' && (_jsx(Box, { width: totalWidth, height: FOCUSED_REASON_MAX_LINES, flexShrink: 0, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: capReasonForFocusedRow(focusedComponent.aiReason, totalWidth) }) }))] })), cyclesPanelOpen && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: PALETTE.warning, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.warning, children: `Cycles detected (${slotCycles.length}):` }), _jsx(Text, { children: " " }), slotCycles.map((cycle, i) => {
|
|
@@ -810,7 +810,7 @@ export function ScopeGateStep({ components, onConfirm, onQuit, aiFilterStatus =
|
|
|
810
810
|
parts.push(cycle.path[cycle.path.length - 1]);
|
|
811
811
|
const label = `Cycle ${i + 1}: ${parts.join(' → ')}`;
|
|
812
812
|
return (_jsxs(Text, { children: [isCursor ? (_jsx(Text, { color: PALETTE.info, bold: true, children: '▶' })) : (_jsx(Text, { children: " " })), _jsx(Text, { color: PALETTE.warning, inverse: isCursor, children: ' ' + label })] }, i));
|
|
813
|
-
}), _jsx(Text, { dimColor: true, children: "[\u2191/\u2193] move \u00B7 [Enter] jump \u00B7 [c/Esc] close" })] })), pendingRejectCascade && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: PALETTE.warning, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.warning, children: `Rejecting ${pendingRejectCascade.target} will:` }), pendingRejectCascade.ancestors.length > 0 && (_jsx(Text, { children: `- Reject ancestors: ${pendingRejectCascade.ancestors.join(', ')}` })), pendingRejectCascade.descendants.length > 0 && (_jsx(Text, { children: `- Deselect descendants: ${pendingRejectCascade.descendants.join(', ')}` })), _jsx(Text, { dimColor: true, children: "[y] confirm \u00B7 [n]/[Esc] cancel" })] })), searchOpen && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: [`/${searchQuery}`, _jsx(Text, { color: PALETTE.info, children: '▎' }), searchQuery && _jsx(Text, { dimColor: true, children: ` (${totalMatches}/${totalComponents} matches)` })] }), autocompleteCandidates.length > 1 && (_jsx(Text, { dimColor: true, children: ` possibilities: ${autocompleteCandidates.join(' · ').slice(0, 120)}` }))] })), !searchOpen && searchQuery && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `/${searchQuery} (${totalMatches}/${totalComponents} matches) · [Esc] clear` }) })), _jsxs(Box, { columnGap: 2, marginTop: 1, flexWrap: "wrap", children: [includedCount > 0 ? (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.success, children: includedCount }), _jsxs(Text, { dimColor: true, children: ["/", totalComponents, " included"] })] })) : (_jsx(Text, { color: PALETTE.warning, children: "none included" })), legendEntry('[j/k]', 'move'), legendEntry('[a]', 'accept'), legendEntry('[r]', 'reject'), hasGroupRoots && legendEntry('[space]', 'expand/collapse group'), hasGroupRoots && legendEntry('[E/C]', 'expand/collapse all'), legendEntry('[A]', 'toggle all'), legendEntry('[Y]', 'accept non-flagged'), legendEntry('[L]', 'flat', columnOneView === 'flat'), legendEntry('[l]', 'lineage', lineagePanel.isOpen), legendEntry('[i]', 'focus lineage', jumpFilterTarget !== null), hasCycles && legendEntry('[o]', 'only cycles', activeFilters.has('cycles')), hasCycles && legendEntry('[c]', 'cycle list', cyclesPanelOpen), legendEntry('[/]', 'search', searchOpen || searchQuery.length > 0), legendEntry('[f]', 'continue'), legendEntry('[?]', 'help'), legendEntry('[q]', 'quit'), columnPlan.layout === 'three-column' && legendEntry('[Tab/Shift-Tab]', 'switch column'), columnPlan.layout === 'three-column' && legendEntry('[Enter]', 'jump to main'), hasAnyAi && legendEntry('[x]', '
|
|
813
|
+
}), _jsx(Text, { dimColor: true, children: "[\u2191/\u2193] move \u00B7 [Enter] jump \u00B7 [c/Esc] close" })] })), pendingRejectCascade && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: PALETTE.warning, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.warning, children: `Rejecting ${pendingRejectCascade.target} will:` }), pendingRejectCascade.ancestors.length > 0 && (_jsx(Text, { children: `- Reject ancestors: ${pendingRejectCascade.ancestors.join(', ')}` })), pendingRejectCascade.descendants.length > 0 && (_jsx(Text, { children: `- Deselect descendants: ${pendingRejectCascade.descendants.join(', ')}` })), _jsx(Text, { dimColor: true, children: "[y] confirm \u00B7 [n]/[Esc] cancel" })] })), searchOpen && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: [`/${searchQuery}`, _jsx(Text, { color: PALETTE.info, children: '▎' }), searchQuery && _jsx(Text, { dimColor: true, children: ` (${totalMatches}/${totalComponents} matches)` })] }), autocompleteCandidates.length > 1 && (_jsx(Text, { dimColor: true, children: ` possibilities: ${autocompleteCandidates.join(' · ').slice(0, 120)}` }))] })), !searchOpen && searchQuery && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `/${searchQuery} (${totalMatches}/${totalComponents} matches) · [Esc] clear` }) })), _jsxs(Box, { columnGap: 2, marginTop: 1, flexWrap: "wrap", children: [includedCount > 0 ? (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.success, children: includedCount }), _jsxs(Text, { dimColor: true, children: ["/", totalComponents, " included"] })] })) : (_jsx(Text, { color: PALETTE.warning, children: "none included" })), legendEntry('[j/k]', 'move'), legendEntry('[a]', 'accept'), legendEntry('[r]', 'reject'), hasGroupRoots && legendEntry('[space]', 'expand/collapse group'), hasGroupRoots && legendEntry('[E/C]', 'expand/collapse all'), legendEntry('[A]', 'toggle all'), legendEntry('[Y]', 'accept non-flagged'), legendEntry('[L]', 'flat', columnOneView === 'flat'), legendEntry('[l]', 'lineage', lineagePanel.isOpen), legendEntry('[i]', 'focus lineage', jumpFilterTarget !== null), hasCycles && legendEntry('[o]', 'only cycles', activeFilters.has('cycles')), hasCycles && legendEntry('[c]', 'cycle list', cyclesPanelOpen), legendEntry('[/]', 'search', searchOpen || searchQuery.length > 0), legendEntry('[f]', 'continue'), legendEntry('[?]', 'help'), legendEntry('[q]', 'quit'), columnPlan.layout === 'three-column' && legendEntry('[Tab/Shift-Tab]', 'switch column'), columnPlan.layout === 'three-column' && legendEntry('[Enter]', 'jump to main'), hasAnyAi && legendEntry('[x]', 'AI exclusions', aiRationalePanel.isOpen), hasAnyAi && (_jsxs(Text, { children: [_jsx(Text, { color: PALETTE.warning, bold: true, children: "[\u00D7]" }), ' ', _jsx(Text, { dimColor: true, children: "AI recommends excluding" })] }))] })] }));
|
|
814
814
|
}
|
|
815
815
|
function ColumnHeader(props) {
|
|
816
816
|
const { title, width, focused } = props;
|
package/dist/src/session/db.d.ts
CHANGED
package/dist/src/session/db.js
CHANGED
|
@@ -683,7 +683,14 @@ export function loadRawComponents(db, sessionId, allowedNames) {
|
|
|
683
683
|
source: c.source,
|
|
684
684
|
framework: c.framework,
|
|
685
685
|
extractionConfidence: c.extraction_confidence ?? null,
|
|
686
|
-
reviewReasons:
|
|
686
|
+
reviewReasons: (() => {
|
|
687
|
+
try {
|
|
688
|
+
return JSON.parse(c.review_reasons ?? '[]');
|
|
689
|
+
}
|
|
690
|
+
catch {
|
|
691
|
+
return [];
|
|
692
|
+
}
|
|
693
|
+
})(),
|
|
687
694
|
needsReview: Boolean(c.needs_review),
|
|
688
695
|
sourcePath: c.source_path ?? undefined,
|
|
689
696
|
props: (propsByComponent.get(c.component_id) ?? []).map((p) => {
|
|
@@ -750,16 +757,6 @@ export function renameEmptySlots(db, sessionId, componentId, componentName, slot
|
|
|
750
757
|
}
|
|
751
758
|
return { renames, warnings };
|
|
752
759
|
}
|
|
753
|
-
function parseReviewReasons(raw) {
|
|
754
|
-
try {
|
|
755
|
-
const parsed = JSON.parse(raw ?? '[]');
|
|
756
|
-
return Array.isArray(parsed) ? parsed.filter((reason) => typeof reason === 'string') : [];
|
|
757
|
-
}
|
|
758
|
-
catch {
|
|
759
|
-
// A malformed historical value should not prevent callers from loading.
|
|
760
|
-
return [];
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
760
|
function groupBy(items, key) {
|
|
764
761
|
const map = new Map();
|
|
765
762
|
for (const item of items) {
|
|
@@ -933,7 +930,7 @@ export function loadCDFComponents(db, sessionId) {
|
|
|
933
930
|
}
|
|
934
931
|
export function loadScopeComponents(db, sessionId) {
|
|
935
932
|
const rows = db
|
|
936
|
-
.prepare(`SELECT name, component_id, status, reject_reason
|
|
933
|
+
.prepare(`SELECT name, component_id, status, reject_reason FROM raw_components
|
|
937
934
|
WHERE session_id = ? AND status IN ('extracted', 'accepted', 'rejected')
|
|
938
935
|
ORDER BY name`)
|
|
939
936
|
.all(sessionId);
|
|
@@ -949,21 +946,16 @@ export function loadScopeComponents(db, sessionId) {
|
|
|
949
946
|
.all(sessionId);
|
|
950
947
|
const slotsByComponent = groupBy(slotRows, (s) => s.component_id);
|
|
951
948
|
const allowedBySlot = groupBy(allowedRows, (a) => `${a.component_id}::${a.slot_name}`);
|
|
952
|
-
return rows.map((r) => {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
name: s.name,
|
|
963
|
-
allowedComponents: (allowedBySlot.get(`${r.component_id}::${s.name}`) ?? []).map((a) => a.allowed_component),
|
|
964
|
-
})),
|
|
965
|
-
};
|
|
966
|
-
});
|
|
949
|
+
return rows.map((r) => ({
|
|
950
|
+
name: r.name,
|
|
951
|
+
componentId: r.component_id,
|
|
952
|
+
aiDecision: r.status === 'accepted' ? 'accepted' : r.status === 'rejected' ? 'rejected' : null,
|
|
953
|
+
aiReason: r.reject_reason,
|
|
954
|
+
slots: (slotsByComponent.get(r.component_id) ?? []).map((s) => ({
|
|
955
|
+
name: s.name,
|
|
956
|
+
allowedComponents: (allowedBySlot.get(`${r.component_id}::${s.name}`) ?? []).map((a) => a.allowed_component),
|
|
957
|
+
})),
|
|
958
|
+
}));
|
|
967
959
|
}
|
|
968
960
|
export function applyScopeDecisions(db, sessionId, decisions) {
|
|
969
961
|
const now = new Date().toISOString();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.17.1-dev-build-
|
|
3
|
+
"version": "2.17.1-dev-build-c40b4d2.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"react": "^18.3.1",
|
|
35
35
|
"react-devtools-core": "^4.19.1",
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
|
-
"@contentful/experience-design-system-extraction": "2.17.1-dev-build-
|
|
38
|
-
"@contentful/experience-design-system-types": "2.17.1-dev-build-
|
|
37
|
+
"@contentful/experience-design-system-extraction": "2.17.1-dev-build-c40b4d2.0",
|
|
38
|
+
"@contentful/experience-design-system-types": "2.17.1-dev-build-c40b4d2.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@tsconfig/node24": "^24.0.3",
|
|
@@ -147,7 +147,7 @@ For each `RawPropDefinition`, apply in order:
|
|
|
147
147
|
|
|
148
148
|
1. **Framework / DOM / accessibility pass-through?** → `exclude_prop`. These are escape hatches for developers, not configurable surfaces for marketers. Exposing them in the ExO editor adds noise that obscures the props that actually carry intent. Always exclude:
|
|
149
149
|
- Framework internals: `ref`, `innerRef`, event handlers (any `onSomething`), `testId`, `data-testid`, `key`
|
|
150
|
-
- DOM pass-through: `className`, `class`, `classes`, `classNames`, `rootClassName`, `prefixCls`, `style`, `styles`, `id`, `role`, `tabIndex`, `htmlFor`, `for`, `slot`, `is`, `lang`, `dir`, `hidden`, `draggable`, `spellCheck`, `contentEditable`, `inputMode`, `autoComplete`, `autoFocus`, `translate`, `part`, `exportparts`
|
|
150
|
+
- DOM pass-through: `className`, `class`, `classes`, `classNames`, `rootClassName`, `prefixCls`, `style`, `styles`, `id`, `role`, `tabIndex`, `name` (the bare HTML form `name` attribute), `htmlFor`, `for`, `slot`, `is`, `lang`, `dir`, `hidden`, `draggable`, `spellCheck`, `contentEditable`, `inputMode`, `autoComplete`, `autoFocus`, `translate`, `part`, `exportparts`
|
|
151
151
|
- Accessibility pass-through: any `aria-*` or `ariaSomething` prop (including bare `aria` as an aria-attributes object), `aria-label`, `aria-hidden`, `aria-describedby`, `aria-controls`
|
|
152
152
|
- Data attributes: any `data-*` prop
|
|
153
153
|
- **Polymorphic component props**: `as`, `element`, `component` (when typed as an HTML tag string or component reference) — these change rendered HTML, not marketer-visible behavior
|
|
@@ -157,7 +157,6 @@ For each `RawPropDefinition`, apply in order:
|
|
|
157
157
|
- `icon` / `leftIcon` / `rightIcon` / `prefixIcon` / `suffixIcon` — slot or `string` (icon name); see slot guidance below
|
|
158
158
|
- `items` / `options` / `actions` / `links` — usually array content; if the element shape is simple, classify as `string` (comma-separated names/IDs) and note in `description`. Only exclude when elements are deep nested objects with no flat representation.
|
|
159
159
|
- `value` (the bare prop, not `modelValue`) — content prop, usually `string` (or `enum` if from a fixed set). Note: Vue's `modelValue` / `modelModifiers` are excluded by pre-classify because they're v-model framework wiring.
|
|
160
|
-
- `name` — content prop, usually `string`. Treat it as semantic component data, not as a DOM pass-through.
|
|
161
160
|
- `form` (when not the literal `<form>` HTML attribute) — typically content; classify as `string` unless it's a complex form-config object
|
|
162
161
|
- `inputId` / `componentId` — these CAN be content (anchor IDs, marketer-set tracking refs). Classify as `string`, `cdf_category: "content"` when the type is a plain string. Only exclude if the prop is clearly internal (e.g. typed as a generated React ID).
|
|
163
162
|
- `accessibleNameRef` / `accessibleDescriptionRef` (web components) — these are ID references for a11y wiring; classify as `string`, `cdf_category: "state"` (behavioral wiring, not design or content).
|
|
@@ -398,7 +397,7 @@ Before emitting any tool calls, verify:
|
|
|
398
397
|
5. Every `cdf_type: "token"` has `token_kind` (or a warning in `description` if lookup failed)
|
|
399
398
|
6. No `cdf_type: "link"` — all href/url props use `string`
|
|
400
399
|
7. `required` values are JSON booleans, not strings
|
|
401
|
-
8. Framework, DOM, accessibility, and data-* pass-through props are excluded — `className`/`classes`/`classNames`/`rootClassName`/`prefixCls`, `style`, `id`, `role`, `tabIndex`, `aria-*` (and bare `aria`), `data-*`, polymorphic `as`/`element`/`component`, framework theming `dt`/`pt`/`ptOptions`/`unstyled`/`sx`. Discrete positional/geometric props (`top`, `bottom`, `left`, `right`, `rotation`, etc.) ARE classified as `string` design props. Common semantic props (`icon`, `items`, `actions`, `options`, `value`, `
|
|
400
|
+
8. Framework, DOM, accessibility, and data-* pass-through props are excluded — `className`/`classes`/`classNames`/`rootClassName`/`prefixCls`, `style`, `id`, `role`, `tabIndex`, `name` (bare HTML form attribute), `aria-*` (and bare `aria`), `data-*`, polymorphic `as`/`element`/`component`, framework theming `dt`/`pt`/`ptOptions`/`unstyled`/`sx`. Discrete positional/geometric props (`top`, `bottom`, `left`, `right`, `rotation`, etc.) ARE classified as `string` design props. Common semantic props (`icon`, `items`, `actions`, `options`, `value`, `form`, `inputId`, `componentId`) are NOT excluded — classify them per their content/design/state nature.
|
|
402
401
|
9. No `cdf_type: "link"` used — `link` is reserved and rejected by the CLI parser
|
|
403
402
|
10. No `cdf_type: "number"` used — this is not a supported type; use `"string"` with numeric defaults. `cdf_type: "boolean"` IS valid — use it for boolean toggle props.
|
|
404
403
|
11. `classify_component` includes a `rationale` object with all three sub-fields (`rationale.description`, `rationale.props`, `rationale.slots`) populated as non-empty strings.
|