@contentful/experience-design-system-cli 2.37.1-dev-build-6c9e5304.0 → 2.37.1-dev-build-e2dab518.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
@@ -4365,8 +4365,30 @@ function applyScopeDecisions(db, sessionId, decisions) {
4365
4365
  const placeholders = accepted.map(() => "?").join(",");
4366
4366
  db.prepare(`UPDATE raw_components SET status = 'generated' WHERE session_id = ? AND name IN (${placeholders})`).run(sessionId, ...accepted);
4367
4367
  }
4368
+ removeSlotAllowedComponents(db, sessionId, decisions.excludedSlotEdges ?? [], rejected);
4368
4369
  db.prepare("UPDATE sessions SET updated_at = ? WHERE id = ?").run(now, sessionId);
4369
4370
  }
4371
+ function removeSlotAllowedComponents(db, sessionId, edges, rejectedNames) {
4372
+ if (edges.length === 0 && rejectedNames.length === 0)
4373
+ return;
4374
+ const componentId = db.prepare("SELECT component_id FROM raw_components WHERE session_id = ? AND name = ?");
4375
+ const removeEdge = db.prepare("DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ? AND allowed_component = ?");
4376
+ const removeEverywhere = db.prepare("DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND allowed_component = ?");
4377
+ db.exec("BEGIN");
4378
+ try {
4379
+ for (const edge of edges) {
4380
+ const row = componentId.get(sessionId, edge.parent);
4381
+ if (row)
4382
+ removeEdge.run(sessionId, row.component_id, edge.child);
4383
+ }
4384
+ for (const name of rejectedNames)
4385
+ removeEverywhere.run(sessionId, name);
4386
+ db.exec("COMMIT");
4387
+ } catch (e) {
4388
+ db.exec("ROLLBACK");
4389
+ throw e;
4390
+ }
4391
+ }
4370
4392
  function findLatestSessionForCommand(db, command) {
4371
4393
  return getLatestCompletedSessionForCommand(db, command);
4372
4394
  }
@@ -18370,7 +18392,7 @@ function buildVisibleRows(props) {
18370
18392
  if (visited.has(target)) continue;
18371
18393
  visited.add(target);
18372
18394
  const isCycleMember = cycleParticipants.has(target);
18373
- out.push({ name: target, depth: cur.depth + 1, isCycleMember });
18395
+ out.push({ name: target, depth: cur.depth + 1, isCycleMember, parent: cur.name });
18374
18396
  queue.push({ name: target, depth: cur.depth + 1 });
18375
18397
  }
18376
18398
  }
@@ -18417,7 +18439,8 @@ function buildVisibleRows(props) {
18417
18439
  sharedSuffix,
18418
18440
  cycleChild: child.isCycleMember || void 0,
18419
18441
  itemIdx: childRec?.idx ?? -1,
18420
- rootName: key
18442
+ rootName: key,
18443
+ parentName: child.parent
18421
18444
  });
18422
18445
  });
18423
18446
  }
@@ -18500,13 +18523,19 @@ function buildVisibleRows(props) {
18500
18523
  injectedChildren.push({
18501
18524
  name: target,
18502
18525
  depth: parentDepth + 1,
18503
- isCycleChild: true
18526
+ isCycleChild: true,
18527
+ parent: parentName
18504
18528
  });
18505
18529
  }
18506
18530
  };
18507
18531
  emitCycleChildrenOf(root, 0);
18508
18532
  for (const c2 of closureChildren) {
18509
- injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
18533
+ injectedChildren.push({
18534
+ name: c2.name,
18535
+ depth: c2.depth,
18536
+ isCycleChild: false,
18537
+ parent: c2.path[c2.path.length - 2] ?? root
18538
+ });
18510
18539
  emitCycleChildrenOf(c2.name, c2.depth);
18511
18540
  }
18512
18541
  const isLastAtDepth = (idx) => {
@@ -18540,7 +18569,8 @@ function buildVisibleRows(props) {
18540
18569
  sharedSuffix,
18541
18570
  cycleChild: child.isCycleChild || void 0,
18542
18571
  itemIdx: childRec?.idx ?? -1,
18543
- rootName: root
18572
+ rootName: root,
18573
+ parentName: child.parent
18544
18574
  });
18545
18575
  });
18546
18576
  }
@@ -18693,7 +18723,7 @@ function GroupedSidebar(props) {
18693
18723
  const rs = itemName ? renderStatusByKey?.get(itemName) : void 0;
18694
18724
  const badge = itemName ? previewBadge(previewAnnotationByKey?.get(itemName)) : null;
18695
18725
  const showInheritance = rs !== void 0 && row.kind !== "cycle" && row.kind !== "empty" && row.kind !== "group-root";
18696
- 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");
18726
+ 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");
18697
18727
  const selState = supportsSelectionGlyph ? selectionStateByKey.get(itemName) ?? "undecided" : void 0;
18698
18728
  const isCycleRow = row.kind === "cycle";
18699
18729
  const selStyle = selectionGlyphStyleFor(selState, isCycleRow);
@@ -18723,7 +18753,7 @@ function GroupedSidebar(props) {
18723
18753
  color: isSelected ? PALETTE2.info : labelStyle.dim ? PALETTE2.muted : labelStyle.color,
18724
18754
  bold: isSelected || labelStyle.bold,
18725
18755
  inverse: false,
18726
- underline: isSelected && !focused,
18756
+ underline: isSelected && focused,
18727
18757
  wrap: wrapLabels ? "wrap" : "truncate",
18728
18758
  children: [
18729
18759
  " ",
@@ -19382,6 +19412,7 @@ function resolveKey(mode, key, ctx) {
19382
19412
  function resolveMain(key, ctx) {
19383
19413
  const { input } = key;
19384
19414
  const inMain = ctx.focusedColumn === "main";
19415
+ if (key.ctrl && input === "z") return { type: "undo" };
19385
19416
  if (input === "q" || key.escape) {
19386
19417
  if (key.escape && ctx.jumpFilterActive) return { type: "clear-jump-filter" };
19387
19418
  if (key.escape && ctx.searchActive) return { type: "clear-search" };
@@ -19640,21 +19671,42 @@ function rejectNeedsConfirmation(graph, name, state) {
19640
19671
  const ancestors = toReject.filter((n) => n !== name);
19641
19672
  return ancestors.length + toDeselect.length >= 2;
19642
19673
  }
19643
- function partitionDecisions(components, state) {
19674
+ function removeSlotChild(components, edge) {
19675
+ return components.map(
19676
+ (component) => component.name === edge.parent ? {
19677
+ ...component,
19678
+ slots: component.slots?.map((slot) => ({
19679
+ ...slot,
19680
+ allowedComponents: slot.allowedComponents.filter((name) => name !== edge.child)
19681
+ }))
19682
+ } : component
19683
+ );
19684
+ }
19685
+ function withoutSlotChildren(components, removedEdges) {
19686
+ return removedEdges.reduce(removeSlotChild, components);
19687
+ }
19688
+ function partitionDecisions(components, state, removedEdges) {
19644
19689
  const accepted = [];
19645
19690
  const rejected = [];
19646
19691
  for (const c2 of components) {
19647
19692
  if (state.get(c2.name) === "accepted") accepted.push(c2.name);
19648
19693
  else rejected.push(c2.name);
19649
19694
  }
19650
- return { accepted, rejected };
19695
+ const rejectedNames = new Set(rejected);
19696
+ const excludedSlotEdges = removedEdges.filter((e) => !rejectedNames.has(e.parent) && !rejectedNames.has(e.child));
19697
+ return { accepted, rejected, excludedSlotEdges };
19651
19698
  }
19652
19699
 
19653
19700
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/use-scope-gate.ts
19654
- function useScopeGate({ components, onConfirm, onQuit }) {
19701
+ function useScopeGate({ components: loadedComponents, onConfirm, onQuit }) {
19655
19702
  const { columns: totalWidth, rows: terminalRows } = useTerminalSize();
19656
19703
  const columnPlan = useMemo2(() => computeColumnWidths(totalWidth), [totalWidth]);
19657
19704
  const [decisions, setDecisions] = useState12(/* @__PURE__ */ new Map());
19705
+ const [removedEdges, setRemovedEdges] = useState12([]);
19706
+ const components = useMemo2(
19707
+ () => withoutSlotChildren(loadedComponents, removedEdges),
19708
+ [loadedComponents, removedEdges]
19709
+ );
19658
19710
  const [nav, setNav] = useState12({ cursor: 0, scrollOffset: 0 });
19659
19711
  const [focusedColumn, setFocusedColumn] = useState12("main");
19660
19712
  const [addedComponentsCursor, setAddedComponentsCursor] = useState12(0);
@@ -19826,6 +19878,14 @@ function useScopeGate({ components, onConfirm, onQuit }) {
19826
19878
  return void 0;
19827
19879
  }
19828
19880
  };
19881
+ const focusedNestedEdge = () => {
19882
+ const row = visibleRows[safeCursor];
19883
+ if (row?.kind !== "group-child" || !row.parentName || row.itemIdx < 0) return void 0;
19884
+ const child = groupedItems[row.itemIdx]?.key;
19885
+ return child ? { parent: row.parentName, child } : void 0;
19886
+ };
19887
+ const removeFromParentSlot = (edge) => setRemovedEdges((edges) => [...edges, edge]);
19888
+ const undoSlotRemoval = () => setRemovedEdges((edges) => edges.slice(0, -1));
19829
19889
  const jumpCursorTo = (name) => {
19830
19890
  let idx = -1;
19831
19891
  for (let i = 0; i < visibleRows.length; i++) {
@@ -19925,7 +19985,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
19925
19985
  onQuit();
19926
19986
  return;
19927
19987
  case "confirm":
19928
- onConfirm(partitionDecisions(components, decisions));
19988
+ onConfirm(partitionDecisions(components, decisions, removedEdges));
19929
19989
  return;
19930
19990
  case "close-help":
19931
19991
  setShowHelp(false);
@@ -19978,6 +20038,11 @@ function useScopeGate({ components, onConfirm, onQuit }) {
19978
20038
  return;
19979
20039
  }
19980
20040
  case "reject": {
20041
+ const edge = focusedColumn === "main" ? focusedNestedEdge() : void 0;
20042
+ if (edge) {
20043
+ removeFromParentSlot(edge);
20044
+ return;
20045
+ }
19981
20046
  const key = focusedColumn === "added-components" ? addedComponents[safeAddedComponentsCursor]?.name : focusedRowKey();
19982
20047
  if (key) requestReject(key);
19983
20048
  return;
@@ -19985,6 +20050,9 @@ function useScopeGate({ components, onConfirm, onQuit }) {
19985
20050
  case "toggle-all":
19986
20051
  decide({ type: "toggle-all" });
19987
20052
  return;
20053
+ case "undo":
20054
+ undoSlotRemoval();
20055
+ return;
19988
20056
  case "accept-unflagged":
19989
20057
  decide({ type: "accept-unflagged" });
19990
20058
  return;
@@ -22627,8 +22695,8 @@ function FieldEditor({
22627
22695
  return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", width, borderStyle: "single", borderColor: PALETTE3.warning, children: [
22628
22696
  /* @__PURE__ */ jsx27(Text23, { bold: true, color: PALETTE3.warning, children: "FIELD EDITOR \u2014 no fields" }),
22629
22697
  /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.warning, children: "\u26A0 No properties classified for this component. The LLM didn't find anything to classify." }),
22630
- /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: "You can add fields manually below or reject this component." }),
22631
- /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: "Enter to save \xB7 Esc to discard" })
22698
+ /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: "There is nothing to edit here." }),
22699
+ /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: active ? "[Esc] go back to the list" : "In the list: [r] reject this component \xB7 [a] accept it" })
22632
22700
  ] });
22633
22701
  }
22634
22702
  const hasEmptyProperties = props.length === 0 && slots.length === 0;
@@ -22843,7 +22911,7 @@ function FieldEditor({
22843
22911
  children: [
22844
22912
  hasEmptyProperties && /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.warning, children: "\u26A0 No properties classified for this component. The LLM didn't find anything to classify. Reject this component or add fields manually." }),
22845
22913
  header,
22846
- /* @__PURE__ */ jsx27(Box21, { flexDirection: "column", width: innerWidth, flexShrink: 0, children: focusLevel === "field" && !inComponentDesc && currentProp ? /* @__PURE__ */ jsx27(
22914
+ /* @__PURE__ */ jsx27(Box21, { flexDirection: "column", width: innerWidth, flexShrink: 0, children: focusLevel === "field" && !inComponentDesc && !inSlots && currentProp ? /* @__PURE__ */ jsx27(
22847
22915
  PropRow,
22848
22916
  {
22849
22917
  prop: currentProp,
@@ -23752,6 +23820,7 @@ function finalizeReviewSession(extractSessionId, components) {
23752
23820
  db.exec("ROLLBACK");
23753
23821
  throw error;
23754
23822
  }
23823
+ removeSlotAllowedComponents(db, extractSessionId, [], toReject);
23755
23824
  } finally {
23756
23825
  db.close();
23757
23826
  }
@@ -25483,33 +25552,50 @@ function GenerateReviewStepView({
25483
25552
  const cycle = cycleRows[cyclesCursor];
25484
25553
  return cycle ? enumerateCycleBreaks(cycle, components) : [];
25485
25554
  }, [cycleRows, cyclesCursor, components]);
25486
- const handleBreakEdge = (edge) => {
25487
- const target = components.find((component) => component.key === edge.fromComponent);
25488
- const slot = target?.entry.$slots?.[edge.slotName];
25489
- if (!target || !slot || !Array.isArray(slot.$allowedComponents)) return;
25490
- const nextEntry = {
25491
- ...target.entry,
25492
- $slots: {
25493
- ...target.entry.$slots,
25494
- [edge.slotName]: {
25495
- ...slot,
25496
- $allowedComponents: slot.$allowedComponents.filter((value) => value !== edge.toComponent)
25497
- }
25498
- }
25499
- };
25555
+ const saveSlotChange = (parentKey, nextEntry, historyLabel) => {
25500
25556
  const next = components.map(
25501
- (component) => component.key === target.key ? { ...component, entry: nextEntry } : component
25557
+ (component) => component.key === parentKey ? { ...component, entry: nextEntry } : component
25502
25558
  );
25503
25559
  setComponents(next);
25504
25560
  const db = openPipelineDb();
25505
25561
  try {
25506
- storeCDFComponents(db, extractSessionId, [{ key: target.key, entry: nextEntry }]);
25562
+ storeCDFComponents(db, extractSessionId, [{ key: parentKey, entry: nextEntry }]);
25507
25563
  } finally {
25508
25564
  db.close();
25509
25565
  }
25510
25566
  recomputeCycles(next);
25511
25567
  livePreviewHook.trigger();
25512
- pushHistorySnapshot(next, "break-cycle-edge");
25568
+ pushHistorySnapshot(next, historyLabel);
25569
+ };
25570
+ const handleBreakEdge = (edge) => {
25571
+ const target = components.find((component) => component.key === edge.fromComponent);
25572
+ const slot = target?.entry.$slots?.[edge.slotName];
25573
+ if (!target || !slot || !Array.isArray(slot.$allowedComponents)) return;
25574
+ saveSlotChange(
25575
+ target.key,
25576
+ {
25577
+ ...target.entry,
25578
+ $slots: {
25579
+ ...target.entry.$slots,
25580
+ [edge.slotName]: {
25581
+ ...slot,
25582
+ $allowedComponents: slot.$allowedComponents.filter((value) => value !== edge.toComponent)
25583
+ }
25584
+ }
25585
+ },
25586
+ "break-cycle-edge"
25587
+ );
25588
+ };
25589
+ const handleRemoveSlotChild = (parentKey, childKey) => {
25590
+ const parent = components.find((component) => component.key === parentKey);
25591
+ if (!parent?.entry.$slots) return;
25592
+ const slots = Object.fromEntries(
25593
+ Object.entries(parent.entry.$slots).map(([slotName, slot]) => [
25594
+ slotName,
25595
+ Array.isArray(slot.$allowedComponents) ? { ...slot, $allowedComponents: slot.$allowedComponents.filter((value) => value !== childKey) } : slot
25596
+ ])
25597
+ );
25598
+ saveSlotChange(parentKey, { ...parent.entry, $slots: slots }, "remove-slot-child");
25513
25599
  };
25514
25600
  const handleRejectComponent = (key) => {
25515
25601
  const rejectCascade2 = computeRejectCascade(key, componentGraph);
@@ -25839,6 +25925,11 @@ function GenerateReviewStepView({
25839
25925
  if (input === "r") {
25840
25926
  const current = components[selectedIdx];
25841
25927
  if (!current) return;
25928
+ const row = visibleRowsMemo[cursorRowIdx];
25929
+ if (row?.kind === "group-child" && row.parentName) {
25930
+ handleRemoveSlotChild(row.parentName, current.key);
25931
+ return;
25932
+ }
25842
25933
  handleRejectComponent(current.key);
25843
25934
  return;
25844
25935
  }
package/dist/index.js CHANGED
@@ -4386,8 +4386,30 @@ function applyScopeDecisions(db, sessionId, decisions) {
4386
4386
  const placeholders = accepted.map(() => "?").join(",");
4387
4387
  db.prepare(`UPDATE raw_components SET status = 'generated' WHERE session_id = ? AND name IN (${placeholders})`).run(sessionId, ...accepted);
4388
4388
  }
4389
+ removeSlotAllowedComponents(db, sessionId, decisions.excludedSlotEdges ?? [], rejected);
4389
4390
  db.prepare("UPDATE sessions SET updated_at = ? WHERE id = ?").run(now, sessionId);
4390
4391
  }
4392
+ function removeSlotAllowedComponents(db, sessionId, edges, rejectedNames) {
4393
+ if (edges.length === 0 && rejectedNames.length === 0)
4394
+ return;
4395
+ const componentId = db.prepare("SELECT component_id FROM raw_components WHERE session_id = ? AND name = ?");
4396
+ const removeEdge = db.prepare("DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ? AND allowed_component = ?");
4397
+ const removeEverywhere = db.prepare("DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND allowed_component = ?");
4398
+ db.exec("BEGIN");
4399
+ try {
4400
+ for (const edge of edges) {
4401
+ const row = componentId.get(sessionId, edge.parent);
4402
+ if (row)
4403
+ removeEdge.run(sessionId, row.component_id, edge.child);
4404
+ }
4405
+ for (const name of rejectedNames)
4406
+ removeEverywhere.run(sessionId, name);
4407
+ db.exec("COMMIT");
4408
+ } catch (e) {
4409
+ db.exec("ROLLBACK");
4410
+ throw e;
4411
+ }
4412
+ }
4391
4413
  function findLatestSessionForCommand(db, command) {
4392
4414
  return getLatestCompletedSessionForCommand(db, command);
4393
4415
  }
@@ -21777,7 +21799,7 @@ function buildVisibleRows(props) {
21777
21799
  if (visited.has(target)) continue;
21778
21800
  visited.add(target);
21779
21801
  const isCycleMember = cycleParticipants.has(target);
21780
- out.push({ name: target, depth: cur.depth + 1, isCycleMember });
21802
+ out.push({ name: target, depth: cur.depth + 1, isCycleMember, parent: cur.name });
21781
21803
  queue.push({ name: target, depth: cur.depth + 1 });
21782
21804
  }
21783
21805
  }
@@ -21824,7 +21846,8 @@ function buildVisibleRows(props) {
21824
21846
  sharedSuffix,
21825
21847
  cycleChild: child.isCycleMember || void 0,
21826
21848
  itemIdx: childRec?.idx ?? -1,
21827
- rootName: key
21849
+ rootName: key,
21850
+ parentName: child.parent
21828
21851
  });
21829
21852
  });
21830
21853
  }
@@ -21907,13 +21930,19 @@ function buildVisibleRows(props) {
21907
21930
  injectedChildren.push({
21908
21931
  name: target,
21909
21932
  depth: parentDepth + 1,
21910
- isCycleChild: true
21933
+ isCycleChild: true,
21934
+ parent: parentName
21911
21935
  });
21912
21936
  }
21913
21937
  };
21914
21938
  emitCycleChildrenOf(root, 0);
21915
21939
  for (const c2 of closureChildren) {
21916
- injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
21940
+ injectedChildren.push({
21941
+ name: c2.name,
21942
+ depth: c2.depth,
21943
+ isCycleChild: false,
21944
+ parent: c2.path[c2.path.length - 2] ?? root
21945
+ });
21917
21946
  emitCycleChildrenOf(c2.name, c2.depth);
21918
21947
  }
21919
21948
  const isLastAtDepth = (idx) => {
@@ -21947,7 +21976,8 @@ function buildVisibleRows(props) {
21947
21976
  sharedSuffix,
21948
21977
  cycleChild: child.isCycleChild || void 0,
21949
21978
  itemIdx: childRec?.idx ?? -1,
21950
- rootName: root
21979
+ rootName: root,
21980
+ parentName: child.parent
21951
21981
  });
21952
21982
  });
21953
21983
  }
@@ -22100,7 +22130,7 @@ function GroupedSidebar(props) {
22100
22130
  const rs = itemName ? renderStatusByKey?.get(itemName) : void 0;
22101
22131
  const badge = itemName ? previewBadge(previewAnnotationByKey?.get(itemName)) : null;
22102
22132
  const showInheritance = rs !== void 0 && row.kind !== "cycle" && row.kind !== "empty" && row.kind !== "group-root";
22103
- 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");
22133
+ 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");
22104
22134
  const selState = supportsSelectionGlyph ? selectionStateByKey.get(itemName) ?? "undecided" : void 0;
22105
22135
  const isCycleRow = row.kind === "cycle";
22106
22136
  const selStyle = selectionGlyphStyleFor(selState, isCycleRow);
@@ -22130,7 +22160,7 @@ function GroupedSidebar(props) {
22130
22160
  color: isSelected ? PALETTE2.info : labelStyle.dim ? PALETTE2.muted : labelStyle.color,
22131
22161
  bold: isSelected || labelStyle.bold,
22132
22162
  inverse: false,
22133
- underline: isSelected && !focused,
22163
+ underline: isSelected && focused,
22134
22164
  wrap: wrapLabels ? "wrap" : "truncate",
22135
22165
  children: [
22136
22166
  " ",
@@ -22881,6 +22911,7 @@ function resolveKey(mode, key, ctx) {
22881
22911
  function resolveMain(key, ctx) {
22882
22912
  const { input } = key;
22883
22913
  const inMain = ctx.focusedColumn === "main";
22914
+ if (key.ctrl && input === "z") return { type: "undo" };
22884
22915
  if (input === "q" || key.escape) {
22885
22916
  if (key.escape && ctx.jumpFilterActive) return { type: "clear-jump-filter" };
22886
22917
  if (key.escape && ctx.searchActive) return { type: "clear-search" };
@@ -23179,14 +23210,30 @@ function rejectNeedsConfirmation(graph, name, state) {
23179
23210
  const ancestors = toReject.filter((n) => n !== name);
23180
23211
  return ancestors.length + toDeselect.length >= 2;
23181
23212
  }
23182
- function partitionDecisions(components, state) {
23213
+ function removeSlotChild(components, edge) {
23214
+ return components.map(
23215
+ (component) => component.name === edge.parent ? {
23216
+ ...component,
23217
+ slots: component.slots?.map((slot) => ({
23218
+ ...slot,
23219
+ allowedComponents: slot.allowedComponents.filter((name) => name !== edge.child)
23220
+ }))
23221
+ } : component
23222
+ );
23223
+ }
23224
+ function withoutSlotChildren(components, removedEdges) {
23225
+ return removedEdges.reduce(removeSlotChild, components);
23226
+ }
23227
+ function partitionDecisions(components, state, removedEdges) {
23183
23228
  const accepted = [];
23184
23229
  const rejected = [];
23185
23230
  for (const c2 of components) {
23186
23231
  if (state.get(c2.name) === "accepted") accepted.push(c2.name);
23187
23232
  else rejected.push(c2.name);
23188
23233
  }
23189
- return { accepted, rejected };
23234
+ const rejectedNames = new Set(rejected);
23235
+ const excludedSlotEdges = removedEdges.filter((e) => !rejectedNames.has(e.parent) && !rejectedNames.has(e.child));
23236
+ return { accepted, rejected, excludedSlotEdges };
23190
23237
  }
23191
23238
  var init_state = __esm({
23192
23239
  "packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/state.ts"() {
@@ -23198,10 +23245,15 @@ var init_state = __esm({
23198
23245
 
23199
23246
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/use-scope-gate.ts
23200
23247
  import { useEffect as useEffect7, useMemo as useMemo2, useRef as useRef4, useState as useState12 } from "react";
23201
- function useScopeGate({ components, onConfirm, onQuit }) {
23248
+ function useScopeGate({ components: loadedComponents, onConfirm, onQuit }) {
23202
23249
  const { columns: totalWidth, rows: terminalRows } = useTerminalSize();
23203
23250
  const columnPlan = useMemo2(() => computeColumnWidths(totalWidth), [totalWidth]);
23204
23251
  const [decisions, setDecisions] = useState12(/* @__PURE__ */ new Map());
23252
+ const [removedEdges, setRemovedEdges] = useState12([]);
23253
+ const components = useMemo2(
23254
+ () => withoutSlotChildren(loadedComponents, removedEdges),
23255
+ [loadedComponents, removedEdges]
23256
+ );
23205
23257
  const [nav, setNav] = useState12({ cursor: 0, scrollOffset: 0 });
23206
23258
  const [focusedColumn, setFocusedColumn] = useState12("main");
23207
23259
  const [addedComponentsCursor, setAddedComponentsCursor] = useState12(0);
@@ -23373,6 +23425,14 @@ function useScopeGate({ components, onConfirm, onQuit }) {
23373
23425
  return void 0;
23374
23426
  }
23375
23427
  };
23428
+ const focusedNestedEdge = () => {
23429
+ const row = visibleRows[safeCursor];
23430
+ if (row?.kind !== "group-child" || !row.parentName || row.itemIdx < 0) return void 0;
23431
+ const child = groupedItems[row.itemIdx]?.key;
23432
+ return child ? { parent: row.parentName, child } : void 0;
23433
+ };
23434
+ const removeFromParentSlot = (edge) => setRemovedEdges((edges) => [...edges, edge]);
23435
+ const undoSlotRemoval = () => setRemovedEdges((edges) => edges.slice(0, -1));
23376
23436
  const jumpCursorTo = (name) => {
23377
23437
  let idx = -1;
23378
23438
  for (let i = 0; i < visibleRows.length; i++) {
@@ -23472,7 +23532,7 @@ function useScopeGate({ components, onConfirm, onQuit }) {
23472
23532
  onQuit();
23473
23533
  return;
23474
23534
  case "confirm":
23475
- onConfirm(partitionDecisions(components, decisions));
23535
+ onConfirm(partitionDecisions(components, decisions, removedEdges));
23476
23536
  return;
23477
23537
  case "close-help":
23478
23538
  setShowHelp(false);
@@ -23525,6 +23585,11 @@ function useScopeGate({ components, onConfirm, onQuit }) {
23525
23585
  return;
23526
23586
  }
23527
23587
  case "reject": {
23588
+ const edge = focusedColumn === "main" ? focusedNestedEdge() : void 0;
23589
+ if (edge) {
23590
+ removeFromParentSlot(edge);
23591
+ return;
23592
+ }
23528
23593
  const key = focusedColumn === "added-components" ? addedComponents[safeAddedComponentsCursor]?.name : focusedRowKey();
23529
23594
  if (key) requestReject(key);
23530
23595
  return;
@@ -23532,6 +23597,9 @@ function useScopeGate({ components, onConfirm, onQuit }) {
23532
23597
  case "toggle-all":
23533
23598
  decide({ type: "toggle-all" });
23534
23599
  return;
23600
+ case "undo":
23601
+ undoSlotRemoval();
23602
+ return;
23535
23603
  case "accept-unflagged":
23536
23604
  decide({ type: "accept-unflagged" });
23537
23605
  return;
@@ -26446,8 +26514,8 @@ function FieldEditor({
26446
26514
  return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", width, borderStyle: "single", borderColor: PALETTE3.warning, children: [
26447
26515
  /* @__PURE__ */ jsx27(Text23, { bold: true, color: PALETTE3.warning, children: "FIELD EDITOR \u2014 no fields" }),
26448
26516
  /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.warning, children: "\u26A0 No properties classified for this component. The LLM didn't find anything to classify." }),
26449
- /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: "You can add fields manually below or reject this component." }),
26450
- /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: "Enter to save \xB7 Esc to discard" })
26517
+ /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: "There is nothing to edit here." }),
26518
+ /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.muted, children: active ? "[Esc] go back to the list" : "In the list: [r] reject this component \xB7 [a] accept it" })
26451
26519
  ] });
26452
26520
  }
26453
26521
  const hasEmptyProperties = props.length === 0 && slots.length === 0;
@@ -26662,7 +26730,7 @@ function FieldEditor({
26662
26730
  children: [
26663
26731
  hasEmptyProperties && /* @__PURE__ */ jsx27(Text23, { color: PALETTE3.warning, children: "\u26A0 No properties classified for this component. The LLM didn't find anything to classify. Reject this component or add fields manually." }),
26664
26732
  header,
26665
- /* @__PURE__ */ jsx27(Box21, { flexDirection: "column", width: innerWidth, flexShrink: 0, children: focusLevel === "field" && !inComponentDesc && currentProp ? /* @__PURE__ */ jsx27(
26733
+ /* @__PURE__ */ jsx27(Box21, { flexDirection: "column", width: innerWidth, flexShrink: 0, children: focusLevel === "field" && !inComponentDesc && !inSlots && currentProp ? /* @__PURE__ */ jsx27(
26666
26734
  PropRow,
26667
26735
  {
26668
26736
  prop: currentProp,
@@ -27666,6 +27734,7 @@ function finalizeReviewSession(extractSessionId, components) {
27666
27734
  db.exec("ROLLBACK");
27667
27735
  throw error;
27668
27736
  }
27737
+ removeSlotAllowedComponents(db, extractSessionId, [], toReject);
27669
27738
  } finally {
27670
27739
  db.close();
27671
27740
  }
@@ -29444,33 +29513,50 @@ function GenerateReviewStepView({
29444
29513
  const cycle = cycleRows[cyclesCursor];
29445
29514
  return cycle ? enumerateCycleBreaks(cycle, components) : [];
29446
29515
  }, [cycleRows, cyclesCursor, components]);
29447
- const handleBreakEdge = (edge) => {
29448
- const target = components.find((component) => component.key === edge.fromComponent);
29449
- const slot = target?.entry.$slots?.[edge.slotName];
29450
- if (!target || !slot || !Array.isArray(slot.$allowedComponents)) return;
29451
- const nextEntry = {
29452
- ...target.entry,
29453
- $slots: {
29454
- ...target.entry.$slots,
29455
- [edge.slotName]: {
29456
- ...slot,
29457
- $allowedComponents: slot.$allowedComponents.filter((value) => value !== edge.toComponent)
29458
- }
29459
- }
29460
- };
29516
+ const saveSlotChange = (parentKey, nextEntry, historyLabel) => {
29461
29517
  const next = components.map(
29462
- (component) => component.key === target.key ? { ...component, entry: nextEntry } : component
29518
+ (component) => component.key === parentKey ? { ...component, entry: nextEntry } : component
29463
29519
  );
29464
29520
  setComponents(next);
29465
29521
  const db = openPipelineDb();
29466
29522
  try {
29467
- storeCDFComponents(db, extractSessionId, [{ key: target.key, entry: nextEntry }]);
29523
+ storeCDFComponents(db, extractSessionId, [{ key: parentKey, entry: nextEntry }]);
29468
29524
  } finally {
29469
29525
  db.close();
29470
29526
  }
29471
29527
  recomputeCycles(next);
29472
29528
  livePreviewHook.trigger();
29473
- pushHistorySnapshot(next, "break-cycle-edge");
29529
+ pushHistorySnapshot(next, historyLabel);
29530
+ };
29531
+ const handleBreakEdge = (edge) => {
29532
+ const target = components.find((component) => component.key === edge.fromComponent);
29533
+ const slot = target?.entry.$slots?.[edge.slotName];
29534
+ if (!target || !slot || !Array.isArray(slot.$allowedComponents)) return;
29535
+ saveSlotChange(
29536
+ target.key,
29537
+ {
29538
+ ...target.entry,
29539
+ $slots: {
29540
+ ...target.entry.$slots,
29541
+ [edge.slotName]: {
29542
+ ...slot,
29543
+ $allowedComponents: slot.$allowedComponents.filter((value) => value !== edge.toComponent)
29544
+ }
29545
+ }
29546
+ },
29547
+ "break-cycle-edge"
29548
+ );
29549
+ };
29550
+ const handleRemoveSlotChild = (parentKey, childKey) => {
29551
+ const parent = components.find((component) => component.key === parentKey);
29552
+ if (!parent?.entry.$slots) return;
29553
+ const slots = Object.fromEntries(
29554
+ Object.entries(parent.entry.$slots).map(([slotName, slot]) => [
29555
+ slotName,
29556
+ Array.isArray(slot.$allowedComponents) ? { ...slot, $allowedComponents: slot.$allowedComponents.filter((value) => value !== childKey) } : slot
29557
+ ])
29558
+ );
29559
+ saveSlotChange(parentKey, { ...parent.entry, $slots: slots }, "remove-slot-child");
29474
29560
  };
29475
29561
  const handleRejectComponent = (key) => {
29476
29562
  const rejectCascade2 = computeRejectCascade(key, componentGraph);
@@ -29800,6 +29886,11 @@ function GenerateReviewStepView({
29800
29886
  if (input === "r") {
29801
29887
  const current = components[selectedIdx];
29802
29888
  if (!current) return;
29889
+ const row = visibleRowsMemo[cursorRowIdx];
29890
+ if (row?.kind === "group-child" && row.parentName) {
29891
+ handleRemoveSlotChild(row.parentName, current.key);
29892
+ return;
29893
+ }
29803
29894
  handleRejectComponent(current.key);
29804
29895
  return;
29805
29896
  }
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-6c9e5304.0",
3
+ "version": "2.37.1-dev-build-e2dab518.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -80,5 +80,7 @@ export type GateAction = {
80
80
  type: 'toggle-jump-filter';
81
81
  } | {
82
82
  type: 'toggle-view';
83
+ } | {
84
+ type: 'undo';
83
85
  };
84
86
  export declare function resolveKey(mode: GateMode, key: KeyPress, ctx: GateContext): GateAction;
@@ -45,6 +45,8 @@ export interface VisibleRow {
45
45
  cycleChild?: boolean;
46
46
  itemIdx: number;
47
47
  rootName?: string;
48
+ /** The parent whose slot the nested row sits in. */
49
+ parentName?: string;
48
50
  }
49
51
  export declare function buildVisibleRows(props: {
50
52
  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 withoutSlotChildren(components: ScopeComponent[], removedEdges: ParentChildEdge[]): ScopeComponent[];
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, removedEdges: 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. Missing means none were removed. */
23
+ excludedSlotEdges?: ParentChildEdge[];
17
24
  }
18
25
  export type ScopeGateLoad = {
19
26
  ok: true;
@@ -10,7 +10,7 @@ export interface PendingRejectCascade {
10
10
  ancestors: string[];
11
11
  descendants: string[];
12
12
  }
13
- export declare function useScopeGate({ components, onConfirm, onQuit }: UseScopeGateOptions): {
13
+ export declare function useScopeGate({ components: loadedComponents, onConfirm, onQuit }: UseScopeGateOptions): {
14
14
  totalWidth: number;
15
15
  columnPlan: {
16
16
  layout: "single" | "two-column";
@@ -1,11 +1,9 @@
1
1
  import type { GenerationHandoff } from '@contentful/experience-design-system-types/handoff';
2
+ import type { ScopeDecisions } from '../07-scope-gate/types.js';
2
3
  export interface GenerationInput {
3
4
  /** The extraction session the scope was decided on. */
4
5
  sessionId: string;
5
- decisions: {
6
- accepted: string[];
7
- rejected: string[];
8
- };
6
+ decisions: ScopeDecisions;
9
7
  /**
10
8
  * The cache check that ran while the scope gate was open, which restores cached definitions into the pipeline
11
9
  * database. The stage waits for it before saving the decisions, since both write to that database.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-6c9e5304.0",
3
+ "version": "2.37.1-dev-build-e2dab518.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-6c9e5304.0",
3
+ "version": "2.37.1-dev-build-e2dab518.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,8 +45,8 @@
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-6c9e5304.0",
49
- "@contentful/experience-design-system-types": "2.37.1-dev-build-6c9e5304.0"
48
+ "@contentful/experience-design-system-types": "2.37.1-dev-build-e2dab518.0",
49
+ "@contentful/experience-design-system-backend-pipeline": "2.37.1-dev-build-e2dab518.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@tsconfig/node24": "^24.0.4",