create-bl-theme 1.0.7 → 1.0.9
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/.claude/settings.local.json +4 -1
- package/CLAUDE.md +73 -0
- package/README.md +42 -0
- package/bin/cli.js +196 -19
- package/package.json +1 -1
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
|
@@ -45,9 +45,41 @@ The validator checks:
|
|
|
45
45
|
- Required files (metadata.json, style.rics or style.css, images/)
|
|
46
46
|
- RICS syntax validation (for .rics files)
|
|
47
47
|
- Valid JSON structure and required fields
|
|
48
|
+
- Semver version format
|
|
48
49
|
- Image integrity (detects corrupted files)
|
|
49
50
|
- Image dimensions (recommends 1280x720, but other sizes work fine)
|
|
50
51
|
|
|
52
|
+
### Bump version
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Bump patch version (1.0.0 → 1.0.1)
|
|
56
|
+
create-bl-theme bump patch
|
|
57
|
+
|
|
58
|
+
# Bump minor version (1.0.0 → 1.1.0)
|
|
59
|
+
create-bl-theme bump minor
|
|
60
|
+
|
|
61
|
+
# Bump major version (1.0.0 → 2.0.0)
|
|
62
|
+
create-bl-theme bump major
|
|
63
|
+
|
|
64
|
+
# Bump in a specific directory
|
|
65
|
+
create-bl-theme bump patch ./my-theme
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Check publishing status
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Check if theme is registered and ready to publish
|
|
72
|
+
create-bl-theme publish
|
|
73
|
+
|
|
74
|
+
# Check a specific directory
|
|
75
|
+
create-bl-theme publish ./my-theme
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The publish command:
|
|
79
|
+
- Checks if your theme is registered in the theme store
|
|
80
|
+
- Shows your current version vs. the registry version
|
|
81
|
+
- Provides setup instructions for auto-publishing
|
|
82
|
+
|
|
51
83
|
## Generated Structure
|
|
52
84
|
|
|
53
85
|
```
|
|
@@ -130,6 +162,16 @@ Better Lyrics supports **GitHub Flavored Markdown (GFM)** in DESCRIPTION.md, so
|
|
|
130
162
|
```
|
|
131
163
|
4. Open a pull request
|
|
132
164
|
|
|
165
|
+
### Auto-Publishing
|
|
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:
|
|
168
|
+
|
|
169
|
+
1. Bump your version: `create-bl-theme bump patch`
|
|
170
|
+
2. Commit and push
|
|
171
|
+
3. The registry automatically validates and publishes your update
|
|
172
|
+
|
|
173
|
+
Use `create-bl-theme publish` to check your publishing status.
|
|
174
|
+
|
|
133
175
|
## License
|
|
134
176
|
|
|
135
177
|
MIT
|
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,12 +22,95 @@ 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
|
+
|
|
28
|
+
// Load package.json for version
|
|
29
|
+
const pkg = JSON.parse(
|
|
30
|
+
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
|
|
31
|
+
);
|
|
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
|
+
|
|
25
97
|
async function main() {
|
|
26
98
|
const args = process.argv.slice(2);
|
|
27
99
|
const command = args[0];
|
|
28
100
|
|
|
101
|
+
// Handle flags first (no banner)
|
|
102
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
103
|
+
console.log(pkg.version);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
108
|
+
showHelp();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
29
112
|
console.log();
|
|
30
|
-
console.log(pc.bold(pc.cyan(" Better Lyrics Theme Creator")));
|
|
113
|
+
console.log(pc.bold(pc.cyan(" Better Lyrics Theme Creator")) + pc.dim(` v${pkg.version}`));
|
|
31
114
|
console.log(pc.dim(" Create themes for Better Lyrics extension"));
|
|
32
115
|
console.log();
|
|
33
116
|
|
|
@@ -41,8 +124,8 @@ async function main() {
|
|
|
41
124
|
return;
|
|
42
125
|
}
|
|
43
126
|
|
|
44
|
-
if (command === "
|
|
45
|
-
|
|
127
|
+
if (command === "bump") {
|
|
128
|
+
await bump(args[1], args[2]);
|
|
46
129
|
return;
|
|
47
130
|
}
|
|
48
131
|
|
|
@@ -50,16 +133,27 @@ async function main() {
|
|
|
50
133
|
}
|
|
51
134
|
|
|
52
135
|
function showHelp() {
|
|
53
|
-
console.log(
|
|
136
|
+
console.log(`
|
|
137
|
+
${pc.bold(pc.cyan("create-bl-theme"))} ${pc.dim(`v${pkg.version}`)}
|
|
138
|
+
CLI for Better Lyrics themes
|
|
139
|
+
|
|
140
|
+
${pc.bold("Usage:")}
|
|
54
141
|
${pc.cyan("create-bl-theme")} [name] Create a new theme
|
|
55
142
|
${pc.cyan("create-bl-theme")} validate [dir|url] Validate a theme (local or GitHub)
|
|
56
143
|
${pc.cyan("create-bl-theme")} publish [dir] Check publishing status
|
|
144
|
+
${pc.cyan("create-bl-theme")} bump [type] [dir] Bump version (patch, minor, major)
|
|
145
|
+
|
|
146
|
+
${pc.bold("Options:")}
|
|
147
|
+
${pc.cyan("-v, --version")} Show version number
|
|
148
|
+
${pc.cyan("-h, --help")} Show this help message
|
|
57
149
|
|
|
58
150
|
${pc.bold("Examples:")}
|
|
59
151
|
${pc.dim("$")} create-bl-theme my-awesome-theme
|
|
60
152
|
${pc.dim("$")} create-bl-theme validate ./my-theme
|
|
61
153
|
${pc.dim("$")} create-bl-theme validate https://github.com/user/theme-repo
|
|
62
154
|
${pc.dim("$")} create-bl-theme publish
|
|
155
|
+
${pc.dim("$")} create-bl-theme bump patch
|
|
156
|
+
${pc.dim("$")} create-bl-theme bump minor ./my-theme
|
|
63
157
|
|
|
64
158
|
${pc.bold("Theme Structure:")}
|
|
65
159
|
my-theme/
|
|
@@ -590,6 +684,11 @@ async function validate(dir) {
|
|
|
590
684
|
|
|
591
685
|
console.log();
|
|
592
686
|
|
|
687
|
+
// Prompt to install GitHub App if validation passed
|
|
688
|
+
if (errors.length === 0) {
|
|
689
|
+
await promptGitHubAppInstall();
|
|
690
|
+
}
|
|
691
|
+
|
|
593
692
|
// Cleanup temp directory if we cloned from GitHub
|
|
594
693
|
if (tempDir) {
|
|
595
694
|
try {
|
|
@@ -674,30 +773,23 @@ async function publish(dir) {
|
|
|
674
773
|
console.log(` 2. Add { "repo": "${repo}" } to index.json`);
|
|
675
774
|
console.log(" 3. Open a pull request");
|
|
676
775
|
console.log();
|
|
677
|
-
console.log("After your PR is merged, install the GitHub App for auto-updates:");
|
|
678
|
-
|
|
776
|
+
console.log(pc.dim("After your PR is merged, install the GitHub App for auto-updates:"));
|
|
777
|
+
await promptGitHubAppInstall();
|
|
679
778
|
process.exit(0);
|
|
680
779
|
}
|
|
681
780
|
|
|
682
781
|
// 7. Theme is registered
|
|
683
782
|
console.log(pc.green("Theme is registered in the theme store."));
|
|
684
783
|
console.log();
|
|
685
|
-
console.log(pc.bold("
|
|
686
|
-
console.log();
|
|
687
|
-
console.log(
|
|
688
|
-
console.log("
|
|
689
|
-
console.log(pc.cyan(" https://github.com/marketplace/better-lyrics-themes"));
|
|
690
|
-
console.log();
|
|
691
|
-
console.log(" 2. Push changes to your repo:");
|
|
692
|
-
console.log(pc.dim(" git add ."));
|
|
693
|
-
console.log(pc.dim(' git commit -m "feat: update theme"'));
|
|
694
|
-
console.log(pc.dim(" git push"));
|
|
784
|
+
console.log(pc.bold("To publish updates:"));
|
|
785
|
+
console.log(pc.dim(" git add ."));
|
|
786
|
+
console.log(pc.dim(' git commit -m "feat: update theme"'));
|
|
787
|
+
console.log(pc.dim(" git push"));
|
|
695
788
|
console.log();
|
|
696
789
|
console.log("The registry will automatically:");
|
|
697
790
|
console.log(" - Validate your theme");
|
|
698
791
|
console.log(" - Update the lockfile");
|
|
699
792
|
console.log(" - Vendor your theme files");
|
|
700
|
-
console.log(" - Show a commit status on your push");
|
|
701
793
|
console.log();
|
|
702
794
|
|
|
703
795
|
// 8. Check current lockfile status
|
|
@@ -739,7 +831,8 @@ async function publish(dir) {
|
|
|
739
831
|
}
|
|
740
832
|
}
|
|
741
833
|
|
|
742
|
-
|
|
834
|
+
// Prompt to install GitHub App for auto-updates
|
|
835
|
+
await promptGitHubAppInstall();
|
|
743
836
|
}
|
|
744
837
|
|
|
745
838
|
function checkIsGitRepo(themePath) {
|
|
@@ -788,6 +881,90 @@ async function checkLockStatus(repo) {
|
|
|
788
881
|
}
|
|
789
882
|
}
|
|
790
883
|
|
|
884
|
+
async function bump(typeOrDir, dirArg) {
|
|
885
|
+
// Handle flexible argument order: bump [type] [dir] or bump [dir]
|
|
886
|
+
let type = "patch";
|
|
887
|
+
let dir = ".";
|
|
888
|
+
|
|
889
|
+
const validTypes = ["patch", "minor", "major"];
|
|
890
|
+
|
|
891
|
+
if (typeOrDir) {
|
|
892
|
+
if (validTypes.includes(typeOrDir)) {
|
|
893
|
+
type = typeOrDir;
|
|
894
|
+
dir = dirArg || ".";
|
|
895
|
+
} else {
|
|
896
|
+
// Assume it's a directory
|
|
897
|
+
dir = typeOrDir;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const fullPath = path.resolve(process.cwd(), dir);
|
|
902
|
+
const metadataPath = path.join(fullPath, "metadata.json");
|
|
903
|
+
|
|
904
|
+
// Check metadata.json exists
|
|
905
|
+
if (!fs.existsSync(metadataPath)) {
|
|
906
|
+
console.log(pc.red("Error: metadata.json not found."));
|
|
907
|
+
process.exit(1);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
let metadata;
|
|
911
|
+
try {
|
|
912
|
+
metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
|
913
|
+
} catch (e) {
|
|
914
|
+
console.log(pc.red(`Error: Invalid metadata.json - ${e.message}`));
|
|
915
|
+
process.exit(1);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const currentVersion = metadata.version;
|
|
919
|
+
if (!currentVersion) {
|
|
920
|
+
console.log(pc.red("Error: No version field in metadata.json"));
|
|
921
|
+
process.exit(1);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Parse current version
|
|
925
|
+
const match = currentVersion.match(/^(\d+)\.(\d+)\.(\d+)(.*)$/);
|
|
926
|
+
if (!match) {
|
|
927
|
+
console.log(pc.red(`Error: Invalid version format "${currentVersion}"`));
|
|
928
|
+
process.exit(1);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
let [, major, minor, patch, prerelease] = match;
|
|
932
|
+
major = parseInt(major, 10);
|
|
933
|
+
minor = parseInt(minor, 10);
|
|
934
|
+
patch = parseInt(patch, 10);
|
|
935
|
+
|
|
936
|
+
// Bump version
|
|
937
|
+
switch (type) {
|
|
938
|
+
case "major":
|
|
939
|
+
major++;
|
|
940
|
+
minor = 0;
|
|
941
|
+
patch = 0;
|
|
942
|
+
prerelease = "";
|
|
943
|
+
break;
|
|
944
|
+
case "minor":
|
|
945
|
+
minor++;
|
|
946
|
+
patch = 0;
|
|
947
|
+
prerelease = "";
|
|
948
|
+
break;
|
|
949
|
+
case "patch":
|
|
950
|
+
default:
|
|
951
|
+
patch++;
|
|
952
|
+
prerelease = "";
|
|
953
|
+
break;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const newVersion = `${major}.${minor}.${patch}`;
|
|
957
|
+
metadata.version = newVersion;
|
|
958
|
+
|
|
959
|
+
// Write back
|
|
960
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2) + "\n");
|
|
961
|
+
|
|
962
|
+
console.log(pc.green(` ${currentVersion} → ${newVersion}`));
|
|
963
|
+
console.log();
|
|
964
|
+
console.log(pc.dim(` Updated ${metadataPath}`));
|
|
965
|
+
console.log();
|
|
966
|
+
}
|
|
967
|
+
|
|
791
968
|
main().catch((err) => {
|
|
792
969
|
console.error(pc.red(err.message));
|
|
793
970
|
process.exit(1);
|