formalconf 2.0.16 → 2.0.17

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 +67 -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.17",
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",
@@ -5921,6 +5921,25 @@ var COLOR_VARIABLES = [
5921
5921
  function isColorVariable(name) {
5922
5922
  return COLOR_VARIABLES.includes(name);
5923
5923
  }
5924
+ function getNestedValue(obj, path) {
5925
+ return path.split(".").reduce((acc, key) => {
5926
+ if (acc && typeof acc === "object" && key in acc) {
5927
+ return acc[key];
5928
+ }
5929
+ return;
5930
+ }, obj);
5931
+ }
5932
+ function processConditionals(template, context) {
5933
+ template = template.replace(/\{\{#if\s+(\S+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, variable, content) => {
5934
+ const value = getNestedValue(context, variable);
5935
+ return value ? content : "";
5936
+ });
5937
+ template = template.replace(/\{\{#unless\s+(\S+)\}\}([\s\S]*?)\{\{\/unless\}\}/g, (_, variable, content) => {
5938
+ const value = getNestedValue(context, variable);
5939
+ return !value ? content : "";
5940
+ });
5941
+ return template;
5942
+ }
5924
5943
  function getContextValue(context, variableName, modifier) {
5925
5944
  if (variableName.startsWith("theme.")) {
5926
5945
  const key = variableName.slice(6);
@@ -5932,6 +5951,13 @@ function getContextValue(context, variableName, modifier) {
5932
5951
  const value = context.gtk[key];
5933
5952
  return value !== undefined ? String(value) : undefined;
5934
5953
  }
5954
+ if (variableName.startsWith("neovim.")) {
5955
+ if (!context.neovim)
5956
+ return;
5957
+ const key = variableName.slice(7);
5958
+ const value = context.neovim[key];
5959
+ return value !== undefined ? String(value) : undefined;
5960
+ }
5935
5961
  if (variableName === "mode") {
5936
5962
  return context.mode;
5937
5963
  }
@@ -5942,7 +5968,8 @@ function getContextValue(context, variableName, modifier) {
5942
5968
  return;
5943
5969
  }
5944
5970
  function renderTemplate(template, context) {
5945
- return template.replace(VARIABLE_REGEX, (match, variable) => {
5971
+ let result = processConditionals(template, context);
5972
+ result = result.replace(VARIABLE_REGEX, (match, variable) => {
5946
5973
  const { name, modifier } = parseVariableReference(variable);
5947
5974
  const value = getContextValue(context, name, modifier);
5948
5975
  if (value === undefined) {
@@ -5950,9 +5977,11 @@ function renderTemplate(template, context) {
5950
5977
  }
5951
5978
  return value;
5952
5979
  });
5980
+ return result;
5953
5981
  }
5954
5982
  function renderDualModeTemplate(template, contexts) {
5955
- let result = template.replace(/\{\{dark\.([a-zA-Z0-9_.]+)\}\}/g, (match, variable) => {
5983
+ let result = processConditionals(template, contexts);
5984
+ result = result.replace(/\{\{dark\.([a-zA-Z0-9_.]+)\}\}/g, (match, variable) => {
5956
5985
  const { name, modifier } = parseVariableReference(variable);
5957
5986
  const value = getContextValue(contexts.dark, name, modifier);
5958
5987
  return value ?? match;
@@ -6066,10 +6095,11 @@ async function installAllTemplates() {
6066
6095
  await installTemplate(name);
6067
6096
  }
6068
6097
  }
6069
- function getTemplateType(filename) {
6070
- const dualModeTemplates = ["ghostty.conf.template", "neovim.lua.template", "lynk.css.template"];
6071
- if (dualModeTemplates.includes(filename)) {
6072
- return "dual";
6098
+ async function getTemplateType(filename) {
6099
+ const manifest = await loadBundledManifest();
6100
+ const meta = manifest.templates[filename];
6101
+ if (meta?.mode) {
6102
+ return meta.mode;
6073
6103
  }
6074
6104
  if (filename.includes("-dark.") || filename.includes("-light.")) {
6075
6105
  return "partial";
@@ -6083,33 +6113,18 @@ function getPartialMode(filename) {
6083
6113
  return "light";
6084
6114
  return;
6085
6115
  }
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";
6096
- }
6097
- if (templateName.startsWith("waybar-light")) {
6098
- return "style-light.css";
6116
+ async function getOutputFilename(templateName) {
6117
+ const manifest = await loadBundledManifest();
6118
+ const meta = manifest.templates[templateName];
6119
+ if (meta?.output) {
6120
+ return meta.output;
6099
6121
  }
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;
6122
+ return templateName.replace(/\.template$/, "");
6123
+ }
6124
+ async function getTemplateTargets(templateName) {
6125
+ const manifest = await loadBundledManifest();
6126
+ const meta = manifest.templates[templateName];
6127
+ return meta?.targets ?? [];
6113
6128
  }
6114
6129
  async function listInstalledTemplates() {
6115
6130
  if (!existsSync11(TEMPLATES_DIR)) {
@@ -6122,97 +6137,16 @@ async function listInstalledTemplates() {
6122
6137
  templates.push({
6123
6138
  name: entry.name,
6124
6139
  path: join9(TEMPLATES_DIR, entry.name),
6125
- outputName: getOutputFilename(entry.name),
6126
- type: getTemplateType(entry.name),
6127
- partialMode: getPartialMode(entry.name)
6140
+ outputName: await getOutputFilename(entry.name),
6141
+ type: await getTemplateType(entry.name),
6142
+ partialMode: getPartialMode(entry.name),
6143
+ targets: await getTemplateTargets(entry.name)
6128
6144
  });
6129
6145
  }
6130
6146
  }
6131
6147
  return templates;
6132
6148
  }
6133
6149
 
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
6150
  // src/lib/template-engine/engine.ts
6217
6151
  function buildThemeMetadata(theme, mode) {
6218
6152
  return {
@@ -6232,7 +6166,7 @@ function buildGtkMetadata(theme, mode) {
6232
6166
  };
6233
6167
  }
6234
6168
  function buildTemplateContext(theme, palette, mode) {
6235
- return {
6169
+ const context = {
6236
6170
  color0: hexToColorVariable(palette.color0),
6237
6171
  color1: hexToColorVariable(palette.color1),
6238
6172
  color2: hexToColorVariable(palette.color2),
@@ -6260,6 +6194,15 @@ function buildTemplateContext(theme, palette, mode) {
6260
6194
  gtk: buildGtkMetadata(theme, mode),
6261
6195
  mode
6262
6196
  };
6197
+ if (theme.neovim) {
6198
+ context.neovim = {
6199
+ repo: theme.neovim.repo,
6200
+ colorscheme: theme.neovim.colorscheme,
6201
+ light_colorscheme: theme.neovim.light_colorscheme,
6202
+ opts: theme.neovim.opts
6203
+ };
6204
+ }
6205
+ return context;
6263
6206
  }
6264
6207
  function buildDualModeContext(theme) {
6265
6208
  if (!theme.dark || !theme.light) {
@@ -6332,31 +6275,9 @@ async function writeRenderedTemplates(results) {
6332
6275
  await writeFile(result.outputPath, result.content);
6333
6276
  }
6334
6277
  }
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
6278
  async function generateThemeConfigs(theme, mode) {
6354
6279
  const results = await renderAllTemplates(theme, mode);
6355
6280
  await writeRenderedTemplates(results);
6356
- const neovimResult = await generateNeovimConfigFile(theme, mode);
6357
- if (neovimResult) {
6358
- results.push(neovimResult);
6359
- }
6360
6281
  return results;
6361
6282
  }
6362
6283
 
@@ -6827,12 +6748,13 @@ async function applyJsonTheme(themePath, mode, saveMapping, identifier) {
6827
6748
  rmSync(BACKGROUNDS_TARGET_DIR, { recursive: true, force: true });
6828
6749
  }
6829
6750
  const results = await generateThemeConfigs(theme, mode);
6830
- const ghosttyThemesDir = join13(HOME_DIR, ".config", "ghostty", "themes");
6831
6751
  for (const result of results) {
6832
6752
  const filename = basename4(result.outputPath);
6833
- if (filename === "formalconf-dark" || filename === "formalconf-light") {
6834
- await ensureDir2(ghosttyThemesDir);
6835
- const targetPath2 = join13(ghosttyThemesDir, filename);
6753
+ const templateTargets = result.template.targets ?? [];
6754
+ for (const target of templateTargets) {
6755
+ const expandedTarget = target.replace(/^~/, HOME_DIR);
6756
+ await ensureDir2(expandedTarget);
6757
+ const targetPath2 = join13(expandedTarget, filename);
6836
6758
  copyFileSync2(result.outputPath, targetPath2);
6837
6759
  }
6838
6760
  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.17",
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",