ctheme 0.1.6 → 0.1.7
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/README.md +8 -4
- package/bin/ctheme.js +114 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,8 +42,9 @@ Apply a bundled theme live:
|
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
ctheme use solarized
|
|
45
|
-
ctheme use
|
|
46
|
-
ctheme use tide
|
|
45
|
+
ctheme use tide
|
|
46
|
+
ctheme use tide --dark
|
|
47
|
+
ctheme use solarized --light
|
|
47
48
|
ctheme use harbor --default
|
|
48
49
|
ctheme use velvet --font "Space Mono"
|
|
49
50
|
```
|
|
@@ -59,6 +60,7 @@ See available themes:
|
|
|
59
60
|
```bash
|
|
60
61
|
ctheme list
|
|
61
62
|
ctheme preview solarized
|
|
63
|
+
ctheme preview tide --dark
|
|
62
64
|
ctheme status
|
|
63
65
|
```
|
|
64
66
|
|
|
@@ -107,8 +109,8 @@ Examples:
|
|
|
107
109
|
|
|
108
110
|
```bash
|
|
109
111
|
ctheme term apple-terminal --theme noir --font "SF Mono"
|
|
110
|
-
ctheme term ghostty --theme
|
|
111
|
-
ctheme term kitty --theme ember --font "Berkeley Mono" --font-size 14 --write
|
|
112
|
+
ctheme term ghostty --theme tide --dark --font "JetBrains Mono" --font-size 15 --write
|
|
113
|
+
ctheme term kitty --theme ember --light --font "Berkeley Mono" --font-size 14 --write
|
|
112
114
|
ctheme term wezterm --theme paper --font "IBM Plex Mono" --font-size 14
|
|
113
115
|
```
|
|
114
116
|
|
|
@@ -161,6 +163,8 @@ Bundled themes intentionally vary their font pairings so they do not all feel th
|
|
|
161
163
|
The npm install bootstrap preinstalls the curated font set those presets rely on, so the bundled themes render correctly immediately on macOS.
|
|
162
164
|
The default code and terminal font choices are biased toward tighter, denser monospace faces rather than wide-spaced ones.
|
|
163
165
|
Every bundled preset family now has both `-light` and `-dark` variants.
|
|
166
|
+
Using the family name alone, like `tide`, follows your system appearance automatically.
|
|
167
|
+
You can still override the mode manually with `--light` or `--dark`.
|
|
164
168
|
|
|
165
169
|
## Contributing
|
|
166
170
|
|
package/bin/ctheme.js
CHANGED
|
@@ -496,7 +496,7 @@ async function main() {
|
|
|
496
496
|
runUse(args[1], parseFlags(args.slice(2)));
|
|
497
497
|
return;
|
|
498
498
|
case "preview":
|
|
499
|
-
runPreview(args[1]);
|
|
499
|
+
runPreview(args[1], parseFlags(args.slice(2)));
|
|
500
500
|
return;
|
|
501
501
|
case "make":
|
|
502
502
|
await runMake(args[1], parseFlags(args.slice(2)));
|
|
@@ -538,7 +538,7 @@ Commands:
|
|
|
538
538
|
status Show ctheme paths and saved terminal restore state
|
|
539
539
|
reset [--target <name>] Restore the terminal UI back to its original state
|
|
540
540
|
use <name> [flags] Apply a theme to the actual terminal immediately
|
|
541
|
-
preview <name>
|
|
541
|
+
preview <name> [flags] Preview a theme palette in the terminal
|
|
542
542
|
make [name] [flags] Create a custom theme or launch the theme wizard
|
|
543
543
|
wizard [name] [flags] Launch the interactive theme wizard directly
|
|
544
544
|
init <name> [flags] Create an editable starter JSON file
|
|
@@ -549,17 +549,18 @@ Commands:
|
|
|
549
549
|
Examples:
|
|
550
550
|
ctheme reset
|
|
551
551
|
ctheme use solarized
|
|
552
|
+
ctheme use tide --dark
|
|
552
553
|
ctheme use harbor --default
|
|
553
554
|
ctheme make
|
|
554
555
|
ctheme wizard
|
|
555
556
|
ctheme make arjun --from velvet --accent "#ff4d6d"
|
|
556
557
|
ctheme init custom-light --preset harbor
|
|
557
558
|
ctheme live neon
|
|
558
|
-
ctheme live solarized --font "
|
|
559
|
+
ctheme live solarized --light --font "JetBrains Mono"
|
|
559
560
|
ctheme font install "Manrope"
|
|
560
561
|
ctheme font list
|
|
561
562
|
ctheme term apple-terminal --theme noir --font "SF Mono"
|
|
562
|
-
ctheme term ghostty --font "JetBrains Mono" --font-size 15
|
|
563
|
+
ctheme term ghostty --theme tide --dark --font "JetBrains Mono" --font-size 15
|
|
563
564
|
|
|
564
565
|
Flags for make:
|
|
565
566
|
--preset <name> Base preset name
|
|
@@ -589,8 +590,10 @@ Font subcommands:
|
|
|
589
590
|
|
|
590
591
|
Flags for term:
|
|
591
592
|
Targets: apple-terminal, current, ghostty, kitty, wezterm
|
|
593
|
+
--dark Force the dark variant for bundled theme families
|
|
592
594
|
--font <family>
|
|
593
595
|
--font-size <number>
|
|
596
|
+
--light Force the light variant for bundled theme families
|
|
594
597
|
--theme <name>
|
|
595
598
|
--opacity <number>
|
|
596
599
|
--write Write snippet into your terminal config location when supported
|
|
@@ -598,10 +601,16 @@ Flags for term:
|
|
|
598
601
|
--default Apple Terminal: set as default and startup profile
|
|
599
602
|
|
|
600
603
|
Flags for use:
|
|
604
|
+
--dark Force the dark variant for bundled theme families
|
|
601
605
|
--font <family>
|
|
602
606
|
--font-size <number>
|
|
607
|
+
--light Force the light variant for bundled theme families
|
|
603
608
|
--default Apple Terminal: set as default and startup profile
|
|
604
609
|
|
|
610
|
+
Flags for preview:
|
|
611
|
+
--dark Force the dark variant for bundled theme families
|
|
612
|
+
--light Force the light variant for bundled theme families
|
|
613
|
+
|
|
605
614
|
Flags for reset:
|
|
606
615
|
--target <name> current, apple-terminal, ghostty, kitty, wezterm
|
|
607
616
|
`);
|
|
@@ -668,7 +677,8 @@ function derivePaletteVariant(palette, targetMode) {
|
|
|
668
677
|
|
|
669
678
|
function runList() {
|
|
670
679
|
const paths = getThemePaths();
|
|
671
|
-
const
|
|
680
|
+
const bundled = getBundledThemeFamilies();
|
|
681
|
+
const custom = new Set();
|
|
672
682
|
|
|
673
683
|
if (fs.existsSync(paths.paletteDir)) {
|
|
674
684
|
for (const entry of fs.readdirSync(paths.paletteDir)) {
|
|
@@ -678,9 +688,12 @@ function runList() {
|
|
|
678
688
|
}
|
|
679
689
|
}
|
|
680
690
|
|
|
691
|
+
for (const name of bundled) {
|
|
692
|
+
console.log(`${name} (bundled, auto light/dark)`);
|
|
693
|
+
}
|
|
694
|
+
|
|
681
695
|
for (const name of [...custom].sort()) {
|
|
682
|
-
|
|
683
|
-
console.log(`${name} (${source})`);
|
|
696
|
+
console.log(`${name} (custom)`);
|
|
684
697
|
}
|
|
685
698
|
}
|
|
686
699
|
|
|
@@ -740,9 +753,9 @@ function runReset(flags) {
|
|
|
740
753
|
fail(`Unsupported reset target: ${target}`);
|
|
741
754
|
}
|
|
742
755
|
|
|
743
|
-
function runPreview(themeName) {
|
|
744
|
-
|
|
745
|
-
const theme = buildTheme(
|
|
756
|
+
function runPreview(themeName, flags) {
|
|
757
|
+
const resolvedThemeName = resolveThemeName(themeName, flags || {});
|
|
758
|
+
const theme = buildTheme(resolvedThemeName, getBasePalette(resolvedThemeName));
|
|
746
759
|
|
|
747
760
|
const palette = [
|
|
748
761
|
["accent", theme.colors.accent],
|
|
@@ -767,7 +780,8 @@ async function runMake(name, flags) {
|
|
|
767
780
|
return;
|
|
768
781
|
}
|
|
769
782
|
|
|
770
|
-
const
|
|
783
|
+
const presetName = resolveBaseThemeName(flags.from || flags.preset || "noir", flags || {});
|
|
784
|
+
const preset = getBasePalette(presetName);
|
|
771
785
|
|
|
772
786
|
const customized = {
|
|
773
787
|
...preset,
|
|
@@ -800,7 +814,8 @@ function runInit(name, flags) {
|
|
|
800
814
|
fail(`Theme already exists: ${filePath}. Re-run with --force to overwrite.`);
|
|
801
815
|
}
|
|
802
816
|
|
|
803
|
-
const
|
|
817
|
+
const presetName = resolveBaseThemeName(flags.from || flags.preset || "noir", flags || {});
|
|
818
|
+
const palette = getBasePalette(presetName);
|
|
804
819
|
writeThemeAssets(paths, name, palette);
|
|
805
820
|
console.log(`Created starter theme at ${filePath}`);
|
|
806
821
|
console.log("Edit the JSON, then run: ctheme make " + name + " --from " + name);
|
|
@@ -813,7 +828,7 @@ async function runMakeWizard(initialName, flags) {
|
|
|
813
828
|
});
|
|
814
829
|
|
|
815
830
|
try {
|
|
816
|
-
const availablePresets =
|
|
831
|
+
const availablePresets = getBundledThemeFamilies();
|
|
817
832
|
console.log("Theme wizard");
|
|
818
833
|
console.log("Press Enter to keep the default shown in brackets.");
|
|
819
834
|
console.log(`Presets: ${availablePresets.join(", ")}`);
|
|
@@ -826,7 +841,8 @@ async function runMakeWizard(initialName, flags) {
|
|
|
826
841
|
"Base preset",
|
|
827
842
|
flags.from || flags.preset || "noir"
|
|
828
843
|
);
|
|
829
|
-
const
|
|
844
|
+
const resolvedBasePresetName = resolveBaseThemeName(basePresetName, flags || {});
|
|
845
|
+
const preset = getBasePalette(resolvedBasePresetName);
|
|
830
846
|
const defaultName = initialName || `${basePresetName}-custom`;
|
|
831
847
|
const themeName = await promptWithDefault(rl, "Theme name", defaultName);
|
|
832
848
|
|
|
@@ -865,7 +881,7 @@ async function runMakeWizard(initialName, flags) {
|
|
|
865
881
|
console.log("\nTheme summary:");
|
|
866
882
|
printThemeSummary({
|
|
867
883
|
name: themeName,
|
|
868
|
-
preset:
|
|
884
|
+
preset: resolvedBasePresetName,
|
|
869
885
|
accent,
|
|
870
886
|
bg,
|
|
871
887
|
surface,
|
|
@@ -973,10 +989,9 @@ function runTerminal(target, flags) {
|
|
|
973
989
|
}
|
|
974
990
|
|
|
975
991
|
const normalizedTarget = normalizeTerminalTarget(target);
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
const
|
|
979
|
-
const presetFonts = getThemeFonts(themeName);
|
|
992
|
+
const resolvedThemeName = resolveThemeName(flags.theme || "noir", flags || {});
|
|
993
|
+
const theme = buildTheme(resolvedThemeName, getBasePalette(resolvedThemeName));
|
|
994
|
+
const presetFonts = getThemeFonts(resolvedThemeName);
|
|
980
995
|
const font = flags.font || presetFonts.terminal || "JetBrains Mono";
|
|
981
996
|
const fontSize = Number(flags["font-size"] || 15);
|
|
982
997
|
const opacity = flags.opacity ? Number(flags.opacity) : null;
|
|
@@ -1290,6 +1305,86 @@ function detectCurrentTerminalTarget() {
|
|
|
1290
1305
|
return "";
|
|
1291
1306
|
}
|
|
1292
1307
|
|
|
1308
|
+
function getBundledThemeFamilies() {
|
|
1309
|
+
return Object.keys(BASE_PRESETS)
|
|
1310
|
+
.map((name) => name.replace(/-(light|dark)$/, ""))
|
|
1311
|
+
.filter((name, index, list) => list.indexOf(name) === index)
|
|
1312
|
+
.sort();
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function resolveBaseThemeName(name, flags) {
|
|
1316
|
+
return resolveThemeName(name, flags);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
function resolveThemeName(themeName, flags) {
|
|
1320
|
+
assertThemeName(themeName);
|
|
1321
|
+
|
|
1322
|
+
const wantsDark = Boolean(flags.dark);
|
|
1323
|
+
const wantsLight = Boolean(flags.light);
|
|
1324
|
+
if (wantsDark && wantsLight) {
|
|
1325
|
+
fail("Choose either --dark or --light, not both.");
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (themeExists(themeName) && /-(light|dark)$/.test(themeName)) {
|
|
1329
|
+
return themeName;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
const desiredMode = wantsDark ? "dark" : wantsLight ? "light" : detectSystemAppearanceMode();
|
|
1333
|
+
const desiredVariant = `${themeName}-${desiredMode}`;
|
|
1334
|
+
if (themeExists(desiredVariant)) {
|
|
1335
|
+
return desiredVariant;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
if (themeExists(themeName)) {
|
|
1339
|
+
return themeName;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const fallbackVariant = `${themeName}-${desiredMode === "dark" ? "light" : "dark"}`;
|
|
1343
|
+
if (themeExists(fallbackVariant)) {
|
|
1344
|
+
return fallbackVariant;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
fail(`Theme not found: ${themeName}`);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
function themeExists(name) {
|
|
1351
|
+
if (PRESETS[name]) return true;
|
|
1352
|
+
const paths = getThemePaths();
|
|
1353
|
+
return fs.existsSync(path.join(paths.paletteDir, `${name}.json`));
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function detectSystemAppearanceMode() {
|
|
1357
|
+
const envMode = String(process.env.CTHEME_MODE || "").trim().toLowerCase();
|
|
1358
|
+
if (envMode === "dark" || envMode === "light") {
|
|
1359
|
+
return envMode;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
if (process.platform === "darwin") {
|
|
1363
|
+
try {
|
|
1364
|
+
const result = execFileSync("defaults", ["read", "-g", "AppleInterfaceStyle"], { encoding: "utf8" }).trim();
|
|
1365
|
+
return result.toLowerCase() === "dark" ? "dark" : "light";
|
|
1366
|
+
} catch (_error) {
|
|
1367
|
+
return "light";
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
if (process.platform === "win32") {
|
|
1372
|
+
try {
|
|
1373
|
+
const result = execFileSync("reg", [
|
|
1374
|
+
"query",
|
|
1375
|
+
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
|
1376
|
+
"/v",
|
|
1377
|
+
"AppsUseLightTheme"
|
|
1378
|
+
], { encoding: "utf8" });
|
|
1379
|
+
return /0x0\b/i.test(result) ? "dark" : "light";
|
|
1380
|
+
} catch (_error) {
|
|
1381
|
+
return "dark";
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
return "dark";
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1293
1388
|
function getThemeFonts(name) {
|
|
1294
1389
|
if (PRESETS[name] && PRESETS[name].fonts) {
|
|
1295
1390
|
return PRESETS[name].fonts;
|