bluedither 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/cli/commands/publish.js +48 -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,15 +164,55 @@ 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
|
|
|
198
|
+
// Read template HTML if available
|
|
199
|
+
let templateHtml = null;
|
|
200
|
+
for (const tmplPath of ['template/index.html', 'theme/template/index.html']) {
|
|
201
|
+
const full = resolve(dir, tmplPath);
|
|
202
|
+
if (existsSync(full)) {
|
|
203
|
+
templateHtml = readFileSync(full, 'utf-8');
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
166
208
|
const theme = await publishTheme({
|
|
167
209
|
name: config.name,
|
|
168
210
|
slug,
|
|
169
211
|
description: tokens.content?.subHeadline || '',
|
|
170
212
|
tokens_json: tokens,
|
|
171
213
|
package_path: packagePath,
|
|
214
|
+
screenshot_url: screenshotUrl,
|
|
215
|
+
template_html: templateHtml,
|
|
172
216
|
}, creds.access_token);
|
|
173
217
|
|
|
174
218
|
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',
|