@contentful/experience-design-system-cli 2.37.1-dev-build-40c29bf0.0 → 2.37.1-dev-build-444a2d6a.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/app.js CHANGED
@@ -7544,7 +7544,7 @@ function buildVisibleRows(props) {
7544
7544
  if (visited.has(target)) continue;
7545
7545
  visited.add(target);
7546
7546
  const isCycleMember = cycleParticipants.has(target);
7547
- out.push({ name: target, depth: cur.depth + 1, isCycleMember });
7547
+ out.push({ name: target, depth: cur.depth + 1, isCycleMember, parent: cur.name });
7548
7548
  queue.push({ name: target, depth: cur.depth + 1 });
7549
7549
  }
7550
7550
  }
@@ -7591,7 +7591,8 @@ function buildVisibleRows(props) {
7591
7591
  sharedSuffix,
7592
7592
  cycleChild: child.isCycleMember || void 0,
7593
7593
  itemIdx: childRec?.idx ?? -1,
7594
- rootName: key
7594
+ rootName: key,
7595
+ parentName: child.parent
7595
7596
  });
7596
7597
  });
7597
7598
  }
@@ -7674,13 +7675,19 @@ function buildVisibleRows(props) {
7674
7675
  injectedChildren.push({
7675
7676
  name: target,
7676
7677
  depth: parentDepth + 1,
7677
- isCycleChild: true
7678
+ isCycleChild: true,
7679
+ parent: parentName
7678
7680
  });
7679
7681
  }
7680
7682
  };
7681
7683
  emitCycleChildrenOf(root, 0);
7682
7684
  for (const c2 of closureChildren) {
7683
- injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
7685
+ injectedChildren.push({
7686
+ name: c2.name,
7687
+ depth: c2.depth,
7688
+ isCycleChild: false,
7689
+ parent: c2.path[c2.path.length - 2] ?? root
7690
+ });
7684
7691
  emitCycleChildrenOf(c2.name, c2.depth);
7685
7692
  }
7686
7693
  const isLastAtDepth = (idx) => {
@@ -7714,7 +7721,8 @@ function buildVisibleRows(props) {
7714
7721
  sharedSuffix,
7715
7722
  cycleChild: child.isCycleChild || void 0,
7716
7723
  itemIdx: childRec?.idx ?? -1,
7717
- rootName: root
7724
+ rootName: root,
7725
+ parentName: child.parent
7718
7726
  });
7719
7727
  });
7720
7728
  }
@@ -7838,6 +7846,7 @@ function GroupedSidebar(props) {
7838
7846
  showFlatTier,
7839
7847
  viewMode,
7840
7848
  selectionStateByKey,
7849
+ excludedSlotEdges,
7841
7850
  aiFlaggedByKey,
7842
7851
  dimPredicate,
7843
7852
  visibleRows: providedRows,
@@ -7867,8 +7876,9 @@ function GroupedSidebar(props) {
7867
7876
  const rs = itemName ? renderStatusByKey?.get(itemName) : void 0;
7868
7877
  const badge = itemName ? previewBadge(previewAnnotationByKey?.get(itemName)) : null;
7869
7878
  const showInheritance = rs !== void 0 && row.kind !== "cycle" && row.kind !== "empty" && row.kind !== "group-root";
7870
- const supportsSelectionGlyph = selectionStateByKey !== void 0 && itemName !== void 0 && (row.kind === "standalone" || row.kind === "group-root" || row.kind === "group-child" || row.kind === "flat" || row.kind === "cycle");
7871
- const selState = supportsSelectionGlyph ? selectionStateByKey.get(itemName) ?? "undecided" : void 0;
7879
+ const supportsSelectionGlyph = selectionStateByKey !== void 0 && itemName !== void 0 && (row.kind === "standalone" || row.kind === "group-root" || row.kind === "group-child" || row.kind === "flat" || row.kind === "empty" || row.kind === "cycle");
7880
+ const edgeExcluded = row.kind === "group-child" && row.parentName !== void 0 && itemName !== void 0 && excludedSlotEdges?.some((edge) => edge.parent === row.parentName && edge.child === itemName) === true;
7881
+ const selState = supportsSelectionGlyph ? edgeExcluded ? "undecided" : selectionStateByKey.get(itemName) ?? "undecided" : void 0;
7872
7882
  const isCycleRow = row.kind === "cycle";
7873
7883
  const selStyle = selectionGlyphStyleFor(selState, isCycleRow);
7874
7884
  const selGlyph = selStyle.glyph;
@@ -7897,7 +7907,7 @@ function GroupedSidebar(props) {
7897
7907
  color: isSelected ? PALETTE2.info : labelStyle.dim ? PALETTE2.muted : labelStyle.color,
7898
7908
  bold: isSelected || labelStyle.bold,
7899
7909
  inverse: false,
7900
- underline: isSelected && !focused,
7910
+ underline: isSelected && focused,
7901
7911
  wrap: wrapLabels ? "wrap" : "truncate",
7902
7912
  children: [
7903
7913
  " ",
@@ -8799,14 +8809,20 @@ function rejectNeedsConfirmation(graph, name, state) {
8799
8809
  const ancestors = toReject.filter((n) => n !== name);
8800
8810
  return ancestors.length + toDeselect.length >= 2;
8801
8811
  }
8802
- function partitionDecisions(components, state) {
8812
+ function withEdgeExcluded(edges, edge, excluded) {
8813
+ const others = edges.filter((e) => e.parent !== edge.parent || e.child !== edge.child);
8814
+ return excluded ? [...others, edge] : others;
8815
+ }
8816
+ function partitionDecisions(components, state, excludedEdges) {
8803
8817
  const accepted = [];
8804
8818
  const rejected = [];
8805
8819
  for (const c2 of components) {
8806
8820
  if (state.get(c2.name) === "accepted") accepted.push(c2.name);
8807
8821
  else rejected.push(c2.name);
8808
8822
  }
8809
- return { accepted, rejected };
8823
+ const rejectedNames = new Set(rejected);
8824
+ const excludedSlotEdges = excludedEdges.filter((e) => !rejectedNames.has(e.parent) && !rejectedNames.has(e.child));
8825
+ return { accepted, rejected, excludedSlotEdges };
8810
8826
  }
8811
8827
 
8812
8828
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/use-scope-gate.ts
@@ -8814,6 +8830,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
8814
8830
  const { columns: totalWidth, rows: terminalRows } = useTerminalSize();
8815
8831
  const columnPlan = useMemo2(() => computeColumnWidths(totalWidth), [totalWidth]);
8816
8832
  const [decisions, setDecisions] = useState12(/* @__PURE__ */ new Map());
8833
+ const [excludedEdges, setExcludedEdges] = useState12([]);
8817
8834
  const [nav, setNav] = useState12({ cursor: 0, scrollOffset: 0 });
8818
8835
  const [focusedColumn, setFocusedColumn] = useState12("main");
8819
8836
  const [addedComponentsCursor, setAddedComponentsCursor] = useState12(0);
@@ -8985,6 +9002,13 @@ function useScopeGate({ components, onConfirm, onQuit }) {
8985
9002
  return void 0;
8986
9003
  }
8987
9004
  };
9005
+ const focusedNestedEdge = () => {
9006
+ const row = visibleRows[safeCursor];
9007
+ if (row?.kind !== "group-child" || !row.parentName || row.itemIdx < 0) return void 0;
9008
+ const child = groupedItems[row.itemIdx]?.key;
9009
+ return child ? { parent: row.parentName, child } : void 0;
9010
+ };
9011
+ const setEdgeExcluded = (edge, excluded) => setExcludedEdges((prev) => withEdgeExcluded(prev, edge, excluded));
8988
9012
  const jumpCursorTo = (name) => {
8989
9013
  let idx = -1;
8990
9014
  for (let i = 0; i < visibleRows.length; i++) {
@@ -9084,7 +9108,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
9084
9108
  onQuit();
9085
9109
  return;
9086
9110
  case "confirm":
9087
- onConfirm(partitionDecisions(components, decisions));
9111
+ onConfirm(partitionDecisions(components, decisions, excludedEdges));
9088
9112
  return;
9089
9113
  case "close-help":
9090
9114
  setShowHelp(false);
@@ -9132,11 +9156,18 @@ function useScopeGate({ components, onConfirm, onQuit }) {
9132
9156
  }
9133
9157
  return;
9134
9158
  case "accept": {
9159
+ const edge = focusedNestedEdge();
9160
+ if (edge) setEdgeExcluded(edge, false);
9135
9161
  const key = focusedRowKey();
9136
9162
  if (key) decide({ type: "accept", name: key });
9137
9163
  return;
9138
9164
  }
9139
9165
  case "reject": {
9166
+ const edge = focusedColumn === "main" ? focusedNestedEdge() : void 0;
9167
+ if (edge) {
9168
+ setEdgeExcluded(edge, true);
9169
+ return;
9170
+ }
9140
9171
  const key = focusedColumn === "added-components" ? addedComponents[safeAddedComponentsCursor]?.name : focusedRowKey();
9141
9172
  if (key) requestReject(key);
9142
9173
  return;
@@ -9238,6 +9269,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
9238
9269
  visibleCount,
9239
9270
  panelMaxRows,
9240
9271
  // sidebar
9272
+ excludedEdges,
9241
9273
  groupedItems,
9242
9274
  graph,
9243
9275
  hasGroupRoots,
@@ -9363,6 +9395,7 @@ function ScopeGateView({ components, onConfirm, onQuit }) {
9363
9395
  visibleCount: gate.visibleCount,
9364
9396
  showFlatTier: false,
9365
9397
  selectionStateByKey: gate.selectionStateByKey,
9398
+ excludedSlotEdges: gate.excludedEdges,
9366
9399
  aiFlaggedByKey: gate.aiFlaggedByKey,
9367
9400
  dimPredicate: gate.dimPredicate,
9368
9401
  visibleRows: gate.visibleRows,
@@ -9542,6 +9575,8 @@ var ScopeSchema = z5.object({
9542
9575
  accepted: z5.array(z5.string()),
9543
9576
  /** Everything else at the gate, which counts as rejected. */
9544
9577
  rejected: z5.array(z5.string()),
9578
+ /** Slot edges the user removed at the gate. The child stays accepted; only that parent stops allowing it. */
9579
+ excludedSlotEdges: z5._default(z5.array(z5.object({ parent: z5.string(), child: z5.string() })), []),
9545
9580
  /**
9546
9581
  * Components the generation cache already holds, found by the check that ran while the gate was open and which has
9547
9582
  * finished (and restored them) before the wizard starts. Empty when the cache is off or the check failed.
package/dist/index.js CHANGED
@@ -10294,7 +10294,7 @@ function buildVisibleRows(props) {
10294
10294
  if (visited.has(target)) continue;
10295
10295
  visited.add(target);
10296
10296
  const isCycleMember = cycleParticipants.has(target);
10297
- out.push({ name: target, depth: cur.depth + 1, isCycleMember });
10297
+ out.push({ name: target, depth: cur.depth + 1, isCycleMember, parent: cur.name });
10298
10298
  queue.push({ name: target, depth: cur.depth + 1 });
10299
10299
  }
10300
10300
  }
@@ -10341,7 +10341,8 @@ function buildVisibleRows(props) {
10341
10341
  sharedSuffix,
10342
10342
  cycleChild: child.isCycleMember || void 0,
10343
10343
  itemIdx: childRec?.idx ?? -1,
10344
- rootName: key
10344
+ rootName: key,
10345
+ parentName: child.parent
10345
10346
  });
10346
10347
  });
10347
10348
  }
@@ -10424,13 +10425,19 @@ function buildVisibleRows(props) {
10424
10425
  injectedChildren.push({
10425
10426
  name: target,
10426
10427
  depth: parentDepth + 1,
10427
- isCycleChild: true
10428
+ isCycleChild: true,
10429
+ parent: parentName
10428
10430
  });
10429
10431
  }
10430
10432
  };
10431
10433
  emitCycleChildrenOf(root, 0);
10432
10434
  for (const c2 of closureChildren) {
10433
- injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
10435
+ injectedChildren.push({
10436
+ name: c2.name,
10437
+ depth: c2.depth,
10438
+ isCycleChild: false,
10439
+ parent: c2.path[c2.path.length - 2] ?? root
10440
+ });
10434
10441
  emitCycleChildrenOf(c2.name, c2.depth);
10435
10442
  }
10436
10443
  const isLastAtDepth = (idx) => {
@@ -10464,7 +10471,8 @@ function buildVisibleRows(props) {
10464
10471
  sharedSuffix,
10465
10472
  cycleChild: child.isCycleChild || void 0,
10466
10473
  itemIdx: childRec?.idx ?? -1,
10467
- rootName: root
10474
+ rootName: root,
10475
+ parentName: child.parent
10468
10476
  });
10469
10477
  });
10470
10478
  }
@@ -10588,6 +10596,7 @@ function GroupedSidebar(props) {
10588
10596
  showFlatTier,
10589
10597
  viewMode,
10590
10598
  selectionStateByKey,
10599
+ excludedSlotEdges,
10591
10600
  aiFlaggedByKey,
10592
10601
  dimPredicate,
10593
10602
  visibleRows: providedRows,
@@ -10617,8 +10626,9 @@ function GroupedSidebar(props) {
10617
10626
  const rs = itemName ? renderStatusByKey?.get(itemName) : void 0;
10618
10627
  const badge = itemName ? previewBadge(previewAnnotationByKey?.get(itemName)) : null;
10619
10628
  const showInheritance = rs !== void 0 && row.kind !== "cycle" && row.kind !== "empty" && row.kind !== "group-root";
10620
- const supportsSelectionGlyph = selectionStateByKey !== void 0 && itemName !== void 0 && (row.kind === "standalone" || row.kind === "group-root" || row.kind === "group-child" || row.kind === "flat" || row.kind === "cycle");
10621
- const selState = supportsSelectionGlyph ? selectionStateByKey.get(itemName) ?? "undecided" : void 0;
10629
+ const supportsSelectionGlyph = selectionStateByKey !== void 0 && itemName !== void 0 && (row.kind === "standalone" || row.kind === "group-root" || row.kind === "group-child" || row.kind === "flat" || row.kind === "empty" || row.kind === "cycle");
10630
+ const edgeExcluded = row.kind === "group-child" && row.parentName !== void 0 && itemName !== void 0 && excludedSlotEdges?.some((edge) => edge.parent === row.parentName && edge.child === itemName) === true;
10631
+ const selState = supportsSelectionGlyph ? edgeExcluded ? "undecided" : selectionStateByKey.get(itemName) ?? "undecided" : void 0;
10622
10632
  const isCycleRow = row.kind === "cycle";
10623
10633
  const selStyle = selectionGlyphStyleFor(selState, isCycleRow);
10624
10634
  const selGlyph = selStyle.glyph;
@@ -10647,7 +10657,7 @@ function GroupedSidebar(props) {
10647
10657
  color: isSelected ? PALETTE2.info : labelStyle.dim ? PALETTE2.muted : labelStyle.color,
10648
10658
  bold: isSelected || labelStyle.bold,
10649
10659
  inverse: false,
10650
- underline: isSelected && !focused,
10660
+ underline: isSelected && focused,
10651
10661
  wrap: wrapLabels ? "wrap" : "truncate",
10652
10662
  children: [
10653
10663
  " ",
@@ -11681,14 +11691,20 @@ function rejectNeedsConfirmation(graph, name, state) {
11681
11691
  const ancestors = toReject.filter((n) => n !== name);
11682
11692
  return ancestors.length + toDeselect.length >= 2;
11683
11693
  }
11684
- function partitionDecisions(components, state) {
11694
+ function withEdgeExcluded(edges, edge, excluded) {
11695
+ const others = edges.filter((e) => e.parent !== edge.parent || e.child !== edge.child);
11696
+ return excluded ? [...others, edge] : others;
11697
+ }
11698
+ function partitionDecisions(components, state, excludedEdges) {
11685
11699
  const accepted = [];
11686
11700
  const rejected = [];
11687
11701
  for (const c2 of components) {
11688
11702
  if (state.get(c2.name) === "accepted") accepted.push(c2.name);
11689
11703
  else rejected.push(c2.name);
11690
11704
  }
11691
- return { accepted, rejected };
11705
+ const rejectedNames = new Set(rejected);
11706
+ const excludedSlotEdges = excludedEdges.filter((e) => !rejectedNames.has(e.parent) && !rejectedNames.has(e.child));
11707
+ return { accepted, rejected, excludedSlotEdges };
11692
11708
  }
11693
11709
  var init_state = __esm({
11694
11710
  "packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/state.ts"() {
@@ -11704,6 +11720,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
11704
11720
  const { columns: totalWidth, rows: terminalRows } = useTerminalSize();
11705
11721
  const columnPlan = useMemo2(() => computeColumnWidths(totalWidth), [totalWidth]);
11706
11722
  const [decisions, setDecisions] = useState12(/* @__PURE__ */ new Map());
11723
+ const [excludedEdges, setExcludedEdges] = useState12([]);
11707
11724
  const [nav, setNav] = useState12({ cursor: 0, scrollOffset: 0 });
11708
11725
  const [focusedColumn, setFocusedColumn] = useState12("main");
11709
11726
  const [addedComponentsCursor, setAddedComponentsCursor] = useState12(0);
@@ -11875,6 +11892,13 @@ function useScopeGate({ components, onConfirm, onQuit }) {
11875
11892
  return void 0;
11876
11893
  }
11877
11894
  };
11895
+ const focusedNestedEdge = () => {
11896
+ const row = visibleRows[safeCursor];
11897
+ if (row?.kind !== "group-child" || !row.parentName || row.itemIdx < 0) return void 0;
11898
+ const child = groupedItems[row.itemIdx]?.key;
11899
+ return child ? { parent: row.parentName, child } : void 0;
11900
+ };
11901
+ const setEdgeExcluded = (edge, excluded) => setExcludedEdges((prev) => withEdgeExcluded(prev, edge, excluded));
11878
11902
  const jumpCursorTo = (name) => {
11879
11903
  let idx = -1;
11880
11904
  for (let i = 0; i < visibleRows.length; i++) {
@@ -11974,7 +11998,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
11974
11998
  onQuit();
11975
11999
  return;
11976
12000
  case "confirm":
11977
- onConfirm(partitionDecisions(components, decisions));
12001
+ onConfirm(partitionDecisions(components, decisions, excludedEdges));
11978
12002
  return;
11979
12003
  case "close-help":
11980
12004
  setShowHelp(false);
@@ -12022,11 +12046,18 @@ function useScopeGate({ components, onConfirm, onQuit }) {
12022
12046
  }
12023
12047
  return;
12024
12048
  case "accept": {
12049
+ const edge = focusedNestedEdge();
12050
+ if (edge) setEdgeExcluded(edge, false);
12025
12051
  const key = focusedRowKey();
12026
12052
  if (key) decide({ type: "accept", name: key });
12027
12053
  return;
12028
12054
  }
12029
12055
  case "reject": {
12056
+ const edge = focusedColumn === "main" ? focusedNestedEdge() : void 0;
12057
+ if (edge) {
12058
+ setEdgeExcluded(edge, true);
12059
+ return;
12060
+ }
12030
12061
  const key = focusedColumn === "added-components" ? addedComponents[safeAddedComponentsCursor]?.name : focusedRowKey();
12031
12062
  if (key) requestReject(key);
12032
12063
  return;
@@ -12128,6 +12159,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
12128
12159
  visibleCount,
12129
12160
  panelMaxRows,
12130
12161
  // sidebar
12162
+ excludedEdges,
12131
12163
  groupedItems,
12132
12164
  graph,
12133
12165
  hasGroupRoots,
@@ -12240,6 +12272,7 @@ function ScopeGateView({ components, onConfirm, onQuit }) {
12240
12272
  visibleCount: gate.visibleCount,
12241
12273
  showFlatTier: false,
12242
12274
  selectionStateByKey: gate.selectionStateByKey,
12275
+ excludedSlotEdges: gate.excludedEdges,
12243
12276
  aiFlaggedByKey: gate.aiFlaggedByKey,
12244
12277
  dimPredicate: gate.dimPredicate,
12245
12278
  visibleRows: gate.visibleRows,
@@ -12488,6 +12521,8 @@ var init_handoff = __esm({
12488
12521
  accepted: z5.array(z5.string()),
12489
12522
  /** Everything else at the gate, which counts as rejected. */
12490
12523
  rejected: z5.array(z5.string()),
12524
+ /** Slot edges the user removed at the gate. The child stays accepted; only that parent stops allowing it. */
12525
+ excludedSlotEdges: z5._default(z5.array(z5.object({ parent: z5.string(), child: z5.string() })), []),
12491
12526
  /**
12492
12527
  * Components the generation cache already holds, found by the check that ran while the gate was open and which has
12493
12528
  * finished (and restored them) before the wizard starts. Empty when the cache is off or the check failed.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-40c29bf0.0",
3
+ "version": "2.37.1-dev-build-444a2d6a.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { CDFComponentEntry } from '@contentful/experience-design-system-types';
3
3
  import { type ComponentGraphNode, type NodeStatus } from '../../graph/composite-closure.js';
4
+ import type { ParentChildEdge } from '../../types.js';
4
5
  import type { PreviewAnnotation, RenderStatus } from '../types.js';
5
6
  export interface GroupedSidebarItem {
6
7
  key: string;
@@ -27,6 +28,8 @@ export interface GroupedSidebarProps {
27
28
  alwaysExpanded?: boolean;
28
29
  showFlatTier?: boolean;
29
30
  selectionStateByKey?: Map<string, 'accepted' | 'rejected' | 'undecided'>;
31
+ /** Slot edges the user removed; their nested rows render unchecked. */
32
+ excludedSlotEdges?: ParentChildEdge[];
30
33
  aiFlaggedByKey?: Map<string, boolean>;
31
34
  dimPredicate?: (componentKey: string) => boolean;
32
35
  viewMode?: 'grouped' | 'flat';
@@ -45,6 +48,8 @@ export interface VisibleRow {
45
48
  cycleChild?: boolean;
46
49
  itemIdx: number;
47
50
  rootName?: string;
51
+ /** The parent whose slot the nested row sits in. */
52
+ parentName?: string;
48
53
  }
49
54
  export declare function buildVisibleRows(props: {
50
55
  items: GroupedSidebarItem[];
@@ -1,5 +1,5 @@
1
1
  import { type ScopeGraph } from './graph/index.js';
2
- import type { Decision, ScopeComponent, ScopeDecisions } from './types.js';
2
+ import type { Decision, ParentChildEdge, ScopeComponent, ScopeDecisions } from './types.js';
3
3
  export type DecisionState = Map<string, Decision>;
4
4
  export type DecisionAction = {
5
5
  type: 'accept';
@@ -15,5 +15,6 @@ export type DecisionAction = {
15
15
  export declare function reduceDecisions(state: DecisionState, action: DecisionAction, components: ScopeComponent[], graph: ScopeGraph): DecisionState;
16
16
  /** Whether rejecting `name` touches 2 or more other components, so the screen has to ask first. */
17
17
  export declare function rejectNeedsConfirmation(graph: ScopeGraph, name: string, state: DecisionState): boolean;
18
+ export declare function withEdgeExcluded(edges: ParentChildEdge[], edge: ParentChildEdge, excluded: boolean): ParentChildEdge[];
18
19
  /** Everything not accepted counts as rejected, as legacy's `partition` did. */
19
- export declare function partitionDecisions(components: ScopeComponent[], state: DecisionState): ScopeDecisions;
20
+ export declare function partitionDecisions(components: ScopeComponent[], state: DecisionState, excludedEdges: ParentChildEdge[]): ScopeDecisions;
@@ -10,10 +10,17 @@ export type ScopeComponent = Omit<ScopeComponentRow, 'aiDecision' | 'aiReason' |
10
10
  slots?: ScopeComponentRow['slots'];
11
11
  };
12
12
  export type Decision = 'accepted' | 'rejected' | 'undecided';
13
+ /** One slot edge: `parent` allows `child` in a slot. */
14
+ export interface ParentChildEdge {
15
+ parent: string;
16
+ child: string;
17
+ }
13
18
  /** What the user confirmed. Becomes `ImportHandoff.scope`; the legacy wizard saves it and starts generation. */
14
19
  export interface ScopeDecisions {
15
20
  accepted: string[];
16
21
  rejected: string[];
22
+ /** Slot edges removed at the gate. The child keeps its own decision. */
23
+ excludedSlotEdges: ParentChildEdge[];
17
24
  }
18
25
  export type ScopeGateLoad = {
19
26
  ok: true;
@@ -1,4 +1,4 @@
1
- import type { ScopeComponent, ScopeDecisions } from './types.js';
1
+ import type { ParentChildEdge, ScopeComponent, ScopeDecisions } from './types.js';
2
2
  interface UseScopeGateOptions {
3
3
  components: ScopeComponent[];
4
4
  onConfirm: (decisions: ScopeDecisions) => void;
@@ -20,6 +20,7 @@ export declare function useScopeGate({ components, onConfirm, onQuit }: UseScope
20
20
  panelLayout: import("./columns.js").ScopeGatePanelLayout;
21
21
  visibleCount: number;
22
22
  panelMaxRows: number;
23
+ excludedEdges: ParentChildEdge[];
23
24
  groupedItems: import("./graph/index.js").SidebarItem[];
24
25
  graph: import("./graph/index.js").ScopeGraph;
25
26
  hasGroupRoots: boolean;
@@ -46,6 +46,8 @@ export interface VisibleRow {
46
46
  cycleChild?: boolean;
47
47
  itemIdx: number;
48
48
  rootName?: string;
49
+ /** The parent whose slot the nested row sits in. */
50
+ parentName?: string;
49
51
  }
50
52
  export declare function buildVisibleRows(props: {
51
53
  items: GroupedSidebarItem[];
@@ -1,4 +1,5 @@
1
1
  import { type ImportHandoff } from '@contentful/experience-design-system-types/handoff';
2
+ import type { ParentChildEdge } from '../../session/db.js';
2
3
  /** The wizard state the early stages produce. Every field is one the stage used to write. */
3
4
  export interface HandoffState {
4
5
  credentialsSkipped: boolean;
@@ -18,6 +19,7 @@ export interface HandoffScope {
18
19
  decisions: {
19
20
  accepted: string[];
20
21
  rejected: string[];
22
+ excludedSlotEdges: ParentChildEdge[];
21
23
  };
22
24
  /** Components the cache check (finished and restored before the wizard started) found already generated. */
23
25
  cachedNames: string[];
@@ -1,8 +1,10 @@
1
+ import { type ParentChildEdge } from '../../session/db.js';
1
2
  export declare function runScopeGate(opts: {
2
3
  sessionId: string;
3
4
  decisions: {
4
5
  accepted: string[];
5
6
  rejected: string[];
7
+ excludedSlotEdges?: ParentChildEdge[];
6
8
  };
7
9
  onAdvanceToGenerate: (info: {
8
10
  sessionId: string;
@@ -3633,8 +3633,31 @@ function applyScopeDecisions(db, sessionId2, decisions) {
3633
3633
  ...accepted
3634
3634
  );
3635
3635
  }
3636
+ removeSlotAllowedComponents(db, sessionId2, decisions.excludedSlotEdges ?? [], rejected);
3636
3637
  db.prepare("UPDATE sessions SET updated_at = ? WHERE id = ?").run(now, sessionId2);
3637
3638
  }
3639
+ function removeSlotAllowedComponents(db, sessionId2, edges, rejectedNames) {
3640
+ if (edges.length === 0 && rejectedNames.length === 0) return;
3641
+ const componentId = db.prepare("SELECT component_id FROM raw_components WHERE session_id = ? AND name = ?");
3642
+ const removeEdge = db.prepare(
3643
+ "DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ? AND allowed_component = ?"
3644
+ );
3645
+ const removeEverywhere = db.prepare(
3646
+ "DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND allowed_component = ?"
3647
+ );
3648
+ db.exec("BEGIN");
3649
+ try {
3650
+ for (const edge of edges) {
3651
+ const row = componentId.get(sessionId2, edge.parent);
3652
+ if (row) removeEdge.run(sessionId2, row.component_id, edge.child);
3653
+ }
3654
+ for (const name of rejectedNames) removeEverywhere.run(sessionId2, name);
3655
+ db.exec("COMMIT");
3656
+ } catch (e) {
3657
+ db.exec("ROLLBACK");
3658
+ throw e;
3659
+ }
3660
+ }
3638
3661
  function findLatestSessionForCommand(db, command) {
3639
3662
  return getLatestCompletedSessionForCommand(db, command);
3640
3663
  }
@@ -9864,7 +9887,7 @@ function scopeFromHandoff(handoff) {
9864
9887
  const { scope } = handoff;
9865
9888
  if (!scope) return null;
9866
9889
  return {
9867
- decisions: { accepted: scope.accepted, rejected: scope.rejected },
9890
+ decisions: { accepted: scope.accepted, rejected: scope.rejected, excludedSlotEdges: scope.excludedSlotEdges },
9868
9891
  cachedNames: scope.cachedNames
9869
9892
  };
9870
9893
  }
@@ -19504,7 +19527,7 @@ function buildVisibleRows(props) {
19504
19527
  if (visited.has(target)) continue;
19505
19528
  visited.add(target);
19506
19529
  const isCycleMember = cycleParticipants.has(target);
19507
- out.push({ name: target, depth: cur.depth + 1, isCycleMember });
19530
+ out.push({ name: target, depth: cur.depth + 1, isCycleMember, parent: cur.name });
19508
19531
  queue.push({ name: target, depth: cur.depth + 1 });
19509
19532
  }
19510
19533
  }
@@ -19551,7 +19574,8 @@ function buildVisibleRows(props) {
19551
19574
  sharedSuffix,
19552
19575
  cycleChild: child.isCycleMember || void 0,
19553
19576
  itemIdx: childRec?.idx ?? -1,
19554
- rootName: key
19577
+ rootName: key,
19578
+ parentName: child.parent
19555
19579
  });
19556
19580
  });
19557
19581
  }
@@ -19634,13 +19658,19 @@ function buildVisibleRows(props) {
19634
19658
  injectedChildren.push({
19635
19659
  name: target,
19636
19660
  depth: parentDepth + 1,
19637
- isCycleChild: true
19661
+ isCycleChild: true,
19662
+ parent: parentName
19638
19663
  });
19639
19664
  }
19640
19665
  };
19641
19666
  emitCycleChildrenOf(root, 0);
19642
19667
  for (const c2 of closureChildren) {
19643
- injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
19668
+ injectedChildren.push({
19669
+ name: c2.name,
19670
+ depth: c2.depth,
19671
+ isCycleChild: false,
19672
+ parent: c2.path[c2.path.length - 2] ?? root
19673
+ });
19644
19674
  emitCycleChildrenOf(c2.name, c2.depth);
19645
19675
  }
19646
19676
  const isLastAtDepth = (idx) => {
@@ -19674,7 +19704,8 @@ function buildVisibleRows(props) {
19674
19704
  sharedSuffix,
19675
19705
  cycleChild: child.isCycleChild || void 0,
19676
19706
  itemIdx: childRec?.idx ?? -1,
19677
- rootName: root
19707
+ rootName: root,
19708
+ parentName: child.parent
19678
19709
  });
19679
19710
  });
19680
19711
  }
@@ -19827,7 +19858,7 @@ function GroupedSidebar(props) {
19827
19858
  const rs = itemName ? renderStatusByKey?.get(itemName) : void 0;
19828
19859
  const badge = itemName ? previewBadge(previewAnnotationByKey?.get(itemName)) : null;
19829
19860
  const showInheritance = rs !== void 0 && row.kind !== "cycle" && row.kind !== "empty" && row.kind !== "group-root";
19830
- const supportsSelectionGlyph = selectionStateByKey !== void 0 && itemName !== void 0 && (row.kind === "standalone" || row.kind === "group-root" || row.kind === "group-child" || row.kind === "flat" || row.kind === "cycle");
19861
+ const supportsSelectionGlyph = selectionStateByKey !== void 0 && itemName !== void 0 && (row.kind === "standalone" || row.kind === "group-root" || row.kind === "group-child" || row.kind === "flat" || row.kind === "empty" || row.kind === "cycle");
19831
19862
  const selState = supportsSelectionGlyph ? selectionStateByKey.get(itemName) ?? "undecided" : void 0;
19832
19863
  const isCycleRow = row.kind === "cycle";
19833
19864
  const selStyle = selectionGlyphStyleFor(selState, isCycleRow);
@@ -19857,7 +19888,7 @@ function GroupedSidebar(props) {
19857
19888
  color: isSelected ? PALETTE.info : labelStyle.dim ? PALETTE.muted : labelStyle.color,
19858
19889
  bold: isSelected || labelStyle.bold,
19859
19890
  inverse: false,
19860
- underline: isSelected && !focused,
19891
+ underline: isSelected && focused,
19861
19892
  wrap: wrapLabels ? "wrap" : "truncate",
19862
19893
  children: [
19863
19894
  " ",
@@ -22261,8 +22292,8 @@ function FieldEditor({
22261
22292
  return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", width, borderStyle: "single", borderColor: PALETTE.warning, children: [
22262
22293
  /* @__PURE__ */ jsx28(Text24, { bold: true, color: PALETTE.warning, children: "FIELD EDITOR \u2014 no fields" }),
22263
22294
  /* @__PURE__ */ jsx28(Text24, { color: PALETTE.warning, children: "\u26A0 No properties classified for this component. The LLM didn't find anything to classify." }),
22264
- /* @__PURE__ */ jsx28(Text24, { color: PALETTE.muted, children: "You can add fields manually below or reject this component." }),
22265
- /* @__PURE__ */ jsx28(Text24, { color: PALETTE.muted, children: "Enter to save \xB7 Esc to discard" })
22295
+ /* @__PURE__ */ jsx28(Text24, { color: PALETTE.muted, children: "There is nothing to edit here." }),
22296
+ /* @__PURE__ */ jsx28(Text24, { color: PALETTE.muted, children: active ? "[Esc] go back to the list" : "In the list: [r] reject this component \xB7 [a] accept it" })
22266
22297
  ] });
22267
22298
  }
22268
22299
  const hasEmptyProperties = props.length === 0 && slots.length === 0;
@@ -22477,7 +22508,7 @@ function FieldEditor({
22477
22508
  children: [
22478
22509
  hasEmptyProperties && /* @__PURE__ */ jsx28(Text24, { color: PALETTE.warning, children: "\u26A0 No properties classified for this component. The LLM didn't find anything to classify. Reject this component or add fields manually." }),
22479
22510
  header,
22480
- /* @__PURE__ */ jsx28(Box23, { flexDirection: "column", width: innerWidth, flexShrink: 0, children: focusLevel === "field" && !inComponentDesc && currentProp ? /* @__PURE__ */ jsx28(
22511
+ /* @__PURE__ */ jsx28(Box23, { flexDirection: "column", width: innerWidth, flexShrink: 0, children: focusLevel === "field" && !inComponentDesc && !inSlots && currentProp ? /* @__PURE__ */ jsx28(
22481
22512
  PropRow,
22482
22513
  {
22483
22514
  prop: currentProp,
@@ -23901,6 +23932,7 @@ function finalizeReviewSession(extractSessionId, components) {
23901
23932
  db.exec("ROLLBACK");
23902
23933
  throw error;
23903
23934
  }
23935
+ removeSlotAllowedComponents(db, extractSessionId, [], toReject);
23904
23936
  } finally {
23905
23937
  db.close();
23906
23938
  }
@@ -25242,33 +25274,50 @@ function GenerateReviewStepView({
25242
25274
  const cycle = cycleRows[cyclesCursor];
25243
25275
  return cycle ? enumerateCycleBreaks(cycle, components) : [];
25244
25276
  }, [cycleRows, cyclesCursor, components]);
25245
- const handleBreakEdge = (edge) => {
25246
- const target = components.find((component) => component.key === edge.fromComponent);
25247
- const slot = target?.entry.$slots?.[edge.slotName];
25248
- if (!target || !slot || !Array.isArray(slot.$allowedComponents)) return;
25249
- const nextEntry = {
25250
- ...target.entry,
25251
- $slots: {
25252
- ...target.entry.$slots,
25253
- [edge.slotName]: {
25254
- ...slot,
25255
- $allowedComponents: slot.$allowedComponents.filter((value) => value !== edge.toComponent)
25256
- }
25257
- }
25258
- };
25277
+ const saveSlotChange = (parentKey, nextEntry, historyLabel) => {
25259
25278
  const next = components.map(
25260
- (component) => component.key === target.key ? { ...component, entry: nextEntry } : component
25279
+ (component) => component.key === parentKey ? { ...component, entry: nextEntry } : component
25261
25280
  );
25262
25281
  setComponents(next);
25263
25282
  const db = openPipelineDb();
25264
25283
  try {
25265
- storeCDFComponents(db, extractSessionId, [{ key: target.key, entry: nextEntry }]);
25284
+ storeCDFComponents(db, extractSessionId, [{ key: parentKey, entry: nextEntry }]);
25266
25285
  } finally {
25267
25286
  db.close();
25268
25287
  }
25269
25288
  recomputeCycles(next);
25270
25289
  livePreviewHook.trigger();
25271
- pushHistorySnapshot(next, "break-cycle-edge");
25290
+ pushHistorySnapshot(next, historyLabel);
25291
+ };
25292
+ const handleBreakEdge = (edge) => {
25293
+ const target = components.find((component) => component.key === edge.fromComponent);
25294
+ const slot = target?.entry.$slots?.[edge.slotName];
25295
+ if (!target || !slot || !Array.isArray(slot.$allowedComponents)) return;
25296
+ saveSlotChange(
25297
+ target.key,
25298
+ {
25299
+ ...target.entry,
25300
+ $slots: {
25301
+ ...target.entry.$slots,
25302
+ [edge.slotName]: {
25303
+ ...slot,
25304
+ $allowedComponents: slot.$allowedComponents.filter((value) => value !== edge.toComponent)
25305
+ }
25306
+ }
25307
+ },
25308
+ "break-cycle-edge"
25309
+ );
25310
+ };
25311
+ const handleRemoveSlotChild = (parentKey, childKey) => {
25312
+ const parent = components.find((component) => component.key === parentKey);
25313
+ if (!parent?.entry.$slots) return;
25314
+ const slots = Object.fromEntries(
25315
+ Object.entries(parent.entry.$slots).map(([slotName, slot]) => [
25316
+ slotName,
25317
+ Array.isArray(slot.$allowedComponents) ? { ...slot, $allowedComponents: slot.$allowedComponents.filter((value) => value !== childKey) } : slot
25318
+ ])
25319
+ );
25320
+ saveSlotChange(parentKey, { ...parent.entry, $slots: slots }, "remove-slot-child");
25272
25321
  };
25273
25322
  const handleRejectComponent = (key) => {
25274
25323
  const rejectCascade = computeRejectCascade(key, componentGraph);
@@ -25598,6 +25647,11 @@ function GenerateReviewStepView({
25598
25647
  if (input === "r") {
25599
25648
  const current = components[selectedIdx];
25600
25649
  if (!current) return;
25650
+ const row = visibleRowsMemo[cursorRowIdx];
25651
+ if (row?.kind === "group-child" && row.parentName) {
25652
+ handleRemoveSlotChild(row.parentName, current.key);
25653
+ return;
25654
+ }
25601
25655
  handleRejectComponent(current.key);
25602
25656
  return;
25603
25657
  }
@@ -99,10 +99,21 @@ export type ScopeComponentRow = {
99
99
  }>;
100
100
  };
101
101
  export declare function loadScopeComponents(db: DatabaseSync, sessionId: string): ScopeComponentRow[];
102
+ /** A parent component's slot allowing a child component. */
103
+ export interface ParentChildEdge {
104
+ parent: string;
105
+ child: string;
106
+ }
102
107
  export declare function applyScopeDecisions(db: DatabaseSync, sessionId: string, decisions: {
103
108
  accepted: string[];
104
109
  rejected: string[];
110
+ excludedSlotEdges?: ParentChildEdge[];
105
111
  }): void;
112
+ /**
113
+ * Drops excluded parent→child edges from slot allowed lists. Rejected
114
+ * components are also removed from every slot, since they will not exist.
115
+ */
116
+ export declare function removeSlotAllowedComponents(db: DatabaseSync, sessionId: string, edges: ParentChildEdge[], rejectedNames: string[]): void;
106
117
  export declare function findLatestSessionForCommand(db: DatabaseSync, command: CommandName): string | null;
107
118
  export declare function seedCDFFromPriorSession(db: DatabaseSync, targetSessionId: string): number;
108
119
  export declare function seedCDFFromPreviewResponse(db: DatabaseSync, sessionId: string, removedItems: ComponentTypeSummary[]): number;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-40c29bf0.0",
3
+ "version": "2.37.1-dev-build-444a2d6a.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-40c29bf0.0",
3
+ "version": "2.37.1-dev-build-444a2d6a.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,9 +45,9 @@
45
45
  "ts-morph": "^27.0.2",
46
46
  "typescript": "^5.9.3",
47
47
  "zod": "^4.6.5",
48
- "@contentful/experience-design-system-backend-pipeline": "2.37.1-dev-build-40c29bf0.0",
49
- "@contentful/experience-design-system-types": "2.37.1-dev-build-40c29bf0.0",
50
- "@contentful/experience-design-system-generation": "2.37.1-dev-build-40c29bf0.0"
48
+ "@contentful/experience-design-system-generation": "2.37.1-dev-build-444a2d6a.0",
49
+ "@contentful/experience-design-system-types": "2.37.1-dev-build-444a2d6a.0",
50
+ "@contentful/experience-design-system-backend-pipeline": "2.37.1-dev-build-444a2d6a.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@tsconfig/node24": "^24.0.4",