formalconf 2.0.16 → 2.0.18

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 +185 -145
  2. package/package.json +1 -1
@@ -470,7 +470,7 @@ function StatusIndicator({
470
470
  // package.json
471
471
  var package_default = {
472
472
  name: "formalconf",
473
- version: "2.0.16",
473
+ version: "2.0.18",
474
474
  description: "Dotfiles management TUI for macOS and Linux - config management, package sync, and theme switching",
475
475
  type: "module",
476
476
  main: "./dist/formalconf.js",
@@ -2189,6 +2189,9 @@ async function detectAvailablePackageManagers() {
2189
2189
  managers.push("flatpak");
2190
2190
  }
2191
2191
  }
2192
+ if (await commandExists("cargo")) {
2193
+ managers.push("cargo");
2194
+ }
2192
2195
  return managers;
2193
2196
  }
2194
2197
  async function getPlatformInfo() {
@@ -3116,6 +3119,98 @@ class Flatpak {
3116
3119
  }
3117
3120
  }
3118
3121
 
3122
+ // src/lib/package-managers/cargo.ts
3123
+ init_runtime();
3124
+ init_runtime();
3125
+ async function runCargoCommand(args, callbacks) {
3126
+ const cmd = ["cargo", ...args];
3127
+ if (callbacks?.onLog) {
3128
+ const exitCode = await execStreaming(cmd, callbacks.onLog);
3129
+ return exitCode === 0;
3130
+ }
3131
+ const result = await exec(cmd);
3132
+ return result.success;
3133
+ }
3134
+
3135
+ class Cargo {
3136
+ type = "cargo";
3137
+ displayName = "Cargo";
3138
+ async isAvailable() {
3139
+ return commandExists("cargo");
3140
+ }
3141
+ async update(callbacks) {
3142
+ return true;
3143
+ }
3144
+ async install(packages, callbacks) {
3145
+ if (packages.length === 0)
3146
+ return true;
3147
+ for (const pkg of packages) {
3148
+ const success = await runCargoCommand(["install", pkg], callbacks);
3149
+ if (!success)
3150
+ return false;
3151
+ }
3152
+ return true;
3153
+ }
3154
+ async uninstall(packages, callbacks) {
3155
+ if (packages.length === 0)
3156
+ return true;
3157
+ for (const pkg of packages) {
3158
+ const success = await runCargoCommand(["uninstall", pkg], callbacks);
3159
+ if (!success)
3160
+ return false;
3161
+ }
3162
+ return true;
3163
+ }
3164
+ async upgrade(packages, callbacks) {
3165
+ if (packages && packages.length > 0) {
3166
+ for (const pkg of packages) {
3167
+ const success = await runCargoCommand(["install", "--force", pkg], callbacks);
3168
+ if (!success)
3169
+ return false;
3170
+ }
3171
+ return true;
3172
+ }
3173
+ const installed = await this.listInstalled();
3174
+ for (const pkg of installed) {
3175
+ await runCargoCommand(["install", "--force", pkg.name], callbacks);
3176
+ }
3177
+ return true;
3178
+ }
3179
+ async listInstalled() {
3180
+ const result = await exec(["cargo", "install", "--list"]);
3181
+ if (!result.success)
3182
+ return [];
3183
+ const packages = [];
3184
+ const lines = result.stdout.split(`
3185
+ `);
3186
+ for (const line of lines) {
3187
+ const match = line.match(/^(\S+)\s+v([\d.]+(?:-[\w.]+)?)/);
3188
+ if (match) {
3189
+ packages.push({
3190
+ name: match[1],
3191
+ version: match[2]
3192
+ });
3193
+ }
3194
+ }
3195
+ return packages;
3196
+ }
3197
+ async listOutdated() {
3198
+ return [];
3199
+ }
3200
+ async cleanup(callbacks) {
3201
+ return true;
3202
+ }
3203
+ async isInstalled(packages) {
3204
+ const result = new Map;
3205
+ const installed = await this.listInstalled();
3206
+ const installedSet = new Set(installed.map((p) => p.name));
3207
+ for (const pkg of packages) {
3208
+ result.set(pkg, installedSet.has(pkg));
3209
+ }
3210
+ return result;
3211
+ }
3212
+ }
3213
+
3119
3214
  // src/lib/package-managers/index.ts
3120
3215
  var managerInstances = new Map;
3121
3216
  function getPackageManager(type) {
@@ -3148,6 +3243,9 @@ function getPackageManager(type) {
3148
3243
  case "flatpak":
3149
3244
  manager = new Flatpak;
3150
3245
  break;
3246
+ case "cargo":
3247
+ manager = new Cargo;
3248
+ break;
3151
3249
  default:
3152
3250
  throw new Error(`Unknown package manager type: ${type}`);
3153
3251
  }
@@ -3449,6 +3547,16 @@ async function getPackageSetsForPlatform(config, platform) {
3449
3547
  packages: masIds
3450
3548
  });
3451
3549
  }
3550
+ const cargo = getPackageManager("cargo");
3551
+ const globalCargo = config.global?.cargo || [];
3552
+ const macosCargo = config.macos?.cargo || [];
3553
+ const allMacosCargo = [...globalCargo, ...macosCargo];
3554
+ if (allMacosCargo.length > 0 && await cargo.isAvailable()) {
3555
+ sets.push({
3556
+ manager: cargo,
3557
+ packages: allMacosCargo
3558
+ });
3559
+ }
3452
3560
  } else {
3453
3561
  const distro = platform.distro;
3454
3562
  const globalPkgs = config.global?.packages || [];
@@ -3514,6 +3622,16 @@ async function getPackageSetsForPlatform(config, platform) {
3514
3622
  packages: flatpakApps
3515
3623
  });
3516
3624
  }
3625
+ const cargo = getPackageManager("cargo");
3626
+ const globalCargo = config.global?.cargo || [];
3627
+ const linuxCargo = config.linux?.cargo || [];
3628
+ const allLinuxCargo = [...globalCargo, ...linuxCargo];
3629
+ if (allLinuxCargo.length > 0 && await cargo.isAvailable()) {
3630
+ sets.push({
3631
+ manager: cargo,
3632
+ packages: allLinuxCargo
3633
+ });
3634
+ }
3517
3635
  }
3518
3636
  return sets;
3519
3637
  }
@@ -5921,6 +6039,25 @@ var COLOR_VARIABLES = [
5921
6039
  function isColorVariable(name) {
5922
6040
  return COLOR_VARIABLES.includes(name);
5923
6041
  }
6042
+ function getNestedValue(obj, path) {
6043
+ return path.split(".").reduce((acc, key) => {
6044
+ if (acc && typeof acc === "object" && key in acc) {
6045
+ return acc[key];
6046
+ }
6047
+ return;
6048
+ }, obj);
6049
+ }
6050
+ function processConditionals(template, context) {
6051
+ template = template.replace(/\{\{#if\s+(\S+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, variable, content) => {
6052
+ const value = getNestedValue(context, variable);
6053
+ return value ? content : "";
6054
+ });
6055
+ template = template.replace(/\{\{#unless\s+(\S+)\}\}([\s\S]*?)\{\{\/unless\}\}/g, (_, variable, content) => {
6056
+ const value = getNestedValue(context, variable);
6057
+ return !value ? content : "";
6058
+ });
6059
+ return template;
6060
+ }
5924
6061
  function getContextValue(context, variableName, modifier) {
5925
6062
  if (variableName.startsWith("theme.")) {
5926
6063
  const key = variableName.slice(6);
@@ -5932,6 +6069,13 @@ function getContextValue(context, variableName, modifier) {
5932
6069
  const value = context.gtk[key];
5933
6070
  return value !== undefined ? String(value) : undefined;
5934
6071
  }
6072
+ if (variableName.startsWith("neovim.")) {
6073
+ if (!context.neovim)
6074
+ return;
6075
+ const key = variableName.slice(7);
6076
+ const value = context.neovim[key];
6077
+ return value !== undefined ? String(value) : undefined;
6078
+ }
5935
6079
  if (variableName === "mode") {
5936
6080
  return context.mode;
5937
6081
  }
@@ -5942,7 +6086,8 @@ function getContextValue(context, variableName, modifier) {
5942
6086
  return;
5943
6087
  }
5944
6088
  function renderTemplate(template, context) {
5945
- return template.replace(VARIABLE_REGEX, (match, variable) => {
6089
+ let result = processConditionals(template, context);
6090
+ result = result.replace(VARIABLE_REGEX, (match, variable) => {
5946
6091
  const { name, modifier } = parseVariableReference(variable);
5947
6092
  const value = getContextValue(context, name, modifier);
5948
6093
  if (value === undefined) {
@@ -5950,9 +6095,11 @@ function renderTemplate(template, context) {
5950
6095
  }
5951
6096
  return value;
5952
6097
  });
6098
+ return result;
5953
6099
  }
5954
6100
  function renderDualModeTemplate(template, contexts) {
5955
- let result = template.replace(/\{\{dark\.([a-zA-Z0-9_.]+)\}\}/g, (match, variable) => {
6101
+ let result = processConditionals(template, contexts);
6102
+ result = result.replace(/\{\{dark\.([a-zA-Z0-9_.]+)\}\}/g, (match, variable) => {
5956
6103
  const { name, modifier } = parseVariableReference(variable);
5957
6104
  const value = getContextValue(contexts.dark, name, modifier);
5958
6105
  return value ?? match;
@@ -6066,10 +6213,11 @@ async function installAllTemplates() {
6066
6213
  await installTemplate(name);
6067
6214
  }
6068
6215
  }
6069
- function getTemplateType(filename) {
6070
- const dualModeTemplates = ["ghostty.conf.template", "neovim.lua.template", "lynk.css.template"];
6071
- if (dualModeTemplates.includes(filename)) {
6072
- return "dual";
6216
+ async function getTemplateType(filename) {
6217
+ const manifest = await loadBundledManifest();
6218
+ const meta = manifest.templates[filename];
6219
+ if (meta?.mode) {
6220
+ return meta.mode;
6073
6221
  }
6074
6222
  if (filename.includes("-dark.") || filename.includes("-light.")) {
6075
6223
  return "partial";
@@ -6083,33 +6231,18 @@ function getPartialMode(filename) {
6083
6231
  return "light";
6084
6232
  return;
6085
6233
  }
6086
- function getOutputFilename(templateName) {
6087
- let output = templateName.replace(/\.template$/, "");
6088
- if (templateName.startsWith("kitty-dark")) {
6089
- return "dark-theme.auto.conf";
6090
- }
6091
- if (templateName.startsWith("kitty-light")) {
6092
- return "light-theme.auto.conf";
6093
- }
6094
- if (templateName.startsWith("waybar-dark")) {
6095
- return "style-dark.css";
6234
+ async function getOutputFilename(templateName) {
6235
+ const manifest = await loadBundledManifest();
6236
+ const meta = manifest.templates[templateName];
6237
+ if (meta?.output) {
6238
+ return meta.output;
6096
6239
  }
6097
- if (templateName.startsWith("waybar-light")) {
6098
- return "style-light.css";
6099
- }
6100
- if (templateName === "ghostty-dark.theme.template") {
6101
- return "formalconf-dark";
6102
- }
6103
- if (templateName === "ghostty-light.theme.template") {
6104
- return "formalconf-light";
6105
- }
6106
- if (templateName.startsWith("btop-dark")) {
6107
- return "formalconf-dark.theme";
6108
- }
6109
- if (templateName.startsWith("btop-light")) {
6110
- return "formalconf-light.theme";
6111
- }
6112
- return output;
6240
+ return templateName.replace(/\.template$/, "");
6241
+ }
6242
+ async function getTemplateTargets(templateName) {
6243
+ const manifest = await loadBundledManifest();
6244
+ const meta = manifest.templates[templateName];
6245
+ return meta?.targets ?? [];
6113
6246
  }
6114
6247
  async function listInstalledTemplates() {
6115
6248
  if (!existsSync11(TEMPLATES_DIR)) {
@@ -6122,97 +6255,16 @@ async function listInstalledTemplates() {
6122
6255
  templates.push({
6123
6256
  name: entry.name,
6124
6257
  path: join9(TEMPLATES_DIR, entry.name),
6125
- outputName: getOutputFilename(entry.name),
6126
- type: getTemplateType(entry.name),
6127
- partialMode: getPartialMode(entry.name)
6258
+ outputName: await getOutputFilename(entry.name),
6259
+ type: await getTemplateType(entry.name),
6260
+ partialMode: getPartialMode(entry.name),
6261
+ targets: await getTemplateTargets(entry.name)
6128
6262
  });
6129
6263
  }
6130
6264
  }
6131
6265
  return templates;
6132
6266
  }
6133
6267
 
6134
- // src/lib/neovim/generator.ts
6135
- function toLua(value, indent = 2) {
6136
- const spaces = " ".repeat(indent);
6137
- if (value === null || value === undefined) {
6138
- return "nil";
6139
- }
6140
- if (typeof value === "boolean") {
6141
- return value ? "true" : "false";
6142
- }
6143
- if (typeof value === "number") {
6144
- return String(value);
6145
- }
6146
- if (typeof value === "string") {
6147
- const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
6148
- return `"${escaped}"`;
6149
- }
6150
- if (Array.isArray(value)) {
6151
- if (value.length === 0) {
6152
- return "{}";
6153
- }
6154
- const items = value.map((v) => `${spaces}${toLua(v, indent + 2)}`);
6155
- return `{
6156
- ${items.join(`,
6157
- `)}
6158
- ${" ".repeat(indent - 2)}}`;
6159
- }
6160
- if (typeof value === "object") {
6161
- const entries = Object.entries(value);
6162
- if (entries.length === 0) {
6163
- return "{}";
6164
- }
6165
- const items = entries.map(([k, v]) => {
6166
- const key = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(k) ? k : `["${k}"]`;
6167
- return `${spaces}${key} = ${toLua(v, indent + 2)}`;
6168
- });
6169
- return `{
6170
- ${items.join(`,
6171
- `)}
6172
- ${" ".repeat(indent - 2)}}`;
6173
- }
6174
- return "nil";
6175
- }
6176
- function generateOptsSection(opts) {
6177
- if (!opts || Object.keys(opts).length === 0) {
6178
- return "";
6179
- }
6180
- return ` opts = ${toLua(opts, 6)},`;
6181
- }
6182
- function generateNeovimConfig(theme, mode) {
6183
- if (!theme.neovim) {
6184
- return "";
6185
- }
6186
- const { repo, colorscheme, light_colorscheme, opts } = theme.neovim;
6187
- const effectiveColorscheme = mode === "light" && light_colorscheme ? light_colorscheme : colorscheme;
6188
- const optsSection = generateOptsSection(opts);
6189
- const hasDualMode = light_colorscheme && light_colorscheme !== colorscheme;
6190
- const lines = [
6191
- `-- ${theme.title} (${mode}) - Generated by FormalConf`,
6192
- `-- Neovim colorscheme configuration for LazyVim`,
6193
- ``,
6194
- `return {`,
6195
- ` {`,
6196
- ` "${repo}",`,
6197
- ` name = "${theme.title.toLowerCase().replace(/\s+/g, "-")}",`,
6198
- ` priority = 1000,`
6199
- ];
6200
- if (optsSection) {
6201
- lines.push(optsSection);
6202
- }
6203
- lines.push(` },`, ` {`, ` "LazyVim/LazyVim",`, ` opts = {`, ` colorscheme = "${effectiveColorscheme}",`, ` },`, ` },`);
6204
- if (hasDualMode) {
6205
- lines.push(` {`, ` "f-person/auto-dark-mode.nvim",`, ` opts = {`, ` update_background = false,`, ` set_dark_mode = function()`, ` vim.cmd("colorscheme ${colorscheme}")`, ` end,`, ` set_light_mode = function()`, ` vim.cmd("colorscheme ${light_colorscheme}")`, ` end,`, ` },`, ` },`);
6206
- }
6207
- lines.push(`}`);
6208
- return lines.join(`
6209
- `) + `
6210
- `;
6211
- }
6212
- function hasNeovimConfig(theme) {
6213
- return !!theme.neovim?.repo && !!theme.neovim?.colorscheme;
6214
- }
6215
-
6216
6268
  // src/lib/template-engine/engine.ts
6217
6269
  function buildThemeMetadata(theme, mode) {
6218
6270
  return {
@@ -6232,7 +6284,7 @@ function buildGtkMetadata(theme, mode) {
6232
6284
  };
6233
6285
  }
6234
6286
  function buildTemplateContext(theme, palette, mode) {
6235
- return {
6287
+ const context = {
6236
6288
  color0: hexToColorVariable(palette.color0),
6237
6289
  color1: hexToColorVariable(palette.color1),
6238
6290
  color2: hexToColorVariable(palette.color2),
@@ -6260,6 +6312,15 @@ function buildTemplateContext(theme, palette, mode) {
6260
6312
  gtk: buildGtkMetadata(theme, mode),
6261
6313
  mode
6262
6314
  };
6315
+ if (theme.neovim) {
6316
+ context.neovim = {
6317
+ repo: theme.neovim.repo,
6318
+ colorscheme: theme.neovim.colorscheme,
6319
+ light_colorscheme: theme.neovim.light_colorscheme,
6320
+ opts: theme.neovim.opts
6321
+ };
6322
+ }
6323
+ return context;
6263
6324
  }
6264
6325
  function buildDualModeContext(theme) {
6265
6326
  if (!theme.dark || !theme.light) {
@@ -6332,31 +6393,9 @@ async function writeRenderedTemplates(results) {
6332
6393
  await writeFile(result.outputPath, result.content);
6333
6394
  }
6334
6395
  }
6335
- async function generateNeovimConfigFile(theme, mode) {
6336
- if (!hasNeovimConfig(theme)) {
6337
- return null;
6338
- }
6339
- const content = generateNeovimConfig(theme, mode);
6340
- const outputPath = join10(GENERATED_DIR, "neovim.lua");
6341
- await writeFile(outputPath, content);
6342
- return {
6343
- template: {
6344
- name: "neovim.lua",
6345
- path: "",
6346
- outputName: "neovim.lua",
6347
- type: "single"
6348
- },
6349
- content,
6350
- outputPath
6351
- };
6352
- }
6353
6396
  async function generateThemeConfigs(theme, mode) {
6354
6397
  const results = await renderAllTemplates(theme, mode);
6355
6398
  await writeRenderedTemplates(results);
6356
- const neovimResult = await generateNeovimConfigFile(theme, mode);
6357
- if (neovimResult) {
6358
- results.push(neovimResult);
6359
- }
6360
6399
  return results;
6361
6400
  }
6362
6401
 
@@ -6827,12 +6866,13 @@ async function applyJsonTheme(themePath, mode, saveMapping, identifier) {
6827
6866
  rmSync(BACKGROUNDS_TARGET_DIR, { recursive: true, force: true });
6828
6867
  }
6829
6868
  const results = await generateThemeConfigs(theme, mode);
6830
- const ghosttyThemesDir = join13(HOME_DIR, ".config", "ghostty", "themes");
6831
6869
  for (const result of results) {
6832
6870
  const filename = basename4(result.outputPath);
6833
- if (filename === "formalconf-dark" || filename === "formalconf-light") {
6834
- await ensureDir2(ghosttyThemesDir);
6835
- const targetPath2 = join13(ghosttyThemesDir, filename);
6871
+ const templateTargets = result.template.targets ?? [];
6872
+ for (const target of templateTargets) {
6873
+ const expandedTarget = target.replace(/^~/, HOME_DIR);
6874
+ await ensureDir2(expandedTarget);
6875
+ const targetPath2 = join13(expandedTarget, filename);
6836
6876
  copyFileSync2(result.outputPath, targetPath2);
6837
6877
  }
6838
6878
  const targetPath = join13(THEME_TARGET_DIR, filename);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formalconf",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "description": "Dotfiles management TUI for macOS and Linux - config management, package sync, and theme switching",
5
5
  "type": "module",
6
6
  "main": "./dist/formalconf.js",