create-bl-theme 1.0.8 → 1.0.10

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.
@@ -2,7 +2,9 @@
2
2
  "permissions": {
3
3
  "allow": [
4
4
  "Bash(cat:*)",
5
- "Bash(node /Users/boidu/Developer/create-bl-theme/bin/cli.js bump:*)"
5
+ "Bash(node /Users/boidu/Developer/create-bl-theme/bin/cli.js bump:*)",
6
+ "Bash(npm:*)",
7
+ "Bash(node /Users/boidu/Developer/create-bl-theme/bin/cli.js:*)"
6
8
  ],
7
9
  "deny": [],
8
10
  "ask": []
package/CLAUDE.md ADDED
@@ -0,0 +1,73 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **create-bl-theme** is a CLI tool for scaffolding and managing themes for the Better Lyrics browser extension. It provides commands to create new themes, validate their structure, manage versioning, and handle publishing to a theme registry.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Run the CLI locally during development
13
+ node bin/cli.js [command]
14
+
15
+ # Create a new theme (interactive prompts)
16
+ node bin/cli.js [theme-name]
17
+
18
+ # Validate a theme (supports local paths or GitHub URLs)
19
+ node bin/cli.js validate [dir|url]
20
+
21
+ # Check publishing status against the theme registry
22
+ node bin/cli.js publish [dir]
23
+
24
+ # Bump version (patch/minor/major)
25
+ node bin/cli.js bump [patch|minor|major] [dir]
26
+
27
+ # Show version/help
28
+ node bin/cli.js --version
29
+ node bin/cli.js --help
30
+ ```
31
+
32
+ ## Architecture
33
+
34
+ The entire CLI is implemented in a single file: `bin/cli.js` (ES6 module).
35
+
36
+ **Main routing pattern:**
37
+ - `main()` parses `process.argv` and routes to command handlers
38
+ - Commands: `create()`, `validate()`, `publish()`, `bump()`
39
+
40
+ **Key external dependencies:**
41
+ - `rics` - RICS CSS preprocessor for syntax validation via `compileWithDetails()`
42
+ - `prompts` - Interactive CLI prompts
43
+ - `image-size` - Image dimension validation
44
+ - `picocolors` - Terminal colors
45
+
46
+ **External API calls:**
47
+ - Theme registry: `https://raw.githubusercontent.com/better-lyrics/themes/master/index.json`
48
+ - Lock file: `https://raw.githubusercontent.com/better-lyrics/themes/master/index.lock.json`
49
+
50
+ ## Theme Structure Generated
51
+
52
+ ```
53
+ theme-name/
54
+ ├── metadata.json # Required: id, title, creators, minVersion, hasShaders, version, images
55
+ ├── style.rics # Required: styles using RICS preprocessor (plain CSS is valid)
56
+ ├── images/ # Required: at least one screenshot
57
+ ├── DESCRIPTION.md # Optional: rich markdown description
58
+ ├── shader.json # Required if hasShaders: true
59
+ └── README.md
60
+ ```
61
+
62
+ ## Validation Rules
63
+
64
+ - `metadata.json` requires fields: id, title, creators, minVersion, hasShaders, version, images
65
+ - Version must be strict semver (e.g., `1.0.0`)
66
+ - Image IDs: lowercase letters, numbers, hyphens only
67
+ - Images: PNG, JPG, GIF, WebP; warns if not 16:9 aspect ratio
68
+ - RICS syntax validated with detailed error reporting
69
+ - Shader.json required when `hasShaders: true`
70
+
71
+ ## RICS Notes
72
+
73
+ RICS is a lightweight CSS preprocessor with full CSS parity. Plain CSS is valid RICS. Supports variables, nesting, mixins. Documentation: https://github.com/better-lyrics/rics
package/README.md CHANGED
@@ -164,7 +164,7 @@ Better Lyrics supports **GitHub Flavored Markdown (GFM)** in DESCRIPTION.md, so
164
164
 
165
165
  ### Auto-Publishing
166
166
 
167
- After your theme is registered, install the [Better Lyrics Themes](https://github.com/marketplace/better-lyrics-themes) GitHub App on your repo. This enables automatic updates:
167
+ After your theme is registered, install the [Better Lyrics Harmonizer](https://github.com/apps/better-lyrics-harmonizer/installations/new) GitHub App on your repo. This enables automatic updates:
168
168
 
169
169
  1. Bump your version: `create-bl-theme bump patch`
170
170
  2. Commit and push
package/bin/cli.js CHANGED
@@ -7,7 +7,7 @@ import path from "path";
7
7
  import os from "os";
8
8
  import { fileURLToPath } from "url";
9
9
  import { imageSize } from "image-size";
10
- import { execSync } from "child_process";
10
+ import { execSync, spawn } from "child_process";
11
11
  import { compileWithDetails } from "rics";
12
12
 
13
13
  const __filename = fileURLToPath(import.meta.url);
@@ -22,11 +22,78 @@ const RECOMMENDED_HEIGHT = 720;
22
22
  // GitHub URL patterns
23
23
  const GITHUB_URL_PATTERN = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^\/]+)\/([^\/]+)(?:\/)?(?:\.git)?$/;
24
24
 
25
+ // GitHub App URL for auto-updates
26
+ const GITHUB_APP_URL = "https://github.com/apps/better-lyrics-harmonizer/installations/new";
27
+
25
28
  // Load package.json for version
26
29
  const pkg = JSON.parse(
27
30
  fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
28
31
  );
29
32
 
33
+ // Cross-platform browser opener
34
+ function openBrowser(url) {
35
+ const platform = process.platform;
36
+ let command;
37
+ let args;
38
+
39
+ if (platform === "darwin") {
40
+ command = "open";
41
+ args = [url];
42
+ } else if (platform === "win32") {
43
+ command = "cmd";
44
+ args = ["/c", "start", "", url];
45
+ } else {
46
+ command = "xdg-open";
47
+ args = [url];
48
+ }
49
+
50
+ return new Promise((resolve, reject) => {
51
+ const child = spawn(command, args, {
52
+ detached: true,
53
+ stdio: "ignore",
54
+ });
55
+
56
+ child.on("error", (err) => {
57
+ reject(err);
58
+ });
59
+
60
+ child.unref();
61
+ resolve();
62
+ });
63
+ }
64
+
65
+ // Prompt to install GitHub App for auto-updates
66
+ async function promptGitHubAppInstall() {
67
+ console.log();
68
+ console.log(pc.bold("GitHub App Installation"));
69
+ console.log();
70
+ console.log("Install Better Lyrics Harmonizer for automatic theme publishing.");
71
+ console.log(pc.dim("This is a one-time setup. After installing, push changes with a bumped"));
72
+ console.log(pc.dim("version and they'll auto-publish to the theme store (with commit status)."));
73
+ console.log();
74
+
75
+ const response = await prompts({
76
+ type: "confirm",
77
+ name: "openBrowser",
78
+ message: "Open browser to install Better Lyrics Harmonizer?",
79
+ initial: true,
80
+ });
81
+
82
+ if (response.openBrowser) {
83
+ try {
84
+ await openBrowser(GITHUB_APP_URL);
85
+ console.log(pc.green(" Browser opened!"));
86
+ } catch (err) {
87
+ console.log(pc.yellow(" Could not open browser automatically."));
88
+ }
89
+ }
90
+
91
+ console.log();
92
+ console.log(pc.dim(" Install URL:"));
93
+ console.log(pc.cyan(` ${GITHUB_APP_URL}`));
94
+ console.log();
95
+ }
96
+
30
97
  async function main() {
31
98
  const args = process.argv.slice(2);
32
99
  const command = args[0];
@@ -103,7 +170,28 @@ ${pc.bold("Documentation:")}
103
170
  `);
104
171
  }
105
172
 
173
+ function isDirectoryEmpty(dirPath) {
174
+ const entries = fs.readdirSync(dirPath);
175
+ return entries.every((entry) => entry.startsWith("."));
176
+ }
177
+
106
178
  async function create(targetDir) {
179
+ if (targetDir) {
180
+ const earlyPath = path.resolve(process.cwd(), targetDir);
181
+ if (fs.existsSync(earlyPath) && !isDirectoryEmpty(earlyPath)) {
182
+ const { proceed } = await prompts({
183
+ type: "confirm",
184
+ name: "proceed",
185
+ message: `Directory "${targetDir}" is not empty. Continue anyway? (files may be overwritten)`,
186
+ initial: false,
187
+ });
188
+ if (!proceed) {
189
+ console.log(pc.red("\nCancelled."));
190
+ process.exit(1);
191
+ }
192
+ }
193
+ }
194
+
107
195
  const questions = [
108
196
  {
109
197
  type: targetDir ? null : "text",
@@ -187,9 +275,17 @@ async function create(targetDir) {
187
275
  const dir = targetDir || response.directory;
188
276
  const fullPath = path.resolve(process.cwd(), dir);
189
277
 
190
- if (fs.existsSync(fullPath)) {
191
- console.log(pc.red(`\nError: Directory "${dir}" already exists.`));
192
- process.exit(1);
278
+ if (!targetDir && fs.existsSync(fullPath) && !isDirectoryEmpty(fullPath)) {
279
+ const { proceed } = await prompts({
280
+ type: "confirm",
281
+ name: "proceed",
282
+ message: `Directory "${dir}" is not empty. Continue anyway? (files may be overwritten)`,
283
+ initial: false,
284
+ });
285
+ if (!proceed) {
286
+ console.log(pc.red("\nCancelled."));
287
+ process.exit(1);
288
+ }
193
289
  }
194
290
 
195
291
  console.log();
@@ -308,12 +404,17 @@ MIT
308
404
  console.log(pc.green(" Theme scaffolded successfully!"));
309
405
  console.log();
310
406
  console.log(pc.bold(" Next steps:"));
311
- console.log(` ${pc.dim("1.")} cd ${dir}`);
312
- console.log(` ${pc.dim("2.")} Edit ${pc.cyan("style.rics")} with your theme styles`);
407
+ let stepNum = 1;
408
+ if (dir !== ".") {
409
+ console.log(` ${pc.dim(`${stepNum}.`)} cd ${dir}`);
410
+ stepNum++;
411
+ }
412
+ console.log(` ${pc.dim(`${stepNum}.`)} Edit ${pc.cyan("style.rics")} with your theme styles`);
413
+ stepNum++;
313
414
  console.log(
314
- ` ${pc.dim("3.")} Add a preview screenshot to ${pc.cyan("images/preview.png")}`
415
+ ` ${pc.dim(`${stepNum}.`)} Add a preview screenshot to ${pc.cyan("images/preview.png")}`
315
416
  );
316
- let stepNum = 4;
417
+ stepNum++;
317
418
  if (response.useDescriptionFile) {
318
419
  console.log(` ${pc.dim(`${stepNum}.`)} Edit ${pc.cyan("DESCRIPTION.md")} with your theme description`);
319
420
  stepNum++;
@@ -617,6 +718,11 @@ async function validate(dir) {
617
718
 
618
719
  console.log();
619
720
 
721
+ // Prompt to install GitHub App if validation passed
722
+ if (errors.length === 0) {
723
+ await promptGitHubAppInstall();
724
+ }
725
+
620
726
  // Cleanup temp directory if we cloned from GitHub
621
727
  if (tempDir) {
622
728
  try {
@@ -701,30 +807,23 @@ async function publish(dir) {
701
807
  console.log(` 2. Add { "repo": "${repo}" } to index.json`);
702
808
  console.log(" 3. Open a pull request");
703
809
  console.log();
704
- console.log("After your PR is merged, install the GitHub App for auto-updates:");
705
- console.log(pc.cyan(" https://github.com/marketplace/better-lyrics-themes"));
810
+ console.log(pc.dim("After your PR is merged, install the GitHub App for auto-updates:"));
811
+ await promptGitHubAppInstall();
706
812
  process.exit(0);
707
813
  }
708
814
 
709
815
  // 7. Theme is registered
710
816
  console.log(pc.green("Theme is registered in the theme store."));
711
817
  console.log();
712
- console.log(pc.bold("Auto-publishing Setup:"));
713
- console.log();
714
- console.log("To enable automatic updates when you push:");
715
- console.log(" 1. Install the GitHub App on your repo:");
716
- console.log(pc.cyan(" https://github.com/marketplace/better-lyrics-themes"));
717
- console.log();
718
- console.log(" 2. Push changes to your repo:");
719
- console.log(pc.dim(" git add ."));
720
- console.log(pc.dim(' git commit -m "feat: update theme"'));
721
- console.log(pc.dim(" git push"));
818
+ console.log(pc.bold("To publish updates:"));
819
+ console.log(pc.dim(" git add ."));
820
+ console.log(pc.dim(' git commit -m "feat: update theme"'));
821
+ console.log(pc.dim(" git push"));
722
822
  console.log();
723
823
  console.log("The registry will automatically:");
724
824
  console.log(" - Validate your theme");
725
825
  console.log(" - Update the lockfile");
726
826
  console.log(" - Vendor your theme files");
727
- console.log(" - Show a commit status on your push");
728
827
  console.log();
729
828
 
730
829
  // 8. Check current lockfile status
@@ -766,7 +865,8 @@ async function publish(dir) {
766
865
  }
767
866
  }
768
867
 
769
- console.log();
868
+ // Prompt to install GitHub App for auto-updates
869
+ await promptGitHubAppInstall();
770
870
  }
771
871
 
772
872
  function checkIsGitRepo(themePath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bl-theme",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "CLI tool to scaffold Better Lyrics themes",
5
5
  "type": "module",
6
6
  "bin": {