ripencli 0.2.4 → 0.2.6

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.
Files changed (2) hide show
  1. package/dist/cli.js +396 -82
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -522,7 +522,12 @@ async function updatePackages(manager, packages, cwd, global = false, onLine) {
522
522
  }
523
523
  //#endregion
524
524
  //#region src/config.ts
525
- const DEFAULT_CONFIG = { groupByScope: true };
525
+ const DEFAULT_CONFIG = {
526
+ groupByScope: false,
527
+ groupScopes: [],
528
+ groupsOnTop: false,
529
+ frequencySort: false
530
+ };
526
531
  const CONFIG_DIR = join(homedir(), ".config", "ripen");
527
532
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
528
533
  function loadConfig() {
@@ -543,6 +548,23 @@ function saveConfig(config) {
543
548
  writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
544
549
  } catch {}
545
550
  }
551
+ const FREQUENCY_PATH = join(CONFIG_DIR, "frequency.json");
552
+ function loadFrequency() {
553
+ try {
554
+ const raw = readFileSync(FREQUENCY_PATH, "utf-8");
555
+ return JSON.parse(raw);
556
+ } catch {
557
+ return {};
558
+ }
559
+ }
560
+ function incrementFrequency(packageNames) {
561
+ try {
562
+ const freq = loadFrequency();
563
+ for (const name of packageNames) freq[name] = (freq[name] ?? 0) + 1;
564
+ mkdirSync(CONFIG_DIR, { recursive: true });
565
+ writeFileSync(FREQUENCY_PATH, JSON.stringify(freq, null, 2) + "\n", "utf-8");
566
+ } catch {}
567
+ }
546
568
  //#endregion
547
569
  //#region src/ui/PackageList.tsx
548
570
  const TYPE_COLORS = {
@@ -565,7 +587,7 @@ const GROUP_CHROME = 5;
565
587
  function getScope(name) {
566
588
  return name.match(/^(@[^/]+)\//)?.[1] ?? null;
567
589
  }
568
- function buildDisplayRows(packages, groupByScope) {
590
+ function buildDisplayRows(packages, groupByScope = false, groupScopes = [], groupsOnTop = false, frequencySort = false, frequency = {}) {
569
591
  const grouped = /* @__PURE__ */ new Map();
570
592
  packages.forEach((pkg, i) => {
571
593
  if (!grouped.has(pkg.type)) grouped.set(pkg.type, []);
@@ -574,6 +596,12 @@ function buildDisplayRows(packages, groupByScope) {
574
596
  index: i
575
597
  });
576
598
  });
599
+ const freqSort = (a, b) => {
600
+ const fa = frequency[a.pkg.name] ?? 0;
601
+ const fb = frequency[b.pkg.name] ?? 0;
602
+ if (fb !== fa) return fb - fa;
603
+ return a.pkg.name.localeCompare(b.pkg.name);
604
+ };
577
605
  const rows = [];
578
606
  for (const type of GROUP_ORDER) {
579
607
  const items = grouped.get(type);
@@ -585,54 +613,105 @@ function buildDisplayRows(packages, groupByScope) {
585
613
  label: GROUP_LABELS[type] ?? type,
586
614
  packages: allPkgs
587
615
  });
588
- if (groupByScope) {
616
+ if (groupByScope && groupScopes.length > 0) {
589
617
  const scopeMap = /* @__PURE__ */ new Map();
590
- const unscoped = [];
618
+ const ungrouped = [];
591
619
  for (const item of items) {
592
620
  const scope = getScope(item.pkg.name);
593
- if (scope) {
621
+ if (scope && groupScopes.includes(scope)) {
594
622
  if (!scopeMap.has(scope)) scopeMap.set(scope, []);
595
623
  scopeMap.get(scope).push(item);
596
- } else unscoped.push(item);
624
+ } else ungrouped.push(item);
597
625
  }
598
- const emittedScopes = /* @__PURE__ */ new Set();
599
- for (const item of items) {
600
- const scope = getScope(item.pkg.name);
601
- if (scope && scopeMap.get(scope).length >= 2) {
602
- if (!emittedScopes.has(scope)) {
603
- emittedScopes.add(scope);
604
- const scopeItems = scopeMap.get(scope);
605
- const scopeKey = `${type}::${scope}`;
606
- rows.push({
607
- kind: "scope-header",
608
- groupType: type,
609
- scope,
610
- packageIndices: scopeItems.map((si) => si.index),
611
- packages: scopeItems.map((si) => si.pkg)
612
- });
613
- for (const si of scopeItems) rows.push({
614
- kind: "package",
615
- pkg: si.pkg,
616
- packageIndex: si.index,
617
- indented: true,
618
- scopeKey
619
- });
620
- }
621
- } else rows.push({
626
+ const scopeGroups = [];
627
+ for (const [scope, scopeItems] of scopeMap) if (scopeItems.length >= 2) scopeGroups.push({
628
+ scope,
629
+ items: scopeItems
630
+ });
631
+ else ungrouped.push(...scopeItems);
632
+ if (frequencySort) scopeGroups.sort((a, b) => {
633
+ const maxA = Math.max(...a.items.map((i) => frequency[i.pkg.name] ?? 0));
634
+ const maxB = Math.max(...b.items.map((i) => frequency[i.pkg.name] ?? 0));
635
+ if (maxB !== maxA) return maxB - maxA;
636
+ return a.scope.localeCompare(b.scope);
637
+ });
638
+ else scopeGroups.sort((a, b) => a.scope.localeCompare(b.scope));
639
+ const emitScopeGroups = () => {
640
+ for (const { scope, items: scopeItems } of scopeGroups) {
641
+ if (frequencySort) scopeItems.sort(freqSort);
642
+ const scopeKey = `${type}::${scope}`;
643
+ rows.push({
644
+ kind: "scope-header",
645
+ groupType: type,
646
+ scope,
647
+ packageIndices: scopeItems.map((si) => si.index),
648
+ packages: scopeItems.map((si) => si.pkg)
649
+ });
650
+ for (const si of scopeItems) rows.push({
651
+ kind: "package",
652
+ pkg: si.pkg,
653
+ packageIndex: si.index,
654
+ indented: true,
655
+ scopeKey
656
+ });
657
+ }
658
+ };
659
+ const emitUngrouped = () => {
660
+ if (frequencySort) ungrouped.sort(freqSort);
661
+ for (const item of ungrouped) rows.push({
622
662
  kind: "package",
623
663
  pkg: item.pkg,
624
664
  packageIndex: item.index,
625
665
  indented: false,
626
666
  scopeKey: null
627
667
  });
668
+ };
669
+ if (groupsOnTop || frequencySort) {
670
+ emitScopeGroups();
671
+ emitUngrouped();
672
+ } else {
673
+ const emittedScopes = /* @__PURE__ */ new Set();
674
+ for (const item of items) {
675
+ const scope = getScope(item.pkg.name);
676
+ const group = scope ? scopeGroups.find((g) => g.scope === scope) : void 0;
677
+ if (group) {
678
+ if (!emittedScopes.has(scope)) {
679
+ emittedScopes.add(scope);
680
+ const scopeKey = `${type}::${scope}`;
681
+ rows.push({
682
+ kind: "scope-header",
683
+ groupType: type,
684
+ scope,
685
+ packageIndices: group.items.map((si) => si.index),
686
+ packages: group.items.map((si) => si.pkg)
687
+ });
688
+ for (const si of group.items) rows.push({
689
+ kind: "package",
690
+ pkg: si.pkg,
691
+ packageIndex: si.index,
692
+ indented: true,
693
+ scopeKey
694
+ });
695
+ }
696
+ } else rows.push({
697
+ kind: "package",
698
+ pkg: item.pkg,
699
+ packageIndex: item.index,
700
+ indented: false,
701
+ scopeKey: null
702
+ });
703
+ }
628
704
  }
629
- } else for (const item of items) rows.push({
630
- kind: "package",
631
- pkg: item.pkg,
632
- packageIndex: item.index,
633
- indented: false,
634
- scopeKey: null
635
- });
705
+ } else {
706
+ const sorted = frequencySort ? [...items].sort(freqSort) : items;
707
+ for (const item of sorted) rows.push({
708
+ kind: "package",
709
+ pkg: item.pkg,
710
+ packageIndex: item.index,
711
+ indented: false,
712
+ scopeKey: null
713
+ });
714
+ }
636
715
  }
637
716
  return rows;
638
717
  }
@@ -682,9 +761,16 @@ function computeMaxPerGroup(terminalRows, groupCount) {
682
761
  const perGroup = Math.floor(available / groupCount);
683
762
  return Math.max(3, perGroup);
684
763
  }
685
- function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope, isActive = true }) {
764
+ function PackageList({ packages, onToggle, onToggleGroup, onToggleMany, onSelectVersion, onViewChangelog, onConfirm, onOpenSettings, groupByScope = false, groupScopes, groupsOnTop = false, frequencySort = false, frequency = {}, isActive = true }) {
686
765
  const [focusedIndex, setFocusedIndex] = useState(0);
687
- const allRows = useMemo(() => buildDisplayRows(packages, groupByScope), [packages, groupByScope]);
766
+ const allRows = useMemo(() => buildDisplayRows(packages, groupByScope, groupScopes, groupsOnTop, frequencySort, frequency), [
767
+ packages,
768
+ groupByScope,
769
+ groupScopes,
770
+ groupsOnTop,
771
+ frequencySort,
772
+ frequency
773
+ ]);
688
774
  const allScopeKeys = useMemo(() => {
689
775
  const keys = /* @__PURE__ */ new Set();
690
776
  for (const row of allRows) if (row.kind === "scope-header") keys.add(`${row.groupType}::${row.scope}`);
@@ -1609,25 +1695,92 @@ function UpdateResults({ results, onDone }) {
1609
1695
  }
1610
1696
  //#endregion
1611
1697
  //#region src/ui/Settings.tsx
1612
- const SETTINGS = [{
1613
- key: "groupByScope",
1614
- label: "Group packages by scope",
1615
- description: "Sub-group scoped packages (@org/pkg) under their scope prefix"
1616
- }];
1617
1698
  function Settings({ config, onConfigChange, onClose }) {
1618
- const [cursor, setCursor] = useState(0);
1699
+ const [inputMode, setInputMode] = useState(false);
1700
+ const [inputValue, setInputValue] = useState("");
1701
+ const scopes = config.groupScopes;
1702
+ const rows = [
1703
+ { type: "toggle-frequency" },
1704
+ { type: "toggle-group" },
1705
+ { type: "toggle-groups-top" },
1706
+ { type: "list-header" },
1707
+ ...scopes.map((_, i) => ({
1708
+ type: "list-item",
1709
+ listItemIndex: i
1710
+ }))
1711
+ ];
1712
+ const [flatCursor, setFlatCursor] = useState(0);
1713
+ const currentRow = rows[flatCursor];
1714
+ const addScope = (value) => {
1715
+ const scope = value.startsWith("@") ? value : `@${value}`;
1716
+ if (scopes.includes(scope)) return;
1717
+ onConfigChange({
1718
+ ...config,
1719
+ groupScopes: [...scopes, scope]
1720
+ });
1721
+ };
1722
+ const removeScope = (scope) => {
1723
+ onConfigChange({
1724
+ ...config,
1725
+ groupScopes: scopes.filter((s) => s !== scope)
1726
+ });
1727
+ };
1619
1728
  useInput((input, key) => {
1620
- if (key.upArrow) setCursor((c) => Math.max(0, c - 1));
1621
- if (key.downArrow) setCursor((c) => Math.min(SETTINGS.length - 1, c + 1));
1729
+ if (inputMode) {
1730
+ if (key.escape) {
1731
+ setInputMode(false);
1732
+ setInputValue("");
1733
+ return;
1734
+ }
1735
+ if (key.return) {
1736
+ const value = inputValue.trim();
1737
+ if (value) addScope(value);
1738
+ setInputMode(false);
1739
+ setInputValue("");
1740
+ return;
1741
+ }
1742
+ if (key.backspace || key.delete) {
1743
+ setInputValue((v) => v.slice(0, -1));
1744
+ return;
1745
+ }
1746
+ if (input && !key.ctrl && !key.meta) setInputValue((v) => v + input);
1747
+ return;
1748
+ }
1749
+ if (key.upArrow) setFlatCursor((c) => Math.max(0, c - 1));
1750
+ if (key.downArrow) setFlatCursor((c) => Math.min(rows.length - 1, c + 1));
1622
1751
  if (input === " " || key.return) {
1623
- const setting = SETTINGS[cursor];
1624
- onConfigChange({
1752
+ if (currentRow?.type === "toggle-frequency") onConfigChange({
1753
+ ...config,
1754
+ frequencySort: !config.frequencySort
1755
+ });
1756
+ else if (currentRow?.type === "toggle-group") onConfigChange({
1757
+ ...config,
1758
+ groupByScope: !config.groupByScope
1759
+ });
1760
+ else if (currentRow?.type === "toggle-groups-top") onConfigChange({
1625
1761
  ...config,
1626
- [setting.key]: !config[setting.key]
1762
+ groupsOnTop: !config.groupsOnTop
1627
1763
  });
1764
+ else if (currentRow?.type === "list-header") {
1765
+ setInputMode(true);
1766
+ setInputValue("");
1767
+ }
1768
+ }
1769
+ if (input === "a" && !key.ctrl && currentRow?.type === "list-header") {
1770
+ setInputMode(true);
1771
+ setInputValue("");
1772
+ }
1773
+ if ((key.backspace || key.delete) && currentRow?.type === "list-item" && currentRow.listItemIndex !== void 0) {
1774
+ const scope = scopes[currentRow.listItemIndex];
1775
+ removeScope(scope);
1776
+ if (flatCursor >= rows.length - 1) setFlatCursor(Math.max(0, flatCursor - 1));
1628
1777
  }
1629
1778
  if (key.escape || input === "s") onClose();
1630
1779
  });
1780
+ const freqToggleFocused = currentRow?.type === "toggle-frequency";
1781
+ const groupToggleFocused = currentRow?.type === "toggle-group";
1782
+ const groupsTopFocused = currentRow?.type === "toggle-groups-top";
1783
+ const listHeaderFocused = currentRow?.type === "list-header";
1631
1784
  return /* @__PURE__ */ jsxs(Box, {
1632
1785
  flexDirection: "column",
1633
1786
  children: [
@@ -1647,41 +1800,179 @@ function Settings({ config, onConfigChange, onClose }) {
1647
1800
  ]
1648
1801
  })
1649
1802
  }),
1650
- SETTINGS.map((setting, i) => {
1651
- const isFocused = cursor === i;
1652
- const isEnabled = config[setting.key];
1653
- return /* @__PURE__ */ jsxs(Box, {
1654
- flexDirection: "column",
1655
- marginBottom: 1,
1656
- children: [/* @__PURE__ */ jsxs(Box, {
1657
- gap: 1,
1658
- children: [
1659
- /* @__PURE__ */ jsx(Text, {
1803
+ /* @__PURE__ */ jsxs(Box, {
1804
+ flexDirection: "column",
1805
+ marginBottom: 1,
1806
+ children: [/* @__PURE__ */ jsxs(Box, {
1807
+ gap: 1,
1808
+ children: [
1809
+ /* @__PURE__ */ jsx(Text, {
1810
+ color: "greenBright",
1811
+ children: freqToggleFocused ? ">" : " "
1812
+ }),
1813
+ /* @__PURE__ */ jsxs(Text, {
1814
+ color: config.frequencySort ? "greenBright" : "gray",
1815
+ children: [
1816
+ "[",
1817
+ config.frequencySort ? "x" : " ",
1818
+ "]"
1819
+ ]
1820
+ }),
1821
+ /* @__PURE__ */ jsx(Text, {
1822
+ bold: freqToggleFocused,
1823
+ color: freqToggleFocused ? "whiteBright" : "white",
1824
+ children: "Sort by update frequency"
1825
+ })
1826
+ ]
1827
+ }), /* @__PURE__ */ jsx(Box, {
1828
+ marginLeft: 6,
1829
+ children: /* @__PURE__ */ jsx(Text, {
1830
+ color: "gray",
1831
+ children: "Packages you update often appear at the top"
1832
+ })
1833
+ })]
1834
+ }),
1835
+ /* @__PURE__ */ jsxs(Box, {
1836
+ flexDirection: "column",
1837
+ marginBottom: 1,
1838
+ children: [/* @__PURE__ */ jsxs(Box, {
1839
+ gap: 1,
1840
+ children: [
1841
+ /* @__PURE__ */ jsx(Text, {
1842
+ color: "greenBright",
1843
+ children: groupToggleFocused ? ">" : " "
1844
+ }),
1845
+ /* @__PURE__ */ jsxs(Text, {
1846
+ color: config.groupByScope ? "greenBright" : "gray",
1847
+ children: [
1848
+ "[",
1849
+ config.groupByScope ? "x" : " ",
1850
+ "]"
1851
+ ]
1852
+ }),
1853
+ /* @__PURE__ */ jsx(Text, {
1854
+ bold: groupToggleFocused,
1855
+ color: groupToggleFocused ? "whiteBright" : "white",
1856
+ children: "Enable scope grouping"
1857
+ })
1858
+ ]
1859
+ }), /* @__PURE__ */ jsx(Box, {
1860
+ marginLeft: 6,
1861
+ children: /* @__PURE__ */ jsx(Text, {
1862
+ color: "gray",
1863
+ children: "Group scoped packages listed below under their scope prefix"
1864
+ })
1865
+ })]
1866
+ }),
1867
+ /* @__PURE__ */ jsxs(Box, {
1868
+ flexDirection: "column",
1869
+ marginBottom: 1,
1870
+ children: [/* @__PURE__ */ jsxs(Box, {
1871
+ gap: 1,
1872
+ children: [
1873
+ /* @__PURE__ */ jsx(Text, {
1874
+ dimColor: !config.groupByScope,
1875
+ color: "greenBright",
1876
+ children: groupsTopFocused ? ">" : " "
1877
+ }),
1878
+ /* @__PURE__ */ jsxs(Text, {
1879
+ dimColor: !config.groupByScope,
1880
+ color: config.groupsOnTop && config.groupByScope ? "greenBright" : "gray",
1881
+ children: [
1882
+ "[",
1883
+ config.groupsOnTop ? "x" : " ",
1884
+ "]"
1885
+ ]
1886
+ }),
1887
+ /* @__PURE__ */ jsx(Text, {
1888
+ dimColor: !config.groupByScope,
1889
+ bold: groupsTopFocused,
1890
+ color: !config.groupByScope ? "gray" : groupsTopFocused ? "whiteBright" : "white",
1891
+ children: "Show grouped scopes on top"
1892
+ })
1893
+ ]
1894
+ }), /* @__PURE__ */ jsx(Box, {
1895
+ marginLeft: 6,
1896
+ children: /* @__PURE__ */ jsx(Text, {
1897
+ dimColor: !config.groupByScope,
1898
+ color: "gray",
1899
+ children: "Grouped scope packages appear before ungrouped ones"
1900
+ })
1901
+ })]
1902
+ }),
1903
+ /* @__PURE__ */ jsxs(Box, {
1904
+ flexDirection: "column",
1905
+ marginBottom: 1,
1906
+ children: [
1907
+ /* @__PURE__ */ jsxs(Box, {
1908
+ flexDirection: "column",
1909
+ marginBottom: 0,
1910
+ children: [/* @__PURE__ */ jsxs(Box, {
1911
+ gap: 1,
1912
+ children: [/* @__PURE__ */ jsx(Text, {
1913
+ dimColor: !config.groupByScope,
1660
1914
  color: "greenBright",
1661
- children: isFocused ? "" : " "
1662
- }),
1663
- /* @__PURE__ */ jsxs(Text, {
1664
- color: isEnabled ? "greenBright" : "gray",
1665
- children: [
1666
- "[",
1667
- isEnabled ? "✓" : " ",
1668
- "]"
1669
- ]
1670
- }),
1671
- /* @__PURE__ */ jsx(Text, {
1672
- bold: isFocused,
1673
- color: isFocused ? "whiteBright" : "white",
1674
- children: setting.label
1915
+ children: listHeaderFocused ? ">" : " "
1916
+ }), /* @__PURE__ */ jsx(Text, {
1917
+ dimColor: !config.groupByScope,
1918
+ bold: listHeaderFocused,
1919
+ color: !config.groupByScope ? "gray" : listHeaderFocused ? "whiteBright" : "white",
1920
+ children: "Grouped scopes"
1921
+ })]
1922
+ }), /* @__PURE__ */ jsx(Box, {
1923
+ marginLeft: 4,
1924
+ children: /* @__PURE__ */ jsx(Text, {
1925
+ dimColor: !config.groupByScope,
1926
+ color: "gray",
1927
+ children: "Scoped packages (@scope/*) listed here will be sub-grouped together"
1675
1928
  })
1676
- ]
1677
- }), /* @__PURE__ */ jsx(Box, {
1678
- marginLeft: 6,
1929
+ })]
1930
+ }),
1931
+ scopes.map((scope, i) => {
1932
+ const itemFocused = flatCursor === 4 + i;
1933
+ return /* @__PURE__ */ jsxs(Box, {
1934
+ marginLeft: 4,
1935
+ gap: 1,
1936
+ children: [
1937
+ /* @__PURE__ */ jsx(Text, {
1938
+ dimColor: !config.groupByScope,
1939
+ color: "greenBright",
1940
+ children: itemFocused ? ">" : " "
1941
+ }),
1942
+ /* @__PURE__ */ jsx(Text, {
1943
+ dimColor: !config.groupByScope,
1944
+ color: !config.groupByScope ? "gray" : itemFocused ? "whiteBright" : "white",
1945
+ children: scope
1946
+ }),
1947
+ itemFocused && /* @__PURE__ */ jsxs(Text, {
1948
+ dimColor: !config.groupByScope,
1949
+ color: "gray",
1950
+ children: [" ", "delete to remove"]
1951
+ })
1952
+ ]
1953
+ }, scope);
1954
+ }),
1955
+ inputMode && currentRow?.type === "list-header" ? /* @__PURE__ */ jsxs(Box, {
1956
+ marginLeft: 4,
1957
+ gap: 1,
1958
+ children: [/* @__PURE__ */ jsx(Text, {
1959
+ color: "greenBright",
1960
+ children: "+"
1961
+ }), /* @__PURE__ */ jsxs(Text, {
1962
+ color: "cyan",
1963
+ children: [inputValue || "", /* @__PURE__ */ jsx(Text, {
1964
+ color: "gray",
1965
+ children: "|"
1966
+ })]
1967
+ })]
1968
+ }) : listHeaderFocused ? /* @__PURE__ */ jsx(Box, {
1969
+ marginLeft: 4,
1679
1970
  children: /* @__PURE__ */ jsx(Text, {
1680
1971
  color: "gray",
1681
- children: setting.description
1972
+ children: "press enter or a to add"
1682
1973
  })
1683
- })]
1684
- }, setting.key);
1974
+ }) : null
1975
+ ]
1685
1976
  }),
1686
1977
  /* @__PURE__ */ jsx(Box, {
1687
1978
  marginTop: 1,
@@ -1690,7 +1981,7 @@ function Settings({ config, onConfigChange, onClose }) {
1690
1981
  children: [
1691
1982
  /* @__PURE__ */ jsx(Text, {
1692
1983
  color: "white",
1693
- children: "↑↓"
1984
+ children: "arrow keys"
1694
1985
  }),
1695
1986
  " navigate",
1696
1987
  " ",
@@ -1700,6 +1991,18 @@ function Settings({ config, onConfigChange, onClose }) {
1700
1991
  }),
1701
1992
  " toggle",
1702
1993
  " ",
1994
+ /* @__PURE__ */ jsx(Text, {
1995
+ color: "white",
1996
+ children: "a"
1997
+ }),
1998
+ " add",
1999
+ " ",
2000
+ /* @__PURE__ */ jsx(Text, {
2001
+ color: "white",
2002
+ children: "delete"
2003
+ }),
2004
+ " remove",
2005
+ " ",
1703
2006
  /* @__PURE__ */ jsx(Text, {
1704
2007
  color: "white",
1705
2008
  children: "esc"
@@ -1805,6 +2108,7 @@ function App({ project, global, version, installManager }) {
1805
2108
  const [selfUpdateError, setSelfUpdateError] = useState(null);
1806
2109
  const [selfUpdating, setSelfUpdating] = useState(false);
1807
2110
  const [config, setConfig] = useState(() => loadConfig());
2111
+ const [frequency, setFrequency] = useState(() => loadFrequency());
1808
2112
  const [packages, setPackages] = useState([]);
1809
2113
  const [activeIndex, setActiveIndex] = useState(0);
1810
2114
  const [results, setResults] = useState([]);
@@ -1937,8 +2241,14 @@ function App({ project, global, version, installManager }) {
1937
2241
  const onLine = (line) => {
1938
2242
  setOutputLines((prev) => [...prev.slice(-(MAX_TERMINAL_LINES - 1)), line]);
1939
2243
  };
1940
- setResults(await updatePackages(project.manager, selected, project.cwd, global, onLine));
2244
+ const res = await updatePackages(project.manager, selected, project.cwd, global, onLine);
2245
+ setResults(res);
1941
2246
  setScreen("results");
2247
+ const successNames = res.filter((r) => r.success).map((r) => r.name);
2248
+ if (successNames.length > 0) {
2249
+ incrementFrequency(successNames);
2250
+ setFrequency(loadFrequency());
2251
+ }
1942
2252
  };
1943
2253
  if (screen === "self-update-check") return /* @__PURE__ */ jsxs(Box, {
1944
2254
  flexDirection: "column",
@@ -2156,6 +2466,10 @@ function App({ project, global, version, installManager }) {
2156
2466
  onConfirm: handleConfirm,
2157
2467
  onOpenSettings: () => setScreen("settings"),
2158
2468
  groupByScope: config.groupByScope,
2469
+ groupScopes: config.groupScopes,
2470
+ groupsOnTop: config.groupsOnTop,
2471
+ frequencySort: config.frequencySort,
2472
+ frequency,
2159
2473
  isActive: isListActive
2160
2474
  })
2161
2475
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ripencli",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Interactive dependency updater for npm, pnpm, and yarn",
5
5
  "license": "MIT",
6
6
  "author": {