ctheme 0.1.0 → 0.1.1

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/DEVELOPMENT.md ADDED
@@ -0,0 +1,245 @@
1
+ # Development
2
+
3
+ ## Purpose
4
+
5
+ `ctheme` is a terminal theming CLI.
6
+
7
+ Scope:
8
+
9
+ - terminal color themes
10
+ - terminal font selection
11
+ - live Apple Terminal profile switching
12
+ - config snippet generation for Ghostty, Kitty, and WezTerm
13
+ - custom theme authoring
14
+ - Google Fonts discovery and install helpers
15
+
16
+ Out of scope:
17
+
18
+ - editor themes
19
+ - app chrome theming
20
+ - IDE plugins
21
+ - Codex-specific config behavior
22
+
23
+ ## Repository Layout
24
+
25
+ ```text
26
+ bin/ctheme.js Main CLI entrypoint and all runtime logic
27
+ README.md User-facing docs
28
+ DEVELOPMENT.md Contributor docs
29
+ package.json npm package metadata
30
+ ```
31
+
32
+ This project is intentionally small. Most behavior lives in one file so it stays easy to ship and debug.
33
+
34
+ ## Runtime State
35
+
36
+ `ctheme` keeps its own state under:
37
+
38
+ ```text
39
+ ~/.ctheme/themes
40
+ ~/.ctheme/snippets
41
+ ~/.ctheme/snippets/.ctheme-state.json
42
+ ~/Library/Fonts/pretty-code
43
+ ```
44
+
45
+ What each path is for:
46
+
47
+ - `~/.ctheme/themes`: saved custom theme JSON palettes
48
+ - `~/.ctheme/snippets`: generated terminal config snippets and AppleScript files
49
+ - `~/.ctheme/snippets/.ctheme-state.json`: restore state for `ctheme reset`
50
+ - `~/Library/Fonts/pretty-code`: Google Fonts installed by the CLI
51
+
52
+ Relevant env vars:
53
+
54
+ ```text
55
+ CTHEME_HOME
56
+ CTHEME_THEME_DIR
57
+ CTHEME_SNIPPET_DIR
58
+ ```
59
+
60
+ ## Command Map
61
+
62
+ Main commands:
63
+
64
+ - `ctheme use <theme>`: apply a theme live to the current terminal when supported
65
+ - `ctheme reset`: restore the original terminal state
66
+ - `ctheme list`: list bundled and saved themes
67
+ - `ctheme preview <theme>`: inspect palette colors in the current shell
68
+ - `ctheme make`: create a theme from flags or launch the wizard
69
+ - `ctheme wizard`: force the interactive onboarding flow
70
+ - `ctheme init <name>`: create a starter theme file
71
+ - `ctheme term <target>`: generate or apply terminal-target snippets
72
+ - `ctheme font ...`: search, install, and list fonts
73
+
74
+ ## Internal Architecture
75
+
76
+ ### 1. Presets
77
+
78
+ Bundled presets are plain palette objects in `PRESETS`.
79
+
80
+ Each preset should carry:
81
+
82
+ - colors
83
+ - terminal font
84
+ - UI font
85
+ - code font
86
+
87
+ The bundled presets should feel visibly distinct. Avoid making every theme share the same font pairing.
88
+
89
+ ### 2. Theme Building
90
+
91
+ `buildTheme()` turns a palette into the normalized theme object used by preview and terminal rendering.
92
+
93
+ Important rule:
94
+
95
+ - readability wins over palette purity
96
+
97
+ Light themes in particular are normalized through contrast helpers so accents and syntax colors remain readable.
98
+
99
+ ### 3. Terminal Writers
100
+
101
+ Target-specific renderers:
102
+
103
+ - `renderAppleTerminalProfile()`
104
+ - `renderGhosttySnippet()`
105
+ - `renderKittySnippet()`
106
+ - `renderWeztermSnippet()`
107
+
108
+ Target-specific application:
109
+
110
+ - Apple Terminal applies live through AppleScript
111
+ - Ghostty and Kitty get include lines appended
112
+ - WezTerm writes a standalone file when safe
113
+
114
+ ### 4. Wizard Flow
115
+
116
+ The wizard is implemented in `runMakeWizard()`.
117
+
118
+ Current flow:
119
+
120
+ 1. pick a base preset
121
+ 2. name the theme
122
+ 3. adjust colors
123
+ 4. choose fonts
124
+ 5. review summary
125
+ 6. save
126
+ 7. optionally install fonts
127
+ 8. optionally apply live
128
+
129
+ When changing the wizard:
130
+
131
+ - keep prompts short
132
+ - always show a default
133
+ - keep blank input as “accept default”
134
+ - prefer a summary/confirmation before writing files
135
+
136
+ ### 5. Font Helpers
137
+
138
+ Font listing uses macOS AppKit through JXA.
139
+
140
+ Google Fonts install flow:
141
+
142
+ 1. search metadata from `fonts.google.com`
143
+ 2. resolve family path from `google/fonts`
144
+ 3. download font files into `~/Library/Fonts/pretty-code`
145
+
146
+ Do not bundle font files in the repo.
147
+
148
+ ## Local Development
149
+
150
+ Install the CLI locally:
151
+
152
+ ```bash
153
+ npm install -g . --force
154
+ ```
155
+
156
+ Basic smoke checks:
157
+
158
+ ```bash
159
+ ctheme --help
160
+ ctheme list
161
+ ctheme preview solarized
162
+ ctheme font list
163
+ ctheme status
164
+ ```
165
+
166
+ ## Manual Test Matrix
167
+
168
+ ### Apple Terminal
169
+
170
+ ```bash
171
+ ctheme use solarized
172
+ ctheme use harbor --default
173
+ ctheme reset
174
+ ```
175
+
176
+ Verify:
177
+
178
+ - theme applies immediately
179
+ - `--default` changes startup/default profile
180
+ - `reset` restores the saved profile or `Basic`
181
+
182
+ ### Wizard
183
+
184
+ ```bash
185
+ ctheme wizard
186
+ ctheme make
187
+ ctheme make mytheme --preset harbor --terminal-font "Space Mono"
188
+ ```
189
+
190
+ Verify:
191
+
192
+ - defaults are shown
193
+ - summary appears before writing
194
+ - saved JSON lands in `~/.ctheme/themes`
195
+ - `ctheme use mytheme` picks up the saved terminal font
196
+
197
+ ### Fonts
198
+
199
+ ```bash
200
+ ctheme font search "mono"
201
+ ctheme font install "IBM Plex Mono"
202
+ ctheme font list
203
+ ```
204
+
205
+ Verify:
206
+
207
+ - search returns real families
208
+ - install downloads files into `~/Library/Fonts/pretty-code`
209
+ - list prints real family names, not ObjC bridge values
210
+
211
+ ### Pack/Publish
212
+
213
+ ```bash
214
+ npm test
215
+ npm pack --dry-run
216
+ npm publish --dry-run
217
+ ```
218
+
219
+ ## Release Process
220
+
221
+ 1. update version in `package.json`
222
+ 2. run smoke tests
223
+ 3. run `npm pack --dry-run`
224
+ 4. run `npm publish --dry-run`
225
+ 5. commit
226
+ 6. push `main`
227
+ 7. publish for real with `npm publish`
228
+
229
+ ## Contribution Rules
230
+
231
+ - Keep the product terminal-focused.
232
+ - Prefer short commands over complicated command trees.
233
+ - Preserve live-apply behavior wherever possible.
234
+ - When a target cannot reload live, say that clearly.
235
+ - Avoid growing the tool into a general desktop-theme manager.
236
+ - Do not reintroduce Codex/editor/app-specific behavior.
237
+ - Do not add hidden state outside `~/.ctheme` and the managed fonts directory.
238
+
239
+ ## Git Hygiene
240
+
241
+ This repo should stay self-contained.
242
+
243
+ - commit from `/Users/arjunkshah21/Downloads/pretty-code`
244
+ - push to `origin main`
245
+ - do not mix unrelated parent-directory changes into this repo
package/README.md CHANGED
@@ -71,6 +71,7 @@ The wizard walks through:
71
71
  - terminal font
72
72
  - UI font
73
73
  - code font
74
+ - theme summary before save
74
75
  - optional Google Fonts install
75
76
  - optional live apply
76
77
 
package/bin/ctheme.js CHANGED
@@ -406,179 +406,6 @@ const PRESETS = {
406
406
  }
407
407
  };
408
408
 
409
- const APP_PRESETS = {
410
- evergreen: {
411
- codeThemeId: "everforest",
412
- theme: {
413
- accent: "#93b259",
414
- contrast: 45,
415
- fonts: { code: "IBM Plex Mono", ui: "Manrope" },
416
- ink: "#5c6a72",
417
- opaqueWindows: true,
418
- semanticColors: {
419
- diffAdded: "#8da101",
420
- diffRemoved: "#f85552",
421
- skill: "#df69ba"
422
- },
423
- surface: "#fdf6e3"
424
- },
425
- variant: "light"
426
- },
427
- "evergreen-dark": {
428
- codeThemeId: "everforest",
429
- theme: {
430
- accent: "#a7c080",
431
- contrast: 60,
432
- fonts: { code: "IBM Plex Mono", ui: "Manrope" },
433
- ink: "#d3c6aa",
434
- opaqueWindows: true,
435
- semanticColors: {
436
- diffAdded: "#a7c080",
437
- diffRemoved: "#e67e80",
438
- skill: "#d699b6"
439
- },
440
- surface: "#2d353b"
441
- },
442
- variant: "dark"
443
- },
444
- solarized: {
445
- codeThemeId: "solarized",
446
- theme: {
447
- accent: "#268bd2",
448
- contrast: 45,
449
- fonts: { code: "Source Code Pro", ui: "Manrope" },
450
- ink: "#586e75",
451
- opaqueWindows: true,
452
- semanticColors: {
453
- diffAdded: "#859900",
454
- diffRemoved: "#dc322f",
455
- skill: "#d33682"
456
- },
457
- surface: "#fdf6e3"
458
- },
459
- variant: "light"
460
- },
461
- "solarized-dark": {
462
- codeThemeId: "solarized",
463
- theme: {
464
- accent: "#268bd2",
465
- contrast: 60,
466
- fonts: { code: "Source Code Pro", ui: "Manrope" },
467
- ink: "#839496",
468
- opaqueWindows: false,
469
- semanticColors: {
470
- diffAdded: "#859900",
471
- diffRemoved: "#dc322f",
472
- skill: "#d33682"
473
- },
474
- surface: "#002b36"
475
- },
476
- variant: "dark"
477
- },
478
- nord: {
479
- codeThemeId: "nord",
480
- theme: {
481
- accent: "#5e81ac",
482
- contrast: 48,
483
- fonts: { code: "Space Mono", ui: "Manrope" },
484
- ink: "#4c566a",
485
- opaqueWindows: true,
486
- semanticColors: {
487
- diffAdded: "#a3be8c",
488
- diffRemoved: "#bf616a",
489
- skill: "#b48ead"
490
- },
491
- surface: "#eceff4"
492
- },
493
- variant: "light"
494
- },
495
- "nord-dark": {
496
- codeThemeId: "nord",
497
- theme: {
498
- accent: "#88c0d0",
499
- contrast: 60,
500
- fonts: { code: "Space Mono", ui: "Manrope" },
501
- ink: "#e5e9f0",
502
- opaqueWindows: true,
503
- semanticColors: {
504
- diffAdded: "#a3be8c",
505
- diffRemoved: "#bf616a",
506
- skill: "#b48ead"
507
- },
508
- surface: "#2e3440"
509
- },
510
- variant: "dark"
511
- },
512
- rosepine: {
513
- codeThemeId: "rose-pine-dawn",
514
- theme: {
515
- accent: "#56949f",
516
- contrast: 44,
517
- fonts: { code: "DM Mono", ui: "Manrope" },
518
- ink: "#575279",
519
- opaqueWindows: true,
520
- semanticColors: {
521
- diffAdded: "#286983",
522
- diffRemoved: "#b4637a",
523
- skill: "#907aa9"
524
- },
525
- surface: "#faf4ed"
526
- },
527
- variant: "light"
528
- },
529
- "rosepine-dark": {
530
- codeThemeId: "rose-pine",
531
- theme: {
532
- accent: "#9ccfd8",
533
- contrast: 58,
534
- fonts: { code: "DM Mono", ui: "Manrope" },
535
- ink: "#e0def4",
536
- opaqueWindows: true,
537
- semanticColors: {
538
- diffAdded: "#31748f",
539
- diffRemoved: "#eb6f92",
540
- skill: "#c4a7e7"
541
- },
542
- surface: "#191724"
543
- },
544
- variant: "dark"
545
- },
546
- graphite: {
547
- codeThemeId: "github",
548
- theme: {
549
- accent: "#586069",
550
- contrast: 42,
551
- fonts: { code: "IBM Plex Mono", ui: "Manrope" },
552
- ink: "#24292f",
553
- opaqueWindows: true,
554
- semanticColors: {
555
- diffAdded: "#1a7f37",
556
- diffRemoved: "#cf222e",
557
- skill: "#8250df"
558
- },
559
- surface: "#f6f8fa"
560
- },
561
- variant: "light"
562
- },
563
- "graphite-dark": {
564
- codeThemeId: "github",
565
- theme: {
566
- accent: "#7d8590",
567
- contrast: 58,
568
- fonts: { code: "IBM Plex Mono", ui: "Manrope" },
569
- ink: "#c9d1d9",
570
- opaqueWindows: true,
571
- semanticColors: {
572
- diffAdded: "#3fb950",
573
- diffRemoved: "#f85149",
574
- skill: "#d2a8ff"
575
- },
576
- surface: "#0d1117"
577
- },
578
- variant: "dark"
579
- }
580
- };
581
-
582
409
  async function main() {
583
410
  try {
584
411
  const args = process.argv.slice(2);
@@ -862,11 +689,13 @@ async function runMakeWizard(initialName, flags) {
862
689
 
863
690
  try {
864
691
  const availablePresets = Object.keys(PRESETS).sort();
865
- console.log(`Theme wizard`);
692
+ console.log("Theme wizard");
693
+ console.log("Press Enter to keep the default shown in brackets.");
866
694
  console.log(`Presets: ${availablePresets.join(", ")}`);
867
695
  console.log(`UI font ideas: ${FONT_CHOICES.ui.join(", ")}`);
868
696
  console.log(`Code/terminal font ideas: ${FONT_CHOICES.code.join(", ")}`);
869
697
 
698
+ console.log("\nStep 1: Base");
870
699
  const basePresetName = await promptWithDefault(
871
700
  rl,
872
701
  "Base preset",
@@ -876,6 +705,7 @@ async function runMakeWizard(initialName, flags) {
876
705
  const defaultName = initialName || `${basePresetName}-custom`;
877
706
  const themeName = await promptWithDefault(rl, "Theme name", defaultName);
878
707
 
708
+ console.log("\nStep 2: Colors");
879
709
  const accent = await promptWithDefault(rl, "Accent color", flags.accent || preset.accent);
880
710
  const bg = await promptWithDefault(rl, "Background color", flags.bg || preset.bg);
881
711
  const surface = await promptWithDefault(rl, "Surface color", flags.surface || preset.surface);
@@ -888,6 +718,7 @@ async function runMakeWizard(initialName, flags) {
888
718
  const muted = await promptWithDefault(rl, "Muted color", flags.muted || preset.muted);
889
719
  const dim = await promptWithDefault(rl, "Dim color", flags.dim || preset.dim);
890
720
 
721
+ console.log("\nStep 3: Fonts");
891
722
  const fonts = {
892
723
  terminal: await promptWithDefault(
893
724
  rl,
@@ -906,6 +737,26 @@ async function runMakeWizard(initialName, flags) {
906
737
  )
907
738
  };
908
739
 
740
+ console.log("\nTheme summary:");
741
+ printThemeSummary({
742
+ name: themeName,
743
+ preset: basePresetName,
744
+ accent,
745
+ bg,
746
+ surface,
747
+ surfaceAlt,
748
+ fg,
749
+ muted,
750
+ dim,
751
+ fonts
752
+ });
753
+
754
+ const shouldWrite = await promptYesNo(rl, "Save this theme?", true);
755
+ if (!shouldWrite) {
756
+ console.log("Cancelled.");
757
+ return;
758
+ }
759
+
909
760
  const autoInstallFonts = await promptYesNo(rl, "Install any missing Google fonts automatically?", true);
910
761
  if (autoInstallFonts) {
911
762
  installFontsBestEffort([fonts.terminal, fonts.ui, fonts.code]);
@@ -1367,6 +1218,21 @@ function installFontsBestEffort(fonts) {
1367
1218
  }
1368
1219
  }
1369
1220
 
1221
+ function printThemeSummary(theme) {
1222
+ console.log(` name: ${theme.name}`);
1223
+ console.log(` preset: ${theme.preset}`);
1224
+ console.log(` accent: ${theme.accent}`);
1225
+ console.log(` bg: ${theme.bg}`);
1226
+ console.log(` surface: ${theme.surface}`);
1227
+ console.log(` surface alt: ${theme.surfaceAlt}`);
1228
+ console.log(` text: ${theme.fg}`);
1229
+ console.log(` muted: ${theme.muted}`);
1230
+ console.log(` dim: ${theme.dim}`);
1231
+ console.log(` terminal font: ${theme.fonts.terminal}`);
1232
+ console.log(` ui font: ${theme.fonts.ui}`);
1233
+ console.log(` code font: ${theme.fonts.code}`);
1234
+ }
1235
+
1370
1236
  function writeThemeAssets(paths, name, palette) {
1371
1237
  ensureDir(paths.paletteDir);
1372
1238
  fs.writeFileSync(path.join(paths.paletteDir, `${name}.json`), `${JSON.stringify(palette, null, 2)}\n`, "utf8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctheme",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Theme manager for making terminals look better with colors, fonts, and live profile switching",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,6 +20,7 @@
20
20
  ],
21
21
  "files": [
22
22
  "bin",
23
+ "DEVELOPMENT.md",
23
24
  "README.md"
24
25
  ],
25
26
  "bin": {