formalconf 2.0.15 → 2.0.16
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 +386 -66
- 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.16",
|
|
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 = [
|
|
@@ -5673,14 +5971,14 @@ function renderDualModeTemplate(template, contexts) {
|
|
|
5673
5971
|
|
|
5674
5972
|
// src/lib/template-engine/versioning.ts
|
|
5675
5973
|
init_runtime();
|
|
5676
|
-
import { existsSync as
|
|
5677
|
-
import { join as
|
|
5974
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6 } from "fs";
|
|
5975
|
+
import { join as join9 } from "path";
|
|
5678
5976
|
var DEFAULT_MANIFEST = {
|
|
5679
5977
|
version: 1,
|
|
5680
5978
|
templates: {}
|
|
5681
5979
|
};
|
|
5682
5980
|
async function loadTemplatesManifest() {
|
|
5683
|
-
if (!
|
|
5981
|
+
if (!existsSync11(TEMPLATES_MANIFEST_PATH)) {
|
|
5684
5982
|
return { ...DEFAULT_MANIFEST };
|
|
5685
5983
|
}
|
|
5686
5984
|
try {
|
|
@@ -5695,7 +5993,7 @@ async function saveTemplatesManifest(manifest) {
|
|
|
5695
5993
|
await writeFile(TEMPLATES_MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
5696
5994
|
}
|
|
5697
5995
|
async function loadBundledManifest() {
|
|
5698
|
-
if (!
|
|
5996
|
+
if (!existsSync11(BUNDLED_MANIFEST_PATH)) {
|
|
5699
5997
|
return { version: 1, templates: {} };
|
|
5700
5998
|
}
|
|
5701
5999
|
try {
|
|
@@ -5746,13 +6044,13 @@ async function installTemplate(templateName) {
|
|
|
5746
6044
|
if (!bundledMeta) {
|
|
5747
6045
|
throw new Error(`Template '${templateName}' not found in bundled templates`);
|
|
5748
6046
|
}
|
|
5749
|
-
const sourcePath =
|
|
5750
|
-
if (!
|
|
6047
|
+
const sourcePath = join9(BUNDLED_TEMPLATES_DIR, templateName);
|
|
6048
|
+
if (!existsSync11(sourcePath)) {
|
|
5751
6049
|
throw new Error(`Template file not found: ${sourcePath}`);
|
|
5752
6050
|
}
|
|
5753
6051
|
await ensureDir2(TEMPLATES_DIR);
|
|
5754
6052
|
const content = await readText(sourcePath);
|
|
5755
|
-
const destPath =
|
|
6053
|
+
const destPath = join9(TEMPLATES_DIR, templateName);
|
|
5756
6054
|
await writeFile(destPath, content);
|
|
5757
6055
|
const manifest = await loadTemplatesManifest();
|
|
5758
6056
|
manifest.templates[templateName] = {
|
|
@@ -5814,7 +6112,7 @@ function getOutputFilename(templateName) {
|
|
|
5814
6112
|
return output;
|
|
5815
6113
|
}
|
|
5816
6114
|
async function listInstalledTemplates() {
|
|
5817
|
-
if (!
|
|
6115
|
+
if (!existsSync11(TEMPLATES_DIR)) {
|
|
5818
6116
|
return [];
|
|
5819
6117
|
}
|
|
5820
6118
|
const entries = readdirSync6(TEMPLATES_DIR, { withFileTypes: true });
|
|
@@ -5823,7 +6121,7 @@ async function listInstalledTemplates() {
|
|
|
5823
6121
|
if (entry.isFile() && entry.name.endsWith(".template")) {
|
|
5824
6122
|
templates.push({
|
|
5825
6123
|
name: entry.name,
|
|
5826
|
-
path:
|
|
6124
|
+
path: join9(TEMPLATES_DIR, entry.name),
|
|
5827
6125
|
outputName: getOutputFilename(entry.name),
|
|
5828
6126
|
type: getTemplateType(entry.name),
|
|
5829
6127
|
partialMode: getPartialMode(entry.name)
|
|
@@ -6005,7 +6303,7 @@ async function renderTemplateFile(templateFile, theme, mode) {
|
|
|
6005
6303
|
return {
|
|
6006
6304
|
template: templateFile,
|
|
6007
6305
|
content,
|
|
6008
|
-
outputPath:
|
|
6306
|
+
outputPath: join10(GENERATED_DIR, templateFile.outputName)
|
|
6009
6307
|
};
|
|
6010
6308
|
}
|
|
6011
6309
|
async function renderAllTemplates(theme, mode) {
|
|
@@ -6039,7 +6337,7 @@ async function generateNeovimConfigFile(theme, mode) {
|
|
|
6039
6337
|
return null;
|
|
6040
6338
|
}
|
|
6041
6339
|
const content = generateNeovimConfig(theme, mode);
|
|
6042
|
-
const outputPath =
|
|
6340
|
+
const outputPath = join10(GENERATED_DIR, "neovim.lua");
|
|
6043
6341
|
await writeFile(outputPath, content);
|
|
6044
6342
|
return {
|
|
6045
6343
|
template: {
|
|
@@ -6064,8 +6362,8 @@ async function generateThemeConfigs(theme, mode) {
|
|
|
6064
6362
|
|
|
6065
6363
|
// src/lib/migration/extractor.ts
|
|
6066
6364
|
init_runtime();
|
|
6067
|
-
import { existsSync as
|
|
6068
|
-
import { join as
|
|
6365
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7 } from "fs";
|
|
6366
|
+
import { join as join11 } from "path";
|
|
6069
6367
|
function normalizeHex2(hex) {
|
|
6070
6368
|
hex = hex.replace(/^(#|0x)/i, "");
|
|
6071
6369
|
if (hex.length === 3) {
|
|
@@ -6206,7 +6504,7 @@ async function extractFromGhostty(path) {
|
|
|
6206
6504
|
return { colors: colors5, source: "ghostty" };
|
|
6207
6505
|
}
|
|
6208
6506
|
async function extractColors(filePath) {
|
|
6209
|
-
if (!
|
|
6507
|
+
if (!existsSync12(filePath)) {
|
|
6210
6508
|
return null;
|
|
6211
6509
|
}
|
|
6212
6510
|
const filename = filePath.toLowerCase();
|
|
@@ -6228,7 +6526,7 @@ async function extractColors(filePath) {
|
|
|
6228
6526
|
return null;
|
|
6229
6527
|
}
|
|
6230
6528
|
async function extractFromLegacyTheme(themePath) {
|
|
6231
|
-
if (!
|
|
6529
|
+
if (!existsSync12(themePath)) {
|
|
6232
6530
|
return null;
|
|
6233
6531
|
}
|
|
6234
6532
|
const files = readdirSync7(themePath, { withFileTypes: true });
|
|
@@ -6240,12 +6538,12 @@ async function extractFromLegacyTheme(themePath) {
|
|
|
6240
6538
|
for (const preferred of preferredFiles) {
|
|
6241
6539
|
const match = files.find((f) => f.name.toLowerCase() === preferred.toLowerCase());
|
|
6242
6540
|
if (match) {
|
|
6243
|
-
return extractColors(
|
|
6541
|
+
return extractColors(join11(themePath, match.name));
|
|
6244
6542
|
}
|
|
6245
6543
|
}
|
|
6246
6544
|
for (const file of files) {
|
|
6247
6545
|
if (file.isFile() && (file.name.endsWith(".conf") || file.name.endsWith(".toml"))) {
|
|
6248
|
-
const result = await extractColors(
|
|
6546
|
+
const result = await extractColors(join11(themePath, file.name));
|
|
6249
6547
|
if (result && Object.keys(result.colors).length > 0) {
|
|
6250
6548
|
return result;
|
|
6251
6549
|
}
|
|
@@ -6334,19 +6632,19 @@ function generateThemeJson(name, colors5, options = {}) {
|
|
|
6334
6632
|
init_runtime();
|
|
6335
6633
|
|
|
6336
6634
|
// src/lib/wallpaper.ts
|
|
6337
|
-
import { existsSync as
|
|
6338
|
-
import { join as
|
|
6635
|
+
import { existsSync as existsSync13, readdirSync as readdirSync8, unlinkSync } from "fs";
|
|
6636
|
+
import { join as join12 } from "path";
|
|
6339
6637
|
init_runtime();
|
|
6340
6638
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
6341
6639
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
6342
6640
|
function clearBackgroundsDir() {
|
|
6343
|
-
if (!
|
|
6641
|
+
if (!existsSync13(BACKGROUNDS_TARGET_DIR)) {
|
|
6344
6642
|
return;
|
|
6345
6643
|
}
|
|
6346
6644
|
const entries = readdirSync8(BACKGROUNDS_TARGET_DIR, { withFileTypes: true });
|
|
6347
6645
|
for (const entry of entries) {
|
|
6348
6646
|
if (entry.isFile() || entry.isSymbolicLink()) {
|
|
6349
|
-
unlinkSync(
|
|
6647
|
+
unlinkSync(join12(BACKGROUNDS_TARGET_DIR, entry.name));
|
|
6350
6648
|
}
|
|
6351
6649
|
}
|
|
6352
6650
|
}
|
|
@@ -6410,7 +6708,7 @@ async function downloadWallpaper(url, filename, timeoutMs = DEFAULT_TIMEOUT_MS)
|
|
|
6410
6708
|
};
|
|
6411
6709
|
}
|
|
6412
6710
|
const ext = getExtension(url, contentType);
|
|
6413
|
-
const outputPath =
|
|
6711
|
+
const outputPath = join12(BACKGROUNDS_TARGET_DIR, `${filename}.${ext}`);
|
|
6414
6712
|
await writeBuffer(outputPath, arrayBuffer);
|
|
6415
6713
|
return { success: true, path: outputPath };
|
|
6416
6714
|
} catch (err) {
|
|
@@ -6467,11 +6765,11 @@ async function listAllThemes() {
|
|
|
6467
6765
|
});
|
|
6468
6766
|
}
|
|
6469
6767
|
}
|
|
6470
|
-
if (
|
|
6768
|
+
if (existsSync14(THEMES_DIR)) {
|
|
6471
6769
|
const entries = readdirSync9(THEMES_DIR, { withFileTypes: true });
|
|
6472
6770
|
for (const entry of entries) {
|
|
6473
6771
|
if (entry.isDirectory()) {
|
|
6474
|
-
const themePath =
|
|
6772
|
+
const themePath = join13(THEMES_DIR, entry.name);
|
|
6475
6773
|
const theme = await parseTheme(themePath, entry.name);
|
|
6476
6774
|
themes.push({
|
|
6477
6775
|
displayName: theme.name,
|
|
@@ -6488,10 +6786,10 @@ async function listAllThemes() {
|
|
|
6488
6786
|
return themes;
|
|
6489
6787
|
}
|
|
6490
6788
|
function clearDirectory(dir) {
|
|
6491
|
-
if (
|
|
6789
|
+
if (existsSync14(dir)) {
|
|
6492
6790
|
const entries = readdirSync9(dir, { withFileTypes: true });
|
|
6493
6791
|
for (const entry of entries) {
|
|
6494
|
-
const fullPath =
|
|
6792
|
+
const fullPath = join13(dir, entry.name);
|
|
6495
6793
|
if (entry.isSymbolicLink() || entry.isFile()) {
|
|
6496
6794
|
unlinkSync2(fullPath);
|
|
6497
6795
|
} else if (entry.isDirectory()) {
|
|
@@ -6501,7 +6799,7 @@ function clearDirectory(dir) {
|
|
|
6501
6799
|
}
|
|
6502
6800
|
}
|
|
6503
6801
|
function createSymlink(source, target) {
|
|
6504
|
-
if (
|
|
6802
|
+
if (existsSync14(target)) {
|
|
6505
6803
|
unlinkSync2(target);
|
|
6506
6804
|
}
|
|
6507
6805
|
symlinkSync(source, target);
|
|
@@ -6525,20 +6823,20 @@ async function applyJsonTheme(themePath, mode, saveMapping, identifier) {
|
|
|
6525
6823
|
await installAllTemplates();
|
|
6526
6824
|
}
|
|
6527
6825
|
clearDirectory(THEME_TARGET_DIR);
|
|
6528
|
-
if (
|
|
6826
|
+
if (existsSync14(BACKGROUNDS_TARGET_DIR)) {
|
|
6529
6827
|
rmSync(BACKGROUNDS_TARGET_DIR, { recursive: true, force: true });
|
|
6530
6828
|
}
|
|
6531
6829
|
const results = await generateThemeConfigs(theme, mode);
|
|
6532
|
-
const ghosttyThemesDir =
|
|
6830
|
+
const ghosttyThemesDir = join13(HOME_DIR, ".config", "ghostty", "themes");
|
|
6533
6831
|
for (const result of results) {
|
|
6534
6832
|
const filename = basename4(result.outputPath);
|
|
6535
6833
|
if (filename === "formalconf-dark" || filename === "formalconf-light") {
|
|
6536
6834
|
await ensureDir2(ghosttyThemesDir);
|
|
6537
|
-
const targetPath2 =
|
|
6538
|
-
|
|
6835
|
+
const targetPath2 = join13(ghosttyThemesDir, filename);
|
|
6836
|
+
copyFileSync2(result.outputPath, targetPath2);
|
|
6539
6837
|
}
|
|
6540
|
-
const targetPath =
|
|
6541
|
-
|
|
6838
|
+
const targetPath = join13(THEME_TARGET_DIR, filename);
|
|
6839
|
+
copyFileSync2(result.outputPath, targetPath);
|
|
6542
6840
|
}
|
|
6543
6841
|
let wallpaperPaths = [];
|
|
6544
6842
|
let wallpaperErrors = [];
|
|
@@ -6551,6 +6849,10 @@ async function applyJsonTheme(themePath, mode, saveMapping, identifier) {
|
|
|
6551
6849
|
if (getOS() === "linux") {
|
|
6552
6850
|
gtkResult = await applyGtkTheme(theme, mode);
|
|
6553
6851
|
}
|
|
6852
|
+
let qtResult = null;
|
|
6853
|
+
if (getOS() === "linux") {
|
|
6854
|
+
qtResult = await applyQtTheme(theme, mode);
|
|
6855
|
+
}
|
|
6554
6856
|
if (saveMapping) {
|
|
6555
6857
|
await setDeviceTheme(identifier);
|
|
6556
6858
|
}
|
|
@@ -6586,6 +6888,21 @@ GTK theme: ${gtkResult.themeName}`;
|
|
|
6586
6888
|
Warning: GTK theme failed - ${gtkResult.error}`;
|
|
6587
6889
|
}
|
|
6588
6890
|
}
|
|
6891
|
+
if (qtResult && !qtResult.skipped) {
|
|
6892
|
+
if (qtResult.success) {
|
|
6893
|
+
output += `
|
|
6894
|
+
QT theme: ${qtResult.themeName}`;
|
|
6895
|
+
const setupReminder = await getAndMarkSetupReminder();
|
|
6896
|
+
if (setupReminder) {
|
|
6897
|
+
output += `
|
|
6898
|
+
|
|
6899
|
+
${setupReminder}`;
|
|
6900
|
+
}
|
|
6901
|
+
} else {
|
|
6902
|
+
output += `
|
|
6903
|
+
Warning: QT theme failed - ${qtResult.error}`;
|
|
6904
|
+
}
|
|
6905
|
+
}
|
|
6589
6906
|
const hookEnv = {
|
|
6590
6907
|
FORMALCONF_THEME: identifier,
|
|
6591
6908
|
FORMALCONF_THEME_MODE: mode,
|
|
@@ -6597,6 +6914,9 @@ GTK theme: ${gtkResult.themeName}`;
|
|
|
6597
6914
|
if (gtkResult?.success && gtkResult.themeName) {
|
|
6598
6915
|
hookEnv.FORMALCONF_GTK_THEME = gtkResult.themeName;
|
|
6599
6916
|
}
|
|
6917
|
+
if (qtResult?.success && qtResult.themeName) {
|
|
6918
|
+
hookEnv.FORMALCONF_QT_THEME = qtResult.themeName;
|
|
6919
|
+
}
|
|
6600
6920
|
const hookSummary = await runHooks("theme-change", hookEnv);
|
|
6601
6921
|
if (hookSummary.executed > 0) {
|
|
6602
6922
|
output += `
|
|
@@ -6611,27 +6931,27 @@ Hooks: ${hookSummary.succeeded}/${hookSummary.executed} succeeded`;
|
|
|
6611
6931
|
return { output, success: true };
|
|
6612
6932
|
}
|
|
6613
6933
|
async function applyLegacyTheme(themeName, saveMapping) {
|
|
6614
|
-
const themeDir =
|
|
6615
|
-
if (!
|
|
6934
|
+
const themeDir = join13(THEMES_DIR, themeName);
|
|
6935
|
+
if (!existsSync14(themeDir)) {
|
|
6616
6936
|
return { output: `Theme '${themeName}' not found`, success: false };
|
|
6617
6937
|
}
|
|
6618
6938
|
await ensureConfigDir();
|
|
6619
6939
|
await ensureDir2(THEME_TARGET_DIR);
|
|
6620
6940
|
const theme = await parseTheme(themeDir, themeName);
|
|
6621
6941
|
clearDirectory(THEME_TARGET_DIR);
|
|
6622
|
-
if (
|
|
6942
|
+
if (existsSync14(BACKGROUNDS_TARGET_DIR)) {
|
|
6623
6943
|
rmSync(BACKGROUNDS_TARGET_DIR, { recursive: true, force: true });
|
|
6624
6944
|
}
|
|
6625
6945
|
const entries = readdirSync9(themeDir, { withFileTypes: true });
|
|
6626
6946
|
for (const entry of entries) {
|
|
6627
|
-
const source =
|
|
6947
|
+
const source = join13(themeDir, entry.name);
|
|
6628
6948
|
if (entry.isFile() && entry.name !== "theme.yaml" && entry.name !== "light.mode") {
|
|
6629
|
-
const target =
|
|
6949
|
+
const target = join13(THEME_TARGET_DIR, entry.name);
|
|
6630
6950
|
createSymlink(source, target);
|
|
6631
6951
|
}
|
|
6632
6952
|
}
|
|
6633
6953
|
if (theme.hasBackgrounds) {
|
|
6634
|
-
const backgroundsSource =
|
|
6954
|
+
const backgroundsSource = join13(themeDir, "backgrounds");
|
|
6635
6955
|
createSymlink(backgroundsSource, BACKGROUNDS_TARGET_DIR);
|
|
6636
6956
|
}
|
|
6637
6957
|
if (saveMapping) {
|
|
@@ -6671,12 +6991,12 @@ Hooks: ${hookSummary.succeeded}/${hookSummary.executed} succeeded`;
|
|
|
6671
6991
|
}
|
|
6672
6992
|
async function applyTheme(themeIdentifier, saveMapping = false) {
|
|
6673
6993
|
const { name, mode } = parseThemeIdentifier(themeIdentifier);
|
|
6674
|
-
const jsonPath =
|
|
6675
|
-
if (
|
|
6994
|
+
const jsonPath = join13(THEMES_DIR, `${name}.json`);
|
|
6995
|
+
if (existsSync14(jsonPath) && mode) {
|
|
6676
6996
|
return applyJsonTheme(jsonPath, mode, saveMapping, themeIdentifier);
|
|
6677
6997
|
}
|
|
6678
|
-
const legacyPath =
|
|
6679
|
-
if (
|
|
6998
|
+
const legacyPath = join13(THEMES_DIR, name);
|
|
6999
|
+
if (existsSync14(legacyPath)) {
|
|
6680
7000
|
return applyLegacyTheme(name, saveMapping);
|
|
6681
7001
|
}
|
|
6682
7002
|
const allThemes = await listAllThemes();
|
|
@@ -6695,8 +7015,8 @@ Did you mean:`;
|
|
|
6695
7015
|
}
|
|
6696
7016
|
async function showThemeInfo(themeIdentifier) {
|
|
6697
7017
|
const { name, mode } = parseThemeIdentifier(themeIdentifier);
|
|
6698
|
-
const jsonPath =
|
|
6699
|
-
if (
|
|
7018
|
+
const jsonPath = join13(THEMES_DIR, `${name}.json`);
|
|
7019
|
+
if (existsSync14(jsonPath)) {
|
|
6700
7020
|
const theme2 = await loadThemeJson(jsonPath);
|
|
6701
7021
|
const modes = getAvailableModes(theme2);
|
|
6702
7022
|
console.log(`
|
|
@@ -6736,8 +7056,8 @@ ${colors5.green}Wallpapers:${colors5.reset}`);
|
|
|
6736
7056
|
}
|
|
6737
7057
|
return;
|
|
6738
7058
|
}
|
|
6739
|
-
const themeDir =
|
|
6740
|
-
if (!
|
|
7059
|
+
const themeDir = join13(THEMES_DIR, name);
|
|
7060
|
+
if (!existsSync14(themeDir)) {
|
|
6741
7061
|
console.error(`${colors5.red}Error: Theme '${themeIdentifier}' not found${colors5.reset}`);
|
|
6742
7062
|
process.exit(1);
|
|
6743
7063
|
}
|
|
@@ -6846,8 +7166,8 @@ To add themes:`);
|
|
|
6846
7166
|
}
|
|
6847
7167
|
}
|
|
6848
7168
|
async function migrateTheme(themeName) {
|
|
6849
|
-
const legacyPath =
|
|
6850
|
-
if (!
|
|
7169
|
+
const legacyPath = join13(THEMES_DIR, themeName);
|
|
7170
|
+
if (!existsSync14(legacyPath)) {
|
|
6851
7171
|
console.error(`${colors5.red}Error: Legacy theme '${themeName}' not found${colors5.reset}`);
|
|
6852
7172
|
process.exit(1);
|
|
6853
7173
|
}
|
|
@@ -6864,13 +7184,13 @@ async function migrateTheme(themeName) {
|
|
|
6864
7184
|
console.log(`${colors5.yellow}Warning: Missing colors will be filled with defaults:${colors5.reset}`);
|
|
6865
7185
|
console.log(` ${missing.join(", ")}`);
|
|
6866
7186
|
}
|
|
6867
|
-
const isLight =
|
|
7187
|
+
const isLight = existsSync14(join13(legacyPath, "light.mode"));
|
|
6868
7188
|
const themeJson = generateThemeJson(themeName, result.colors, {
|
|
6869
7189
|
description: `Migrated from legacy theme`,
|
|
6870
7190
|
isLight
|
|
6871
7191
|
});
|
|
6872
|
-
const outputPath =
|
|
6873
|
-
if (
|
|
7192
|
+
const outputPath = join13(THEMES_DIR, `${themeName}.json`);
|
|
7193
|
+
if (existsSync14(outputPath)) {
|
|
6874
7194
|
console.error(`${colors5.red}Error: JSON theme '${themeName}.json' already exists${colors5.reset}`);
|
|
6875
7195
|
console.error(`Delete or rename it first, then try again.`);
|
|
6876
7196
|
process.exit(1);
|
package/package.json
CHANGED