@contentful/experience-design-system-cli 2.27.2-dev-build-240d86b.0 → 2.27.2-dev-build-d9f331e.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.27.2-dev-build-240d86b.0",
3
+ "version": "2.27.2-dev-build-d9f331e.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/dist/src/index.js CHANGED
@@ -16207,25 +16207,103 @@ var init_path_prompt = __esm({
16207
16207
  }
16208
16208
  });
16209
16209
 
16210
+ // packages/experience-design-system-cli/src/runs/picker-labels.ts
16211
+ function pathSegments(projectPath) {
16212
+ const normalised = projectPath.replace(/\\/g, "/");
16213
+ return normalised.split("/").filter((s) => s.length > 0);
16214
+ }
16215
+ function disambiguateLabels(runs) {
16216
+ const entries = runs.map((r) => ({
16217
+ id: r.id,
16218
+ segments: pathSegments(r.projectPath),
16219
+ level: 1
16220
+ }));
16221
+ const labelAt = (e) => {
16222
+ if (e.segments.length === 0) return e.id;
16223
+ const take = Math.min(e.level, e.segments.length);
16224
+ return e.segments.slice(-take).join("/");
16225
+ };
16226
+ let stable = false;
16227
+ while (!stable) {
16228
+ stable = true;
16229
+ const groups = /* @__PURE__ */ new Map();
16230
+ for (const e of entries) {
16231
+ const key = labelAt(e);
16232
+ const arr = groups.get(key);
16233
+ if (arr) arr.push(e);
16234
+ else groups.set(key, [e]);
16235
+ }
16236
+ for (const group of groups.values()) {
16237
+ if (group.length < 2) continue;
16238
+ const anyExpandable = group.some((e) => e.level < e.segments.length);
16239
+ if (!anyExpandable) continue;
16240
+ for (const e of group) {
16241
+ if (e.level < e.segments.length) e.level += 1;
16242
+ }
16243
+ stable = false;
16244
+ }
16245
+ }
16246
+ const result = /* @__PURE__ */ new Map();
16247
+ const orderedGroups = [];
16248
+ const byLabel = /* @__PURE__ */ new Map();
16249
+ for (const e of entries) {
16250
+ const key = labelAt(e);
16251
+ const existing = byLabel.get(key);
16252
+ if (existing) {
16253
+ existing.push(e);
16254
+ } else {
16255
+ const fresh = [e];
16256
+ byLabel.set(key, fresh);
16257
+ orderedGroups.push(fresh);
16258
+ }
16259
+ }
16260
+ for (const group of orderedGroups) {
16261
+ if (group.length === 1) {
16262
+ const only = group[0];
16263
+ result.set(only.id, labelAt(only));
16264
+ } else {
16265
+ group.forEach((e, i) => {
16266
+ result.set(e.id, `${labelAt(e)} (#${i + 1})`);
16267
+ });
16268
+ }
16269
+ }
16270
+ return result;
16271
+ }
16272
+ function pad22(n) {
16273
+ return String(n).padStart(2, "0");
16274
+ }
16275
+ function formatRelativeTime(iso, now = /* @__PURE__ */ new Date()) {
16276
+ const then = new Date(iso);
16277
+ if (Number.isNaN(then.getTime())) return iso;
16278
+ const diffMs = now.getTime() - then.getTime();
16279
+ const diffSec = Math.floor(diffMs / 1e3);
16280
+ if (diffSec < 60) return "just now";
16281
+ const diffMin = Math.floor(diffSec / 60);
16282
+ if (diffMin < 60) return `${diffMin}m ago`;
16283
+ const diffHr = Math.floor(diffMin / 60);
16284
+ if (diffHr < 24) return `${diffHr}h ago`;
16285
+ const diffDay = Math.floor(diffHr / 24);
16286
+ if (diffDay < 7) return `${diffDay}d ago`;
16287
+ return `${then.getFullYear()}-${pad22(then.getMonth() + 1)}-${pad22(then.getDate())} ${pad22(then.getHours())}:${pad22(then.getMinutes())}`;
16288
+ }
16289
+ var init_picker_labels = __esm({
16290
+ "packages/experience-design-system-cli/src/runs/picker-labels.ts"() {
16291
+ "use strict";
16292
+ }
16293
+ });
16294
+
16210
16295
  // packages/experience-design-system-cli/src/runs/run-picker.tsx
16211
- import { useState as useState10 } from "react";
16296
+ import { useMemo as useMemo2, useState as useState10 } from "react";
16212
16297
  import { Box as Box20, Text as Text20 } from "ink";
16213
16298
  import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
16214
- function formatCreatedAt(iso) {
16215
- const d = new Date(iso);
16216
- if (Number.isNaN(d.getTime())) return iso;
16217
- const yyyy = d.getFullYear();
16218
- const mm = String(d.getMonth() + 1).padStart(2, "0");
16219
- const dd = String(d.getDate()).padStart(2, "0");
16220
- const hh = String(d.getHours()).padStart(2, "0");
16221
- const mi = String(d.getMinutes()).padStart(2, "0");
16222
- return `${yyyy}-${mm}-${dd} ${hh}:${mi}`;
16223
- }
16224
- function runLine(run) {
16225
- const date = formatCreatedAt(run.createdAt);
16226
- const pushedTag = run.pushedTo ? "pushed" : "not pushed";
16299
+ function pushStatus(run) {
16300
+ if (!run.pushedTo) return "not pushed";
16301
+ return `\u2192 ${run.pushedTo.spaceId}/${run.pushedTo.environmentId}`;
16302
+ }
16303
+ function runLine(run, label) {
16304
+ const when = formatRelativeTime(run.createdAt);
16227
16305
  const count = `${run.componentCount} component${run.componentCount === 1 ? "" : "s"}`;
16228
- return `${run.id} - ${date} - ${run.projectPath} (${count}, ${pushedTag})`;
16306
+ return `${label} ${when} ${count} ${pushStatus(run)}`;
16229
16307
  }
16230
16308
  function ActionScreen({
16231
16309
  runId,
@@ -16293,6 +16371,12 @@ function RunPicker({ runs, staleRunIds, onSelect, onCancel }) {
16293
16371
  const [actionRunId, setActionRunId] = useState10(null);
16294
16372
  const rows = buildRows(runs, expanded);
16295
16373
  const clampedFocus = Math.min(focusIdx, rows.length - 1);
16374
+ const visibleRunLabels = useMemo2(() => {
16375
+ const visibleRuns = rows.filter((r) => r.kind === "run").map((r) => ({ id: r.run.id, projectPath: r.run.projectPath }));
16376
+ return disambiguateLabels(visibleRuns);
16377
+ }, [rows]);
16378
+ const focusedRow = rows[clampedFocus];
16379
+ const focusedRunId = focusedRow?.kind === "run" ? focusedRow.run.id : null;
16296
16380
  const selectRow = (row) => {
16297
16381
  if (row.kind === "show-all") {
16298
16382
  setExpanded(true);
@@ -16355,9 +16439,10 @@ function RunPicker({ runs, staleRunIds, onSelect, onCancel }) {
16355
16439
  const cursor = focused ? ">" : " ";
16356
16440
  if (row.kind === "run") {
16357
16441
  const isStale = staleRunIds?.has(row.run.id) ?? false;
16442
+ const label = visibleRunLabels.get(row.run.id) ?? row.run.projectPath;
16358
16443
  return /* @__PURE__ */ jsxs20(Box20, { gap: 1, children: [
16359
16444
  /* @__PURE__ */ jsx20(Text20, { color, children: cursor }),
16360
- /* @__PURE__ */ jsx20(Text20, { color, children: runLine(row.run) }),
16445
+ /* @__PURE__ */ jsx20(Text20, { color, children: runLine(row.run, label) }),
16361
16446
  isStale ? /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: " (stale)" }) : null
16362
16447
  ] }, `run-${row.run.id}`);
16363
16448
  }
@@ -16376,6 +16461,10 @@ function RunPicker({ runs, staleRunIds, onSelect, onCancel }) {
16376
16461
  /* @__PURE__ */ jsx20(Text20, { color, children: "[n] Start a new run" })
16377
16462
  ] }, "new");
16378
16463
  }) }),
16464
+ focusedRunId ? /* @__PURE__ */ jsx20(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
16465
+ "ID: ",
16466
+ focusedRunId
16467
+ ] }) }) : null,
16379
16468
  /* @__PURE__ */ jsxs20(Box20, { gap: 3, marginTop: 1, children: [
16380
16469
  /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "[j/k] Navigate" }),
16381
16470
  /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "[Enter] Select" }),
@@ -16389,6 +16478,7 @@ var init_run_picker = __esm({
16389
16478
  "packages/experience-design-system-cli/src/runs/run-picker.tsx"() {
16390
16479
  "use strict";
16391
16480
  init_useImmediateInput();
16481
+ init_picker_labels();
16392
16482
  COLLAPSED_LIMIT = 10;
16393
16483
  COLLAPSE_THRESHOLD = 12;
16394
16484
  ACTION_OPTIONS = [
@@ -17327,7 +17417,7 @@ var init_preview_diff = __esm({
17327
17417
  });
17328
17418
 
17329
17419
  // packages/experience-design-system-cli/src/import/tui/steps/WizardPreviewStep.tsx
17330
- import { useState as useState16, useMemo as useMemo2 } from "react";
17420
+ import { useState as useState16, useMemo as useMemo3 } from "react";
17331
17421
  import { Box as Box28, Text as Text28, useStdout as useStdout3 } from "ink";
17332
17422
  import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs28 } from "react/jsx-runtime";
17333
17423
  function buildPreviewDiffLines(preview) {
@@ -17433,7 +17523,7 @@ function WizardPreviewStep({
17433
17523
  const { stdout } = useStdout3();
17434
17524
  const terminalRows = stdout?.rows ?? 40;
17435
17525
  const viewportHeight = Math.max(terminalRows - 14, 10);
17436
- const allDiffLines = useMemo2(() => {
17526
+ const allDiffLines = useMemo3(() => {
17437
17527
  if (!diffExpanded) return [];
17438
17528
  return buildPreviewDiffLines(preview).map((line) => ({
17439
17529
  key: line.key,
@@ -19179,12 +19269,12 @@ var init_lineage = __esm({
19179
19269
  });
19180
19270
 
19181
19271
  // packages/experience-design-system-cli/src/import/tui/hooks/useLineage.ts
19182
- import { useMemo as useMemo3 } from "react";
19272
+ import { useMemo as useMemo4 } from "react";
19183
19273
  function useLineage(focusedKey, graph) {
19184
- const graphKey = useMemo3(() => JSON.stringify(graph), [graph]);
19185
- const stableGraph = useMemo3(() => graph, [graphKey]);
19186
- const closures = useMemo3(() => computeAllClosures(stableGraph), [stableGraph]);
19187
- const entries = useMemo3(() => {
19274
+ const graphKey = useMemo4(() => JSON.stringify(graph), [graph]);
19275
+ const stableGraph = useMemo4(() => graph, [graphKey]);
19276
+ const closures = useMemo4(() => computeAllClosures(stableGraph), [stableGraph]);
19277
+ const entries = useMemo4(() => {
19188
19278
  if (!focusedKey) return [];
19189
19279
  const name = focusedKey;
19190
19280
  const tree = buildAncestorTree(name, stableGraph);
@@ -19218,7 +19308,7 @@ function useLineage(focusedKey, graph) {
19218
19308
  }
19219
19309
  return out;
19220
19310
  }, [focusedKey, stableGraph, closures]);
19221
- const jumpables = useMemo3(
19311
+ const jumpables = useMemo4(
19222
19312
  () => entries.map((entry, i) => ({ entry, i })).filter(({ entry }) => entry.kind === "ancestor" || entry.kind === "descendant"),
19223
19313
  [entries]
19224
19314
  );
@@ -19864,7 +19954,7 @@ var init_scope_gate_columns = __esm({
19864
19954
 
19865
19955
  // packages/experience-design-system-cli/src/import/tui/steps/ScopeGateStep.tsx
19866
19956
  import { Box as Box38, Text as Text40, useStdout as useStdout4 } from "ink";
19867
- import React20, { useEffect as useEffect10, useMemo as useMemo4, useRef as useRef4, useState as useState21 } from "react";
19957
+ import React20, { useEffect as useEffect10, useMemo as useMemo5, useRef as useRef4, useState as useState21 } from "react";
19868
19958
  import { Fragment as Fragment11, jsx as jsx41, jsxs as jsxs40 } from "react/jsx-runtime";
19869
19959
  function capReasonForFocusedRow(reason, width) {
19870
19960
  const safeWidth = Math.max(20, width);
@@ -19897,7 +19987,7 @@ function ScopeGateStep({
19897
19987
  }) {
19898
19988
  const { stdout } = useStdout4();
19899
19989
  const totalWidth = stdout?.columns ?? 80;
19900
- const columnPlan = useMemo4(() => computeColumnWidths(totalWidth), [totalWidth]);
19990
+ const columnPlan = useMemo5(() => computeColumnWidths(totalWidth), [totalWidth]);
19901
19991
  const sidebarWidth = columnPlan.main;
19902
19992
  const [userDecisions, setUserDecisions] = useState21(/* @__PURE__ */ new Map());
19903
19993
  const [nav, setNav] = useState21({ cursor: 0, scrollOffset: 0 });
@@ -19932,7 +20022,7 @@ function ScopeGateStep({
19932
20022
  return next;
19933
20023
  });
19934
20024
  };
19935
- const groupedItems = useMemo4(
20025
+ const groupedItems = useMemo5(
19936
20026
  () => components.map((c2) => ({
19937
20027
  key: c2.name,
19938
20028
  entry: toSidebarEntry(c2),
@@ -19940,21 +20030,21 @@ function ScopeGateStep({
19940
20030
  })),
19941
20031
  [components]
19942
20032
  );
19943
- const graph = useMemo4(() => buildComponentGraph(groupedItems), [groupedItems]);
19944
- const slotCycles = useMemo4(() => {
20033
+ const graph = useMemo5(() => buildComponentGraph(groupedItems), [groupedItems]);
20034
+ const slotCycles = useMemo5(() => {
19945
20035
  try {
19946
20036
  return findSlotCycles(graph);
19947
20037
  } catch {
19948
20038
  return [];
19949
20039
  }
19950
20040
  }, [graph]);
19951
- const cycleParticipants = useMemo4(() => {
20041
+ const cycleParticipants = useMemo5(() => {
19952
20042
  const set = /* @__PURE__ */ new Set();
19953
20043
  for (const c2 of slotCycles) for (const n of c2.path) set.add(n);
19954
20044
  return set;
19955
20045
  }, [slotCycles]);
19956
- const cycleUnits = useMemo4(() => buildCycleUnits(slotCycles), [slotCycles]);
19957
- const cyclesJumpables = useMemo4(
20046
+ const cycleUnits = useMemo5(() => buildCycleUnits(slotCycles), [slotCycles]);
20047
+ const cyclesJumpables = useMemo5(
19958
20048
  () => slotCycles.map((c2, i) => ({
19959
20049
  cycleIndex: i,
19960
20050
  jumpTarget: c2.path[0]
@@ -19962,7 +20052,7 @@ function ScopeGateStep({
19962
20052
  [slotCycles]
19963
20053
  );
19964
20054
  const hasCycles = slotCycles.length > 0;
19965
- const closures = useMemo4(() => computeAllClosures(graph), [graph]);
20055
+ const closures = useMemo5(() => computeAllClosures(graph), [graph]);
19966
20056
  const [expandedGroups, setExpandedGroups] = useState21(() => {
19967
20057
  const seed = new Set(closures.keys());
19968
20058
  for (const p of cycleParticipants) seed.add(p);
@@ -19977,7 +20067,7 @@ function ScopeGateStep({
19977
20067
  for (const p of cycleParticipants) seed.add(p);
19978
20068
  setExpandedGroups(seed);
19979
20069
  }, [closures, cycleParticipants]);
19980
- const hasGroupRoots = useMemo4(() => {
20070
+ const hasGroupRoots = useMemo5(() => {
19981
20071
  if (cycleParticipants.size > 0) return true;
19982
20072
  for (const c2 of closures.values()) if (c2.nodes.length > 1) return true;
19983
20073
  return false;
@@ -19990,12 +20080,12 @@ function ScopeGateStep({
19990
20080
  return next;
19991
20081
  });
19992
20082
  };
19993
- const brokenKeys = useMemo4(() => {
20083
+ const brokenKeys = useMemo5(() => {
19994
20084
  const set = /* @__PURE__ */ new Set();
19995
20085
  for (const c2 of components) if (isAiFlagged(c2)) set.add(c2.name);
19996
20086
  return set;
19997
20087
  }, [components]);
19998
- const filterVisibleKeys = useMemo4(() => {
20088
+ const filterVisibleKeys = useMemo5(() => {
19999
20089
  if (jumpFilterTarget) {
20000
20090
  return findAllAncestors2(jumpFilterTarget, graph);
20001
20091
  }
@@ -20011,7 +20101,7 @@ function ScopeGateStep({
20011
20101
  })();
20012
20102
  return intersectFilterKeys(categoryKeys, searchKeys);
20013
20103
  }, [jumpFilterTarget, activeFilters, cycleParticipants, brokenKeys, searchQuery, groupedItems, graph]);
20014
- const visibleRows = useMemo4(
20104
+ const visibleRows = useMemo5(
20015
20105
  () => buildVisibleRows({
20016
20106
  items: groupedItems,
20017
20107
  cycleParticipants,
@@ -20028,19 +20118,19 @@ function ScopeGateStep({
20028
20118
  const currentRow = visibleRows[safeCursor];
20029
20119
  const currentRowKey = currentRow && currentRow.itemIdx >= 0 ? groupedItems[currentRow.itemIdx]?.key : void 0;
20030
20120
  const focusedComponent = currentRowKey ? components.find((c2) => c2.name === currentRowKey) : void 0;
20031
- const selectionStateByKey = useMemo4(() => {
20121
+ const selectionStateByKey = useMemo5(() => {
20032
20122
  const map = /* @__PURE__ */ new Map();
20033
20123
  for (const c2 of components) {
20034
20124
  map.set(c2.name, getState(c2.name));
20035
20125
  }
20036
20126
  return map;
20037
20127
  }, [components, userDecisions]);
20038
- const aiFlaggedByKey = useMemo4(() => {
20128
+ const aiFlaggedByKey = useMemo5(() => {
20039
20129
  const map = /* @__PURE__ */ new Map();
20040
20130
  for (const c2 of components) map.set(c2.name, isAiFlagged(c2));
20041
20131
  return map;
20042
20132
  }, [components]);
20043
- const dimPredicate = useMemo4(
20133
+ const dimPredicate = useMemo5(
20044
20134
  () => buildFlatDimPredicate({
20045
20135
  viewMode: columnOneView,
20046
20136
  searchQuery,
@@ -20113,7 +20203,7 @@ function ScopeGateStep({
20113
20203
  return { cursor: nextCursor, scrollOffset: nextScroll };
20114
20204
  });
20115
20205
  }, [visibleRows]);
20116
- const searchMatches = useMemo4(() => {
20206
+ const searchMatches = useMemo5(() => {
20117
20207
  if (!searchQuery) return [];
20118
20208
  return components.filter((c2) => fuzzyMatches(searchQuery, c2.name)).map((c2) => c2.name);
20119
20209
  }, [components, searchQuery]);
@@ -20509,13 +20599,13 @@ function ScopeGateStep({
20509
20599
  return;
20510
20600
  }
20511
20601
  });
20512
- const includedCount = useMemo4(() => components.filter((c2) => isIncluded(c2.name)).length, [components, userDecisions]);
20602
+ const includedCount = useMemo5(() => components.filter((c2) => isIncluded(c2.name)).length, [components, userDecisions]);
20513
20603
  const hasAnyAi = components.some(isAiFlagged);
20514
20604
  const aiExcludedCount = components.filter(isAiFlagged).length;
20515
20605
  const aiExcludedWithReasons = components.filter(
20516
20606
  (c2) => isAiFlagged(c2) && c2.aiReason !== null && c2.aiReason !== void 0 && c2.aiReason !== ""
20517
20607
  );
20518
- const aiRows = useMemo4(
20608
+ const aiRows = useMemo5(
20519
20609
  () => aiExcludedWithReasons.map((c2) => ({
20520
20610
  label: `${c2.name} \u2014 ${c2.aiReason}`,
20521
20611
  jumpTarget: c2.name
@@ -20526,15 +20616,15 @@ function ScopeGateStep({
20526
20616
  const nothingIncluded = components.length > 0 && components.every((c2) => !isIncluded(c2.name));
20527
20617
  const totalComponents = components.length;
20528
20618
  const totalMatches = searchQuery ? searchMatches.length : 0;
20529
- const addedComponents = useMemo4(
20619
+ const addedComponents = useMemo5(
20530
20620
  () => buildAddedComponentsList(components, selectionStateByKey, cycleParticipants),
20531
20621
  [components, selectionStateByKey, cycleParticipants]
20532
20622
  );
20533
- const addedGroups = useMemo4(
20623
+ const addedGroups = useMemo5(
20534
20624
  () => buildAddedGroupsList(closures, selectionStateByKey, cycleParticipants, cycleUnits),
20535
20625
  [closures, selectionStateByKey, cycleParticipants, cycleUnits]
20536
20626
  );
20537
- const counters = useMemo4(
20627
+ const counters = useMemo5(
20538
20628
  () => computeCounters(components, closures, selectionStateByKey),
20539
20629
  [components, closures, selectionStateByKey]
20540
20630
  );
@@ -20995,7 +21085,7 @@ var init_ScopeGateStep = __esm({
20995
21085
 
20996
21086
  // packages/experience-design-system-cli/src/import/tui/steps/AtomicScopeGateStep.tsx
20997
21087
  import { Box as Box39, Text as Text41, useStdout as useStdout5 } from "ink";
20998
- import React21, { useMemo as useMemo5, useState as useState22 } from "react";
21088
+ import React21, { useMemo as useMemo6, useState as useState22 } from "react";
20999
21089
  import { jsx as jsx42, jsxs as jsxs41 } from "react/jsx-runtime";
21000
21090
  function truncateReason(reason) {
21001
21091
  if (reason === null || reason === void 0 || reason === "") return "<no reason given>";
@@ -21142,7 +21232,7 @@ function AtomicScopeGateStep({
21142
21232
  }
21143
21233
  });
21144
21234
  const total = flatList.length;
21145
- const includedCount = useMemo5(
21235
+ const includedCount = useMemo6(
21146
21236
  () => flatList.filter((c2) => isIncluded(c2)).length,
21147
21237
  [components, userExcluded, userUnExcluded]
21148
21238
  );
@@ -23667,7 +23757,7 @@ var init_history = __esm({
23667
23757
  });
23668
23758
 
23669
23759
  // packages/experience-design-system-cli/src/import/tui/steps/GenerateReviewStep.tsx
23670
- import { useEffect as useEffect13, useMemo as useMemo6, useRef as useRef7, useState as useState26 } from "react";
23760
+ import { useEffect as useEffect13, useMemo as useMemo7, useRef as useRef7, useState as useState26 } from "react";
23671
23761
  import { Box as Box44, Text as Text46, useStdout as useStdout6 } from "ink";
23672
23762
  import { Fragment as Fragment14, jsx as jsx47, jsxs as jsxs46 } from "react/jsx-runtime";
23673
23763
  function sortComponentsForSidebar2(components, cycleParticipants) {
@@ -23963,12 +24053,12 @@ function GenerateReviewStep({
23963
24053
  } catch {
23964
24054
  }
23965
24055
  };
23966
- const componentGraph = useMemo6(() => buildComponentGraph(components), [components]);
23967
- const sidebarGraph = useMemo6(
24056
+ const componentGraph = useMemo7(() => buildComponentGraph(components), [components]);
24057
+ const sidebarGraph = useMemo7(
23968
24058
  () => buildComponentGraph(components, { stripRejectedEdges: true }),
23969
24059
  [components]
23970
24060
  );
23971
- const closures = useMemo6(() => computeAllClosures(componentGraph), [componentGraph]);
24061
+ const closures = useMemo7(() => computeAllClosures(componentGraph), [componentGraph]);
23972
24062
  useEffect13(() => {
23973
24063
  if (seededGroupsRef.current) return;
23974
24064
  if (closures.size === 0 && slotCycles.length === 0) return;
@@ -23977,14 +24067,14 @@ function GenerateReviewStep({
23977
24067
  for (const cyc of slotCycles) for (const p of cyc.path) seed.add(p);
23978
24068
  setExpandedGroups(seed);
23979
24069
  }, [closures, slotCycles]);
23980
- const directIssues = useMemo6(() => {
24070
+ const directIssues = useMemo7(() => {
23981
24071
  const m = /* @__PURE__ */ new Map();
23982
24072
  for (const c2 of components) {
23983
24073
  if (c2.status === "rejected") m.set(c2.key, "error");
23984
24074
  }
23985
24075
  return m;
23986
24076
  }, [components]);
23987
- const cycleView = useMemo6(() => computeCycleView(components), [components]);
24077
+ const cycleView = useMemo7(() => computeCycleView(components), [components]);
23988
24078
  useEffect13(() => {
23989
24079
  const decision = computeAutoRejectDecision({
23990
24080
  loading,
@@ -24094,7 +24184,7 @@ function GenerateReviewStep({
24094
24184
  setLoadError(String(e));
24095
24185
  }
24096
24186
  };
24097
- const groupedItemsMemo = useMemo6(
24187
+ const groupedItemsMemo = useMemo7(
24098
24188
  () => components.map((c2) => ({
24099
24189
  key: c2.key,
24100
24190
  entry: c2.entry,
@@ -24102,12 +24192,12 @@ function GenerateReviewStep({
24102
24192
  })),
24103
24193
  [components, directIssues]
24104
24194
  );
24105
- const brokenKeys = useMemo6(
24195
+ const brokenKeys = useMemo7(
24106
24196
  () => new Set(breakingChanges.map((b) => b.componentName)),
24107
24197
  [breakingChanges]
24108
24198
  );
24109
- const breakingRows = useMemo6(() => buildBreakingRows(breakingChanges), [breakingChanges]);
24110
- const filterVisibleKeys = useMemo6(() => {
24199
+ const breakingRows = useMemo7(() => buildBreakingRows(breakingChanges), [breakingChanges]);
24200
+ const filterVisibleKeys = useMemo7(() => {
24111
24201
  if (jumpFilterTarget) {
24112
24202
  return findAllAncestors2(jumpFilterTarget, sidebarGraph);
24113
24203
  }
@@ -24123,7 +24213,7 @@ function GenerateReviewStep({
24123
24213
  })();
24124
24214
  return intersectFilterKeys(categoryKeys, searchKeys);
24125
24215
  }, [jumpFilterTarget, activeFilters, cycleView, brokenKeys, searchQuery, groupedItemsMemo, sidebarGraph]);
24126
- const visibleRowsMemo = useMemo6(
24216
+ const visibleRowsMemo = useMemo7(
24127
24217
  () => buildVisibleRows({
24128
24218
  items: groupedItemsMemo,
24129
24219
  cycleParticipants: cycleView.structural,
@@ -24134,7 +24224,7 @@ function GenerateReviewStep({
24134
24224
  }),
24135
24225
  [groupedItemsMemo, cycleView, expandedGroups, columnOneView, sidebarGraph, filterVisibleKeys]
24136
24226
  );
24137
- const selectableRowPositions = useMemo6(() => {
24227
+ const selectableRowPositions = useMemo7(() => {
24138
24228
  const out = [];
24139
24229
  for (let i = 0; i < visibleRowsMemo.length; i++) {
24140
24230
  if (visibleRowsMemo[i].itemIdx >= 0) out.push(i);
@@ -24191,7 +24281,7 @@ function GenerateReviewStep({
24191
24281
  db.close();
24192
24282
  }
24193
24283
  }, [selectedIdx, components, extractSessionId]);
24194
- const renderStatusByKey = useMemo6(() => {
24284
+ const renderStatusByKey = useMemo7(() => {
24195
24285
  const merged = /* @__PURE__ */ new Map();
24196
24286
  for (const closure of closures.values()) {
24197
24287
  const per = computeRenderStatuses(closure, directIssues);
@@ -24204,7 +24294,7 @@ function GenerateReviewStep({
24204
24294
  }
24205
24295
  return merged;
24206
24296
  }, [closures, directIssues]);
24207
- const selectionStateByKey = useMemo6(() => {
24297
+ const selectionStateByKey = useMemo7(() => {
24208
24298
  const map = /* @__PURE__ */ new Map();
24209
24299
  for (const c2 of components) {
24210
24300
  if (c2.status === "accepted") map.set(c2.key, "accepted");
@@ -24213,7 +24303,7 @@ function GenerateReviewStep({
24213
24303
  }
24214
24304
  return map;
24215
24305
  }, [components]);
24216
- const searchMatches = useMemo6(() => {
24306
+ const searchMatches = useMemo7(() => {
24217
24307
  if (!searchQuery) return [];
24218
24308
  const out = [];
24219
24309
  for (const pos of selectableRowPositions) {
@@ -24223,7 +24313,7 @@ function GenerateReviewStep({
24223
24313
  }
24224
24314
  return out;
24225
24315
  }, [searchQuery, selectableRowPositions, visibleRowsMemo, components]);
24226
- const searchMatchCount = useMemo6(() => {
24316
+ const searchMatchCount = useMemo7(() => {
24227
24317
  if (searchMatches.length === 0) return 0;
24228
24318
  const seen = /* @__PURE__ */ new Set();
24229
24319
  for (const pos of searchMatches) {
@@ -24232,7 +24322,7 @@ function GenerateReviewStep({
24232
24322
  }
24233
24323
  return seen.size;
24234
24324
  }, [searchMatches, visibleRowsMemo]);
24235
- const dimPredicate = useMemo6(
24325
+ const dimPredicate = useMemo7(
24236
24326
  () => buildFlatDimPredicate({
24237
24327
  viewMode: columnOneView,
24238
24328
  searchQuery,
@@ -24300,7 +24390,7 @@ function GenerateReviewStep({
24300
24390
  setDraftValue("");
24301
24391
  setSaveError(null);
24302
24392
  };
24303
- const breakEdges = useMemo6(() => {
24393
+ const breakEdges = useMemo7(() => {
24304
24394
  const cycle = slotCycles[cyclesCursor];
24305
24395
  if (!cycle) return [];
24306
24396
  return enumerateCycleBreaks(cycle, components);
@@ -0,0 +1,24 @@
1
+ export type LabelInput = {
2
+ id: string;
3
+ projectPath: string;
4
+ };
5
+ /**
6
+ * Map each run to a display label. Labels start as the project path's leaf
7
+ * directory and expand one segment at a time (`<parent>/<leaf>`,
8
+ * `<gp>/<parent>/<leaf>`, …) until every label in the input set is unique.
9
+ * When two rows share the exact same absolute path, the collision is broken
10
+ * with numeric suffixes (`(#1)`, `(#2)`, …) in input order.
11
+ */
12
+ export declare function disambiguateLabels(runs: readonly LabelInput[]): Map<string, string>;
13
+ /**
14
+ * Format an ISO-8601 timestamp relative to `now`. Buckets:
15
+ * < 60s → "just now"
16
+ * < 60 min → "Nm ago"
17
+ * < 24 h → "Nh ago"
18
+ * < 7 days → "Nd ago"
19
+ * otherwise → "YYYY-MM-DD HH:MM" (local time)
20
+ *
21
+ * Future timestamps (created after `now`) also render as "just now" — they
22
+ * only occur with clock skew and the delta is not worth surfacing.
23
+ */
24
+ export declare function formatRelativeTime(iso: string, now?: Date): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.27.2-dev-build-240d86b.0",
3
+ "version": "2.27.2-dev-build-d9f331e.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "svelte": "^5.56.4",
40
40
  "ts-morph": "^27.0.2",
41
41
  "typescript": "^5.9.3",
42
- "@contentful/experience-design-system-types": "2.27.2-dev-build-240d86b.0"
42
+ "@contentful/experience-design-system-types": "2.27.2-dev-build-d9f331e.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@tsconfig/node24": "^24.0.4",
@@ -51,9 +51,9 @@
51
51
  "ink-testing-library": "^4.0.0",
52
52
  "typescript-eslint": "^8.67.0",
53
53
  "vitest": "^4.0.16",
54
- "@contentful/experience-design-system-client": "2.27.2-dev-build-240d86b.0",
55
- "@contentful/experience-design-system-generation": "2.27.2-dev-build-240d86b.0",
56
- "@contentful/experience-design-system-extraction": "2.27.2-dev-build-240d86b.0"
54
+ "@contentful/experience-design-system-extraction": "2.27.2-dev-build-d9f331e.0",
55
+ "@contentful/experience-design-system-generation": "2.27.2-dev-build-d9f331e.0",
56
+ "@contentful/experience-design-system-client": "2.27.2-dev-build-d9f331e.0"
57
57
  },
58
58
  "repository": {
59
59
  "type": "git",