bluedither 1.0.7 → 1.0.8
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/cli/commands/publish.js +37 -4
- package/cli/lib/registry.js +17 -0
- package/package.json +1 -1
package/cli/commands/publish.js
CHANGED
|
@@ -5,18 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { existsSync, readFileSync, createReadStream } from 'fs';
|
|
8
|
-
import { resolve } from 'path';
|
|
8
|
+
import { resolve, extname } from 'path';
|
|
9
9
|
import { execSync } from 'child_process';
|
|
10
10
|
import { getCredentials } from '../lib/credentials.js';
|
|
11
|
-
import { uploadPackage, publishTheme, REGISTRY_URL } from '../lib/registry.js';
|
|
11
|
+
import { uploadPackage, publishTheme, uploadScreenshot, REGISTRY_URL } from '../lib/registry.js';
|
|
12
12
|
|
|
13
13
|
export default async function publish(args) {
|
|
14
14
|
if (args.includes('--help')) {
|
|
15
15
|
console.log(`
|
|
16
|
-
bluedither publish
|
|
16
|
+
bluedither publish [--screenshot <path>]
|
|
17
17
|
|
|
18
18
|
Validates, builds, and publishes your theme to the BlueDither marketplace.
|
|
19
19
|
If you're not logged in, a browser window will open for authentication.
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--screenshot <path> Path to a screenshot image (png/jpg/webp)
|
|
23
|
+
Auto-detected: screenshot.png, preview.png, etc.
|
|
20
24
|
`);
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
@@ -160,7 +164,35 @@ export default async function publish(args) {
|
|
|
160
164
|
|
|
161
165
|
const { path: packagePath } = await uploadPackage(zipFile, slug, creds.access_token);
|
|
162
166
|
|
|
163
|
-
|
|
167
|
+
// Upload screenshot if available
|
|
168
|
+
let screenshotUrl = null;
|
|
169
|
+
const ssIdx = args.indexOf('--screenshot');
|
|
170
|
+
const ssPath = ssIdx !== -1 ? resolve(dir, args[ssIdx + 1]) : null;
|
|
171
|
+
|
|
172
|
+
// Auto-detect screenshot file
|
|
173
|
+
const screenshotFile = ssPath
|
|
174
|
+
|| ['screenshot.png', 'screenshot.jpg', 'screenshot.webp', 'preview.png', 'preview.jpg'].map(f => resolve(dir, f)).find(f => existsSync(f));
|
|
175
|
+
|
|
176
|
+
if (screenshotFile && existsSync(screenshotFile)) {
|
|
177
|
+
console.log(`Step 7: Uploading screenshot (${screenshotFile.split(/[\\/]/).pop()})...`);
|
|
178
|
+
try {
|
|
179
|
+
const imgBuffer = readFileSync(screenshotFile);
|
|
180
|
+
const ext = extname(screenshotFile).slice(1) || 'png';
|
|
181
|
+
const mimeType = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp' }[ext] || 'image/png';
|
|
182
|
+
const imgBlob = new Blob([imgBuffer], { type: mimeType });
|
|
183
|
+
const imgFile = new File([imgBlob], `screenshot.${ext}`, { type: mimeType });
|
|
184
|
+
const result = await uploadScreenshot(imgFile, slug, creds.access_token);
|
|
185
|
+
screenshotUrl = result.url;
|
|
186
|
+
console.log(' ✓ Screenshot uploaded');
|
|
187
|
+
} catch (e) {
|
|
188
|
+
console.log(` ⚠ Screenshot upload failed: ${e.message}`);
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
console.log('Step 7: No screenshot found — skipping.');
|
|
192
|
+
console.log(' Tip: Add screenshot.png to your theme directory, or use --screenshot <path>');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('\nStep 8: Publishing theme...');
|
|
164
196
|
const tokens = JSON.parse(readFileSync(resolve(dir, config.tokens), 'utf-8'));
|
|
165
197
|
|
|
166
198
|
const theme = await publishTheme({
|
|
@@ -169,6 +201,7 @@ export default async function publish(args) {
|
|
|
169
201
|
description: tokens.content?.subHeadline || '',
|
|
170
202
|
tokens_json: tokens,
|
|
171
203
|
package_path: packagePath,
|
|
204
|
+
screenshot_url: screenshotUrl,
|
|
172
205
|
}, creds.access_token);
|
|
173
206
|
|
|
174
207
|
console.log(`
|
package/cli/lib/registry.js
CHANGED
|
@@ -39,6 +39,23 @@ export async function uploadPackage(file, slug, accessToken) {
|
|
|
39
39
|
return res.json();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export async function uploadScreenshot(file, slug, accessToken) {
|
|
43
|
+
const formData = new FormData();
|
|
44
|
+
formData.append('file', file);
|
|
45
|
+
formData.append('slug', slug);
|
|
46
|
+
|
|
47
|
+
const res = await fetch(`${REGISTRY_URL}/api/upload/screenshot`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
50
|
+
body: formData,
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const err = await res.json().catch(() => ({}));
|
|
54
|
+
throw new Error(err.error || `Screenshot upload failed: ${res.statusText}`);
|
|
55
|
+
}
|
|
56
|
+
return res.json();
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
export async function publishTheme(metadata, accessToken) {
|
|
43
60
|
const res = await fetch(`${REGISTRY_URL}/api/themes`, {
|
|
44
61
|
method: 'POST',
|