@contentful/experience-design-system-cli 2.14.2 → 2.15.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/README.md +64 -2
- package/dist/package.json +3 -2
- package/dist/src/analyze/command.d.ts +11 -0
- package/dist/src/analyze/command.js +298 -3
- package/dist/src/analyze/composite-closure.d.ts +25 -0
- package/dist/src/analyze/composite-closure.js +0 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.d.ts +4 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.js +22 -0
- package/dist/src/analyze/composition/agent-parser/extract-parser.d.ts +8 -0
- package/dist/src/analyze/composition/agent-parser/extract-parser.js +44 -0
- package/dist/src/analyze/composition/agent-parser/load-prompt.d.ts +7 -0
- package/dist/src/analyze/composition/agent-parser/load-prompt.js +31 -0
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.d.ts +46 -0
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +88 -0
- package/dist/src/analyze/composition/agent-parser/sandbox.d.ts +45 -0
- package/dist/src/analyze/composition/agent-parser/sandbox.js +187 -0
- package/dist/src/analyze/composition/apply-mapping.d.ts +22 -0
- package/dist/src/analyze/composition/apply-mapping.js +67 -0
- package/dist/src/analyze/composition/candidate-critic-agent.d.ts +7 -0
- package/dist/src/analyze/composition/candidate-critic-agent.js +39 -0
- package/dist/src/analyze/composition/candidate-critic.d.ts +32 -0
- package/dist/src/analyze/composition/candidate-critic.js +63 -0
- package/dist/src/analyze/composition/candidate-files.d.ts +34 -0
- package/dist/src/analyze/composition/candidate-files.js +131 -0
- package/dist/src/analyze/composition/composition-cache-key.d.ts +19 -0
- package/dist/src/analyze/composition/composition-cache-key.js +19 -0
- package/dist/src/analyze/composition/interchange-schema.d.ts +37 -0
- package/dist/src/analyze/composition/interchange-schema.js +61 -0
- package/dist/src/analyze/composition/merge-edges.d.ts +19 -0
- package/dist/src/analyze/composition/merge-edges.js +50 -0
- package/dist/src/analyze/composition/parse-map-edges.d.ts +16 -0
- package/dist/src/analyze/composition/parse-map-edges.js +55 -0
- package/dist/src/analyze/composition/resolve-mapping-cli.d.ts +23 -0
- package/dist/src/analyze/composition/resolve-mapping-cli.js +29 -0
- package/dist/src/analyze/composition/resolve-mapping.d.ts +59 -0
- package/dist/src/analyze/composition/resolve-mapping.js +94 -0
- package/dist/src/analyze/cycle-detection.d.ts +27 -0
- package/dist/src/analyze/cycle-detection.js +234 -0
- package/dist/src/analyze/cycle-view.d.ts +7 -0
- package/dist/src/analyze/cycle-view.js +28 -0
- package/dist/src/analyze/fuzzy-search.d.ts +3 -0
- package/dist/src/analyze/fuzzy-search.js +55 -0
- package/dist/src/analyze/issue-inheritance.d.ts +12 -0
- package/dist/src/analyze/issue-inheritance.js +108 -0
- package/dist/src/analyze/lineage.d.ts +29 -0
- package/dist/src/analyze/lineage.js +0 -0
- package/dist/src/analyze/scope-gate-cascade.d.ts +11 -0
- package/dist/src/analyze/scope-gate-cascade.js +170 -0
- package/dist/src/analyze/search-neighborhood.d.ts +3 -0
- package/dist/src/analyze/search-neighborhood.js +56 -0
- package/dist/src/analyze/select/tui/App.js +5 -16
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.d.ts +0 -4
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +3 -15
- package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +29 -38
- package/dist/src/analyze/select/tui/components/FieldEditor.js +309 -185
- package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +13 -1
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +23 -2
- package/dist/src/analyze/select/tui/components/GotoBanner.d.ts +31 -0
- package/dist/src/analyze/select/tui/components/GotoBanner.js +30 -0
- package/dist/src/analyze/select/tui/components/GroupedSidebar.d.ts +91 -0
- package/dist/src/analyze/select/tui/components/GroupedSidebar.js +497 -0
- package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +13 -1
- package/dist/src/analyze/select/tui/components/HelpOverlay.js +9 -3
- package/dist/src/analyze/select/tui/components/JsonEditor.js +3 -3
- package/dist/src/analyze/select/tui/components/LineagePanel.d.ts +14 -0
- package/dist/src/analyze/select/tui/components/LineagePanel.js +22 -0
- package/dist/src/analyze/select/tui/components/RationalePanel.d.ts +0 -7
- package/dist/src/analyze/select/tui/components/RationalePanel.js +3 -15
- package/dist/src/analyze/select/tui/components/Sidebar.d.ts +0 -6
- package/dist/src/analyze/select/tui/components/Sidebar.js +11 -27
- package/dist/src/analyze/select/tui/components/StatusBar.js +2 -1
- package/dist/src/analyze/select/tui/components/removed-components-text.d.ts +7 -0
- package/dist/src/analyze/select/tui/components/removed-components-text.js +11 -0
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +1 -5
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +8 -10
- package/dist/src/analyze/select/tui/theme.d.ts +15 -0
- package/dist/src/analyze/select/tui/theme.js +28 -0
- package/dist/src/analyze/selection-cascade.d.ts +3 -0
- package/dist/src/analyze/selection-cascade.js +19 -0
- package/dist/src/analyze/slot-graph.d.ts +11 -0
- package/dist/src/analyze/slot-graph.js +17 -0
- package/dist/src/apply/api-client.d.ts +2 -1
- package/dist/src/apply/api-client.js +40 -6
- package/dist/src/apply/command.d.ts +17 -1
- package/dist/src/apply/command.js +69 -11
- package/dist/src/apply/error-parser.d.ts +16 -0
- package/dist/src/apply/error-parser.js +117 -0
- package/dist/src/apply/tui/ServerPreviewView.js +3 -1
- package/dist/src/credentials-store.d.ts +6 -0
- package/dist/src/credentials-store.js +5 -1
- package/dist/src/generate/agent-runner.d.ts +6 -0
- package/dist/src/generate/agent-runner.js +24 -8
- package/dist/src/import/command.js +69 -31
- package/dist/src/import/cycle-auto-reject.d.ts +4 -0
- package/dist/src/import/cycle-auto-reject.js +14 -0
- package/dist/src/import/orchestrator.d.ts +20 -7
- package/dist/src/import/orchestrator.js +139 -35
- package/dist/src/import/strip-allowed-components.d.ts +18 -0
- package/dist/src/import/strip-allowed-components.js +22 -0
- package/dist/src/import/tui/CustomPromptBanner.d.ts +0 -5
- package/dist/src/import/tui/CustomPromptBanner.js +2 -1
- package/dist/src/import/tui/WizardApp.d.ts +10 -74
- package/dist/src/import/tui/WizardApp.js +234 -246
- package/dist/src/import/tui/ai-flag.d.ts +5 -0
- package/dist/src/import/tui/ai-flag.js +6 -0
- package/dist/src/import/tui/autocomplete.d.ts +5 -0
- package/dist/src/import/tui/autocomplete.js +32 -0
- package/dist/src/import/tui/components/AutoFilterBanner.d.ts +10 -0
- package/dist/src/import/tui/components/AutoFilterBanner.js +19 -0
- package/dist/src/import/tui/components/CounterStrip.d.ts +12 -0
- package/dist/src/import/tui/components/CounterStrip.js +12 -0
- package/dist/src/import/tui/components/LegendEntry.d.ts +2 -0
- package/dist/src/import/tui/components/LegendEntry.js +6 -0
- package/dist/src/import/tui/cycle-panel-scroll.d.ts +8 -0
- package/dist/src/import/tui/cycle-panel-scroll.js +33 -0
- package/dist/src/import/tui/final-review-host.d.ts +3 -7
- package/dist/src/import/tui/final-review-host.js +9 -4
- package/dist/src/import/tui/group-collapse.d.ts +2 -0
- package/dist/src/import/tui/group-collapse.js +11 -0
- package/dist/src/import/tui/history.d.ts +22 -0
- package/dist/src/import/tui/history.js +53 -0
- package/dist/src/import/tui/hooks/useLineage.d.ts +32 -0
- package/dist/src/import/tui/hooks/useLineage.js +50 -0
- package/dist/src/import/tui/hooks/useOverlayPanel.d.ts +13 -0
- package/dist/src/import/tui/hooks/useOverlayPanel.js +27 -0
- package/dist/src/import/tui/lineage-layout.d.ts +75 -0
- package/dist/src/import/tui/lineage-layout.js +82 -0
- package/dist/src/import/tui/runLivePreview.d.ts +11 -0
- package/dist/src/import/tui/runLivePreview.js +7 -1
- package/dist/src/import/tui/scope-gate-columns.d.ts +31 -0
- package/dist/src/import/tui/scope-gate-columns.js +94 -0
- package/dist/src/import/tui/scope-gate-host.d.ts +3 -1
- package/dist/src/import/tui/scope-gate-host.js +11 -3
- package/dist/src/import/tui/sidebar-width.d.ts +1 -0
- package/dist/src/import/tui/sidebar-width.js +3 -0
- package/dist/src/import/tui/step-filters.d.ts +19 -0
- package/dist/src/import/tui/step-filters.js +42 -0
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.d.ts +42 -0
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +726 -0
- package/dist/src/import/tui/steps/AtomicScopeGateStep.d.ts +3 -0
- package/dist/src/import/tui/steps/AtomicScopeGateStep.js +211 -0
- package/dist/src/import/tui/steps/CredentialsStep.js +5 -18
- package/dist/src/import/tui/steps/DoneStep.d.ts +0 -2
- package/dist/src/import/tui/steps/DoneStep.js +3 -2
- package/dist/src/import/tui/steps/ErrorStep.js +2 -1
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +19 -25
- package/dist/src/import/tui/steps/GenerateReviewStep.js +1258 -229
- package/dist/src/import/tui/steps/PathValidationStep.js +3 -2
- package/dist/src/import/tui/steps/PreviewStep.js +2 -1
- package/dist/src/import/tui/steps/PushDecisionGateStep.d.ts +0 -7
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +3 -6
- package/dist/src/import/tui/steps/PushingStep.js +2 -1
- package/dist/src/import/tui/steps/RunningStep.d.ts +4 -1
- package/dist/src/import/tui/steps/RunningStep.js +3 -2
- package/dist/src/import/tui/steps/ScopeGateStep.d.ts +24 -0
- package/dist/src/import/tui/steps/ScopeGateStep.js +850 -193
- package/dist/src/import/tui/steps/TokenInputStep.js +2 -1
- package/dist/src/import/tui/steps/WelcomeStep.js +2 -1
- package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +6 -0
- package/dist/src/import/tui/steps/WizardPreviewStep.js +95 -56
- package/dist/src/import/tui/steps/auto-reject-decision.d.ts +7 -0
- package/dist/src/import/tui/steps/auto-reject-decision.js +9 -0
- package/dist/src/import/tui/steps/breaking-change-format.d.ts +2 -0
- package/dist/src/import/tui/steps/breaking-change-format.js +24 -0
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.d.ts +14 -0
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
- package/dist/src/import/tui/steps/preview-diff.js +48 -5
- package/dist/src/import/tui/useFinalizePreview.d.ts +34 -0
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.d.ts +3 -0
- package/dist/src/import/tui/useLivePreview.js +1 -0
- package/dist/src/import/tui/wizard-state-transitions.d.ts +5 -59
- package/dist/src/import/tui/wizard-state-transitions.js +5 -60
- package/dist/src/lib/composition-mode.d.ts +28 -0
- package/dist/src/lib/composition-mode.js +37 -0
- package/dist/src/lib/prompt-overrides.d.ts +38 -0
- package/dist/src/lib/prompt-overrides.js +69 -0
- package/dist/src/print/command.js +19 -1
- package/dist/src/runs/modify-launcher.d.ts +3 -0
- package/dist/src/runs/modify-launcher.js +2 -0
- package/dist/src/runs/replay-helpers.js +1 -0
- package/dist/src/runs/run-picker.js +2 -18
- package/dist/src/runs/store.d.ts +4 -0
- package/dist/src/session/db.d.ts +23 -40
- package/dist/src/session/db.js +110 -138
- package/dist/src/setup/composition-mode-prompt.d.ts +14 -0
- package/dist/src/setup/composition-mode-prompt.js +37 -0
- package/package.json +5 -4
- package/prompts/composition-dir-critic.md +7 -0
- package/prompts/composition-edges.md +8 -0
- package/prompts/composition-parser-repair-empty.md +1 -0
- package/prompts/composition-parser-repair-error.md +2 -0
- package/prompts/composition-parser.md +42 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
+
import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
3
4
|
import { Box, Text } from 'ink';
|
|
4
5
|
import { statSync } from 'node:fs';
|
|
5
6
|
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
@@ -83,5 +84,5 @@ export function TokenInputStep({ onConfirm, onSkip, onQuit }) {
|
|
|
83
84
|
}
|
|
84
85
|
});
|
|
85
86
|
const displayValue = inputValue + (cursorVisible ? '█' : ' ');
|
|
86
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, children: "Design tokens" }), _jsx(Text, { dimColor: true, children: "Point me to your raw token file (e.g. ~/design-tokens/tokens.json). You can use ~, relative, or absolute paths. Claude will map it to DTCG format." }), _jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 0, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color:
|
|
87
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, children: "Design tokens" }), _jsx(Text, { dimColor: true, children: "Point me to your raw token file (e.g. ~/design-tokens/tokens.json). You can use ~, relative, or absolute paths. Claude will map it to DTCG format." }), _jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 0, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.info, children: "?" }), _jsx(Text, { children: "Token path (file or directory):" }), _jsx(Text, { children: displayValue })] }), resolvedPath && resolvedPath !== inputValue.trim() && !error && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u2192 ", resolvedPath] }) })), error && (_jsxs(Box, { marginTop: 0, flexDirection: "column", children: [_jsxs(Text, { color: PALETTE.error, children: ["\u2717 ", error] }), resolvedPath && _jsxs(Text, { dimColor: true, children: [" Resolved to: ", resolvedPath] })] }))] }), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[Enter] Submit / Skip if empty" }), _jsx(Text, { dimColor: true, children: "[Tab] Exit typing" }), _jsx(Text, { dimColor: true, children: "[Esc] Clear & exit" }), _jsx(Text, { dimColor: true, children: "[s] Skip" }), _jsx(Text, { dimColor: true, children: "[q] Quit" })] })] }));
|
|
87
88
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
+
import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
3
4
|
import { Box, Text } from 'ink';
|
|
4
5
|
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
5
6
|
export function WelcomeStep({ onContinue, onQuit }) {
|
|
@@ -29,5 +30,5 @@ export function WelcomeStep({ onContinue, onQuit }) {
|
|
|
29
30
|
}
|
|
30
31
|
});
|
|
31
32
|
const displayValue = inputValue + (cursorVisible ? '█' : ' ');
|
|
32
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color:
|
|
33
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: PALETTE.success, children: "\uD83D\uDC4B Hey! Let's import your design system into Contentful." }), _jsx(Text, { dimColor: true, children: "I'll walk you through 5 steps to get your components into Contentful ExO." }), _jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 0, children: [_jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: "Step 1" }), _jsx(Text, { dimColor: true, children: "Extract components from your codebase" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: "Step 2" }), _jsx(Text, { dimColor: true, children: "Review what was extracted" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: "Step 3" }), _jsx(Text, { dimColor: true, children: "Generate CDF definitions with Claude" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: "Step 4" }), _jsx(Text, { dimColor: true, children: "Review generated definitions" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: "Step 5" }), _jsx(Text, { dimColor: true, children: "Push to Contentful" })] }), _jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 0, children: [_jsx(Text, { children: "Where is your component library?" }), _jsxs(Box, { gap: 1, marginTop: 0, children: [_jsx(Text, { color: PALETTE.info, children: "?" }), _jsx(Text, { children: "Project path:" }), _jsx(Text, { children: displayValue })] })] }), _jsxs(Box, { marginTop: 1, gap: 3, children: [_jsx(Text, { dimColor: true, children: "[Enter] Continue" }), _jsx(Text, { dimColor: true, children: "[q] Quit" })] })] }));
|
|
33
34
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ServerPreviewResponse } from '@contentful/experience-design-system-types';
|
|
3
|
+
export interface PreviewDiffLine {
|
|
4
|
+
key: string;
|
|
5
|
+
color: string;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function buildPreviewDiffLines(preview: ServerPreviewResponse): PreviewDiffLine[];
|
|
3
9
|
type WizardPreviewStepProps = {
|
|
4
10
|
preview: ServerPreviewResponse;
|
|
5
11
|
spaceId: string;
|
|
@@ -1,9 +1,94 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useMemo } from 'react';
|
|
3
|
+
import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
3
4
|
import { Box, Text, useStdout } from 'ink';
|
|
4
5
|
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
5
6
|
import { hasBreakingChangesWithImpact } from '../../../apply/manifest.js';
|
|
6
7
|
import { computeComponentDiffLines } from './preview-diff.js';
|
|
8
|
+
export function buildPreviewDiffLines(preview) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
const { components, tokens } = preview;
|
|
11
|
+
for (const item of components.new) {
|
|
12
|
+
const raw = item;
|
|
13
|
+
const name = raw.key ?? raw.$name ?? 'unknown';
|
|
14
|
+
lines.push({ key: `comp-new-${name}`, color: PALETTE.success, text: ` + ${name}` });
|
|
15
|
+
const slots = (raw.$slots ?? {});
|
|
16
|
+
for (const slotName of Object.keys(slots).sort()) {
|
|
17
|
+
lines.push({
|
|
18
|
+
key: `comp-new-${name}-slot-${slotName}`,
|
|
19
|
+
color: PALETTE.success,
|
|
20
|
+
text: ` slot: ${slotName}`,
|
|
21
|
+
});
|
|
22
|
+
const allowed = slots[slotName]?.['$allowedComponents'];
|
|
23
|
+
if (Array.isArray(allowed) && allowed.length > 0) {
|
|
24
|
+
const names = allowed.filter((n) => typeof n === 'string');
|
|
25
|
+
lines.push({
|
|
26
|
+
key: `comp-new-${name}-slot-${slotName}-allow`,
|
|
27
|
+
color: PALETTE.success,
|
|
28
|
+
text: ` allowedComponents: [${names.join(', ')}]`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
for (const item of components.removed) {
|
|
34
|
+
lines.push({ key: `comp-rm-${item.name}`, color: PALETTE.error, text: ` - ${item.name}` });
|
|
35
|
+
}
|
|
36
|
+
for (const item of components.changed) {
|
|
37
|
+
lines.push({
|
|
38
|
+
key: `comp-h-${item.current.name}`,
|
|
39
|
+
color: PALETTE.warning,
|
|
40
|
+
text: ` ~ ${item.current.name}${item.hasPendingDraftChanges ? ' ⚡ has pending draft changes' : ''}`,
|
|
41
|
+
});
|
|
42
|
+
if (item.changeClassification?.classification === 'breaking') {
|
|
43
|
+
const reasons = item.changeClassification.breakingChanges
|
|
44
|
+
.map((bc) => `${'slotId' in bc ? bc.slotId : bc.propertyId}: ${bc.reason}`)
|
|
45
|
+
.join(', ');
|
|
46
|
+
lines.push({ key: `comp-b-${item.current.name}`, color: PALETTE.error, text: ` ⚠ BREAKING: ${reasons}` });
|
|
47
|
+
}
|
|
48
|
+
const diffLines = computeComponentDiffLines(item.current, item.proposed, item.changeClassification);
|
|
49
|
+
for (const d of diffLines) {
|
|
50
|
+
lines.push({ key: `comp-d-${item.current.name}-${d.key}`, color: d.color, text: ` ${d.text}` });
|
|
51
|
+
}
|
|
52
|
+
const proposedSlots = item.proposed['$slots'] ?? {};
|
|
53
|
+
for (const slotName of Object.keys(proposedSlots).sort()) {
|
|
54
|
+
const allowed = proposedSlots[slotName]?.['$allowedComponents'];
|
|
55
|
+
if (Array.isArray(allowed) && allowed.length > 0) {
|
|
56
|
+
const names = allowed.filter((n) => typeof n === 'string');
|
|
57
|
+
const key = `comp-d-${item.current.name}-slot-${slotName}-allow-list`;
|
|
58
|
+
if (!lines.some((l) => l.key === key)) {
|
|
59
|
+
lines.push({
|
|
60
|
+
key,
|
|
61
|
+
color: 'gray',
|
|
62
|
+
text: ` slot ${slotName} allowedComponents: [${names.join(', ')}]`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const item of tokens.new) {
|
|
69
|
+
const raw = item;
|
|
70
|
+
const name = raw.name ?? raw.path ?? 'unknown';
|
|
71
|
+
lines.push({ key: `tok-new-${name}`, color: PALETTE.success, text: ` + ${name}` });
|
|
72
|
+
}
|
|
73
|
+
for (const item of tokens.removed) {
|
|
74
|
+
lines.push({ key: `tok-rm-${item.name}`, color: PALETTE.error, text: ` - ${item.name}` });
|
|
75
|
+
}
|
|
76
|
+
for (const item of tokens.changed) {
|
|
77
|
+
const tokenName = item.current.name;
|
|
78
|
+
lines.push({
|
|
79
|
+
key: `tok-h-${tokenName}`,
|
|
80
|
+
color: PALETTE.warning,
|
|
81
|
+
text: ` ~ ${tokenName}${item.hasPendingDraftChanges ? ' ⚡ has pending draft changes' : ''}`,
|
|
82
|
+
});
|
|
83
|
+
if (item.changeClassification?.classification === 'breaking') {
|
|
84
|
+
const reasons = item.changeClassification.breakingChanges
|
|
85
|
+
.map((bc) => `${'slotId' in bc ? bc.slotId : bc.propertyId}: ${bc.reason}`)
|
|
86
|
+
.join(', ');
|
|
87
|
+
lines.push({ key: `tok-b-${tokenName}`, color: PALETTE.error, text: ` ⚠ BREAKING: ${reasons}` });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
7
92
|
export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber, totalSteps, onConfirm, onEdit, onSaveFiles, onQuit, }) {
|
|
8
93
|
const breakingWithImpact = hasBreakingChangesWithImpact(preview);
|
|
9
94
|
const [diffExpanded, setDiffExpanded] = useState(false);
|
|
@@ -14,56 +99,10 @@ export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber,
|
|
|
14
99
|
const allDiffLines = useMemo(() => {
|
|
15
100
|
if (!diffExpanded)
|
|
16
101
|
return [];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
item.$name ??
|
|
22
|
-
'unknown';
|
|
23
|
-
lines.push({ key: `comp-new-${name}`, element: _jsxs(Text, { color: "green", children: [" + ", name] }) });
|
|
24
|
-
}
|
|
25
|
-
for (const item of components.removed) {
|
|
26
|
-
lines.push({ key: `comp-rm-${item.name}`, element: _jsxs(Text, { color: "red", children: [" - ", item.name] }) });
|
|
27
|
-
}
|
|
28
|
-
for (const item of components.changed) {
|
|
29
|
-
lines.push({
|
|
30
|
-
key: `comp-h-${item.current.name}`,
|
|
31
|
-
element: (_jsxs(Text, { color: "yellow", children: [' ', "~ ", item.current.name, item.hasPendingDraftChanges ? _jsx(Text, { color: "yellow", children: " \u26A1 has pending draft changes" }) : null] })),
|
|
32
|
-
});
|
|
33
|
-
if (item.changeClassification?.classification === 'breaking') {
|
|
34
|
-
const reasons = item.changeClassification.breakingChanges
|
|
35
|
-
.map((bc) => `${bc.propertyId}: ${bc.reason}`)
|
|
36
|
-
.join(', ');
|
|
37
|
-
lines.push({ key: `comp-b-${item.current.name}`, element: _jsxs(Text, { color: "red", children: [" \u26A0 BREAKING: ", reasons] }) });
|
|
38
|
-
}
|
|
39
|
-
const diffLines = computeComponentDiffLines(item.current, item.proposed, item.changeClassification);
|
|
40
|
-
for (const d of diffLines) {
|
|
41
|
-
lines.push({ key: `comp-d-${item.current.name}-${d.key}`, element: _jsxs(Text, { color: d.color, children: [" ", d.text] }) });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
for (const item of tokens.new) {
|
|
45
|
-
const name = item.name ??
|
|
46
|
-
item.path ??
|
|
47
|
-
'unknown';
|
|
48
|
-
lines.push({ key: `tok-new-${name}`, element: _jsxs(Text, { color: "green", children: [" + ", name] }) });
|
|
49
|
-
}
|
|
50
|
-
for (const item of tokens.removed) {
|
|
51
|
-
lines.push({ key: `tok-rm-${item.name}`, element: _jsxs(Text, { color: "red", children: [" - ", item.name] }) });
|
|
52
|
-
}
|
|
53
|
-
for (const item of tokens.changed) {
|
|
54
|
-
const tokenName = item.current.name;
|
|
55
|
-
lines.push({
|
|
56
|
-
key: `tok-h-${tokenName}`,
|
|
57
|
-
element: (_jsxs(Text, { color: "yellow", children: [' ', "~ ", tokenName, item.hasPendingDraftChanges ? _jsx(Text, { color: "yellow", children: " \u26A1 has pending draft changes" }) : null] })),
|
|
58
|
-
});
|
|
59
|
-
if (item.changeClassification?.classification === 'breaking') {
|
|
60
|
-
const reasons = item.changeClassification.breakingChanges
|
|
61
|
-
.map((bc) => `${bc.propertyId}: ${bc.reason}`)
|
|
62
|
-
.join(', ');
|
|
63
|
-
lines.push({ key: `tok-b-${tokenName}`, element: _jsxs(Text, { color: "red", children: [" \u26A0 BREAKING: ", reasons] }) });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return lines;
|
|
102
|
+
return buildPreviewDiffLines(preview).map((line) => ({
|
|
103
|
+
key: line.key,
|
|
104
|
+
element: (_jsx(Text, { color: line.color === 'gray' ? undefined : line.color, dimColor: line.color === 'gray', children: line.text })),
|
|
105
|
+
}));
|
|
67
106
|
}, [diffExpanded, preview]);
|
|
68
107
|
const maxScroll = Math.max(0, allDiffLines.length - viewportHeight);
|
|
69
108
|
useImmediateInput((input, key) => {
|
|
@@ -111,11 +150,11 @@ export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber,
|
|
|
111
150
|
const hasComponents = components.new.length + components.changed.length + components.removed.length > 0;
|
|
112
151
|
const hasTokens = tokens.new.length + tokens.changed.length + tokens.removed.length > 0;
|
|
113
152
|
const hasAnything = hasComponents || hasTokens;
|
|
114
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: '─'.repeat(40) }), _jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, children: ["Step ", stepNumber, " of ", totalSteps] }), _jsx(Text, { bold: true, children: "\u2014" }), _jsx(Text, { bold: true, children: "Push to Contentful" })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(40) })] }), hasAnything ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "Here's what will happen in your space:" }), hasComponents && (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Box, { gap: 1, marginTop: 1, children: _jsx(Text, { bold: true, dimColor: true, children: "ComponentTypes" }) }), components.new.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color:
|
|
153
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: '─'.repeat(40) }), _jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, children: ["Step ", stepNumber, " of ", totalSteps] }), _jsx(Text, { bold: true, children: "\u2014" }), _jsx(Text, { bold: true, children: "Push to Contentful" })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(40) })] }), hasAnything ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "Here's what will happen in your space:" }), hasComponents && (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Box, { gap: 1, marginTop: 1, children: _jsx(Text, { bold: true, dimColor: true, children: "ComponentTypes" }) }), components.new.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: " \uFF0B" }), _jsxs(Text, { children: [components.new.length, " will be created"] })] }), components.new.map((item, i) => {
|
|
115
154
|
const name = item.key ?? item.$name ?? 'unknown';
|
|
116
|
-
return (_jsxs(Text, { color:
|
|
117
|
-
})] })), components.changed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color:
|
|
155
|
+
return (_jsxs(Text, { color: PALETTE.success, children: [' ', "+ ", name] }, `new-${i}`));
|
|
156
|
+
})] })), components.changed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.warning, children: " \uFF5E" }), _jsxs(Text, { children: [components.changed.length, " will be updated"] })] }), components.changed.map((item, i) => {
|
|
118
157
|
const isBreaking = item.changeClassification?.classification === 'breaking';
|
|
119
|
-
return (_jsxs(Text, { color: isBreaking ?
|
|
120
|
-
})] })), components.removed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color:
|
|
158
|
+
return (_jsxs(Text, { color: isBreaking ? PALETTE.error : PALETTE.warning, children: [' ', isBreaking ? '⚠' : '~', " ", item.current.name] }, `chg-${i}`));
|
|
159
|
+
})] })), components.removed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.error, children: " \u2717" }), _jsxs(Text, { children: [components.removed.length, " will be removed"] })] }), components.removed.map((item, i) => (_jsxs(Text, { color: PALETTE.error, children: [' ', "\u2717 ", item.name] }, `rm-${i}`)))] })), components.unchanged.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " \u00B7" }), _jsxs(Text, { dimColor: true, children: [components.unchanged.length, " unchanged"] })] }))] })), hasTokens && (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Box, { gap: 1, marginTop: 1, children: _jsx(Text, { bold: true, dimColor: true, children: "Design Tokens" }) }), tokens.new.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: " \uFF0B" }), _jsxs(Text, { children: [tokens.new.length, " will be created"] })] })), tokens.changed.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.warning, children: " \uFF5E" }), _jsxs(Text, { children: [tokens.changed.length, " will be updated"] })] })), tokens.removed.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.error, children: " \u2717" }), _jsxs(Text, { children: [tokens.removed.length, " will be removed"] })] })), tokens.unchanged.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " \u00B7" }), _jsxs(Text, { dimColor: true, children: [tokens.unchanged.length, " unchanged"] })] }))] })), diffExpanded && allDiffLines.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: '─'.repeat(40) }), _jsxs(Text, { dimColor: true, children: [' ', "Diff (", allDiffLines.length, " lines) \u2014 line ", scrollOffset + 1, "\u2013", Math.min(scrollOffset + viewportHeight, allDiffLines.length), " of ", allDiffLines.length] }), _jsx(Box, { flexDirection: "column", children: allDiffLines.slice(scrollOffset, scrollOffset + viewportHeight).map((line) => (_jsx(Box, { children: line.element }, line.key))) }), maxScroll > 0 && _jsx(Text, { dimColor: true, children: " \u2195 j/k to scroll, f/b to page" })] }))] })) : (_jsx(Text, { dimColor: true, children: "Nothing to push \u2014 everything is already up to date." })), _jsxs(Box, { gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Space:" }), _jsx(Text, { children: spaceId }), _jsx(Text, { dimColor: true, children: "/" }), _jsx(Text, { dimColor: true, children: "Environment:" }), _jsx(Text, { children: environmentId })] }), breakingWithImpact && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: PALETTE.error, bold: true, children: "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply." }) })), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[Enter] Push to Contentful" }), _jsxs(Text, { dimColor: true, children: ["[d] ", diffExpanded ? 'Hide' : 'Show', " diff"] }), diffExpanded && _jsx(Text, { dimColor: true, children: "[j/k] Scroll [f/b] Page" }), onEdit && _jsx(Text, { dimColor: true, children: "[e] Edit definitions" }), onSaveFiles && _jsx(Text, { dimColor: true, children: "[s] Save files instead" }), _jsx(Text, { dimColor: true, children: "[q] Cancel" })] })] }));
|
|
121
160
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface AutoRejectDecisionInput {
|
|
2
|
+
loading: boolean;
|
|
3
|
+
autoRejectFired: boolean;
|
|
4
|
+
hasCycle: boolean;
|
|
5
|
+
}
|
|
6
|
+
export type AutoRejectDecision = 'fire' | 'skip';
|
|
7
|
+
export declare function computeAutoRejectDecision(input: AutoRejectDecisionInput): AutoRejectDecision;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const PROPERTY_REASON_COPY = {
|
|
2
|
+
removed: 'removed',
|
|
3
|
+
added_required_no_default: 'now required, no default',
|
|
4
|
+
type_changed: 'type changed',
|
|
5
|
+
validation_narrowed: 'allowed values narrowed',
|
|
6
|
+
};
|
|
7
|
+
const SLOT_REASON_COPY = {
|
|
8
|
+
slot_removed: 'slot removed',
|
|
9
|
+
slot_allowed_components_narrowed: 'allowed components narrowed',
|
|
10
|
+
};
|
|
11
|
+
export function formatBreakingChange(bc, current) {
|
|
12
|
+
if ('slotId' in bc) {
|
|
13
|
+
const copy = SLOT_REASON_COPY[bc.reason] ?? bc.reason;
|
|
14
|
+
return `${bc.slotId} — ${copy}`;
|
|
15
|
+
}
|
|
16
|
+
const copy = PROPERTY_REASON_COPY[bc.reason] ?? bc.reason;
|
|
17
|
+
const meta = current?.fullProperties?.[bc.propertyId];
|
|
18
|
+
if (meta) {
|
|
19
|
+
const tags = [meta.category, meta.type].filter((t) => typeof t === 'string' && t.length > 0);
|
|
20
|
+
const suffix = tags.length > 0 ? ` (${tags.join(', ')})` : '';
|
|
21
|
+
return `${bc.propertyId}${suffix} — ${copy}`;
|
|
22
|
+
}
|
|
23
|
+
return `${bc.propertyId} — ${copy}`;
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CDFComponentEntry } from '@contentful/experience-design-system-types';
|
|
2
|
+
import type { SlotCycle, SlotEdge } from '../../../analyze/cycle-detection.js';
|
|
3
|
+
export type BreakEdge = SlotEdge;
|
|
4
|
+
/** Row shape accepted by {@link enumerateCycleBreaks}. */
|
|
5
|
+
export interface BreakEnumComponent {
|
|
6
|
+
key: string;
|
|
7
|
+
entry: CDFComponentEntry;
|
|
8
|
+
}
|
|
9
|
+
export declare function enumerateCycleBreaks(cycle: Pick<SlotCycle, 'edges'>, components: BreakEnumComponent[]): BreakEdge[];
|
|
10
|
+
export declare function shouldBreakOverlayGoFullScreen({ rows, edgeCount, reservedChrome, }: {
|
|
11
|
+
rows: number;
|
|
12
|
+
edgeCount: number;
|
|
13
|
+
reservedChrome?: number;
|
|
14
|
+
}): boolean;
|
|
Binary file
|
|
@@ -7,14 +7,12 @@ export function computeComponentDiffLines(current, proposed, changeClassificatio
|
|
|
7
7
|
const proposedProperties = (proposed['$properties'] ?? {});
|
|
8
8
|
const proposedProps = new Set(Object.keys(proposedProperties));
|
|
9
9
|
const oldProps = current.fullProperties;
|
|
10
|
-
// Added props
|
|
11
10
|
for (const name of [...proposedProps].sort()) {
|
|
12
11
|
if (!currentProps.has(name)) {
|
|
13
12
|
const def = proposedProperties[name];
|
|
14
13
|
lines.push({ key: `${name}-add`, color: 'green', text: `+ ${name}: ${formatPropDef(def)}` });
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
// Removed props
|
|
18
16
|
for (const name of [...currentProps].sort()) {
|
|
19
17
|
if (!proposedProps.has(name)) {
|
|
20
18
|
if (oldProps?.[name]) {
|
|
@@ -26,7 +24,6 @@ export function computeComponentDiffLines(current, proposed, changeClassificatio
|
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
|
-
// Modified props — compare old vs new definitions
|
|
30
27
|
const handledProps = new Set();
|
|
31
28
|
for (const name of [...currentProps].sort()) {
|
|
32
29
|
if (!proposedProps.has(name))
|
|
@@ -42,9 +39,10 @@ export function computeComponentDiffLines(current, proposed, changeClassificatio
|
|
|
42
39
|
lines.push(d);
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
|
-
// If no old definitions available, fall back to breaking change reasons
|
|
46
42
|
if (!oldProps && changeClassification?.breakingChanges) {
|
|
47
43
|
for (const bc of changeClassification.breakingChanges) {
|
|
44
|
+
if (!('propertyId' in bc))
|
|
45
|
+
continue;
|
|
48
46
|
if (!currentProps.has(bc.propertyId) || !proposedProps.has(bc.propertyId))
|
|
49
47
|
continue;
|
|
50
48
|
if (handledProps.has(bc.propertyId))
|
|
@@ -55,10 +53,10 @@ export function computeComponentDiffLines(current, proposed, changeClassificatio
|
|
|
55
53
|
lines.push({ key: `${bc.propertyId}-new`, color: 'green', text: `+ ${bc.propertyId}: ${formatPropDef(def)}` });
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
|
-
// Slot diffs
|
|
59
56
|
const currentSlots = new Set(current.slots);
|
|
60
57
|
const proposedSlots = (proposed['$slots'] ?? {});
|
|
61
58
|
const proposedSlotNames = new Set(Object.keys(proposedSlots));
|
|
59
|
+
const currentSlotAllowed = current.currentSlotAllowed ?? {};
|
|
62
60
|
for (const name of [...proposedSlotNames].sort()) {
|
|
63
61
|
if (!currentSlots.has(name)) {
|
|
64
62
|
lines.push({ key: `slot-${name}-add`, color: 'green', text: `+ slot: ${name}` });
|
|
@@ -69,8 +67,53 @@ export function computeComponentDiffLines(current, proposed, changeClassificatio
|
|
|
69
67
|
lines.push({ key: `slot-${name}-rm`, color: 'red', text: `- slot: ${name}` });
|
|
70
68
|
}
|
|
71
69
|
}
|
|
70
|
+
for (const name of [...proposedSlotNames].sort()) {
|
|
71
|
+
const nextAllowed = normalizeAllowedComponents(proposedSlots[name]?.['$allowedComponents']);
|
|
72
|
+
const prevAllowed = normalizeAllowedComponents(currentSlotAllowed[name]);
|
|
73
|
+
const prevExists = currentSlots.has(name);
|
|
74
|
+
if (!prevExists) {
|
|
75
|
+
if (nextAllowed.length > 0) {
|
|
76
|
+
lines.push({
|
|
77
|
+
key: `slot-${name}-allow-new`,
|
|
78
|
+
color: 'green',
|
|
79
|
+
text: `+ slot ${name} allowedComponents: [${nextAllowed.join(', ')}]`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (arraysEqual(prevAllowed, nextAllowed))
|
|
85
|
+
continue;
|
|
86
|
+
lines.push({
|
|
87
|
+
key: `slot-${name}-allow-old`,
|
|
88
|
+
color: 'red',
|
|
89
|
+
text: `- slot ${name} allowedComponents: [${prevAllowed.join(', ')}]`,
|
|
90
|
+
});
|
|
91
|
+
lines.push({
|
|
92
|
+
key: `slot-${name}-allow-new`,
|
|
93
|
+
color: 'green',
|
|
94
|
+
text: `+ slot ${name} allowedComponents: [${nextAllowed.join(', ')}]`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
72
97
|
return lines;
|
|
73
98
|
}
|
|
99
|
+
function normalizeAllowedComponents(value) {
|
|
100
|
+
if (!Array.isArray(value))
|
|
101
|
+
return [];
|
|
102
|
+
const out = [];
|
|
103
|
+
for (const item of value) {
|
|
104
|
+
if (typeof item === 'string' && item.length > 0)
|
|
105
|
+
out.push(item);
|
|
106
|
+
}
|
|
107
|
+
return out.sort();
|
|
108
|
+
}
|
|
109
|
+
function arraysEqual(a, b) {
|
|
110
|
+
if (a.length !== b.length)
|
|
111
|
+
return false;
|
|
112
|
+
for (let i = 0; i < a.length; i++)
|
|
113
|
+
if (a[i] !== b[i])
|
|
114
|
+
return false;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
74
117
|
function normalizeType(type) {
|
|
75
118
|
switch (type.toLowerCase()) {
|
|
76
119
|
case 'boolean':
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ComponentTypeSummary } from '@contentful/experience-design-system-types';
|
|
2
|
+
export type FinalizePreviewStatus = 'idle' | 'running' | 'done' | 'error';
|
|
3
|
+
export type UseFinalizePreviewOptions = {
|
|
4
|
+
/** True while the Finalize dialog is open. Toggling true (re)fires the preview. */
|
|
5
|
+
open: boolean;
|
|
6
|
+
extractSessionId: string | null;
|
|
7
|
+
tokensPath: string;
|
|
8
|
+
spaceId: string;
|
|
9
|
+
environmentId: string;
|
|
10
|
+
cmaToken: string;
|
|
11
|
+
host: string;
|
|
12
|
+
/** Component keys the operator has accepted; drives the scoped preview. */
|
|
13
|
+
acceptedKeys: ReadonlySet<string>;
|
|
14
|
+
};
|
|
15
|
+
export type UseFinalizePreviewReturn = {
|
|
16
|
+
status: FinalizePreviewStatus;
|
|
17
|
+
removed: ComponentTypeSummary[];
|
|
18
|
+
scrollOffset: number;
|
|
19
|
+
/** Adjust the removed-list scroll (clamped to the window). */
|
|
20
|
+
scrollBy: (delta: number) => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Shared Finalize-dialog deletion preview, used by both the composite and
|
|
24
|
+
* atomic generate-review steps so the "what will this push delete" experience
|
|
25
|
+
* is identical.
|
|
26
|
+
*
|
|
27
|
+
* When the dialog opens it fires a preview scoped to the ACCEPTED set (so the
|
|
28
|
+
* result reflects exactly what the push does, independent of the session's
|
|
29
|
+
* not-yet-reclassified generated rows), tracks loading/error, and owns the
|
|
30
|
+
* scroll offset for the (windowed) removed list. Credentials-missing (e.g.
|
|
31
|
+
* `--no-push`) short-circuits to `done` with an empty list — the dialog then
|
|
32
|
+
* simply omits the deletion section.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useFinalizePreview(opts: UseFinalizePreviewOptions): UseFinalizePreviewReturn;
|
|
Binary file
|
|
@@ -9,6 +9,9 @@ export type UseLivePreviewOptions = {
|
|
|
9
9
|
host: string;
|
|
10
10
|
onResult: (response: ServerPreviewResponse | null) => void;
|
|
11
11
|
debounceMs?: number;
|
|
12
|
+
/** Preview an empty-but-present manifest (delete-all diff) when nothing is
|
|
13
|
+
* accepted, so the final-review UI can show what a push would delete. */
|
|
14
|
+
deleteAllComponents?: boolean;
|
|
12
15
|
};
|
|
13
16
|
export type LivePreviewStatus = 'idle' | 'running';
|
|
14
17
|
export type UseLivePreviewReturn = {
|
|
@@ -81,6 +81,7 @@ export function useLivePreview(opts) {
|
|
|
81
81
|
cmaToken: current.cmaToken,
|
|
82
82
|
host: current.host,
|
|
83
83
|
generation,
|
|
84
|
+
deleteAllComponents: current.deleteAllComponents === true,
|
|
84
85
|
});
|
|
85
86
|
// Discard stale responses (generation tag).
|
|
86
87
|
if (result.generation !== latestRef.current)
|
|
@@ -1,45 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure state-machine helpers for the wizard. Extracted from WizardApp.tsx so the
|
|
3
|
-
* transitions can be unit-tested without rendering ink.
|
|
4
|
-
*
|
|
5
|
-
* Bug fix (2026-06): credentials moved BEFORE generate so:
|
|
6
|
-
* 1. Bad creds (401/403) are caught upfront — before the operator pays the cost
|
|
7
|
-
* of extract + LLM classification (potentially many minutes).
|
|
8
|
-
* 2. Live preview in final-review actually fires, because the validated creds
|
|
9
|
-
* are present in WizardApp state when GenerateReviewStep mounts.
|
|
10
|
-
*
|
|
11
|
-
* New order:
|
|
12
|
-
* welcome → token-input → path-validation → extracting → scope-gate
|
|
13
|
-
* → credentials (validates inline) → generating
|
|
14
|
-
* → generating → final-review → push-decision-gate → previewing
|
|
15
|
-
* → preview-gate → pushing → done
|
|
16
|
-
*
|
|
17
|
-
* `--no-push` short-circuits the credentials/preview/push branch — the operator
|
|
18
|
-
* just wants extract + classify + final-review without ever touching Contentful.
|
|
19
|
-
*/
|
|
20
1
|
export type WizardStepAfterScope = 'credentials' | 'generating' | 'push-decision-gate' | 'print-gate';
|
|
21
2
|
export type WizardStepAfterCredentials = 'generating' | 'push-decision-gate';
|
|
22
|
-
/**
|
|
23
|
-
* Transition target after the scope-gate confirms.
|
|
24
|
-
*
|
|
25
|
-
* - acceptedCount > 0:
|
|
26
|
-
* - !noPush → `credentials` (gather + validate creds upfront, before LLM cost)
|
|
27
|
-
* - noPush → `generating` (skip creds entirely; live preview no-ops gracefully)
|
|
28
|
-
* - acceptedCount === 0 (everything rejected):
|
|
29
|
-
* - !noPush → `credentials` (still need creds to push tokens / removals)
|
|
30
|
-
* - noPush → `print-gate` (nothing to do; let the operator save files and exit)
|
|
31
|
-
*/
|
|
32
3
|
export declare function nextStepAfterScopeGate(opts: {
|
|
33
4
|
acceptedCount: number;
|
|
34
5
|
noPush: boolean;
|
|
35
6
|
}): WizardStepAfterScope;
|
|
36
|
-
/**
|
|
37
|
-
* Transition target after credentials are validated (or skipped via the
|
|
38
|
-
* inline validation pings the API). Drives the post-credentials branch:
|
|
39
|
-
* - acceptedCount > 0 → `generating` (run the generator now that creds are good)
|
|
40
|
-
* - acceptedCount === 0 → `push-decision-gate` (no components to generate;
|
|
41
|
-
* skip straight to push-decision and preview)
|
|
42
|
-
*/
|
|
43
7
|
export declare function nextStepAfterCredentialsValidated(opts: {
|
|
44
8
|
acceptedCount: number;
|
|
45
9
|
}): WizardStepAfterCredentials;
|
|
@@ -48,38 +12,20 @@ export declare function shouldSkipFinalReviewAfterCredentials(state: {
|
|
|
48
12
|
finalReviewPassed: boolean;
|
|
49
13
|
}): boolean;
|
|
50
14
|
export declare function resolveNoCacheForGenerate(opts: {
|
|
51
|
-
isFreshSession: boolean;
|
|
52
15
|
cliNoCache: boolean;
|
|
53
16
|
}): boolean;
|
|
54
|
-
/**
|
|
55
|
-
* Skip-credentials spec — Task 2. When the operator pressed `s` on the
|
|
56
|
-
* credentials screen, the wizard advanced without validating creds. That
|
|
57
|
-
* makes `previewImport` impossible (no working token) and undesirable (the
|
|
58
|
-
* whole point is to inspect locally without touching the server). Callers
|
|
59
|
-
* check this before issuing the API call and route to
|
|
60
|
-
* `buildSkippedPreviewTransition()` instead.
|
|
61
|
-
*/
|
|
62
17
|
export declare function shouldBypassPreview(state: {
|
|
63
18
|
credentialsSkipped: boolean;
|
|
64
19
|
}): boolean;
|
|
65
|
-
/**
|
|
66
|
-
* State patch applied when `shouldBypassPreview` is true: skip the preview
|
|
67
|
-
* step entirely, surface no server preview to downstream consumers, and
|
|
68
|
-
* hand control to the push-decision-gate (which will render with push
|
|
69
|
-
* disabled — see Task 3).
|
|
70
|
-
*/
|
|
71
20
|
export declare function buildSkippedPreviewTransition(): {
|
|
72
21
|
step: 'push-decision-gate';
|
|
73
22
|
serverPreview: null;
|
|
74
23
|
};
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
* refuses to issue the API call — instead it routes back to the print-files
|
|
81
|
-
* local-save path via `buildSkippedPushTransition`.
|
|
82
|
-
*/
|
|
24
|
+
export type CycleGateAction = 'block' | 'auto-reject' | 'proceed';
|
|
25
|
+
export declare function resolveCycleGateAction(opts: {
|
|
26
|
+
hasCycles: boolean;
|
|
27
|
+
autoRejectCycles: boolean;
|
|
28
|
+
}): CycleGateAction;
|
|
83
29
|
export declare function shouldRefusePush(state: {
|
|
84
30
|
credentialsSkipped: boolean;
|
|
85
31
|
}): boolean;
|