@contentful/experience-design-system-cli 2.26.1-dev-build-a852362.0 → 2.26.1-dev-build-f03f014.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 +14 -10
- package/dist/src/index.js +34937 -9
- package/dist/src/lib/cli-path.d.ts +4 -0
- package/dist/src/runs/export-helpers.d.ts +1 -12
- package/dist/src/runs/push-helpers.d.ts +1 -6
- package/package.json +12 -7
- package/skills/generate-components.md +427 -0
- package/skills/generate-tokens.md +194 -0
- package/skills/select-components.md +224 -0
- package/dist/src/analytics/apply.js +0 -30
- package/dist/src/analytics/client.js +0 -81
- package/dist/src/analytics/constants.js +0 -4
- package/dist/src/analytics/env.js +0 -14
- package/dist/src/analytics/exit.js +0 -24
- package/dist/src/analytics/index.js +0 -9
- package/dist/src/analytics/normalize.js +0 -18
- package/dist/src/analytics/os.js +0 -13
- package/dist/src/analytics/session.js +0 -15
- package/dist/src/analytics/tracker.js +0 -126
- package/dist/src/analytics/types.js +0 -1
- package/dist/src/analyze/build-analyze-view-rows.js +0 -63
- package/dist/src/analyze/command.js +0 -599
- package/dist/src/analyze/composite-closure.js +0 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.js +0 -22
- package/dist/src/analyze/composition/agent-parser/extract-parser.js +0 -44
- package/dist/src/analyze/composition/agent-parser/load-prompt.js +0 -31
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +0 -88
- package/dist/src/analyze/composition/agent-parser/sandbox.js +0 -187
- package/dist/src/analyze/composition/apply-mapping.js +0 -67
- package/dist/src/analyze/composition/candidate-critic-agent.js +0 -39
- package/dist/src/analyze/composition/candidate-critic.js +0 -63
- package/dist/src/analyze/composition/candidate-files.js +0 -131
- package/dist/src/analyze/composition/composition-cache-key.js +0 -19
- package/dist/src/analyze/composition/interchange-schema.js +0 -61
- package/dist/src/analyze/composition/manifest-doc-evidence.js +0 -136
- package/dist/src/analyze/composition/merge-edges.js +0 -70
- package/dist/src/analyze/composition/parse-map-edges.js +0 -55
- package/dist/src/analyze/composition/resolve-mapping-cli.js +0 -29
- package/dist/src/analyze/composition/resolve-mapping.js +0 -111
- package/dist/src/analyze/cycle-detection.js +0 -234
- package/dist/src/analyze/cycle-view.js +0 -28
- package/dist/src/analyze/fuzzy-search.js +0 -55
- package/dist/src/analyze/issue-inheritance.js +0 -108
- package/dist/src/analyze/lineage.js +0 -0
- package/dist/src/analyze/scope-gate-cascade.js +0 -170
- package/dist/src/analyze/search-neighborhood.js +0 -56
- package/dist/src/analyze/select/command.js +0 -484
- package/dist/src/analyze/select/index.js +0 -5
- package/dist/src/analyze/select/parser.js +0 -53
- package/dist/src/analyze/select/persistence.js +0 -80
- package/dist/src/analyze/select/preview-annotations.js +0 -62
- package/dist/src/analyze/select/stdout.js +0 -3
- package/dist/src/analyze/select/tui/App.js +0 -483
- package/dist/src/analyze/select/tui/components/ComponentDetail.js +0 -48
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +0 -125
- package/dist/src/analyze/select/tui/components/FieldEditor.js +0 -1131
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +0 -36
- package/dist/src/analyze/select/tui/components/GotoBanner.js +0 -30
- package/dist/src/analyze/select/tui/components/GroupedSidebar.js +0 -517
- package/dist/src/analyze/select/tui/components/HelpOverlay.js +0 -17
- package/dist/src/analyze/select/tui/components/JsonEditor.js +0 -131
- package/dist/src/analyze/select/tui/components/JsonPanel.js +0 -66
- package/dist/src/analyze/select/tui/components/LineagePanel.js +0 -22
- package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +0 -29
- package/dist/src/analyze/select/tui/components/QuitDialog.js +0 -14
- package/dist/src/analyze/select/tui/components/RationalePanel.js +0 -80
- package/dist/src/analyze/select/tui/components/Sidebar.js +0 -92
- package/dist/src/analyze/select/tui/components/SourcePanel.js +0 -52
- package/dist/src/analyze/select/tui/components/StatusBar.js +0 -7
- package/dist/src/analyze/select/tui/components/TopBar.js +0 -10
- package/dist/src/analyze/select/tui/components/removed-components-text.js +0 -11
- package/dist/src/analyze/select/tui/hooks/scroll-offset.js +0 -26
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +0 -66
- package/dist/src/analyze/select/tui/hooks/useKeymap.js +0 -67
- package/dist/src/analyze/select/tui/hooks/useSession.js +0 -52
- package/dist/src/analyze/select/tui/hooks/useUndo.js +0 -26
- package/dist/src/analyze/select/tui/theme.js +0 -28
- package/dist/src/analyze/select/types.js +0 -39
- package/dist/src/analyze/select-agent/command.js +0 -558
- package/dist/src/analyze/select-agent/context-builder.js +0 -197
- package/dist/src/analyze/select-agent/show-rationale.js +0 -92
- package/dist/src/analyze/selection-cascade.js +0 -19
- package/dist/src/analyze/slot-graph.js +0 -17
- package/dist/src/analyze/tui/AnalyzeView.js +0 -64
- package/dist/src/apply/api-client.js +0 -440
- package/dist/src/apply/command.js +0 -845
- package/dist/src/apply/manifest.js +0 -1
- package/dist/src/apply/preview-utils.js +0 -26
- package/dist/src/apply/tui/SelectView.js +0 -34
- package/dist/src/apply/tui/ServerApplyView.js +0 -49
- package/dist/src/apply/tui/ServerPreviewView.js +0 -23
- package/dist/src/credentials-store.js +0 -74
- package/dist/src/generate/command.js +0 -597
- package/dist/src/generate/edit/command.js +0 -127
- package/dist/src/generate/tui/GenerateView.js +0 -10
- package/dist/src/host-utils.js +0 -26
- package/dist/src/import/agent-model-resolve.js +0 -35
- package/dist/src/import/auto-filter-resolve.js +0 -16
- package/dist/src/import/command.js +0 -380
- package/dist/src/import/cycle-auto-reject.js +0 -14
- package/dist/src/import/orchestrator.js +0 -603
- package/dist/src/import/path-utils.js +0 -30
- package/dist/src/import/picker-dispatch.js +0 -44
- package/dist/src/import/print-prompt.js +0 -31
- package/dist/src/import/strip-allowed-components.js +0 -22
- package/dist/src/import/tui/CustomPromptBanner.js +0 -8
- package/dist/src/import/tui/WizardApp.js +0 -1670
- package/dist/src/import/tui/ai-flag.js +0 -6
- package/dist/src/import/tui/auto-filter-error.js +0 -29
- package/dist/src/import/tui/autocomplete.js +0 -32
- package/dist/src/import/tui/components/AutoFilterBanner.js +0 -19
- package/dist/src/import/tui/components/CounterStrip.js +0 -12
- package/dist/src/import/tui/components/LegendEntry.js +0 -6
- package/dist/src/import/tui/cycle-panel-scroll.js +0 -33
- package/dist/src/import/tui/final-review-host.js +0 -25
- package/dist/src/import/tui/group-collapse.js +0 -11
- package/dist/src/import/tui/history.js +0 -53
- package/dist/src/import/tui/hooks/useLineage.js +0 -50
- package/dist/src/import/tui/hooks/useOverlayPanel.js +0 -27
- package/dist/src/import/tui/lineage-layout.js +0 -82
- package/dist/src/import/tui/merge-ai-decisions.js +0 -22
- package/dist/src/import/tui/push-decision-gate-helpers.js +0 -8
- package/dist/src/import/tui/push-progress.js +0 -1
- package/dist/src/import/tui/run-print-files-helpers.js +0 -8
- package/dist/src/import/tui/run-teaser.js +0 -9
- package/dist/src/import/tui/runLivePreview.js +0 -82
- package/dist/src/import/tui/runScopeGate.js +0 -28
- package/dist/src/import/tui/scope-gate-columns.js +0 -94
- package/dist/src/import/tui/scope-gate-host.js +0 -28
- package/dist/src/import/tui/sidebar-width.js +0 -3
- package/dist/src/import/tui/spawn-generate.js +0 -53
- package/dist/src/import/tui/step-filters.js +0 -42
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +0 -726
- package/dist/src/import/tui/steps/AtomicScopeGateStep.js +0 -210
- package/dist/src/import/tui/steps/CredentialsStep.js +0 -108
- package/dist/src/import/tui/steps/DoneStep.js +0 -24
- package/dist/src/import/tui/steps/ErrorStep.js +0 -16
- package/dist/src/import/tui/steps/GateStep.js +0 -22
- package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -1618
- package/dist/src/import/tui/steps/PathValidationStep.js +0 -152
- package/dist/src/import/tui/steps/PreviewStep.js +0 -37
- package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +0 -23
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +0 -73
- package/dist/src/import/tui/steps/PushingStep.js +0 -24
- package/dist/src/import/tui/steps/RunningStep.js +0 -21
- package/dist/src/import/tui/steps/ScopeGateStep.js +0 -907
- package/dist/src/import/tui/steps/TokenInputStep.js +0 -88
- package/dist/src/import/tui/steps/WelcomeStep.js +0 -34
- package/dist/src/import/tui/steps/WizardPreviewStep.js +0 -169
- package/dist/src/import/tui/steps/auto-reject-decision.js +0 -9
- package/dist/src/import/tui/steps/breaking-change-format.js +0 -24
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
- package/dist/src/import/tui/steps/preview-diff.js +0 -175
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.js +0 -122
- package/dist/src/import/tui/wizard-422-helpers.js +0 -60
- package/dist/src/import/tui/wizard-generate-progress.js +0 -25
- package/dist/src/import/tui/wizard-save-flow.js +0 -20
- package/dist/src/import/tui/wizard-state-transitions.js +0 -32
- package/dist/src/lib/agent-model-options.js +0 -15
- package/dist/src/lib/command-options.js +0 -30
- package/dist/src/lib/composition-mode.js +0 -39
- package/dist/src/lib/contentful-urls.js +0 -27
- package/dist/src/lib/debug-logger.js +0 -229
- package/dist/src/lib/debug-preamble.js +0 -33
- package/dist/src/lib/error-parser.js +0 -269
- package/dist/src/lib/prompt-overrides.js +0 -69
- package/dist/src/lib/terminal-capabilities.js +0 -51
- package/dist/src/lib/user-agent.js +0 -29
- package/dist/src/output/format.js +0 -110
- package/dist/src/print/command.js +0 -221
- package/dist/src/print/validate/tui/ValidateView.js +0 -37
- package/dist/src/print/validate/validators/cdf-validator.js +0 -104
- package/dist/src/print/validate/validators/dtcg-validator.js +0 -110
- package/dist/src/print/validate/validators/format-errors.js +0 -18
- package/dist/src/program.js +0 -105
- package/dist/src/runs/export-helpers.js +0 -59
- package/dist/src/runs/fingerprint.js +0 -75
- package/dist/src/runs/ls-command.js +0 -137
- package/dist/src/runs/modify-launcher.js +0 -42
- package/dist/src/runs/path-prompt.js +0 -72
- package/dist/src/runs/push-creds-prompt.js +0 -39
- package/dist/src/runs/push-helpers.js +0 -72
- package/dist/src/runs/push-launcher.js +0 -77
- package/dist/src/runs/replay-helpers.js +0 -164
- package/dist/src/runs/resolve-run-target.js +0 -45
- package/dist/src/runs/run-picker-mount.js +0 -72
- package/dist/src/runs/run-picker.js +0 -129
- package/dist/src/runs/save-conflict.js +0 -56
- package/dist/src/runs/save-path-resolver.js +0 -75
- package/dist/src/runs/staleness.js +0 -140
- package/dist/src/runs/store.js +0 -136
- package/dist/src/session/cache-keys.js +0 -44
- package/dist/src/session/command.js +0 -261
- package/dist/src/session/db.js +0 -1538
- package/dist/src/session/migration.js +0 -117
- package/dist/src/session/session-id.js +0 -212
- package/dist/src/session/stats.js +0 -89
- package/dist/src/setup/analytics-prompt.js +0 -24
- package/dist/src/setup/auto-filter-prompt.js +0 -24
- package/dist/src/setup/command.js +0 -966
- package/dist/src/setup/composition-mode-prompt.js +0 -37
- package/dist/src/setup/debug-mode-prompt.js +0 -22
- package/dist/src/types.js +0 -1
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
4
|
-
import { Box, Text } from 'ink';
|
|
5
|
-
import { statSync } from 'node:fs';
|
|
6
|
-
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
7
|
-
import { normalizePath } from '../../path-utils.js';
|
|
8
|
-
export function TokenInputStep({ onConfirm, onSkip, onQuit }) {
|
|
9
|
-
const [inputValue, setInputValue] = useState('');
|
|
10
|
-
const [cursorVisible, setCursorVisible] = useState(true);
|
|
11
|
-
const [error, setError] = useState(null);
|
|
12
|
-
const [resolvedPath, setResolvedPath] = useState(null);
|
|
13
|
-
const [typingMode, setTypingMode] = useState(false);
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
const interval = setInterval(() => setCursorVisible((v) => !v), 500);
|
|
16
|
-
return () => clearInterval(interval);
|
|
17
|
-
}, []);
|
|
18
|
-
useImmediateInput((input, key) => {
|
|
19
|
-
if (key.return) {
|
|
20
|
-
const trimmed = inputValue.trim();
|
|
21
|
-
if (!trimmed) {
|
|
22
|
-
onSkip();
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const normalized = normalizePath(trimmed);
|
|
26
|
-
let stat;
|
|
27
|
-
try {
|
|
28
|
-
stat = statSync(normalized);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
setError(`Path not found: ${normalized}`);
|
|
32
|
-
setResolvedPath(normalized);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (stat.isDirectory()) {
|
|
36
|
-
setError(`That's a directory — provide a path to a token file (e.g. tokens.json), not a folder.`);
|
|
37
|
-
setResolvedPath(normalized);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (!stat.isFile()) {
|
|
41
|
-
setError(`Not a regular file: ${normalized}`);
|
|
42
|
-
setResolvedPath(normalized);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
setError(null);
|
|
46
|
-
setResolvedPath(normalized);
|
|
47
|
-
onConfirm(normalized);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (key.tab) {
|
|
51
|
-
setTypingMode(false);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (key.escape) {
|
|
55
|
-
if (typingMode) {
|
|
56
|
-
setTypingMode(false);
|
|
57
|
-
setInputValue('');
|
|
58
|
-
setError(null);
|
|
59
|
-
setResolvedPath(null);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
onQuit();
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (!typingMode && input === 's') {
|
|
66
|
-
onSkip();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (!typingMode && input === 'q') {
|
|
70
|
-
onQuit();
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
if (key.backspace || key.delete) {
|
|
74
|
-
setInputValue((v) => v.slice(0, -1));
|
|
75
|
-
setError(null);
|
|
76
|
-
setResolvedPath(null);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (input && !key.ctrl && !key.meta) {
|
|
80
|
-
setTypingMode(true);
|
|
81
|
-
setInputValue((v) => v + input);
|
|
82
|
-
setError(null);
|
|
83
|
-
setResolvedPath(null);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
const displayValue = inputValue + (cursorVisible ? '█' : ' ');
|
|
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" })] })] }));
|
|
88
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
4
|
-
import { Box, Text } from 'ink';
|
|
5
|
-
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
6
|
-
export function WelcomeStep({ onContinue, onQuit }) {
|
|
7
|
-
const [inputValue, setInputValue] = useState('');
|
|
8
|
-
const [cursorVisible, setCursorVisible] = useState(true);
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const interval = setInterval(() => setCursorVisible((v) => !v), 500);
|
|
11
|
-
return () => clearInterval(interval);
|
|
12
|
-
}, []);
|
|
13
|
-
useImmediateInput((input, key) => {
|
|
14
|
-
if (key.return) {
|
|
15
|
-
const trimmed = inputValue.trim();
|
|
16
|
-
if (trimmed)
|
|
17
|
-
onContinue(trimmed);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (key.escape || input === 'q') {
|
|
21
|
-
onQuit();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (key.backspace || key.delete) {
|
|
25
|
-
setInputValue((v) => v.slice(0, -1));
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
if (input && !key.ctrl && !key.meta) {
|
|
29
|
-
setInputValue((v) => v + input);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
const displayValue = inputValue + (cursorVisible ? '█' : ' ');
|
|
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" })] })] }));
|
|
34
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useMemo } from 'react';
|
|
3
|
-
import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
4
|
-
import { Box, Text, useStdout } from 'ink';
|
|
5
|
-
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
6
|
-
import { hasBreakingChangesWithImpact } from '../../../apply/manifest.js';
|
|
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
|
-
}
|
|
92
|
-
export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber, totalSteps, allowDeletions: fetchedAllowDeletions = false, onConfirm, onEdit, onSaveFiles, onQuit, }) {
|
|
93
|
-
const breakingWithImpact = hasBreakingChangesWithImpact(preview);
|
|
94
|
-
const [diffExpanded, setDiffExpanded] = useState(false);
|
|
95
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
96
|
-
// Local state exists only to let the user opt OUT of a deletion the fetch
|
|
97
|
-
// already surfaced — it can never turn true when the fetch used false,
|
|
98
|
-
// because there's nothing in `preview` to reveal in that case.
|
|
99
|
-
const [allowDeletions, setAllowDeletions] = useState(fetchedAllowDeletions);
|
|
100
|
-
const { stdout } = useStdout();
|
|
101
|
-
const terminalRows = stdout?.rows ?? 40;
|
|
102
|
-
const viewportHeight = Math.max(terminalRows - 14, 10);
|
|
103
|
-
const allDiffLines = useMemo(() => {
|
|
104
|
-
if (!diffExpanded)
|
|
105
|
-
return [];
|
|
106
|
-
return buildPreviewDiffLines(preview).map((line) => ({
|
|
107
|
-
key: line.key,
|
|
108
|
-
element: (_jsx(Text, { color: line.color === 'gray' ? undefined : line.color, dimColor: line.color === 'gray', children: line.text })),
|
|
109
|
-
}));
|
|
110
|
-
}, [diffExpanded, preview]);
|
|
111
|
-
const maxScroll = Math.max(0, allDiffLines.length - viewportHeight);
|
|
112
|
-
const removedCount = preview.components.removed.length + preview.tokens.removed.length;
|
|
113
|
-
useImmediateInput((input, key) => {
|
|
114
|
-
if (key.return) {
|
|
115
|
-
onConfirm(breakingWithImpact, allowDeletions);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if ((input === 'x' || input === 'X') && fetchedAllowDeletions && removedCount > 0) {
|
|
119
|
-
setAllowDeletions((prev) => !prev);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if (input === 'd' || input === 'D') {
|
|
123
|
-
setDiffExpanded((prev) => !prev);
|
|
124
|
-
setScrollOffset(0);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (diffExpanded) {
|
|
128
|
-
if (input === 'j' || key.downArrow) {
|
|
129
|
-
setScrollOffset((prev) => Math.min(prev + 1, maxScroll));
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (input === 'k' || key.upArrow) {
|
|
133
|
-
setScrollOffset((prev) => Math.max(prev - 1, 0));
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (input === 'f') {
|
|
137
|
-
setScrollOffset((prev) => Math.min(prev + viewportHeight, maxScroll));
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
if (input === 'b') {
|
|
141
|
-
setScrollOffset((prev) => Math.max(prev - viewportHeight, 0));
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if ((input === 'e' || input === 'E') && onEdit) {
|
|
146
|
-
onEdit();
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (input === 's' && onSaveFiles) {
|
|
150
|
-
onSaveFiles();
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (input === 'q' || key.escape) {
|
|
154
|
-
onQuit();
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
const { components, tokens } = preview;
|
|
159
|
-
const hasComponents = components.new.length + components.changed.length + components.removed.length > 0;
|
|
160
|
-
const hasTokens = tokens.new.length + tokens.changed.length + tokens.removed.length > 0;
|
|
161
|
-
const hasAnything = hasComponents || hasTokens;
|
|
162
|
-
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) => {
|
|
163
|
-
const name = item.key ?? item.$name ?? 'unknown';
|
|
164
|
-
return (_jsxs(Text, { color: PALETTE.success, children: [' ', "+ ", name] }, `new-${i}`));
|
|
165
|
-
})] })), 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) => {
|
|
166
|
-
const isBreaking = item.changeClassification?.classification === 'breaking';
|
|
167
|
-
return (_jsxs(Text, { color: isBreaking ? PALETTE.error : PALETTE.warning, children: [' ', isBreaking ? '⚠' : '~', " ", item.current.name] }, `chg-${i}`));
|
|
168
|
-
})] })), components.removed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, children: allowDeletions ? ' ✗' : ' ⊘' }), _jsxs(Text, { children: [components.removed.length, " will be ", allowDeletions ? 'deleted' : 'skipped'] })] }), components.removed.map((item, i) => (_jsxs(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, dimColor: !allowDeletions, children: [' ', allowDeletions ? '✗' : '⊘', " ", 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: allowDeletions ? PALETTE.error : PALETTE.warning, children: allowDeletions ? ' ✗' : ' ⊘' }), _jsxs(Text, { children: [tokens.removed.length, " will be ", allowDeletions ? 'deleted' : 'skipped'] })] })), 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." }) })), allowDeletions && removedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: PALETTE.error, bold: true, children: ["\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] }) })), _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" }), fetchedAllowDeletions && removedCount > 0 && (_jsxs(Text, { dimColor: true, children: ["[x] ", allowDeletions ? '[✓]' : '[ ]', " Allow deletions"] })), _jsx(Text, { dimColor: true, children: "[q] Cancel" })] })] }));
|
|
169
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
}
|
|
Binary file
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
export function computeComponentDiffLines(current, proposed, changeClassification) {
|
|
2
|
-
const lines = [];
|
|
3
|
-
// Use fullProperties keys as authoritative server state (includes state props, not just content+design)
|
|
4
|
-
const currentProps = current.fullProperties
|
|
5
|
-
? new Set(Object.keys(current.fullProperties))
|
|
6
|
-
: new Set([...current.contentProperties, ...current.designProperties]);
|
|
7
|
-
const proposedProperties = (proposed['$properties'] ?? {});
|
|
8
|
-
const proposedProps = new Set(Object.keys(proposedProperties));
|
|
9
|
-
const oldProps = current.fullProperties;
|
|
10
|
-
for (const name of [...proposedProps].sort()) {
|
|
11
|
-
if (!currentProps.has(name)) {
|
|
12
|
-
const def = proposedProperties[name];
|
|
13
|
-
lines.push({ key: `${name}-add`, color: 'green', text: `+ ${name}: ${formatPropDef(def)}` });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
for (const name of [...currentProps].sort()) {
|
|
17
|
-
if (!proposedProps.has(name)) {
|
|
18
|
-
if (oldProps?.[name]) {
|
|
19
|
-
lines.push({ key: `${name}-rm`, color: 'red', text: `- ${name}: ${formatOldProp(oldProps[name])}` });
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
const cat = current.contentProperties.includes(name) ? 'content' : 'design';
|
|
23
|
-
lines.push({ key: `${name}-rm`, color: 'red', text: `- ${name} (${cat})` });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
const handledProps = new Set();
|
|
28
|
-
for (const name of [...currentProps].sort()) {
|
|
29
|
-
if (!proposedProps.has(name))
|
|
30
|
-
continue;
|
|
31
|
-
const newDef = proposedProperties[name];
|
|
32
|
-
const oldDef = oldProps?.[name];
|
|
33
|
-
if (!oldDef)
|
|
34
|
-
continue;
|
|
35
|
-
const diffs = diffProperty(name, oldDef, newDef);
|
|
36
|
-
if (diffs.length > 0) {
|
|
37
|
-
handledProps.add(name);
|
|
38
|
-
for (const d of diffs)
|
|
39
|
-
lines.push(d);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (!oldProps && changeClassification?.breakingChanges) {
|
|
43
|
-
for (const bc of changeClassification.breakingChanges) {
|
|
44
|
-
if (!('propertyId' in bc))
|
|
45
|
-
continue;
|
|
46
|
-
if (!currentProps.has(bc.propertyId) || !proposedProps.has(bc.propertyId))
|
|
47
|
-
continue;
|
|
48
|
-
if (handledProps.has(bc.propertyId))
|
|
49
|
-
continue;
|
|
50
|
-
handledProps.add(bc.propertyId);
|
|
51
|
-
const def = proposedProperties[bc.propertyId];
|
|
52
|
-
lines.push({ key: `${bc.propertyId}-old`, color: 'red', text: `- ${bc.propertyId}: ${bc.reason}` });
|
|
53
|
-
lines.push({ key: `${bc.propertyId}-new`, color: 'green', text: `+ ${bc.propertyId}: ${formatPropDef(def)}` });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const currentSlots = new Set(current.slots);
|
|
57
|
-
const proposedSlots = (proposed['$slots'] ?? {});
|
|
58
|
-
const proposedSlotNames = new Set(Object.keys(proposedSlots));
|
|
59
|
-
const currentSlotAllowed = current.currentSlotAllowed ?? {};
|
|
60
|
-
for (const name of [...proposedSlotNames].sort()) {
|
|
61
|
-
if (!currentSlots.has(name)) {
|
|
62
|
-
lines.push({ key: `slot-${name}-add`, color: 'green', text: `+ slot: ${name}` });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
for (const name of [...currentSlots].sort()) {
|
|
66
|
-
if (!proposedSlotNames.has(name)) {
|
|
67
|
-
lines.push({ key: `slot-${name}-rm`, color: 'red', text: `- slot: ${name}` });
|
|
68
|
-
}
|
|
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
|
-
}
|
|
97
|
-
return lines;
|
|
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
|
-
}
|
|
117
|
-
function normalizeType(type) {
|
|
118
|
-
switch (type.toLowerCase()) {
|
|
119
|
-
case 'boolean':
|
|
120
|
-
return 'boolean';
|
|
121
|
-
case 'number':
|
|
122
|
-
return 'number';
|
|
123
|
-
case 'symbol':
|
|
124
|
-
case 'enum':
|
|
125
|
-
return 'symbol';
|
|
126
|
-
case 'string':
|
|
127
|
-
case 'text':
|
|
128
|
-
case 'richtext':
|
|
129
|
-
case 'media':
|
|
130
|
-
return 'string';
|
|
131
|
-
default:
|
|
132
|
-
return type.toLowerCase();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
export function diffProperty(name, old, newDef) {
|
|
136
|
-
const diffs = [];
|
|
137
|
-
const newType = newDef['$type'] ?? '';
|
|
138
|
-
const newCategory = newDef['$category'] ?? '';
|
|
139
|
-
const newRequired = newDef['$required'] === true;
|
|
140
|
-
const newDefault = newDef['$default'];
|
|
141
|
-
const typeChanged = old.type !== '' && newType !== '' && normalizeType(old.type) !== normalizeType(newType);
|
|
142
|
-
const categoryChanged = old.category !== newCategory;
|
|
143
|
-
const requiredChanged = old.required !== newRequired;
|
|
144
|
-
const defaultChanged = JSON.stringify(old.default) !== JSON.stringify(newDefault);
|
|
145
|
-
if (!typeChanged && !categoryChanged && !requiredChanged && !defaultChanged)
|
|
146
|
-
return [];
|
|
147
|
-
diffs.push({ key: `${name}-old`, color: 'red', text: `- ${name}: ${formatOldProp(old)}` });
|
|
148
|
-
diffs.push({ key: `${name}-new`, color: 'green', text: `+ ${name}: ${formatPropDef(newDef)}` });
|
|
149
|
-
return diffs;
|
|
150
|
-
}
|
|
151
|
-
export function formatOldProp(prop) {
|
|
152
|
-
const parts = [];
|
|
153
|
-
if (prop.type)
|
|
154
|
-
parts.push(prop.type);
|
|
155
|
-
if (prop.category)
|
|
156
|
-
parts.push(prop.category);
|
|
157
|
-
parts.push(prop.required ? 'required' : 'optional');
|
|
158
|
-
if (prop.default !== undefined)
|
|
159
|
-
parts.push(`default=${JSON.stringify(prop.default)}`);
|
|
160
|
-
return parts.join(', ');
|
|
161
|
-
}
|
|
162
|
-
export function formatPropDef(def) {
|
|
163
|
-
const parts = [];
|
|
164
|
-
if (def['$type'])
|
|
165
|
-
parts.push(def['$type']);
|
|
166
|
-
if (def['$category'])
|
|
167
|
-
parts.push(def['$category']);
|
|
168
|
-
if (def['$required'] === true)
|
|
169
|
-
parts.push('required');
|
|
170
|
-
else
|
|
171
|
-
parts.push('optional');
|
|
172
|
-
if (def['$default'] !== undefined)
|
|
173
|
-
parts.push(`default=${JSON.stringify(def['$default'])}`);
|
|
174
|
-
return parts.join(', ');
|
|
175
|
-
}
|
|
Binary file
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { ApiError } from '../../apply/api-client.js';
|
|
3
|
-
import { runLivePreview } from './runLivePreview.js';
|
|
4
|
-
const DEFAULT_DEBOUNCE_MS = 500;
|
|
5
|
-
/**
|
|
6
|
-
* Hook that owns the post-save live-preview side effect for the wizard's
|
|
7
|
-
* final-review step. Centralizes:
|
|
8
|
-
*
|
|
9
|
-
* - 500ms debounce so rapid Ctrl+S spam collapses to a single API call.
|
|
10
|
-
* - Generation tag on every fire so a stale in-flight call's response is
|
|
11
|
-
* discarded if a newer fire has already kicked off (in lieu of an
|
|
12
|
-
* AbortController for v1).
|
|
13
|
-
* - Disable-for-session on 401/403: subsequent triggers no-op until the
|
|
14
|
-
* wizard exits. One-line stderr warning. No modal, no re-prompt — the
|
|
15
|
-
* operator is mid-edit and we must not interrupt the flow.
|
|
16
|
-
* - Status state for the sidebar spinner (`'idle' | 'running'`).
|
|
17
|
-
*
|
|
18
|
-
* Cred-missing / disabled / not-enabled cases short-circuit BEFORE the timer
|
|
19
|
-
* is set so we don't accumulate dead timers.
|
|
20
|
-
*/
|
|
21
|
-
export function useLivePreview(opts) {
|
|
22
|
-
const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
23
|
-
const [status, setStatus] = useState('idle');
|
|
24
|
-
const [disabled, setDisabled] = useState(false);
|
|
25
|
-
const timerRef = useRef(null);
|
|
26
|
-
const generationRef = useRef(0);
|
|
27
|
-
const latestRef = useRef(0);
|
|
28
|
-
const inflightRef = useRef(0);
|
|
29
|
-
// Mirror props in a ref so the trigger callback can stay stable.
|
|
30
|
-
const optsRef = useRef(opts);
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
optsRef.current = opts;
|
|
33
|
-
});
|
|
34
|
-
// Mirror disabled into a ref so the timer callback sees the latest value
|
|
35
|
-
// without redefining `trigger` (which would defeat its memoization).
|
|
36
|
-
const disabledRef = useRef(disabled);
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
disabledRef.current = disabled;
|
|
39
|
-
}, [disabled]);
|
|
40
|
-
// Clear any pending timer on unmount.
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
return () => {
|
|
43
|
-
if (timerRef.current) {
|
|
44
|
-
clearTimeout(timerRef.current);
|
|
45
|
-
timerRef.current = null;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}, []);
|
|
49
|
-
const trigger = useCallback(() => {
|
|
50
|
-
const current = optsRef.current;
|
|
51
|
-
if (!current.enabled)
|
|
52
|
-
return;
|
|
53
|
-
if (disabledRef.current)
|
|
54
|
-
return;
|
|
55
|
-
if (!current.spaceId || !current.environmentId || !current.cmaToken)
|
|
56
|
-
return;
|
|
57
|
-
if (timerRef.current) {
|
|
58
|
-
clearTimeout(timerRef.current);
|
|
59
|
-
timerRef.current = null;
|
|
60
|
-
}
|
|
61
|
-
timerRef.current = setTimeout(() => {
|
|
62
|
-
timerRef.current = null;
|
|
63
|
-
void fire();
|
|
64
|
-
}, debounceMs);
|
|
65
|
-
}, [debounceMs]);
|
|
66
|
-
const fire = async () => {
|
|
67
|
-
const current = optsRef.current;
|
|
68
|
-
if (disabledRef.current)
|
|
69
|
-
return;
|
|
70
|
-
generationRef.current += 1;
|
|
71
|
-
const generation = generationRef.current;
|
|
72
|
-
latestRef.current = generation;
|
|
73
|
-
inflightRef.current += 1;
|
|
74
|
-
setStatus('running');
|
|
75
|
-
try {
|
|
76
|
-
const result = await runLivePreview({
|
|
77
|
-
sessionId: current.sessionId,
|
|
78
|
-
tokensPath: current.tokensPath,
|
|
79
|
-
spaceId: current.spaceId,
|
|
80
|
-
environmentId: current.environmentId,
|
|
81
|
-
cmaToken: current.cmaToken,
|
|
82
|
-
host: current.host,
|
|
83
|
-
generation,
|
|
84
|
-
deleteAllComponents: current.deleteAllComponents === true,
|
|
85
|
-
allowDeletions: current.allowDeletions === true,
|
|
86
|
-
});
|
|
87
|
-
// Discard stale responses (generation tag).
|
|
88
|
-
if (result.generation !== latestRef.current)
|
|
89
|
-
return;
|
|
90
|
-
current.onResult(result.response);
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
|
|
94
|
-
setDisabled(true);
|
|
95
|
-
disabledRef.current = true;
|
|
96
|
-
try {
|
|
97
|
-
process.stderr.write(`live-preview: ${err.status}, disabling for this session\n`);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// best-effort
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
105
|
-
try {
|
|
106
|
-
process.stderr.write(`live-preview: ${msg}\n`);
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
// best-effort
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
|
-
inflightRef.current -= 1;
|
|
115
|
-
if (inflightRef.current <= 0) {
|
|
116
|
-
inflightRef.current = 0;
|
|
117
|
-
setStatus('idle');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
return { trigger, status, disabled };
|
|
122
|
-
}
|