Skip to content

Commit 94a4801

Browse files
committed
Add support for local html and CodePen link screenshot
1 parent ef7d856 commit 94a4801

6 files changed

Lines changed: 215 additions & 114 deletions

File tree

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ A command-line tool for css-doodle to preview and generate images.
1010
npm install -g @css-doodle/cli
1111
```
1212

13-
After installation, you can use command `css-doodle` or `cssd` in the terminal.
13+
>[!NOTE]
14+
> After installation, you can use `cssd` or `css-doodle` command in the terminal.
1415
1516
## Usage
1617

@@ -22,8 +23,8 @@ Options:
2223
-h, --help display help for command
2324
2425
Commands:
25-
render Generate an image from css-doodle file
26-
preview Open a window to preview the css-doodle file
26+
render Generate an image from css|cssd|html file or from codepen links
27+
preview Open a window to preview css|cssd file
2728
generate Generate code using css-doodle generators
2829
config Display/set the configurations
2930
use Shorthand to fetch and use a custom version of css-doodle
@@ -34,20 +35,23 @@ Commands:
3435
## Commands
3536

3637
### render
37-
Generate an image from the css-doodle source file. It'll read from STDIN if no source file specified.
38+
Generate an image from the css-doodle source file. The source file can be a `.css`, `.cssd`, `.html` file or CodePen link.
3839

3940
* `-o, --output`: Custom output filename of the generated image.
4041
* `-x, --scale`: Scale factor of the generated image, defaults to 1.
42+
* `-s, --selector`: CSS selector to target the rendered node, defaults to `css-doodle`.
43+
* `-d, --delay`: Delay in milliseconds after the image is rendered.
4144

4245
```bash
4346
cssd render
4447
cssd render code.css
4548
cssd render code.css -o result.png
4649
cssd render code.css -x 4
50+
cssd render https://codepen.io/yuanchuan/pen/MQEeJo
4751
```
4852

4953
### preview
50-
Open a window to preview the css-doodle file.
54+
Open a window to preview the css-doodle source file. The source file can be either `.css` or `.cssd`.
5155

5256
* `--fullscreen`: Open in fullscreen mode.
5357

bin/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,17 @@ program
4040

4141
program
4242
.command('render')
43-
.description('Generate an image from css-doodle file')
43+
.description('Generate an image from css|cssd|html file or from CodePen link')
4444
.argument('[source]', 'css-doolde source file used to generate the image')
4545
.option('-o, --output <output>', 'Custom filename of the generated image')
46-
.option('-x, --scale <scale>', 'Scale factor of the generated image, defaults to 1')
46+
.option('-x, --scale <scale>', 'Scale factor of the generated image, defaults to `2`')
47+
.option('-s, --selector <selector>', 'CSS selector to target the rendered node, defaults to `css-doodle`')
48+
.option('-d, --delay <delay>', 'Delay in milliseconds after the image is rendered')
4749
.action(handleRender);
4850

4951
program
5052
.command('preview')
51-
.description('Open a window to preview the css-doodle file')
53+
.description('Open a window to preview the css|cssd file')
5254
.argument('[source]', 'css-doodle source file to preview')
5355
.option('--fullscreen', 'open the preview in fullscreen mode')
5456
.action(handlePreview);

lib/handler.js

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,39 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
3-
import os from 'node:os';
4-
import { stdin } from 'node:process';
53
import { execSync } from 'node:child_process';
64

75
import { config, configPath, configDownloadPath } from './static.js'
6+
import { generateSVG, generateShape } from './generate.js';
87
import { parse } from './parse.js';
98
import { preview } from './preview/index.js';
109
import { render } from './render.js';
11-
import { generateSVG, generateShape } from './generate.js';
10+
import { read } from './read.js';
1211

1312
export async function handleRender(source, options) {
14-
let { content, error } = await read(source);
13+
const { content, error, type } = await read(source);
1514
if (error) {
1615
return console.error(error.message);
1716
}
1817
let title = 'image';
1918
if (source) {
20-
let basename = path.basename(source);
21-
let extname = path.extname(basename);
19+
const basename = path.basename(source);
20+
const extname = path.extname(basename);
2221
title = extname ? basename.split(extname)[0] : basename;
2322
}
24-
let output = await render(content, {
25-
title,
26-
output: options.output,
27-
scale: options.scale
28-
});
29-
if (output) {
30-
console.log(`Saved to ${output}.`);
23+
options.title = title;
24+
options.type = type;
25+
26+
try {
27+
const start = Date.now();
28+
const output = await render(content, options);
29+
const time = (Date.now() - start) / 1000;
30+
31+
if (output) {
32+
console.log(`Saved to ${output}. (${time}s)`);
33+
}
34+
} catch (e) {
35+
console.error(e.message);
36+
process.exit(1);
3137
}
3238
}
3339

@@ -45,10 +51,16 @@ export async function handleParse(source) {
4551
}
4652

4753
export async function handlePreview(source, options) {
48-
let { content, error } = await read(source);
54+
let { content, error, type } = await read(source);
4955
if (error) {
5056
return console.error(error.message);
5157
}
58+
if (type === 'codepen') {
59+
return console.error('error: CodePen url is not supported for preview');
60+
}
61+
if (type === 'html') {
62+
return console.error('error: HTML is not supported for preview');
63+
}
5264
if (source === undefined) {
5365
options.fromStdin = true;
5466
preview(content, 'css-doodle', options);
@@ -129,48 +141,6 @@ export function handleUpgrade() {
129141
execSync('npm update -g @css-doodle/cli', { stdio: 'inherit' });
130142
}
131143

132-
async function read(path) {
133-
let content = '';
134-
let error = null;
135-
if (path === undefined) {
136-
console.log('No source file specified, reading from stdin:');
137-
let key = os.platform() === 'win32' ? 'CTRL+Z' : 'CTRL+D';
138-
console.log(`(Press ${key} to finish input.)\n`);
139-
try {
140-
content = await readFromStdin();
141-
console.log('\n');
142-
} catch (e) {
143-
error = e;
144-
}
145-
} else {
146-
try {
147-
content = fs.readFileSync(path, 'utf8');
148-
} catch (e) {
149-
error = e;
150-
}
151-
}
152-
153-
if (content) {
154-
content = content.trim();
155-
}
156-
157-
if (!error && !content) {
158-
error = new Error('error: empty input');
159-
}
160-
161-
return { content, error };
162-
}
163-
164-
function readFromStdin() {
165-
return new Promise((resolve, reject) => {
166-
let content = '';
167-
stdin.setEncoding('utf8');
168-
stdin.on('data', c => content += c);
169-
stdin.on('end', () => resolve(content));
170-
stdin.on('error', reject);
171-
});
172-
}
173-
174144
async function fetchCssDoodleSource(version) {
175145
let result = '', error;
176146

lib/preview/client.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
<meta charset="utf-8">
55
<title>css-doodle</title>
66
<style>
7-
html, body {
7+
html, body, #container {
88
margin: 0;
99
padding: 0;
1010
width: 100%;
1111
height: 100%;
1212
}
13-
body {
13+
body, #container {
1414
display: grid;
1515
place-items: center;
1616
background: #282828;
@@ -66,7 +66,7 @@
6666
updateWindow();
6767
});
6868

69-
doodle.innerHTML = `<style>${data}</style>`;
69+
doodle.innerHTML = `<template>${data}</template>`;
7070
doodle.setAttribute('click-to-update', '');
7171
container.appendChild(doodle);
7272
}
@@ -78,7 +78,6 @@
7878
return Math.min(Math.max(value, min), max);
7979
}
8080

81-
8281
function updateWindow() {
8382
let { width, height } = doodle.getBoundingClientRect();
8483
document.title = title + ` (${Math.round(width)}x${Math.round(height)})`;

lib/read.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import os from 'node:os';
2+
import fs from 'node:fs/promises';
3+
import { stdin } from 'node:process';
4+
5+
export async function read(path) {
6+
let result = { content: '', type: '', error: null };
7+
8+
if (path === undefined) {
9+
result.type = 'stdin';
10+
console.log('No source file specified, reading from stdin:');
11+
let key = os.platform() === 'win32' ? 'CTRL+Z' : 'CTRL+D';
12+
console.log(`(Press ${key} to finish input.)\n`);
13+
try {
14+
result.content = await readFromStdin();
15+
console.log('\n');
16+
} catch (e) {
17+
result.error = e;
18+
}
19+
}
20+
else if (/^(https:\/\/)?codepen\.io/.test(path)) {
21+
result.type = 'codepen';
22+
let match = path.match(/^(https:\/\/)?codepen\.io\/([^\/]+)\/(pen|details|full)\/([^\/]+)$/) || [];
23+
let [user, id] = [match[2], match[4]];
24+
if (user && id) {
25+
result.content = `https://cdpn.io/${user}/fullpage/${id}?nocache=true&view=fullpage`;
26+
} else {
27+
result.error = new Error(
28+
'error: unsupported CodePen url, should be like https://codepen.io/:user/pen/:id'
29+
);
30+
}
31+
}
32+
else if (/\.html$/.test(path)) {
33+
let { content, error } = await readFile(path);
34+
result = { type: 'html', content, error };
35+
}
36+
else if (/\.cssd?$/.test(path)) {
37+
let { content, error } = await readFile(path);
38+
result = { type: 'css', content, error };
39+
}
40+
else {
41+
result.error = new Error('error: invalid input, accept .html, .css, .cssd, and CodePen link');
42+
}
43+
44+
if (result.content) {
45+
result.content = result.content.trim();
46+
}
47+
48+
if (!result.error && !result.content) {
49+
result.error = new Error('error: empty input');
50+
}
51+
52+
return result;
53+
}
54+
55+
function readFromStdin() {
56+
return new Promise((resolve, reject) => {
57+
let content = '';
58+
stdin.setEncoding('utf8');
59+
stdin.on('data', c => content += c);
60+
stdin.on('end', () => resolve(content));
61+
stdin.on('error', reject);
62+
});
63+
}
64+
65+
async function readFile(path) {
66+
let content = '', error;
67+
try {
68+
content = await fs.readFile(path, 'utf8');
69+
} catch (e) {
70+
error = e;
71+
}
72+
return { content, error };
73+
}

0 commit comments

Comments
 (0)