formalconf 2.0.15 → 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.
- package/dist/formalconf.js +449 -207
- package/package.json +1 -1
package/dist/formalconf.js
CHANGED
|
@@ -336,6 +336,9 @@ var BUNDLED_TEMPLATES_DIR = join(ROOT_DIR, "templates");
|
|
|
336
336
|
var BUNDLED_MANIFEST_PATH = join(BUNDLED_TEMPLATES_DIR, "templates.json");
|
|
337
337
|
var GTK_DIR = join(CONFIG_DIR, "gtk");
|
|
338
338
|
var COLLOID_DIR = join(GTK_DIR, "colloid-gtk-theme");
|
|
339
|
+
var QT_DIR = join(CONFIG_DIR, "qt");
|
|
340
|
+
var KVANTUM_CONFIG_DIR = join(HOME_DIR, ".config", "Kvantum");
|
|
341
|
+
var KVANTUM_THEME_DIR = join(KVANTUM_CONFIG_DIR, "FormalConf");
|
|
339
342
|
async function ensureDir2(path) {
|
|
340
343
|
await ensureDir(path);
|
|
341
344
|
}
|
|
@@ -467,7 +470,7 @@ function StatusIndicator({
|
|
|
467
470
|
// package.json
|
|
468
471
|
var package_default = {
|
|
469
472
|
name: "formalconf",
|
|
470
|
-
version: "2.0.
|
|
473
|
+
version: "2.0.17",
|
|
471
474
|
description: "Dotfiles management TUI for macOS and Linux - config management, package sync, and theme switching",
|
|
472
475
|
type: "module",
|
|
473
476
|
main: "./dist/formalconf.js",
|
|
@@ -4666,13 +4669,13 @@ function useThemeGrid({
|
|
|
4666
4669
|
import { parseArgs as parseArgs4 } from "util";
|
|
4667
4670
|
import {
|
|
4668
4671
|
readdirSync as readdirSync9,
|
|
4669
|
-
existsSync as
|
|
4672
|
+
existsSync as existsSync14,
|
|
4670
4673
|
rmSync,
|
|
4671
4674
|
symlinkSync,
|
|
4672
4675
|
unlinkSync as unlinkSync2,
|
|
4673
|
-
copyFileSync
|
|
4676
|
+
copyFileSync as copyFileSync2
|
|
4674
4677
|
} from "fs";
|
|
4675
|
-
import { join as
|
|
4678
|
+
import { join as join13, basename as basename4 } from "path";
|
|
4676
4679
|
|
|
4677
4680
|
// src/lib/theme-parser.ts
|
|
4678
4681
|
init_runtime();
|
|
@@ -5121,9 +5124,304 @@ ${instructions}`
|
|
|
5121
5124
|
themeName: installedThemeName
|
|
5122
5125
|
};
|
|
5123
5126
|
}
|
|
5127
|
+
// src/lib/qt/kvantum.ts
|
|
5128
|
+
import { existsSync as existsSync8, copyFileSync } from "fs";
|
|
5129
|
+
import { join as join7 } from "path";
|
|
5130
|
+
init_runtime();
|
|
5131
|
+
|
|
5132
|
+
// src/lib/qt/palette.ts
|
|
5133
|
+
function hexToRgb2(hex) {
|
|
5134
|
+
const clean = hex.replace("#", "");
|
|
5135
|
+
return {
|
|
5136
|
+
r: parseInt(clean.substring(0, 2), 16),
|
|
5137
|
+
g: parseInt(clean.substring(2, 4), 16),
|
|
5138
|
+
b: parseInt(clean.substring(4, 6), 16)
|
|
5139
|
+
};
|
|
5140
|
+
}
|
|
5141
|
+
function rgbToHex2(r, g, b) {
|
|
5142
|
+
const toHex = (n) => Math.round(Math.max(0, Math.min(255, n))).toString(16).padStart(2, "0");
|
|
5143
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
5144
|
+
}
|
|
5145
|
+
function darken2(hex, percent) {
|
|
5146
|
+
const { r, g, b } = hexToRgb2(hex);
|
|
5147
|
+
const factor = 1 - percent / 100;
|
|
5148
|
+
return rgbToHex2(r * factor, g * factor, b * factor);
|
|
5149
|
+
}
|
|
5150
|
+
function lighten2(hex, percent) {
|
|
5151
|
+
const { r, g, b } = hexToRgb2(hex);
|
|
5152
|
+
const factor = percent / 100;
|
|
5153
|
+
return rgbToHex2(r + (255 - r) * factor, g + (255 - g) * factor, b + (255 - b) * factor);
|
|
5154
|
+
}
|
|
5155
|
+
function createKvantumPalette(palette, mode) {
|
|
5156
|
+
const isDark = mode === "dark";
|
|
5157
|
+
const accent = palette.accent || palette.color4;
|
|
5158
|
+
const altBase = isDark ? lighten2(palette.background, 8) : darken2(palette.background, 5);
|
|
5159
|
+
const button = isDark ? lighten2(palette.background, 15) : darken2(palette.background, 10);
|
|
5160
|
+
const light = isDark ? lighten2(palette.background, 25) : palette.foreground;
|
|
5161
|
+
const dark = isDark ? darken2(palette.background, 15) : darken2(palette.background, 20);
|
|
5162
|
+
const highlightText = isDark ? palette.background : palette.foreground;
|
|
5163
|
+
return {
|
|
5164
|
+
window: palette.background,
|
|
5165
|
+
base: palette.background,
|
|
5166
|
+
altBase,
|
|
5167
|
+
button,
|
|
5168
|
+
light,
|
|
5169
|
+
dark,
|
|
5170
|
+
highlight: accent,
|
|
5171
|
+
text: palette.foreground,
|
|
5172
|
+
windowText: palette.foreground,
|
|
5173
|
+
buttonText: palette.foreground,
|
|
5174
|
+
highlightText,
|
|
5175
|
+
link: palette.color4,
|
|
5176
|
+
linkVisited: palette.color5
|
|
5177
|
+
};
|
|
5178
|
+
}
|
|
5179
|
+
function generateKvantumConfig(palette, mode, baseTheme) {
|
|
5180
|
+
const kv = createKvantumPalette(palette, mode);
|
|
5181
|
+
const inheritFrom = baseTheme || (mode === "dark" ? "KvFlat" : "KvFlatLight");
|
|
5182
|
+
const textDimmed = mode === "dark" ? `${kv.text}c8` : `${kv.text}b4`;
|
|
5183
|
+
const textMoreDimmed = mode === "dark" ? `${kv.text}8c` : `${kv.text}78`;
|
|
5184
|
+
return `[%General]
|
|
5185
|
+
author=FormalConf
|
|
5186
|
+
comment=Auto-generated theme from FormalConf
|
|
5187
|
+
inherits=${inheritFrom}
|
|
5188
|
+
|
|
5189
|
+
[GeneralColors]
|
|
5190
|
+
window.color=${kv.window}
|
|
5191
|
+
inactive.window.color=${kv.window}
|
|
5192
|
+
base.color=${kv.base}
|
|
5193
|
+
inactive.base.color=${kv.base}
|
|
5194
|
+
alt.base.color=${kv.altBase}
|
|
5195
|
+
inactive.alt.base.color=${kv.altBase}
|
|
5196
|
+
button.color=${kv.button}
|
|
5197
|
+
light.color=${kv.light}
|
|
5198
|
+
mid.light.color=${kv.light}
|
|
5199
|
+
dark.color=${kv.dark}
|
|
5200
|
+
mid.color=${kv.altBase}
|
|
5201
|
+
highlight.color=${kv.highlight}
|
|
5202
|
+
inactive.highlight.color=${kv.highlight}
|
|
5203
|
+
text.color=${kv.text}
|
|
5204
|
+
inactive.text.color=${textDimmed}
|
|
5205
|
+
window.text.color=${kv.windowText}
|
|
5206
|
+
inactive.window.text.color=${textMoreDimmed}
|
|
5207
|
+
button.text.color=${kv.buttonText}
|
|
5208
|
+
disabled.text.color=${textMoreDimmed}
|
|
5209
|
+
tooltip.text.color=${kv.text}
|
|
5210
|
+
highlight.text.color=${kv.highlightText}
|
|
5211
|
+
link.color=${kv.link}
|
|
5212
|
+
link.visited.color=${kv.linkVisited}
|
|
5213
|
+
progress.indicator.text.color=${kv.highlightText}
|
|
5214
|
+
|
|
5215
|
+
[Hacks]
|
|
5216
|
+
transparent_dolphin_view=false
|
|
5217
|
+
blur_translucent=true
|
|
5218
|
+
respect_darkness=true
|
|
5219
|
+
|
|
5220
|
+
[PanelButtonCommand]
|
|
5221
|
+
text.normal.color=${kv.text}
|
|
5222
|
+
text.normal.inactive.color=${textMoreDimmed}
|
|
5223
|
+
text.focus.color=${kv.text}
|
|
5224
|
+
text.press.color=${kv.text}
|
|
5225
|
+
text.toggle.color=${kv.text}
|
|
5226
|
+
text.toggle.inactive.color=${textMoreDimmed}
|
|
5227
|
+
|
|
5228
|
+
[Toolbar]
|
|
5229
|
+
text.normal.color=${kv.text}
|
|
5230
|
+
text.focus.color=${kv.text}
|
|
5231
|
+
|
|
5232
|
+
[MenuBar]
|
|
5233
|
+
text.normal.color=${kv.text}
|
|
5234
|
+
|
|
5235
|
+
[MenuBarItem]
|
|
5236
|
+
text.normal.color=${kv.text}
|
|
5237
|
+
text.focus.color=${kv.highlight}
|
|
5238
|
+
|
|
5239
|
+
[MenuItem]
|
|
5240
|
+
text.normal.color=${kv.text}
|
|
5241
|
+
text.focus.color=${kv.text}
|
|
5242
|
+
|
|
5243
|
+
[ItemView]
|
|
5244
|
+
text.normal.color=${kv.text}
|
|
5245
|
+
text.normal.inactive.color=${textDimmed}
|
|
5246
|
+
text.focus.color=${kv.text}
|
|
5247
|
+
text.press.color=${kv.text}
|
|
5248
|
+
text.toggle.color=${kv.text}
|
|
5249
|
+
text.toggle.inactive.color=${textDimmed}
|
|
5250
|
+
|
|
5251
|
+
[Tab]
|
|
5252
|
+
text.normal.color=${textMoreDimmed}
|
|
5253
|
+
text.normal.inactive.color=${textMoreDimmed}
|
|
5254
|
+
text.focus.color=${textDimmed}
|
|
5255
|
+
text.toggle.color=${kv.text}
|
|
5256
|
+
|
|
5257
|
+
[ToolboxTab]
|
|
5258
|
+
text.normal.color=${textDimmed}
|
|
5259
|
+
text.normal.inactive.color=${textMoreDimmed}
|
|
5260
|
+
text.press.color=${kv.text}
|
|
5261
|
+
text.focus.color=${kv.text}
|
|
5262
|
+
|
|
5263
|
+
[HeaderSection]
|
|
5264
|
+
text.normal.color=${textMoreDimmed}
|
|
5265
|
+
text.normal.inactive.color=${textMoreDimmed}
|
|
5266
|
+
text.focus.color=${textDimmed}
|
|
5267
|
+
text.toggle.color=${kv.text}
|
|
5268
|
+
|
|
5269
|
+
[Progressbar]
|
|
5270
|
+
text.normal.color=${textMoreDimmed}
|
|
5271
|
+
text.normal.inactive.color=${textMoreDimmed}
|
|
5272
|
+
text.focus.color=${kv.text}
|
|
5273
|
+
text.press.color=${kv.text}
|
|
5274
|
+
text.toggle.color=${kv.text}
|
|
5275
|
+
|
|
5276
|
+
[TitleBar]
|
|
5277
|
+
text.normal.color=${textMoreDimmed}
|
|
5278
|
+
text.focus.color=${kv.text}
|
|
5279
|
+
|
|
5280
|
+
[GroupBox]
|
|
5281
|
+
text.normal.color=${kv.text}
|
|
5282
|
+
text.press.color=${kv.text}
|
|
5283
|
+
text.focus.color=${kv.text}
|
|
5284
|
+
|
|
5285
|
+
[Dock]
|
|
5286
|
+
text.normal.color=${kv.text}
|
|
5287
|
+
|
|
5288
|
+
[DockTitle]
|
|
5289
|
+
text.normal.color=${kv.text}
|
|
5290
|
+
text.focus.color=${kv.text}
|
|
5291
|
+
|
|
5292
|
+
[RadioButton]
|
|
5293
|
+
text.normal.color=${kv.text}
|
|
5294
|
+
text.focus.color=${kv.text}
|
|
5295
|
+
|
|
5296
|
+
[CheckBox]
|
|
5297
|
+
text.normal.color=${kv.text}
|
|
5298
|
+
text.focus.color=${kv.text}
|
|
5299
|
+
|
|
5300
|
+
[LineEdit]
|
|
5301
|
+
text.normal.color=${kv.text}
|
|
5302
|
+
|
|
5303
|
+
[IndicatorSpinBox]
|
|
5304
|
+
text.normal.color=${kv.text}
|
|
5305
|
+
|
|
5306
|
+
[Menu]
|
|
5307
|
+
text.normal.color=${kv.text}
|
|
5308
|
+
`;
|
|
5309
|
+
}
|
|
5310
|
+
|
|
5311
|
+
// src/lib/qt/kvantum.ts
|
|
5312
|
+
var SYSTEM_KVANTUM_DIR = "/usr/share/Kvantum";
|
|
5313
|
+
var SETUP_SHOWN_FILE = join7(QT_DIR, ".setup-shown");
|
|
5314
|
+
async function checkQtDependencies() {
|
|
5315
|
+
const [kvantum, qt5ct, qt6ct] = await Promise.all([
|
|
5316
|
+
commandExists("kvantummanager"),
|
|
5317
|
+
commandExists("qt5ct"),
|
|
5318
|
+
commandExists("qt6ct")
|
|
5319
|
+
]);
|
|
5320
|
+
const missing = [];
|
|
5321
|
+
if (!kvantum)
|
|
5322
|
+
missing.push("kvantum");
|
|
5323
|
+
return { kvantum, qt5ct, qt6ct, missing };
|
|
5324
|
+
}
|
|
5325
|
+
async function hasShownSetupReminder() {
|
|
5326
|
+
return existsSync8(SETUP_SHOWN_FILE);
|
|
5327
|
+
}
|
|
5328
|
+
async function markSetupReminderShown() {
|
|
5329
|
+
await ensureDir2(QT_DIR);
|
|
5330
|
+
await writeFile(SETUP_SHOWN_FILE, new Date().toISOString());
|
|
5331
|
+
}
|
|
5332
|
+
function getBaseTheme(mode) {
|
|
5333
|
+
return mode === "dark" ? "KvFlat" : "KvFlatLight";
|
|
5334
|
+
}
|
|
5335
|
+
function copyBaseThemeSvg(baseTheme) {
|
|
5336
|
+
const sourceSvg = join7(SYSTEM_KVANTUM_DIR, baseTheme, `${baseTheme}.svg`);
|
|
5337
|
+
const targetSvg = join7(KVANTUM_THEME_DIR, "FormalConf.svg");
|
|
5338
|
+
if (existsSync8(sourceSvg)) {
|
|
5339
|
+
copyFileSync(sourceSvg, targetSvg);
|
|
5340
|
+
return true;
|
|
5341
|
+
}
|
|
5342
|
+
return false;
|
|
5343
|
+
}
|
|
5344
|
+
async function writeKvantumTheme(theme, mode) {
|
|
5345
|
+
const palette = mode === "dark" ? theme.dark : theme.light;
|
|
5346
|
+
if (!palette) {
|
|
5347
|
+
throw new Error(`Theme does not have a ${mode} palette`);
|
|
5348
|
+
}
|
|
5349
|
+
await ensureDir2(KVANTUM_THEME_DIR);
|
|
5350
|
+
const baseTheme = getBaseTheme(mode);
|
|
5351
|
+
copyBaseThemeSvg(baseTheme);
|
|
5352
|
+
const config = generateKvantumConfig(palette, mode, baseTheme);
|
|
5353
|
+
const configPath = join7(KVANTUM_THEME_DIR, "FormalConf.kvconfig");
|
|
5354
|
+
await writeFile(configPath, config);
|
|
5355
|
+
}
|
|
5356
|
+
async function writeKvantumGlobalConfig() {
|
|
5357
|
+
await ensureDir2(KVANTUM_CONFIG_DIR);
|
|
5358
|
+
const globalConfig = `[General]
|
|
5359
|
+
theme=FormalConf
|
|
5360
|
+
`;
|
|
5361
|
+
const configPath = join7(KVANTUM_CONFIG_DIR, "kvantum.kvconfig");
|
|
5362
|
+
await writeFile(configPath, globalConfig);
|
|
5363
|
+
}
|
|
5364
|
+
async function applyQtTheme(theme, mode) {
|
|
5365
|
+
if (getOS() !== "linux") {
|
|
5366
|
+
return {
|
|
5367
|
+
success: true,
|
|
5368
|
+
themeName: "",
|
|
5369
|
+
skipped: true,
|
|
5370
|
+
skipReason: "QT theming only available on Linux"
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
const palette = mode === "dark" ? theme.dark : theme.light;
|
|
5374
|
+
if (!palette) {
|
|
5375
|
+
return {
|
|
5376
|
+
success: false,
|
|
5377
|
+
themeName: "",
|
|
5378
|
+
error: `Theme does not have a ${mode} palette`
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5381
|
+
const deps = await checkQtDependencies();
|
|
5382
|
+
if (!deps.kvantum) {
|
|
5383
|
+
return {
|
|
5384
|
+
success: true,
|
|
5385
|
+
themeName: "",
|
|
5386
|
+
skipped: true,
|
|
5387
|
+
skipReason: "Kvantum not installed (install kvantum for QT theming)"
|
|
5388
|
+
};
|
|
5389
|
+
}
|
|
5390
|
+
try {
|
|
5391
|
+
await writeKvantumTheme(theme, mode);
|
|
5392
|
+
await writeKvantumGlobalConfig();
|
|
5393
|
+
const shownBefore = await hasShownSetupReminder();
|
|
5394
|
+
return {
|
|
5395
|
+
success: true,
|
|
5396
|
+
themeName: "FormalConf (Kvantum)",
|
|
5397
|
+
...!shownBefore && {
|
|
5398
|
+
error: undefined
|
|
5399
|
+
}
|
|
5400
|
+
};
|
|
5401
|
+
} catch (err) {
|
|
5402
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5403
|
+
return {
|
|
5404
|
+
success: false,
|
|
5405
|
+
themeName: "",
|
|
5406
|
+
error: `Failed to write Kvantum theme: ${message}`
|
|
5407
|
+
};
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
function getQtSetupReminder() {
|
|
5411
|
+
return `Note: Add to your shell profile for QT apps to use Kvantum:
|
|
5412
|
+
export QT_QPA_PLATFORMTHEME=kvantum`;
|
|
5413
|
+
}
|
|
5414
|
+
async function getAndMarkSetupReminder() {
|
|
5415
|
+
const shown = await hasShownSetupReminder();
|
|
5416
|
+
if (shown) {
|
|
5417
|
+
return null;
|
|
5418
|
+
}
|
|
5419
|
+
await markSetupReminderShown();
|
|
5420
|
+
return getQtSetupReminder();
|
|
5421
|
+
}
|
|
5124
5422
|
// src/lib/theme-config.ts
|
|
5125
5423
|
import { hostname } from "os";
|
|
5126
|
-
import { existsSync as
|
|
5424
|
+
import { existsSync as existsSync9, readFileSync, writeFileSync } from "fs";
|
|
5127
5425
|
var DEFAULT_CONFIG = {
|
|
5128
5426
|
version: 1,
|
|
5129
5427
|
defaultTheme: null,
|
|
@@ -5133,7 +5431,7 @@ function getDeviceHostname() {
|
|
|
5133
5431
|
return hostname();
|
|
5134
5432
|
}
|
|
5135
5433
|
function loadThemeConfig() {
|
|
5136
|
-
if (!
|
|
5434
|
+
if (!existsSync9(THEME_CONFIG_PATH)) {
|
|
5137
5435
|
return { ...DEFAULT_CONFIG, devices: {} };
|
|
5138
5436
|
}
|
|
5139
5437
|
try {
|
|
@@ -5199,8 +5497,8 @@ function getDefaultTheme() {
|
|
|
5199
5497
|
|
|
5200
5498
|
// src/lib/theme-v2/loader.ts
|
|
5201
5499
|
init_runtime();
|
|
5202
|
-
import { existsSync as
|
|
5203
|
-
import { join as
|
|
5500
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5 } from "fs";
|
|
5501
|
+
import { join as join8, basename as basename2 } from "path";
|
|
5204
5502
|
|
|
5205
5503
|
// src/lib/theme-v2/color.ts
|
|
5206
5504
|
function isValidHex(hex) {
|
|
@@ -5218,7 +5516,7 @@ function normalizeHex(hex) {
|
|
|
5218
5516
|
}
|
|
5219
5517
|
return hex.toUpperCase();
|
|
5220
5518
|
}
|
|
5221
|
-
function
|
|
5519
|
+
function hexToRgb3(hex) {
|
|
5222
5520
|
const normalized = normalizeHex(hex);
|
|
5223
5521
|
if (!isValidHex(normalized)) {
|
|
5224
5522
|
throw new Error(`Invalid hex color: ${hex}`);
|
|
@@ -5230,7 +5528,7 @@ function hexToRgb2(hex) {
|
|
|
5230
5528
|
}
|
|
5231
5529
|
function hexToColorVariable(hex) {
|
|
5232
5530
|
const normalized = normalizeHex(hex);
|
|
5233
|
-
const { r, g, b } =
|
|
5531
|
+
const { r, g, b } = hexToRgb3(normalized);
|
|
5234
5532
|
return {
|
|
5235
5533
|
hex: normalized,
|
|
5236
5534
|
strip: normalized.slice(1),
|
|
@@ -5479,7 +5777,7 @@ ${validationErrors}`);
|
|
|
5479
5777
|
}
|
|
5480
5778
|
}
|
|
5481
5779
|
async function loadThemeJson(themePath) {
|
|
5482
|
-
if (!
|
|
5780
|
+
if (!existsSync10(themePath)) {
|
|
5483
5781
|
throw new ThemeLoadError(themePath, "file not found");
|
|
5484
5782
|
}
|
|
5485
5783
|
let content;
|
|
@@ -5509,14 +5807,14 @@ function getAvailableModes(theme) {
|
|
|
5509
5807
|
return modes;
|
|
5510
5808
|
}
|
|
5511
5809
|
async function listJsonThemes() {
|
|
5512
|
-
if (!
|
|
5810
|
+
if (!existsSync10(THEMES_DIR)) {
|
|
5513
5811
|
return [];
|
|
5514
5812
|
}
|
|
5515
5813
|
const entries = readdirSync5(THEMES_DIR, { withFileTypes: true });
|
|
5516
5814
|
const themes = [];
|
|
5517
5815
|
for (const entry of entries) {
|
|
5518
5816
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
5519
|
-
const path =
|
|
5817
|
+
const path = join8(THEMES_DIR, entry.name);
|
|
5520
5818
|
try {
|
|
5521
5819
|
const theme = await loadThemeJson(path);
|
|
5522
5820
|
themes.push({
|
|
@@ -5535,7 +5833,7 @@ async function listJsonThemes() {
|
|
|
5535
5833
|
|
|
5536
5834
|
// src/lib/template-engine/engine.ts
|
|
5537
5835
|
init_runtime();
|
|
5538
|
-
import { join as
|
|
5836
|
+
import { join as join10 } from "path";
|
|
5539
5837
|
|
|
5540
5838
|
// src/lib/template-engine/modifiers.ts
|
|
5541
5839
|
var VALID_MODIFIERS = [
|
|
@@ -5623,6 +5921,25 @@ var COLOR_VARIABLES = [
|
|
|
5623
5921
|
function isColorVariable(name) {
|
|
5624
5922
|
return COLOR_VARIABLES.includes(name);
|
|
5625
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
|
+
}
|
|
5626
5943
|
function getContextValue(context, variableName, modifier) {
|
|
5627
5944
|
if (variableName.startsWith("theme.")) {
|
|
5628
5945
|
const key = variableName.slice(6);
|
|
@@ -5634,6 +5951,13 @@ function getContextValue(context, variableName, modifier) {
|
|
|
5634
5951
|
const value = context.gtk[key];
|
|
5635
5952
|
return value !== undefined ? String(value) : undefined;
|
|
5636
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
|
+
}
|
|
5637
5961
|
if (variableName === "mode") {
|
|
5638
5962
|
return context.mode;
|
|
5639
5963
|
}
|
|
@@ -5644,7 +5968,8 @@ function getContextValue(context, variableName, modifier) {
|
|
|
5644
5968
|
return;
|
|
5645
5969
|
}
|
|
5646
5970
|
function renderTemplate(template, context) {
|
|
5647
|
-
|
|
5971
|
+
let result = processConditionals(template, context);
|
|
5972
|
+
result = result.replace(VARIABLE_REGEX, (match, variable) => {
|
|
5648
5973
|
const { name, modifier } = parseVariableReference(variable);
|
|
5649
5974
|
const value = getContextValue(context, name, modifier);
|
|
5650
5975
|
if (value === undefined) {
|
|
@@ -5652,9 +5977,11 @@ function renderTemplate(template, context) {
|
|
|
5652
5977
|
}
|
|
5653
5978
|
return value;
|
|
5654
5979
|
});
|
|
5980
|
+
return result;
|
|
5655
5981
|
}
|
|
5656
5982
|
function renderDualModeTemplate(template, contexts) {
|
|
5657
|
-
let result = template
|
|
5983
|
+
let result = processConditionals(template, contexts);
|
|
5984
|
+
result = result.replace(/\{\{dark\.([a-zA-Z0-9_.]+)\}\}/g, (match, variable) => {
|
|
5658
5985
|
const { name, modifier } = parseVariableReference(variable);
|
|
5659
5986
|
const value = getContextValue(contexts.dark, name, modifier);
|
|
5660
5987
|
return value ?? match;
|
|
@@ -5673,14 +6000,14 @@ function renderDualModeTemplate(template, contexts) {
|
|
|
5673
6000
|
|
|
5674
6001
|
// src/lib/template-engine/versioning.ts
|
|
5675
6002
|
init_runtime();
|
|
5676
|
-
import { existsSync as
|
|
5677
|
-
import { join as
|
|
6003
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6 } from "fs";
|
|
6004
|
+
import { join as join9 } from "path";
|
|
5678
6005
|
var DEFAULT_MANIFEST = {
|
|
5679
6006
|
version: 1,
|
|
5680
6007
|
templates: {}
|
|
5681
6008
|
};
|
|
5682
6009
|
async function loadTemplatesManifest() {
|
|
5683
|
-
if (!
|
|
6010
|
+
if (!existsSync11(TEMPLATES_MANIFEST_PATH)) {
|
|
5684
6011
|
return { ...DEFAULT_MANIFEST };
|
|
5685
6012
|
}
|
|
5686
6013
|
try {
|
|
@@ -5695,7 +6022,7 @@ async function saveTemplatesManifest(manifest) {
|
|
|
5695
6022
|
await writeFile(TEMPLATES_MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
5696
6023
|
}
|
|
5697
6024
|
async function loadBundledManifest() {
|
|
5698
|
-
if (!
|
|
6025
|
+
if (!existsSync11(BUNDLED_MANIFEST_PATH)) {
|
|
5699
6026
|
return { version: 1, templates: {} };
|
|
5700
6027
|
}
|
|
5701
6028
|
try {
|
|
@@ -5746,13 +6073,13 @@ async function installTemplate(templateName) {
|
|
|
5746
6073
|
if (!bundledMeta) {
|
|
5747
6074
|
throw new Error(`Template '${templateName}' not found in bundled templates`);
|
|
5748
6075
|
}
|
|
5749
|
-
const sourcePath =
|
|
5750
|
-
if (!
|
|
6076
|
+
const sourcePath = join9(BUNDLED_TEMPLATES_DIR, templateName);
|
|
6077
|
+
if (!existsSync11(sourcePath)) {
|
|
5751
6078
|
throw new Error(`Template file not found: ${sourcePath}`);
|
|
5752
6079
|
}
|
|
5753
6080
|
await ensureDir2(TEMPLATES_DIR);
|
|
5754
6081
|
const content = await readText(sourcePath);
|
|
5755
|
-
const destPath =
|
|
6082
|
+
const destPath = join9(TEMPLATES_DIR, templateName);
|
|
5756
6083
|
await writeFile(destPath, content);
|
|
5757
6084
|
const manifest = await loadTemplatesManifest();
|
|
5758
6085
|
manifest.templates[templateName] = {
|
|
@@ -5768,10 +6095,11 @@ async function installAllTemplates() {
|
|
|
5768
6095
|
await installTemplate(name);
|
|
5769
6096
|
}
|
|
5770
6097
|
}
|
|
5771
|
-
function getTemplateType(filename) {
|
|
5772
|
-
const
|
|
5773
|
-
|
|
5774
|
-
|
|
6098
|
+
async function getTemplateType(filename) {
|
|
6099
|
+
const manifest = await loadBundledManifest();
|
|
6100
|
+
const meta = manifest.templates[filename];
|
|
6101
|
+
if (meta?.mode) {
|
|
6102
|
+
return meta.mode;
|
|
5775
6103
|
}
|
|
5776
6104
|
if (filename.includes("-dark.") || filename.includes("-light.")) {
|
|
5777
6105
|
return "partial";
|
|
@@ -5785,36 +6113,21 @@ function getPartialMode(filename) {
|
|
|
5785
6113
|
return "light";
|
|
5786
6114
|
return;
|
|
5787
6115
|
}
|
|
5788
|
-
function getOutputFilename(templateName) {
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
if (templateName.startsWith("kitty-light")) {
|
|
5794
|
-
return "light-theme.auto.conf";
|
|
5795
|
-
}
|
|
5796
|
-
if (templateName.startsWith("waybar-dark")) {
|
|
5797
|
-
return "style-dark.css";
|
|
5798
|
-
}
|
|
5799
|
-
if (templateName.startsWith("waybar-light")) {
|
|
5800
|
-
return "style-light.css";
|
|
5801
|
-
}
|
|
5802
|
-
if (templateName === "ghostty-dark.theme.template") {
|
|
5803
|
-
return "formalconf-dark";
|
|
5804
|
-
}
|
|
5805
|
-
if (templateName === "ghostty-light.theme.template") {
|
|
5806
|
-
return "formalconf-light";
|
|
6116
|
+
async function getOutputFilename(templateName) {
|
|
6117
|
+
const manifest = await loadBundledManifest();
|
|
6118
|
+
const meta = manifest.templates[templateName];
|
|
6119
|
+
if (meta?.output) {
|
|
6120
|
+
return meta.output;
|
|
5807
6121
|
}
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
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 ?? [];
|
|
5815
6128
|
}
|
|
5816
6129
|
async function listInstalledTemplates() {
|
|
5817
|
-
if (!
|
|
6130
|
+
if (!existsSync11(TEMPLATES_DIR)) {
|
|
5818
6131
|
return [];
|
|
5819
6132
|
}
|
|
5820
6133
|
const entries = readdirSync6(TEMPLATES_DIR, { withFileTypes: true });
|
|
@@ -5823,98 +6136,17 @@ async function listInstalledTemplates() {
|
|
|
5823
6136
|
if (entry.isFile() && entry.name.endsWith(".template")) {
|
|
5824
6137
|
templates.push({
|
|
5825
6138
|
name: entry.name,
|
|
5826
|
-
path:
|
|
5827
|
-
outputName: getOutputFilename(entry.name),
|
|
5828
|
-
type: getTemplateType(entry.name),
|
|
5829
|
-
partialMode: getPartialMode(entry.name)
|
|
6139
|
+
path: join9(TEMPLATES_DIR, 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)
|
|
5830
6144
|
});
|
|
5831
6145
|
}
|
|
5832
6146
|
}
|
|
5833
6147
|
return templates;
|
|
5834
6148
|
}
|
|
5835
6149
|
|
|
5836
|
-
// src/lib/neovim/generator.ts
|
|
5837
|
-
function toLua(value, indent = 2) {
|
|
5838
|
-
const spaces = " ".repeat(indent);
|
|
5839
|
-
if (value === null || value === undefined) {
|
|
5840
|
-
return "nil";
|
|
5841
|
-
}
|
|
5842
|
-
if (typeof value === "boolean") {
|
|
5843
|
-
return value ? "true" : "false";
|
|
5844
|
-
}
|
|
5845
|
-
if (typeof value === "number") {
|
|
5846
|
-
return String(value);
|
|
5847
|
-
}
|
|
5848
|
-
if (typeof value === "string") {
|
|
5849
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
5850
|
-
return `"${escaped}"`;
|
|
5851
|
-
}
|
|
5852
|
-
if (Array.isArray(value)) {
|
|
5853
|
-
if (value.length === 0) {
|
|
5854
|
-
return "{}";
|
|
5855
|
-
}
|
|
5856
|
-
const items = value.map((v) => `${spaces}${toLua(v, indent + 2)}`);
|
|
5857
|
-
return `{
|
|
5858
|
-
${items.join(`,
|
|
5859
|
-
`)}
|
|
5860
|
-
${" ".repeat(indent - 2)}}`;
|
|
5861
|
-
}
|
|
5862
|
-
if (typeof value === "object") {
|
|
5863
|
-
const entries = Object.entries(value);
|
|
5864
|
-
if (entries.length === 0) {
|
|
5865
|
-
return "{}";
|
|
5866
|
-
}
|
|
5867
|
-
const items = entries.map(([k, v]) => {
|
|
5868
|
-
const key = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(k) ? k : `["${k}"]`;
|
|
5869
|
-
return `${spaces}${key} = ${toLua(v, indent + 2)}`;
|
|
5870
|
-
});
|
|
5871
|
-
return `{
|
|
5872
|
-
${items.join(`,
|
|
5873
|
-
`)}
|
|
5874
|
-
${" ".repeat(indent - 2)}}`;
|
|
5875
|
-
}
|
|
5876
|
-
return "nil";
|
|
5877
|
-
}
|
|
5878
|
-
function generateOptsSection(opts) {
|
|
5879
|
-
if (!opts || Object.keys(opts).length === 0) {
|
|
5880
|
-
return "";
|
|
5881
|
-
}
|
|
5882
|
-
return ` opts = ${toLua(opts, 6)},`;
|
|
5883
|
-
}
|
|
5884
|
-
function generateNeovimConfig(theme, mode) {
|
|
5885
|
-
if (!theme.neovim) {
|
|
5886
|
-
return "";
|
|
5887
|
-
}
|
|
5888
|
-
const { repo, colorscheme, light_colorscheme, opts } = theme.neovim;
|
|
5889
|
-
const effectiveColorscheme = mode === "light" && light_colorscheme ? light_colorscheme : colorscheme;
|
|
5890
|
-
const optsSection = generateOptsSection(opts);
|
|
5891
|
-
const hasDualMode = light_colorscheme && light_colorscheme !== colorscheme;
|
|
5892
|
-
const lines = [
|
|
5893
|
-
`-- ${theme.title} (${mode}) - Generated by FormalConf`,
|
|
5894
|
-
`-- Neovim colorscheme configuration for LazyVim`,
|
|
5895
|
-
``,
|
|
5896
|
-
`return {`,
|
|
5897
|
-
` {`,
|
|
5898
|
-
` "${repo}",`,
|
|
5899
|
-
` name = "${theme.title.toLowerCase().replace(/\s+/g, "-")}",`,
|
|
5900
|
-
` priority = 1000,`
|
|
5901
|
-
];
|
|
5902
|
-
if (optsSection) {
|
|
5903
|
-
lines.push(optsSection);
|
|
5904
|
-
}
|
|
5905
|
-
lines.push(` },`, ` {`, ` "LazyVim/LazyVim",`, ` opts = {`, ` colorscheme = "${effectiveColorscheme}",`, ` },`, ` },`);
|
|
5906
|
-
if (hasDualMode) {
|
|
5907
|
-
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,`, ` },`, ` },`);
|
|
5908
|
-
}
|
|
5909
|
-
lines.push(`}`);
|
|
5910
|
-
return lines.join(`
|
|
5911
|
-
`) + `
|
|
5912
|
-
`;
|
|
5913
|
-
}
|
|
5914
|
-
function hasNeovimConfig(theme) {
|
|
5915
|
-
return !!theme.neovim?.repo && !!theme.neovim?.colorscheme;
|
|
5916
|
-
}
|
|
5917
|
-
|
|
5918
6150
|
// src/lib/template-engine/engine.ts
|
|
5919
6151
|
function buildThemeMetadata(theme, mode) {
|
|
5920
6152
|
return {
|
|
@@ -5934,7 +6166,7 @@ function buildGtkMetadata(theme, mode) {
|
|
|
5934
6166
|
};
|
|
5935
6167
|
}
|
|
5936
6168
|
function buildTemplateContext(theme, palette, mode) {
|
|
5937
|
-
|
|
6169
|
+
const context = {
|
|
5938
6170
|
color0: hexToColorVariable(palette.color0),
|
|
5939
6171
|
color1: hexToColorVariable(palette.color1),
|
|
5940
6172
|
color2: hexToColorVariable(palette.color2),
|
|
@@ -5962,6 +6194,15 @@ function buildTemplateContext(theme, palette, mode) {
|
|
|
5962
6194
|
gtk: buildGtkMetadata(theme, mode),
|
|
5963
6195
|
mode
|
|
5964
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;
|
|
5965
6206
|
}
|
|
5966
6207
|
function buildDualModeContext(theme) {
|
|
5967
6208
|
if (!theme.dark || !theme.light) {
|
|
@@ -6005,7 +6246,7 @@ async function renderTemplateFile(templateFile, theme, mode) {
|
|
|
6005
6246
|
return {
|
|
6006
6247
|
template: templateFile,
|
|
6007
6248
|
content,
|
|
6008
|
-
outputPath:
|
|
6249
|
+
outputPath: join10(GENERATED_DIR, templateFile.outputName)
|
|
6009
6250
|
};
|
|
6010
6251
|
}
|
|
6011
6252
|
async function renderAllTemplates(theme, mode) {
|
|
@@ -6034,38 +6275,16 @@ async function writeRenderedTemplates(results) {
|
|
|
6034
6275
|
await writeFile(result.outputPath, result.content);
|
|
6035
6276
|
}
|
|
6036
6277
|
}
|
|
6037
|
-
async function generateNeovimConfigFile(theme, mode) {
|
|
6038
|
-
if (!hasNeovimConfig(theme)) {
|
|
6039
|
-
return null;
|
|
6040
|
-
}
|
|
6041
|
-
const content = generateNeovimConfig(theme, mode);
|
|
6042
|
-
const outputPath = join9(GENERATED_DIR, "neovim.lua");
|
|
6043
|
-
await writeFile(outputPath, content);
|
|
6044
|
-
return {
|
|
6045
|
-
template: {
|
|
6046
|
-
name: "neovim.lua",
|
|
6047
|
-
path: "",
|
|
6048
|
-
outputName: "neovim.lua",
|
|
6049
|
-
type: "single"
|
|
6050
|
-
},
|
|
6051
|
-
content,
|
|
6052
|
-
outputPath
|
|
6053
|
-
};
|
|
6054
|
-
}
|
|
6055
6278
|
async function generateThemeConfigs(theme, mode) {
|
|
6056
6279
|
const results = await renderAllTemplates(theme, mode);
|
|
6057
6280
|
await writeRenderedTemplates(results);
|
|
6058
|
-
const neovimResult = await generateNeovimConfigFile(theme, mode);
|
|
6059
|
-
if (neovimResult) {
|
|
6060
|
-
results.push(neovimResult);
|
|
6061
|
-
}
|
|
6062
6281
|
return results;
|
|
6063
6282
|
}
|
|
6064
6283
|
|
|
6065
6284
|
// src/lib/migration/extractor.ts
|
|
6066
6285
|
init_runtime();
|
|
6067
|
-
import { existsSync as
|
|
6068
|
-
import { join as
|
|
6286
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7 } from "fs";
|
|
6287
|
+
import { join as join11 } from "path";
|
|
6069
6288
|
function normalizeHex2(hex) {
|
|
6070
6289
|
hex = hex.replace(/^(#|0x)/i, "");
|
|
6071
6290
|
if (hex.length === 3) {
|
|
@@ -6206,7 +6425,7 @@ async function extractFromGhostty(path) {
|
|
|
6206
6425
|
return { colors: colors5, source: "ghostty" };
|
|
6207
6426
|
}
|
|
6208
6427
|
async function extractColors(filePath) {
|
|
6209
|
-
if (!
|
|
6428
|
+
if (!existsSync12(filePath)) {
|
|
6210
6429
|
return null;
|
|
6211
6430
|
}
|
|
6212
6431
|
const filename = filePath.toLowerCase();
|
|
@@ -6228,7 +6447,7 @@ async function extractColors(filePath) {
|
|
|
6228
6447
|
return null;
|
|
6229
6448
|
}
|
|
6230
6449
|
async function extractFromLegacyTheme(themePath) {
|
|
6231
|
-
if (!
|
|
6450
|
+
if (!existsSync12(themePath)) {
|
|
6232
6451
|
return null;
|
|
6233
6452
|
}
|
|
6234
6453
|
const files = readdirSync7(themePath, { withFileTypes: true });
|
|
@@ -6240,12 +6459,12 @@ async function extractFromLegacyTheme(themePath) {
|
|
|
6240
6459
|
for (const preferred of preferredFiles) {
|
|
6241
6460
|
const match = files.find((f) => f.name.toLowerCase() === preferred.toLowerCase());
|
|
6242
6461
|
if (match) {
|
|
6243
|
-
return extractColors(
|
|
6462
|
+
return extractColors(join11(themePath, match.name));
|
|
6244
6463
|
}
|
|
6245
6464
|
}
|
|
6246
6465
|
for (const file of files) {
|
|
6247
6466
|
if (file.isFile() && (file.name.endsWith(".conf") || file.name.endsWith(".toml"))) {
|
|
6248
|
-
const result = await extractColors(
|
|
6467
|
+
const result = await extractColors(join11(themePath, file.name));
|
|
6249
6468
|
if (result && Object.keys(result.colors).length > 0) {
|
|
6250
6469
|
return result;
|
|
6251
6470
|
}
|
|
@@ -6334,19 +6553,19 @@ function generateThemeJson(name, colors5, options = {}) {
|
|
|
6334
6553
|
init_runtime();
|
|
6335
6554
|
|
|
6336
6555
|
// src/lib/wallpaper.ts
|
|
6337
|
-
import { existsSync as
|
|
6338
|
-
import { join as
|
|
6556
|
+
import { existsSync as existsSync13, readdirSync as readdirSync8, unlinkSync } from "fs";
|
|
6557
|
+
import { join as join12 } from "path";
|
|
6339
6558
|
init_runtime();
|
|
6340
6559
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
6341
6560
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
6342
6561
|
function clearBackgroundsDir() {
|
|
6343
|
-
if (!
|
|
6562
|
+
if (!existsSync13(BACKGROUNDS_TARGET_DIR)) {
|
|
6344
6563
|
return;
|
|
6345
6564
|
}
|
|
6346
6565
|
const entries = readdirSync8(BACKGROUNDS_TARGET_DIR, { withFileTypes: true });
|
|
6347
6566
|
for (const entry of entries) {
|
|
6348
6567
|
if (entry.isFile() || entry.isSymbolicLink()) {
|
|
6349
|
-
unlinkSync(
|
|
6568
|
+
unlinkSync(join12(BACKGROUNDS_TARGET_DIR, entry.name));
|
|
6350
6569
|
}
|
|
6351
6570
|
}
|
|
6352
6571
|
}
|
|
@@ -6410,7 +6629,7 @@ async function downloadWallpaper(url, filename, timeoutMs = DEFAULT_TIMEOUT_MS)
|
|
|
6410
6629
|
};
|
|
6411
6630
|
}
|
|
6412
6631
|
const ext = getExtension(url, contentType);
|
|
6413
|
-
const outputPath =
|
|
6632
|
+
const outputPath = join12(BACKGROUNDS_TARGET_DIR, `${filename}.${ext}`);
|
|
6414
6633
|
await writeBuffer(outputPath, arrayBuffer);
|
|
6415
6634
|
return { success: true, path: outputPath };
|
|
6416
6635
|
} catch (err) {
|
|
@@ -6467,11 +6686,11 @@ async function listAllThemes() {
|
|
|
6467
6686
|
});
|
|
6468
6687
|
}
|
|
6469
6688
|
}
|
|
6470
|
-
if (
|
|
6689
|
+
if (existsSync14(THEMES_DIR)) {
|
|
6471
6690
|
const entries = readdirSync9(THEMES_DIR, { withFileTypes: true });
|
|
6472
6691
|
for (const entry of entries) {
|
|
6473
6692
|
if (entry.isDirectory()) {
|
|
6474
|
-
const themePath =
|
|
6693
|
+
const themePath = join13(THEMES_DIR, entry.name);
|
|
6475
6694
|
const theme = await parseTheme(themePath, entry.name);
|
|
6476
6695
|
themes.push({
|
|
6477
6696
|
displayName: theme.name,
|
|
@@ -6488,10 +6707,10 @@ async function listAllThemes() {
|
|
|
6488
6707
|
return themes;
|
|
6489
6708
|
}
|
|
6490
6709
|
function clearDirectory(dir) {
|
|
6491
|
-
if (
|
|
6710
|
+
if (existsSync14(dir)) {
|
|
6492
6711
|
const entries = readdirSync9(dir, { withFileTypes: true });
|
|
6493
6712
|
for (const entry of entries) {
|
|
6494
|
-
const fullPath =
|
|
6713
|
+
const fullPath = join13(dir, entry.name);
|
|
6495
6714
|
if (entry.isSymbolicLink() || entry.isFile()) {
|
|
6496
6715
|
unlinkSync2(fullPath);
|
|
6497
6716
|
} else if (entry.isDirectory()) {
|
|
@@ -6501,7 +6720,7 @@ function clearDirectory(dir) {
|
|
|
6501
6720
|
}
|
|
6502
6721
|
}
|
|
6503
6722
|
function createSymlink(source, target) {
|
|
6504
|
-
if (
|
|
6723
|
+
if (existsSync14(target)) {
|
|
6505
6724
|
unlinkSync2(target);
|
|
6506
6725
|
}
|
|
6507
6726
|
symlinkSync(source, target);
|
|
@@ -6525,20 +6744,21 @@ async function applyJsonTheme(themePath, mode, saveMapping, identifier) {
|
|
|
6525
6744
|
await installAllTemplates();
|
|
6526
6745
|
}
|
|
6527
6746
|
clearDirectory(THEME_TARGET_DIR);
|
|
6528
|
-
if (
|
|
6747
|
+
if (existsSync14(BACKGROUNDS_TARGET_DIR)) {
|
|
6529
6748
|
rmSync(BACKGROUNDS_TARGET_DIR, { recursive: true, force: true });
|
|
6530
6749
|
}
|
|
6531
6750
|
const results = await generateThemeConfigs(theme, mode);
|
|
6532
|
-
const ghosttyThemesDir = join12(HOME_DIR, ".config", "ghostty", "themes");
|
|
6533
6751
|
for (const result of results) {
|
|
6534
6752
|
const filename = basename4(result.outputPath);
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
const
|
|
6538
|
-
|
|
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);
|
|
6758
|
+
copyFileSync2(result.outputPath, targetPath2);
|
|
6539
6759
|
}
|
|
6540
|
-
const targetPath =
|
|
6541
|
-
|
|
6760
|
+
const targetPath = join13(THEME_TARGET_DIR, filename);
|
|
6761
|
+
copyFileSync2(result.outputPath, targetPath);
|
|
6542
6762
|
}
|
|
6543
6763
|
let wallpaperPaths = [];
|
|
6544
6764
|
let wallpaperErrors = [];
|
|
@@ -6551,6 +6771,10 @@ async function applyJsonTheme(themePath, mode, saveMapping, identifier) {
|
|
|
6551
6771
|
if (getOS() === "linux") {
|
|
6552
6772
|
gtkResult = await applyGtkTheme(theme, mode);
|
|
6553
6773
|
}
|
|
6774
|
+
let qtResult = null;
|
|
6775
|
+
if (getOS() === "linux") {
|
|
6776
|
+
qtResult = await applyQtTheme(theme, mode);
|
|
6777
|
+
}
|
|
6554
6778
|
if (saveMapping) {
|
|
6555
6779
|
await setDeviceTheme(identifier);
|
|
6556
6780
|
}
|
|
@@ -6586,6 +6810,21 @@ GTK theme: ${gtkResult.themeName}`;
|
|
|
6586
6810
|
Warning: GTK theme failed - ${gtkResult.error}`;
|
|
6587
6811
|
}
|
|
6588
6812
|
}
|
|
6813
|
+
if (qtResult && !qtResult.skipped) {
|
|
6814
|
+
if (qtResult.success) {
|
|
6815
|
+
output += `
|
|
6816
|
+
QT theme: ${qtResult.themeName}`;
|
|
6817
|
+
const setupReminder = await getAndMarkSetupReminder();
|
|
6818
|
+
if (setupReminder) {
|
|
6819
|
+
output += `
|
|
6820
|
+
|
|
6821
|
+
${setupReminder}`;
|
|
6822
|
+
}
|
|
6823
|
+
} else {
|
|
6824
|
+
output += `
|
|
6825
|
+
Warning: QT theme failed - ${qtResult.error}`;
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6589
6828
|
const hookEnv = {
|
|
6590
6829
|
FORMALCONF_THEME: identifier,
|
|
6591
6830
|
FORMALCONF_THEME_MODE: mode,
|
|
@@ -6597,6 +6836,9 @@ GTK theme: ${gtkResult.themeName}`;
|
|
|
6597
6836
|
if (gtkResult?.success && gtkResult.themeName) {
|
|
6598
6837
|
hookEnv.FORMALCONF_GTK_THEME = gtkResult.themeName;
|
|
6599
6838
|
}
|
|
6839
|
+
if (qtResult?.success && qtResult.themeName) {
|
|
6840
|
+
hookEnv.FORMALCONF_QT_THEME = qtResult.themeName;
|
|
6841
|
+
}
|
|
6600
6842
|
const hookSummary = await runHooks("theme-change", hookEnv);
|
|
6601
6843
|
if (hookSummary.executed > 0) {
|
|
6602
6844
|
output += `
|
|
@@ -6611,27 +6853,27 @@ Hooks: ${hookSummary.succeeded}/${hookSummary.executed} succeeded`;
|
|
|
6611
6853
|
return { output, success: true };
|
|
6612
6854
|
}
|
|
6613
6855
|
async function applyLegacyTheme(themeName, saveMapping) {
|
|
6614
|
-
const themeDir =
|
|
6615
|
-
if (!
|
|
6856
|
+
const themeDir = join13(THEMES_DIR, themeName);
|
|
6857
|
+
if (!existsSync14(themeDir)) {
|
|
6616
6858
|
return { output: `Theme '${themeName}' not found`, success: false };
|
|
6617
6859
|
}
|
|
6618
6860
|
await ensureConfigDir();
|
|
6619
6861
|
await ensureDir2(THEME_TARGET_DIR);
|
|
6620
6862
|
const theme = await parseTheme(themeDir, themeName);
|
|
6621
6863
|
clearDirectory(THEME_TARGET_DIR);
|
|
6622
|
-
if (
|
|
6864
|
+
if (existsSync14(BACKGROUNDS_TARGET_DIR)) {
|
|
6623
6865
|
rmSync(BACKGROUNDS_TARGET_DIR, { recursive: true, force: true });
|
|
6624
6866
|
}
|
|
6625
6867
|
const entries = readdirSync9(themeDir, { withFileTypes: true });
|
|
6626
6868
|
for (const entry of entries) {
|
|
6627
|
-
const source =
|
|
6869
|
+
const source = join13(themeDir, entry.name);
|
|
6628
6870
|
if (entry.isFile() && entry.name !== "theme.yaml" && entry.name !== "light.mode") {
|
|
6629
|
-
const target =
|
|
6871
|
+
const target = join13(THEME_TARGET_DIR, entry.name);
|
|
6630
6872
|
createSymlink(source, target);
|
|
6631
6873
|
}
|
|
6632
6874
|
}
|
|
6633
6875
|
if (theme.hasBackgrounds) {
|
|
6634
|
-
const backgroundsSource =
|
|
6876
|
+
const backgroundsSource = join13(themeDir, "backgrounds");
|
|
6635
6877
|
createSymlink(backgroundsSource, BACKGROUNDS_TARGET_DIR);
|
|
6636
6878
|
}
|
|
6637
6879
|
if (saveMapping) {
|
|
@@ -6671,12 +6913,12 @@ Hooks: ${hookSummary.succeeded}/${hookSummary.executed} succeeded`;
|
|
|
6671
6913
|
}
|
|
6672
6914
|
async function applyTheme(themeIdentifier, saveMapping = false) {
|
|
6673
6915
|
const { name, mode } = parseThemeIdentifier(themeIdentifier);
|
|
6674
|
-
const jsonPath =
|
|
6675
|
-
if (
|
|
6916
|
+
const jsonPath = join13(THEMES_DIR, `${name}.json`);
|
|
6917
|
+
if (existsSync14(jsonPath) && mode) {
|
|
6676
6918
|
return applyJsonTheme(jsonPath, mode, saveMapping, themeIdentifier);
|
|
6677
6919
|
}
|
|
6678
|
-
const legacyPath =
|
|
6679
|
-
if (
|
|
6920
|
+
const legacyPath = join13(THEMES_DIR, name);
|
|
6921
|
+
if (existsSync14(legacyPath)) {
|
|
6680
6922
|
return applyLegacyTheme(name, saveMapping);
|
|
6681
6923
|
}
|
|
6682
6924
|
const allThemes = await listAllThemes();
|
|
@@ -6695,8 +6937,8 @@ Did you mean:`;
|
|
|
6695
6937
|
}
|
|
6696
6938
|
async function showThemeInfo(themeIdentifier) {
|
|
6697
6939
|
const { name, mode } = parseThemeIdentifier(themeIdentifier);
|
|
6698
|
-
const jsonPath =
|
|
6699
|
-
if (
|
|
6940
|
+
const jsonPath = join13(THEMES_DIR, `${name}.json`);
|
|
6941
|
+
if (existsSync14(jsonPath)) {
|
|
6700
6942
|
const theme2 = await loadThemeJson(jsonPath);
|
|
6701
6943
|
const modes = getAvailableModes(theme2);
|
|
6702
6944
|
console.log(`
|
|
@@ -6736,8 +6978,8 @@ ${colors5.green}Wallpapers:${colors5.reset}`);
|
|
|
6736
6978
|
}
|
|
6737
6979
|
return;
|
|
6738
6980
|
}
|
|
6739
|
-
const themeDir =
|
|
6740
|
-
if (!
|
|
6981
|
+
const themeDir = join13(THEMES_DIR, name);
|
|
6982
|
+
if (!existsSync14(themeDir)) {
|
|
6741
6983
|
console.error(`${colors5.red}Error: Theme '${themeIdentifier}' not found${colors5.reset}`);
|
|
6742
6984
|
process.exit(1);
|
|
6743
6985
|
}
|
|
@@ -6846,8 +7088,8 @@ To add themes:`);
|
|
|
6846
7088
|
}
|
|
6847
7089
|
}
|
|
6848
7090
|
async function migrateTheme(themeName) {
|
|
6849
|
-
const legacyPath =
|
|
6850
|
-
if (!
|
|
7091
|
+
const legacyPath = join13(THEMES_DIR, themeName);
|
|
7092
|
+
if (!existsSync14(legacyPath)) {
|
|
6851
7093
|
console.error(`${colors5.red}Error: Legacy theme '${themeName}' not found${colors5.reset}`);
|
|
6852
7094
|
process.exit(1);
|
|
6853
7095
|
}
|
|
@@ -6864,13 +7106,13 @@ async function migrateTheme(themeName) {
|
|
|
6864
7106
|
console.log(`${colors5.yellow}Warning: Missing colors will be filled with defaults:${colors5.reset}`);
|
|
6865
7107
|
console.log(` ${missing.join(", ")}`);
|
|
6866
7108
|
}
|
|
6867
|
-
const isLight =
|
|
7109
|
+
const isLight = existsSync14(join13(legacyPath, "light.mode"));
|
|
6868
7110
|
const themeJson = generateThemeJson(themeName, result.colors, {
|
|
6869
7111
|
description: `Migrated from legacy theme`,
|
|
6870
7112
|
isLight
|
|
6871
7113
|
});
|
|
6872
|
-
const outputPath =
|
|
6873
|
-
if (
|
|
7114
|
+
const outputPath = join13(THEMES_DIR, `${themeName}.json`);
|
|
7115
|
+
if (existsSync14(outputPath)) {
|
|
6874
7116
|
console.error(`${colors5.red}Error: JSON theme '${themeName}.json' already exists${colors5.reset}`);
|
|
6875
7117
|
console.error(`Delete or rename it first, then try again.`);
|
|
6876
7118
|
process.exit(1);
|
package/package.json
CHANGED