formalconf 2.0.3 → 2.0.5

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/formalconf.js +305 -75
  2. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // src/cli/formalconf.tsx
3
- import { useState as useState10, useEffect as useEffect6 } from "react";
4
- import { render, useApp as useApp2, useInput as useInput10 } from "ink";
3
+ import { useState as useState11, useEffect as useEffect6 } from "react";
4
+ import { render, useApp as useApp2, useInput as useInput11 } from "ink";
5
5
  import { Spinner as Spinner2 } from "@inkjs/ui";
6
6
 
7
7
  // src/components/layout/Layout.tsx
@@ -414,7 +414,7 @@ function StatusIndicator({
414
414
  // package.json
415
415
  var package_default = {
416
416
  name: "formalconf",
417
- version: "2.0.3",
417
+ version: "2.0.5",
418
418
  description: "Dotfiles management TUI for macOS - config management, package sync, and theme switching",
419
419
  type: "module",
420
420
  main: "./dist/formalconf.js",
@@ -1674,8 +1674,8 @@ function ConfigMenu({ onBack }) {
1674
1674
  }
1675
1675
 
1676
1676
  // src/components/menus/PackageMenu.tsx
1677
- import { useState as useState7, useCallback as useCallback2, useMemo as useMemo2, useRef } from "react";
1678
- import { Box as Box13, Text as Text12, useInput as useInput8 } from "ink";
1677
+ import { useState as useState8, useCallback as useCallback2, useMemo as useMemo2, useRef } from "react";
1678
+ import { Box as Box14, Text as Text13, useInput as useInput9 } from "ink";
1679
1679
 
1680
1680
  // src/components/ScrollableLog.tsx
1681
1681
  import { useState as useState6, useEffect as useEffect3, useMemo } from "react";
@@ -1801,6 +1801,142 @@ function PromptInput({
1801
1801
  }, undefined, false, undefined, this);
1802
1802
  }
1803
1803
 
1804
+ // src/components/OrphanTable.tsx
1805
+ import { useState as useState7 } from "react";
1806
+ import { Box as Box13, Text as Text12, useInput as useInput8 } from "ink";
1807
+ import { jsxDEV as jsxDEV16 } from "react/jsx-dev-runtime";
1808
+ function OrphanTable({ result, onAction, onDismiss }) {
1809
+ const [selectedIndex, setSelectedIndex] = useState7(0);
1810
+ const { orphans } = result;
1811
+ useInput8((input, key) => {
1812
+ if (orphans.length > 0) {
1813
+ if (input === "j" || key.downArrow) {
1814
+ setSelectedIndex((i) => Math.min(i + 1, orphans.length - 1));
1815
+ }
1816
+ if (input === "k" || key.upArrow) {
1817
+ setSelectedIndex((i) => Math.max(i - 1, 0));
1818
+ }
1819
+ if (input === "a") {
1820
+ onAction("add", orphans[selectedIndex]);
1821
+ }
1822
+ if (input === "x") {
1823
+ onAction("uninstall", orphans[selectedIndex]);
1824
+ }
1825
+ }
1826
+ if (key.escape || input === "h" || key.leftArrow) {
1827
+ onDismiss();
1828
+ }
1829
+ });
1830
+ const borderColor = orphans.length > 0 ? colors.warning : colors.success;
1831
+ return /* @__PURE__ */ jsxDEV16(Panel, {
1832
+ title: "Orphaned Packages",
1833
+ borderColor,
1834
+ children: [
1835
+ /* @__PURE__ */ jsxDEV16(Box13, {
1836
+ marginBottom: 1,
1837
+ children: /* @__PURE__ */ jsxDEV16(Text12, {
1838
+ dimColor: true,
1839
+ children: [
1840
+ "Config: ",
1841
+ result.configFormulas,
1842
+ " formulas, ",
1843
+ result.configCasks,
1844
+ " casks | Installed: ",
1845
+ result.installedLeaves,
1846
+ " leaves, ",
1847
+ result.installedCasks,
1848
+ " ",
1849
+ "casks"
1850
+ ]
1851
+ }, undefined, true, undefined, this)
1852
+ }, undefined, false, undefined, this),
1853
+ orphans.length === 0 ? /* @__PURE__ */ jsxDEV16(Box13, {
1854
+ flexDirection: "column",
1855
+ children: [
1856
+ /* @__PURE__ */ jsxDEV16(Text12, {
1857
+ color: colors.success,
1858
+ children: "No orphaned packages found!"
1859
+ }, undefined, false, undefined, this),
1860
+ /* @__PURE__ */ jsxDEV16(Text12, {
1861
+ dimColor: true,
1862
+ children: "All installed packages are in your config."
1863
+ }, undefined, false, undefined, this)
1864
+ ]
1865
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV16(Box13, {
1866
+ flexDirection: "column",
1867
+ children: [
1868
+ /* @__PURE__ */ jsxDEV16(Text12, {
1869
+ color: colors.warning,
1870
+ children: [
1871
+ "Found ",
1872
+ orphans.length,
1873
+ " orphaned package",
1874
+ orphans.length !== 1 ? "s" : "",
1875
+ ":"
1876
+ ]
1877
+ }, undefined, true, undefined, this),
1878
+ /* @__PURE__ */ jsxDEV16(Box13, {
1879
+ marginTop: 1,
1880
+ flexDirection: "column",
1881
+ children: [
1882
+ /* @__PURE__ */ jsxDEV16(Box13, {
1883
+ children: [
1884
+ /* @__PURE__ */ jsxDEV16(Text12, {
1885
+ bold: true,
1886
+ children: [
1887
+ " ",
1888
+ "Name"
1889
+ ]
1890
+ }, undefined, true, undefined, this),
1891
+ /* @__PURE__ */ jsxDEV16(Text12, {
1892
+ bold: true,
1893
+ children: [
1894
+ " ".repeat(28),
1895
+ "Type"
1896
+ ]
1897
+ }, undefined, true, undefined, this)
1898
+ ]
1899
+ }, undefined, true, undefined, this),
1900
+ /* @__PURE__ */ jsxDEV16(Text12, {
1901
+ color: colors.border,
1902
+ children: "─".repeat(50)
1903
+ }, undefined, false, undefined, this),
1904
+ orphans.map((pkg, i) => {
1905
+ const isSelected = i === selectedIndex;
1906
+ return /* @__PURE__ */ jsxDEV16(Box13, {
1907
+ children: [
1908
+ /* @__PURE__ */ jsxDEV16(Text12, {
1909
+ color: isSelected ? colors.primary : undefined,
1910
+ children: [
1911
+ isSelected ? "❯ " : " ",
1912
+ pkg.name.padEnd(30)
1913
+ ]
1914
+ }, undefined, true, undefined, this),
1915
+ /* @__PURE__ */ jsxDEV16(Text12, {
1916
+ color: pkg.type === "formula" ? colors.info : colors.accent,
1917
+ children: pkg.type
1918
+ }, undefined, false, undefined, this)
1919
+ ]
1920
+ }, `${pkg.type}-${pkg.name}`, true, undefined, this);
1921
+ })
1922
+ ]
1923
+ }, undefined, true, undefined, this)
1924
+ ]
1925
+ }, undefined, true, undefined, this),
1926
+ /* @__PURE__ */ jsxDEV16(Box13, {
1927
+ marginTop: 1,
1928
+ children: orphans.length > 0 ? /* @__PURE__ */ jsxDEV16(Text12, {
1929
+ dimColor: true,
1930
+ children: "j/k navigate | a add to config | x uninstall | esc/h back | q quit"
1931
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV16(Text12, {
1932
+ dimColor: true,
1933
+ children: "esc/h back | q quit"
1934
+ }, undefined, false, undefined, this)
1935
+ }, undefined, false, undefined, this)
1936
+ ]
1937
+ }, undefined, true, undefined, this);
1938
+ }
1939
+
1804
1940
  // src/cli/pkg-sync.ts
1805
1941
  import { parseArgs as parseArgs2 } from "util";
1806
1942
 
@@ -2613,21 +2749,88 @@ if (isMainModule3) {
2613
2749
  main3().catch(console.error);
2614
2750
  }
2615
2751
 
2752
+ // src/lib/orphan-detector.ts
2753
+ function getPackageName(fullName) {
2754
+ const parts = fullName.split("/");
2755
+ return parts[parts.length - 1];
2756
+ }
2757
+ async function detectOrphanedPackages() {
2758
+ const config = await loadPkgConfig();
2759
+ const leavesResult = await exec(["brew", "leaves"]);
2760
+ const installedLeaves = leavesResult.success ? leavesResult.stdout.split(`
2761
+ `).filter(Boolean) : [];
2762
+ const casksResult = await exec(["brew", "list", "--cask"]);
2763
+ const installedCasks = casksResult.success ? casksResult.stdout.split(`
2764
+ `).filter(Boolean) : [];
2765
+ const orphans = [];
2766
+ const configPackages = config.packages.map((pkg) => ({
2767
+ full: pkg,
2768
+ short: getPackageName(pkg)
2769
+ }));
2770
+ for (const installed of installedLeaves) {
2771
+ const installedShort = getPackageName(installed);
2772
+ const isInConfig = configPackages.some((cfg) => installed === cfg.full || installed === cfg.short || installedShort === cfg.full || installedShort === cfg.short || installed.endsWith(`/${cfg.short}`));
2773
+ if (!isInConfig) {
2774
+ orphans.push({ name: installed, type: "formula" });
2775
+ }
2776
+ }
2777
+ const configCasksSet = new Set(config.casks);
2778
+ for (const cask of installedCasks) {
2779
+ if (!configCasksSet.has(cask)) {
2780
+ orphans.push({ name: cask, type: "cask" });
2781
+ }
2782
+ }
2783
+ orphans.sort((a, b) => {
2784
+ if (a.type !== b.type)
2785
+ return a.type === "formula" ? -1 : 1;
2786
+ return a.name.localeCompare(b.name);
2787
+ });
2788
+ return {
2789
+ orphans,
2790
+ configFormulas: config.packages.length,
2791
+ configCasks: config.casks.length,
2792
+ installedLeaves: installedLeaves.length,
2793
+ installedCasks: installedCasks.length
2794
+ };
2795
+ }
2796
+ async function addToConfig(pkg) {
2797
+ const config = await loadPkgConfig();
2798
+ if (pkg.type === "formula") {
2799
+ if (!config.packages.includes(pkg.name)) {
2800
+ config.packages.push(pkg.name);
2801
+ config.packages.sort();
2802
+ }
2803
+ } else {
2804
+ if (!config.casks.includes(pkg.name)) {
2805
+ config.casks.push(pkg.name);
2806
+ config.casks.sort();
2807
+ }
2808
+ }
2809
+ await savePkgConfig(config);
2810
+ }
2811
+ async function uninstallPackage(pkg) {
2812
+ const cmd = pkg.type === "cask" ? ["brew", "uninstall", "--cask", pkg.name] : ["brew", "uninstall", pkg.name];
2813
+ const result = await exec(cmd);
2814
+ return result.success;
2815
+ }
2816
+
2616
2817
  // src/components/menus/PackageMenu.tsx
2617
- import { jsxDEV as jsxDEV16 } from "react/jsx-dev-runtime";
2818
+ import { jsxDEV as jsxDEV17 } from "react/jsx-dev-runtime";
2618
2819
  function PackageMenu({ onBack }) {
2619
- const [state, setState] = useState7("menu");
2620
- const [lines, setLines] = useState7([]);
2621
- const [output, setOutput] = useState7("");
2622
- const [isStreamingOp, setIsStreamingOp] = useState7(true);
2623
- const [pendingPrompt, setPendingPrompt] = useState7(null);
2624
- const [success, setSuccess] = useState7(true);
2820
+ const [state, setState] = useState8("menu");
2821
+ const [lines, setLines] = useState8([]);
2822
+ const [output, setOutput] = useState8("");
2823
+ const [isStreamingOp, setIsStreamingOp] = useState8(true);
2824
+ const [pendingPrompt, setPendingPrompt] = useState8(null);
2825
+ const [success, setSuccess] = useState8(true);
2826
+ const [orphanResult, setOrphanResult] = useState8(null);
2827
+ const [isOrphanView, setIsOrphanView] = useState8(false);
2625
2828
  const isRunningRef = useRef(false);
2626
- useInput8((input, key) => {
2829
+ useInput9((input, key) => {
2627
2830
  if (state === "menu" && (key.escape || key.leftArrow || input === "h")) {
2628
2831
  onBack();
2629
2832
  }
2630
- if (state === "result") {
2833
+ if (state === "result" && !isOrphanView) {
2631
2834
  setState("menu");
2632
2835
  setLines([]);
2633
2836
  }
@@ -2689,6 +2892,13 @@ function PackageMenu({ onBack }) {
2689
2892
  result = await runPkgLock(["status"]);
2690
2893
  setOutput(result.output);
2691
2894
  break;
2895
+ case "orphans":
2896
+ setIsStreamingOp(false);
2897
+ setIsOrphanView(true);
2898
+ const orphanData = await detectOrphanedPackages();
2899
+ setOrphanResult(orphanData);
2900
+ result = { output: "", success: true };
2901
+ break;
2692
2902
  default:
2693
2903
  setIsStreamingOp(false);
2694
2904
  result = { output: "Unknown action", success: false };
@@ -2700,17 +2910,17 @@ function PackageMenu({ onBack }) {
2700
2910
  };
2701
2911
  if (state === "running") {
2702
2912
  if (!isStreamingOp) {
2703
- return /* @__PURE__ */ jsxDEV16(LoadingPanel, {
2913
+ return /* @__PURE__ */ jsxDEV17(LoadingPanel, {
2704
2914
  title: "Package Sync"
2705
2915
  }, undefined, false, undefined, this);
2706
2916
  }
2707
- return /* @__PURE__ */ jsxDEV16(Panel, {
2917
+ return /* @__PURE__ */ jsxDEV17(Panel, {
2708
2918
  title: "Package Sync",
2709
2919
  children: [
2710
- /* @__PURE__ */ jsxDEV16(ScrollableLog, {
2920
+ /* @__PURE__ */ jsxDEV17(ScrollableLog, {
2711
2921
  lines
2712
2922
  }, undefined, false, undefined, this),
2713
- pendingPrompt && /* @__PURE__ */ jsxDEV16(PromptInput, {
2923
+ pendingPrompt && /* @__PURE__ */ jsxDEV17(PromptInput, {
2714
2924
  question: pendingPrompt.question,
2715
2925
  options: pendingPrompt.options,
2716
2926
  onAnswer: handlePromptAnswer
@@ -2719,39 +2929,58 @@ function PackageMenu({ onBack }) {
2719
2929
  }, undefined, true, undefined, this);
2720
2930
  }
2721
2931
  if (state === "result") {
2932
+ if (isOrphanView && orphanResult) {
2933
+ const handleOrphanAction = async (action, pkg) => {
2934
+ if (action === "add") {
2935
+ await addToConfig(pkg);
2936
+ } else {
2937
+ await uninstallPackage(pkg);
2938
+ }
2939
+ const updated = await detectOrphanedPackages();
2940
+ setOrphanResult(updated);
2941
+ };
2942
+ return /* @__PURE__ */ jsxDEV17(OrphanTable, {
2943
+ result: orphanResult,
2944
+ onAction: handleOrphanAction,
2945
+ onDismiss: () => {
2946
+ setIsOrphanView(false);
2947
+ setState("menu");
2948
+ }
2949
+ }, undefined, false, undefined, this);
2950
+ }
2722
2951
  if (!isStreamingOp) {
2723
- return /* @__PURE__ */ jsxDEV16(CommandOutput, {
2952
+ return /* @__PURE__ */ jsxDEV17(CommandOutput, {
2724
2953
  title: "Package Sync",
2725
2954
  output,
2726
2955
  success,
2727
2956
  onDismiss: () => setState("menu")
2728
2957
  }, undefined, false, undefined, this);
2729
2958
  }
2730
- return /* @__PURE__ */ jsxDEV16(Panel, {
2959
+ return /* @__PURE__ */ jsxDEV17(Panel, {
2731
2960
  title: "Package Sync",
2732
2961
  borderColor: success ? colors.success : colors.error,
2733
2962
  children: [
2734
- /* @__PURE__ */ jsxDEV16(ScrollableLog, {
2963
+ /* @__PURE__ */ jsxDEV17(ScrollableLog, {
2735
2964
  lines,
2736
2965
  autoScroll: false
2737
2966
  }, undefined, false, undefined, this),
2738
- /* @__PURE__ */ jsxDEV16(Box13, {
2967
+ /* @__PURE__ */ jsxDEV17(Box14, {
2739
2968
  marginTop: 1,
2740
- children: /* @__PURE__ */ jsxDEV16(Text12, {
2969
+ children: /* @__PURE__ */ jsxDEV17(Text13, {
2741
2970
  color: success ? colors.success : colors.error,
2742
2971
  children: success ? "Done" : "Failed"
2743
2972
  }, undefined, false, undefined, this)
2744
2973
  }, undefined, false, undefined, this),
2745
- /* @__PURE__ */ jsxDEV16(Text12, {
2974
+ /* @__PURE__ */ jsxDEV17(Text13, {
2746
2975
  dimColor: true,
2747
2976
  children: "Press any key to continue..."
2748
2977
  }, undefined, false, undefined, this)
2749
2978
  ]
2750
2979
  }, undefined, true, undefined, this);
2751
2980
  }
2752
- return /* @__PURE__ */ jsxDEV16(Panel, {
2981
+ return /* @__PURE__ */ jsxDEV17(Panel, {
2753
2982
  title: "Package Sync",
2754
- children: /* @__PURE__ */ jsxDEV16(VimSelect, {
2983
+ children: /* @__PURE__ */ jsxDEV17(VimSelect, {
2755
2984
  options: [
2756
2985
  { label: "Sync packages", value: "sync" },
2757
2986
  { label: "Sync with purge", value: "sync-purge" },
@@ -2759,6 +2988,7 @@ function PackageMenu({ onBack }) {
2759
2988
  { label: "Upgrade interactive", value: "upgrade-interactive" },
2760
2989
  { label: "Update lockfile", value: "lock-update" },
2761
2990
  { label: "Lockfile status", value: "lock-status" },
2991
+ { label: "Find orphaned packages", value: "orphans" },
2762
2992
  { label: "Back", value: "back" }
2763
2993
  ],
2764
2994
  onChange: handleAction
@@ -2767,14 +2997,14 @@ function PackageMenu({ onBack }) {
2767
2997
  }
2768
2998
 
2769
2999
  // src/components/menus/ThemeMenu.tsx
2770
- import { useState as useState9, useEffect as useEffect5, useMemo as useMemo4 } from "react";
2771
- import { Box as Box15, Text as Text14 } from "ink";
3000
+ import { useState as useState10, useEffect as useEffect5, useMemo as useMemo4 } from "react";
3001
+ import { Box as Box16, Text as Text15 } from "ink";
2772
3002
  import { existsSync as existsSync7, readdirSync as readdirSync5 } from "fs";
2773
3003
  import { join as join6 } from "path";
2774
3004
 
2775
3005
  // src/components/ThemeCard.tsx
2776
- import { Box as Box14, Text as Text13 } from "ink";
2777
- import { jsxDEV as jsxDEV17 } from "react/jsx-dev-runtime";
3006
+ import { Box as Box15, Text as Text14 } from "ink";
3007
+ import { jsxDEV as jsxDEV18 } from "react/jsx-dev-runtime";
2778
3008
  function ThemeCard({ theme, isSelected, width }) {
2779
3009
  const borderColor = isSelected ? colors.accent : colors.border;
2780
3010
  const nameColor = isSelected ? colors.primary : colors.text;
@@ -2784,25 +3014,25 @@ function ThemeCard({ theme, isSelected, width }) {
2784
3014
  if (theme.isLightMode)
2785
3015
  indicators.push("light");
2786
3016
  const indicatorText = indicators.length > 0 ? ` [${indicators.join(" ")}]` : "";
2787
- return /* @__PURE__ */ jsxDEV17(Box14, {
3017
+ return /* @__PURE__ */ jsxDEV18(Box15, {
2788
3018
  flexDirection: "column",
2789
3019
  width,
2790
3020
  borderStyle: borderStyles.panel,
2791
3021
  borderColor,
2792
3022
  paddingX: 1,
2793
- children: /* @__PURE__ */ jsxDEV17(Box14, {
3023
+ children: /* @__PURE__ */ jsxDEV18(Box15, {
2794
3024
  children: [
2795
- /* @__PURE__ */ jsxDEV17(Text13, {
3025
+ /* @__PURE__ */ jsxDEV18(Text14, {
2796
3026
  color: isSelected ? colors.accent : colors.primaryDim,
2797
3027
  children: isSelected ? "● " : " "
2798
3028
  }, undefined, false, undefined, this),
2799
- /* @__PURE__ */ jsxDEV17(Text13, {
3029
+ /* @__PURE__ */ jsxDEV18(Text14, {
2800
3030
  color: nameColor,
2801
3031
  bold: true,
2802
3032
  wrap: "truncate",
2803
3033
  children: theme.name
2804
3034
  }, undefined, false, undefined, this),
2805
- /* @__PURE__ */ jsxDEV17(Text13, {
3035
+ /* @__PURE__ */ jsxDEV18(Text14, {
2806
3036
  color: colors.primaryDim,
2807
3037
  children: indicatorText
2808
3038
  }, undefined, false, undefined, this)
@@ -2812,8 +3042,8 @@ function ThemeCard({ theme, isSelected, width }) {
2812
3042
  }
2813
3043
 
2814
3044
  // src/hooks/useThemeGrid.ts
2815
- import { useState as useState8, useEffect as useEffect4 } from "react";
2816
- import { useInput as useInput9 } from "ink";
3045
+ import { useState as useState9, useEffect as useEffect4 } from "react";
3046
+ import { useInput as useInput10 } from "ink";
2817
3047
  function useThemeGrid({
2818
3048
  itemCount,
2819
3049
  cardHeight = 3,
@@ -2824,8 +3054,8 @@ function useThemeGrid({
2824
3054
  enabled = true
2825
3055
  }) {
2826
3056
  const { columns, rows } = useTerminalSize();
2827
- const [selectedIndex, setSelectedIndex] = useState8(0);
2828
- const [scrollOffset, setScrollOffset] = useState8(0);
3057
+ const [selectedIndex, setSelectedIndex] = useState9(0);
3058
+ const [scrollOffset, setScrollOffset] = useState9(0);
2829
3059
  const availableWidth = columns - 6;
2830
3060
  const cardsPerRow = Math.max(1, Math.floor(availableWidth / minCardWidth));
2831
3061
  const cardWidth = Math.floor(availableWidth / cardsPerRow);
@@ -2840,7 +3070,7 @@ function useThemeGrid({
2840
3070
  setScrollOffset(selectedRow - visibleRows + 1);
2841
3071
  }
2842
3072
  }, [selectedRow, scrollOffset, visibleRows]);
2843
- useInput9((input, key) => {
3073
+ useInput10((input, key) => {
2844
3074
  if (!enabled)
2845
3075
  return;
2846
3076
  if (key.escape && onBack) {
@@ -3135,10 +3365,10 @@ if (isMainModule4) {
3135
3365
  }
3136
3366
 
3137
3367
  // src/components/menus/ThemeMenu.tsx
3138
- import { jsxDEV as jsxDEV18 } from "react/jsx-dev-runtime";
3368
+ import { jsxDEV as jsxDEV19 } from "react/jsx-dev-runtime";
3139
3369
  function ThemeMenu({ onBack }) {
3140
- const [themes, setThemes] = useState9([]);
3141
- const [loading, setLoading] = useState9(true);
3370
+ const [themes, setThemes] = useState10([]);
3371
+ const [loading, setLoading] = useState10(true);
3142
3372
  const { state, output, success, isRunning, isResult, execute, reset } = useMenuAction();
3143
3373
  const grid = useThemeGrid({
3144
3374
  itemCount: themes.length,
@@ -3175,13 +3405,13 @@ function ThemeMenu({ onBack }) {
3175
3405
  return themes.slice(grid.visibleStartIndex, grid.visibleEndIndex);
3176
3406
  }, [themes, grid.visibleStartIndex, grid.visibleEndIndex]);
3177
3407
  if (loading || isRunning) {
3178
- return /* @__PURE__ */ jsxDEV18(LoadingPanel, {
3408
+ return /* @__PURE__ */ jsxDEV19(LoadingPanel, {
3179
3409
  title: "Select Theme",
3180
3410
  label: loading ? "Loading themes..." : "Applying theme..."
3181
3411
  }, undefined, false, undefined, this);
3182
3412
  }
3183
3413
  if (isResult) {
3184
- return /* @__PURE__ */ jsxDEV18(CommandOutput, {
3414
+ return /* @__PURE__ */ jsxDEV19(CommandOutput, {
3185
3415
  title: "Select Theme",
3186
3416
  output,
3187
3417
  success,
@@ -3189,28 +3419,28 @@ function ThemeMenu({ onBack }) {
3189
3419
  }, undefined, false, undefined, this);
3190
3420
  }
3191
3421
  if (themes.length === 0) {
3192
- return /* @__PURE__ */ jsxDEV18(Panel, {
3422
+ return /* @__PURE__ */ jsxDEV19(Panel, {
3193
3423
  title: "Select Theme",
3194
3424
  children: [
3195
- /* @__PURE__ */ jsxDEV18(Box15, {
3425
+ /* @__PURE__ */ jsxDEV19(Box16, {
3196
3426
  flexDirection: "column",
3197
3427
  children: [
3198
- /* @__PURE__ */ jsxDEV18(Text14, {
3428
+ /* @__PURE__ */ jsxDEV19(Text15, {
3199
3429
  color: colors.warning,
3200
3430
  children: "No themes available."
3201
3431
  }, undefined, false, undefined, this),
3202
- /* @__PURE__ */ jsxDEV18(Text14, {
3432
+ /* @__PURE__ */ jsxDEV19(Text15, {
3203
3433
  children: "This system is compatible with omarchy themes."
3204
3434
  }, undefined, false, undefined, this),
3205
- /* @__PURE__ */ jsxDEV18(Text14, {
3435
+ /* @__PURE__ */ jsxDEV19(Text15, {
3206
3436
  dimColor: true,
3207
3437
  children: "Add themes to ~/.config/formalconf/themes/"
3208
3438
  }, undefined, false, undefined, this)
3209
3439
  ]
3210
3440
  }, undefined, true, undefined, this),
3211
- /* @__PURE__ */ jsxDEV18(Box15, {
3441
+ /* @__PURE__ */ jsxDEV19(Box16, {
3212
3442
  marginTop: 1,
3213
- children: /* @__PURE__ */ jsxDEV18(VimSelect, {
3443
+ children: /* @__PURE__ */ jsxDEV19(VimSelect, {
3214
3444
  options: [{ label: "Back", value: "back" }],
3215
3445
  onChange: () => onBack()
3216
3446
  }, undefined, false, undefined, this)
@@ -3218,10 +3448,10 @@ function ThemeMenu({ onBack }) {
3218
3448
  ]
3219
3449
  }, undefined, true, undefined, this);
3220
3450
  }
3221
- return /* @__PURE__ */ jsxDEV18(Panel, {
3451
+ return /* @__PURE__ */ jsxDEV19(Panel, {
3222
3452
  title: "Select Theme",
3223
3453
  children: [
3224
- grid.showScrollUp && /* @__PURE__ */ jsxDEV18(Text14, {
3454
+ grid.showScrollUp && /* @__PURE__ */ jsxDEV19(Text15, {
3225
3455
  dimColor: true,
3226
3456
  children: [
3227
3457
  " ",
@@ -3231,18 +3461,18 @@ function ThemeMenu({ onBack }) {
3231
3461
  grid.scrollOffset > 1 ? "s" : ""
3232
3462
  ]
3233
3463
  }, undefined, true, undefined, this),
3234
- /* @__PURE__ */ jsxDEV18(Box15, {
3464
+ /* @__PURE__ */ jsxDEV19(Box16, {
3235
3465
  flexDirection: "row",
3236
3466
  flexWrap: "wrap",
3237
3467
  height: grid.gridHeight,
3238
3468
  overflow: "hidden",
3239
- children: visibleThemes.map((theme, index) => /* @__PURE__ */ jsxDEV18(ThemeCard, {
3469
+ children: visibleThemes.map((theme, index) => /* @__PURE__ */ jsxDEV19(ThemeCard, {
3240
3470
  theme,
3241
3471
  isSelected: grid.visibleStartIndex + index === grid.selectedIndex,
3242
3472
  width: grid.cardWidth
3243
3473
  }, theme.path, false, undefined, this))
3244
3474
  }, undefined, false, undefined, this),
3245
- grid.showScrollDown && /* @__PURE__ */ jsxDEV18(Text14, {
3475
+ grid.showScrollDown && /* @__PURE__ */ jsxDEV19(Text15, {
3246
3476
  dimColor: true,
3247
3477
  children: [
3248
3478
  " ",
@@ -3252,9 +3482,9 @@ function ThemeMenu({ onBack }) {
3252
3482
  grid.totalRows - grid.scrollOffset - grid.visibleRows > 1 ? "s" : ""
3253
3483
  ]
3254
3484
  }, undefined, true, undefined, this),
3255
- /* @__PURE__ */ jsxDEV18(Box15, {
3485
+ /* @__PURE__ */ jsxDEV19(Box16, {
3256
3486
  marginTop: 1,
3257
- children: /* @__PURE__ */ jsxDEV18(Text14, {
3487
+ children: /* @__PURE__ */ jsxDEV19(Text15, {
3258
3488
  dimColor: true,
3259
3489
  children: "←→↑↓/hjkl navigate • Enter select • Esc back"
3260
3490
  }, undefined, false, undefined, this)
@@ -3264,7 +3494,7 @@ function ThemeMenu({ onBack }) {
3264
3494
  }
3265
3495
 
3266
3496
  // src/cli/formalconf.tsx
3267
- import { jsxDEV as jsxDEV19 } from "react/jsx-dev-runtime";
3497
+ import { jsxDEV as jsxDEV20 } from "react/jsx-dev-runtime";
3268
3498
  var BREADCRUMBS = {
3269
3499
  main: ["Main"],
3270
3500
  config: ["Main", "Config Manager"],
@@ -3272,11 +3502,11 @@ var BREADCRUMBS = {
3272
3502
  themes: ["Main", "Themes"]
3273
3503
  };
3274
3504
  function App() {
3275
- const [appState, setAppState] = useState10("loading");
3276
- const [missingDeps, setMissingDeps] = useState10([]);
3277
- const [screen, setScreen] = useState10("main");
3505
+ const [appState, setAppState] = useState11("loading");
3506
+ const [missingDeps, setMissingDeps] = useState11([]);
3507
+ const [screen, setScreen] = useState11("main");
3278
3508
  const { exit } = useApp2();
3279
- useInput10((input) => {
3509
+ useInput11((input) => {
3280
3510
  if (input === "q")
3281
3511
  exit();
3282
3512
  });
@@ -3299,44 +3529,44 @@ function App() {
3299
3529
  init();
3300
3530
  }, []);
3301
3531
  if (appState === "loading") {
3302
- return /* @__PURE__ */ jsxDEV19(Layout, {
3532
+ return /* @__PURE__ */ jsxDEV20(Layout, {
3303
3533
  breadcrumb: ["Loading"],
3304
- children: /* @__PURE__ */ jsxDEV19(Panel, {
3534
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
3305
3535
  title: "FormalConf",
3306
- children: /* @__PURE__ */ jsxDEV19(Spinner2, {
3536
+ children: /* @__PURE__ */ jsxDEV20(Spinner2, {
3307
3537
  label: "Checking prerequisites..."
3308
3538
  }, undefined, false, undefined, this)
3309
3539
  }, undefined, false, undefined, this)
3310
3540
  }, undefined, false, undefined, this);
3311
3541
  }
3312
3542
  if (appState === "error") {
3313
- return /* @__PURE__ */ jsxDEV19(PrerequisiteError, {
3543
+ return /* @__PURE__ */ jsxDEV20(PrerequisiteError, {
3314
3544
  missing: missingDeps,
3315
3545
  onExit: exit
3316
3546
  }, undefined, false, undefined, this);
3317
3547
  }
3318
3548
  if (appState === "onboarding") {
3319
- return /* @__PURE__ */ jsxDEV19(Onboarding, {
3549
+ return /* @__PURE__ */ jsxDEV20(Onboarding, {
3320
3550
  onComplete: () => setAppState("ready")
3321
3551
  }, undefined, false, undefined, this);
3322
3552
  }
3323
3553
  const goBack = () => setScreen("main");
3324
- return /* @__PURE__ */ jsxDEV19(Layout, {
3554
+ return /* @__PURE__ */ jsxDEV20(Layout, {
3325
3555
  breadcrumb: BREADCRUMBS[screen],
3326
3556
  children: [
3327
- screen === "main" && /* @__PURE__ */ jsxDEV19(MainMenu, {
3557
+ screen === "main" && /* @__PURE__ */ jsxDEV20(MainMenu, {
3328
3558
  onSelect: setScreen
3329
3559
  }, undefined, false, undefined, this),
3330
- screen === "config" && /* @__PURE__ */ jsxDEV19(ConfigMenu, {
3560
+ screen === "config" && /* @__PURE__ */ jsxDEV20(ConfigMenu, {
3331
3561
  onBack: goBack
3332
3562
  }, undefined, false, undefined, this),
3333
- screen === "packages" && /* @__PURE__ */ jsxDEV19(PackageMenu, {
3563
+ screen === "packages" && /* @__PURE__ */ jsxDEV20(PackageMenu, {
3334
3564
  onBack: goBack
3335
3565
  }, undefined, false, undefined, this),
3336
- screen === "themes" && /* @__PURE__ */ jsxDEV19(ThemeMenu, {
3566
+ screen === "themes" && /* @__PURE__ */ jsxDEV20(ThemeMenu, {
3337
3567
  onBack: goBack
3338
3568
  }, undefined, false, undefined, this)
3339
3569
  ]
3340
3570
  }, undefined, true, undefined, this);
3341
3571
  }
3342
- render(/* @__PURE__ */ jsxDEV19(App, {}, undefined, false, undefined, this));
3572
+ render(/* @__PURE__ */ jsxDEV20(App, {}, undefined, false, undefined, this));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formalconf",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Dotfiles management TUI for macOS - config management, package sync, and theme switching",
5
5
  "type": "module",
6
6
  "main": "./dist/formalconf.js",