htmlboard 0.0.0-stage β 0.2.0
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/LICENSE +21 -0
- package/README.md +306 -2
- package/bin/htmlboard.js +294 -0
- package/package.json +48 -4
- package/skills/htmlboard/SKILL.md +95 -0
- package/src/client/explorer.css +372 -0
- package/src/client/explorer.js +952 -0
- package/src/client/favicon.svg +1 -0
- package/src/client/fonts/OFL-bricolage-grotesque.txt +93 -0
- package/src/client/fonts/OFL-inter.txt +93 -0
- package/src/client/fonts/bricolage-grotesque.woff2 +0 -0
- package/src/client/fonts/inter.woff2 +0 -0
- package/src/client/index.html +101 -0
- package/src/client/logo.svg +1 -0
- package/src/client/mark.svg +1 -0
- package/src/index.js +21 -0
- package/src/server.js +529 -0
- package/src/skills.js +116 -0
- package/src/templates/artboards/components.html +199 -0
- package/src/templates/artboards/desktop.html +397 -0
- package/src/templates/artboards/mobile.html +383 -0
- package/src/templates/designs.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HtmlBoard Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,307 @@
|
|
|
1
|
-
|
|
1
|
+
<h1>
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/luizcarlos1405/htmlboard/main/brand/logo-dark.svg">
|
|
4
|
+
<img alt="htmlboard" src="https://raw.githubusercontent.com/luizcarlos1405/htmlboard/main/brand/logo-light.svg" height="56">
|
|
5
|
+
</picture>
|
|
6
|
+
</h1>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
> **Infinite canvas and design explorer for HTML artboards β designed for humans and LLMs.**
|
|
9
|
+
|
|
10
|
+
[](https://www.npmjs.com/package/htmlboard)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://nodejs.org)
|
|
13
|
+
[](package.json)
|
|
14
|
+
|
|
15
|
+
**HtmlBoard** turns any folder of HTML files into a Figma-like interactive infinite canvas. Inspect responsive variants, mobile and desktop layouts, design system components, and full application flows side-by-side with instant live reload.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## π‘ Why HtmlBoard?
|
|
20
|
+
|
|
21
|
+
Modern AI agents and developers generate dozens of HTML mockups, layout variations, and prototypes in seconds. But previewing them has always been awkward:
|
|
22
|
+
- **Tabs upon tabs:** Flipping between dozens of browser tabs breaks context and comparison.
|
|
23
|
+
- **Figma is locked down:** Exporting code into proprietary design tools is slow and out of sync.
|
|
24
|
+
- **Traditional dev servers only show one page:** You lose the holistic bird's-eye view of your product's flow.
|
|
25
|
+
|
|
26
|
+
**HtmlBoard bridges this gap.** It gives you an infinite 2D canvas for HTML artboards β zero config, zero external dependencies, blazing fast, and purpose-built for AI-driven frontend prototyping.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## β¨ Features
|
|
31
|
+
|
|
32
|
+
- πΈ **Infinite Canvas**: Drag or scroll to move, <kbd>Ctrl</kbd>/<kbd>β</kbd> + scroll or pinch to zoom (2% to 800%), with a mouse, a trackpad or a touch screen. Names and section titles stay sharp at any zoom, and the dot grid keeps its spacing.
|
|
33
|
+
- β‘ **Zero Dependencies**: Pure native Node.js ES modules, fonts included. Runs instantly with `npx htmlboard`, offline too.
|
|
34
|
+
- π **State-Preserving Live Reload**: The server watches the project and pushes every save (Server-Sent Events). Saving a design reloads its frame; saving a file designs use (a stylesheet, a script, an image, data) reloads just the designs that loaded it; new, removed and renamed designs show up by themselves. Your view never moves.
|
|
35
|
+
- π **Auto-Discovery**: Point HtmlBoard at any folder. It finds every HTML file, names it after its `<title>`, sizes it from words in its path (mobile, tablet, desktopβ¦) and lays the folders out as rows.
|
|
36
|
+
- π **Declarative Manifest (`designs.json`)**: Optional file to choose the sections, the order, the sizes and the names.
|
|
37
|
+
- π **Search**: Find designs by name, file, tags or section (case and accents don't matter); the others fade out in place.
|
|
38
|
+
- π― **Focus & Zoom-to-Fit**: Click a design in the list or double-click it on the canvas to fit it, <kbd>Shift</kbd> + <kbd>1</kbd> fits everything, <kbd>β</kbd> <kbd>β</kbd> step through the designs.
|
|
39
|
+
- π **Deep Links**: The URL follows the selected design (`http://localhost:3000/#checkout-b`), so you can share or reload a view of one design.
|
|
40
|
+
- β¨οΈ **Keyboard-First Workflow**: Figma-like shortcuts; <kbd>?</kbd> lists them.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## π Quick Start
|
|
45
|
+
|
|
46
|
+
Run instantly in any project containing HTML files:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx htmlboard
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install globally:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install -g htmlboard
|
|
56
|
+
htmlboard
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Scaffold a Starter Project
|
|
60
|
+
|
|
61
|
+
To create a new workspace with starter templates (mobile screen, desktop dashboard, and component library):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx htmlboard --init
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## π οΈ CLI Usage
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
Usage:
|
|
73
|
+
htmlboard [directory] [options]
|
|
74
|
+
npx htmlboard [directory] [options]
|
|
75
|
+
htmlboard skills <install|uninstall> [options] Teach your AI agent to use HtmlBoard
|
|
76
|
+
|
|
77
|
+
Options:
|
|
78
|
+
-p, --port <number> Port to listen on (default: 3000)
|
|
79
|
+
-d, --dir <path> Directory to serve HTML artboards from (default: .)
|
|
80
|
+
-r, --root <path> Serve files from this parent folder, so artboards can link to
|
|
81
|
+
shared files outside the designs folder (default: the designs folder)
|
|
82
|
+
-o, --open Open the explorer automatically in default browser
|
|
83
|
+
--init Scaffold starter designs.json and sample artboards
|
|
84
|
+
-v, --version Display HtmlBoard version
|
|
85
|
+
-h, --help Display help message
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Examples
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Serve current directory on port 3000
|
|
92
|
+
htmlboard
|
|
93
|
+
|
|
94
|
+
# Open in browser automatically
|
|
95
|
+
htmlboard --open
|
|
96
|
+
|
|
97
|
+
# Serve a specific designs directory on port 8080
|
|
98
|
+
htmlboard ./src/designs -p 8080
|
|
99
|
+
|
|
100
|
+
# Artboards in ./design that link to the site's own ../css and ../js
|
|
101
|
+
htmlboard design --root .
|
|
102
|
+
|
|
103
|
+
# Initialize sample templates in a new folder
|
|
104
|
+
htmlboard ./my-new-ui --init --open
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## π€ Working with LLMs (AI-Assisted Prototyping)
|
|
110
|
+
|
|
111
|
+
HtmlBoard is built from the ground up to pair with AI coding agents (Claude, Gemini, Codex, ChatGPT, Antigravity, Cursor).
|
|
112
|
+
|
|
113
|
+
### Install the agent skill
|
|
114
|
+
|
|
115
|
+
HtmlBoard ships an [Agent Skill](https://agentskills.io) that teaches your agent how to use it: where to put artboards, how to size and name them, how `designs.json` works and how to start the canvas. Install it in your project:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx htmlboard skills install
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This writes `.claude/skills/htmlboard/` (Claude Code) and `.agents/skills/htmlboard/` (Codex and other agents that read `.agents/skills`). Commit them so everyone working on the project gets the skill. Then start a new agent session and just ask for a design.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx htmlboard skills install --global # for your user (~/.claude/skills, ~/.agents/skills), all projects
|
|
125
|
+
npx htmlboard skills install --agent claude # only one agent: claude | agents | all (default)
|
|
126
|
+
npx htmlboard skills install --force # update a copy you edited locally
|
|
127
|
+
npx htmlboard skills uninstall # remove it (accepts the same --global / --agent)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Running `install` again after upgrading HtmlBoard updates the skill; a copy you edited is kept unless you pass `--force`.
|
|
131
|
+
|
|
132
|
+
### The workflow
|
|
133
|
+
|
|
134
|
+
1. **Ask your LLM to generate artboards:**
|
|
135
|
+
> *"Create 3 design explorations for our checkout flow in `artboards/checkout-v1.html`, `artboards/checkout-v2.html`, and `artboards/checkout-v3.html`."*
|
|
136
|
+
|
|
137
|
+
2. **Run HtmlBoard in the background:**
|
|
138
|
+
```bash
|
|
139
|
+
npx htmlboard
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
3. **Watch the canvas update live:**
|
|
143
|
+
As the LLM writes or refines each HTML file, HtmlBoard detects the file change and reloads the iframe on the canvas immediately. You never have to refresh your browser or hunt for file URLs.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## π The `designs.json` Manifest
|
|
148
|
+
|
|
149
|
+
While HtmlBoard automatically discovers HTML files, you can add a `designs.json` file in your designs folder for custom layout and organization. Only `sections[].designs[].file` is required; the short form is enough:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"sections": [
|
|
154
|
+
{
|
|
155
|
+
"title": "Home",
|
|
156
|
+
"designs": [
|
|
157
|
+
{ "file": "home-mobile.html", "title": "Home Β· phone", "width": 390, "height": 844 },
|
|
158
|
+
{ "file": "home-mobile.html?theme=dark", "title": "Home Β· phone Β· dark", "width": 390, "height": 844 },
|
|
159
|
+
{ "file": "home-desktop.html", "title": "Home Β· desktop", "width": 1440 }
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Sections are rows (top to bottom) and designs run left to right. A `file` may carry a query string, so one HTML file can show several states. Leave `height` out to let the frame grow with its page. `id` and `category` are derived from the file when left out.
|
|
167
|
+
|
|
168
|
+
The full form adds ids, descriptions (shown in the list), categories and tags (searchable); `name`/`artboards` are synonyms of `title`/`designs`:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"title": "Fintech Mobile & Web App",
|
|
173
|
+
"description": "Exploration of screen flows, responsive layouts, and UI components",
|
|
174
|
+
"sections": [
|
|
175
|
+
{
|
|
176
|
+
"id": "screens",
|
|
177
|
+
"name": "Screen Layouts",
|
|
178
|
+
"description": "Full page layouts across mobile and desktop viewports",
|
|
179
|
+
"artboards": [
|
|
180
|
+
{
|
|
181
|
+
"id": "desktop-dashboard",
|
|
182
|
+
"title": "Desktop Dashboard",
|
|
183
|
+
"file": "artboards/desktop.html",
|
|
184
|
+
"width": 1280,
|
|
185
|
+
"height": 820,
|
|
186
|
+
"category": "desktop",
|
|
187
|
+
"tags": ["desktop", "dashboard", "analytics"]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "mobile-app",
|
|
191
|
+
"title": "Mobile App Feed",
|
|
192
|
+
"file": "artboards/mobile.html",
|
|
193
|
+
"width": 390,
|
|
194
|
+
"height": 844,
|
|
195
|
+
"category": "mobile",
|
|
196
|
+
"tags": ["mobile", "ios", "finance"]
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "components",
|
|
202
|
+
"name": "Component Library",
|
|
203
|
+
"artboards": [
|
|
204
|
+
{
|
|
205
|
+
"id": "ui-primitives",
|
|
206
|
+
"title": "UI Kit Elements",
|
|
207
|
+
"file": "artboards/components.html",
|
|
208
|
+
"width": 780,
|
|
209
|
+
"height": 720,
|
|
210
|
+
"category": "components",
|
|
211
|
+
"tags": ["design-system", "buttons", "inputs"]
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## β¨οΈ Keyboard Shortcuts
|
|
222
|
+
|
|
223
|
+
| Shortcut | Action |
|
|
224
|
+
| :--- | :--- |
|
|
225
|
+
| Drag, <kbd>Space</kbd> + drag | Move around |
|
|
226
|
+
| Scroll (<kbd>Shift</kbd>: sideways) | Move around |
|
|
227
|
+
| <kbd>Ctrl</kbd>/<kbd>β</kbd> + scroll, pinch | Zoom, anchored to the pointer |
|
|
228
|
+
| <kbd>+</kbd> / <kbd>β</kbd> | Zoom in / out |
|
|
229
|
+
| <kbd>Shift</kbd> + <kbd>0</kbd> | Zoom to 100% |
|
|
230
|
+
| <kbd>Shift</kbd> + <kbd>1</kbd> | Fit everything (what the search keeps) |
|
|
231
|
+
| <kbd>Shift</kbd> + <kbd>2</kbd>, double-click | Fit the design |
|
|
232
|
+
| <kbd>β</kbd> / <kbd>β</kbd> | Previous / next design |
|
|
233
|
+
| <kbd>Enter</kbd> | Open the selected design on its own |
|
|
234
|
+
| <kbd>/</kbd> | Search (<kbd>Enter</kbd> fits the first match, <kbd>Esc</kbd> clears) |
|
|
235
|
+
| <kbd>R</kbd> | Reload the designs |
|
|
236
|
+
| <kbd>\</kbd> | Show or hide the list |
|
|
237
|
+
| <kbd>?</kbd> | Show the shortcuts |
|
|
238
|
+
| <kbd>Esc</kbd> | Deselect |
|
|
239
|
+
|
|
240
|
+
On the canvas the designs are pictures: the canvas takes every click and scroll, so <kbd>Ctrl</kbd> + scroll never zooms the page itself. Use **Open** (or <kbd>Enter</kbd>) to try a design's links and hover states on its own.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## π Programmatic API
|
|
245
|
+
|
|
246
|
+
You can also embed HtmlBoard into your own build tools, testing pipelines, or dev environments:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
import { startServer } from 'htmlboard';
|
|
250
|
+
|
|
251
|
+
const instance = await startServer({
|
|
252
|
+
dir: './designs',
|
|
253
|
+
port: 3000
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
console.log(`HtmlBoard running at ${instance.url}`);
|
|
257
|
+
|
|
258
|
+
// Later, when done:
|
|
259
|
+
// await instance.close();
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The skill installer is exported too:
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
import { installSkills } from 'htmlboard';
|
|
266
|
+
|
|
267
|
+
installSkills({ dir: process.cwd(), agent: 'all' });
|
|
268
|
+
// β [{ skill: 'htmlboard', path: 'β¦/.claude/skills/htmlboard', status: 'installed' }, β¦]
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## π Project Structure
|
|
274
|
+
|
|
275
|
+
```text
|
|
276
|
+
htmlboard/
|
|
277
|
+
βββ bin/
|
|
278
|
+
β βββ htmlboard.js # CLI executable
|
|
279
|
+
βββ src/
|
|
280
|
+
β βββ index.js # Library entry point & API exports
|
|
281
|
+
β βββ server.js # HTTP server, SSE live reload, auto-discovery
|
|
282
|
+
β βββ skills.js # `htmlboard skills` installer
|
|
283
|
+
β βββ client/ # The canvas
|
|
284
|
+
β β βββ index.html # Explorer shell: list, canvas, toolbar, shortcuts
|
|
285
|
+
β β βββ explorer.css # Dark chrome, dot grid
|
|
286
|
+
β β βββ explorer.js # Layout, pan/zoom, search, live reload
|
|
287
|
+
β β βββ favicon.svg, logo.svg, mark.svg
|
|
288
|
+
β β βββ fonts/ # Inter and Bricolage Grotesque (OFL)
|
|
289
|
+
β βββ templates/ # Starter scaffold templates
|
|
290
|
+
β βββ designs.json # Starter manifest
|
|
291
|
+
β βββ artboards/ # Sample mobile, desktop, & component screens
|
|
292
|
+
βββ skills/
|
|
293
|
+
β βββ htmlboard/ # Agent skill (SKILL.md) copied by `htmlboard skills install`
|
|
294
|
+
βββ brand/ # The logo's files (from design/tools/logo.py)
|
|
295
|
+
βββ design/ # htmlboard's own designs, on its own canvas: `npx htmlboard design --root .`
|
|
296
|
+
βββ package.json
|
|
297
|
+
βββ README.md
|
|
298
|
+
βββ LICENSE
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## π License
|
|
304
|
+
|
|
305
|
+
MIT Β© Luiz Carlos & Contributors
|
|
306
|
+
|
|
307
|
+
The explorer ships the Inter and Bricolage Grotesque fonts under the SIL Open Font License 1.1 (see `src/client/fonts/`).
|
package/bin/htmlboard.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { exec } from 'node:child_process';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { startServer } from '../src/server.js';
|
|
8
|
+
import { installSkills, uninstallSkills } from '../src/skills.js';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const PKG_PATH = path.join(__dirname, '..', 'package.json');
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
14
|
+
|
|
15
|
+
// Parse command line arguments
|
|
16
|
+
function parseArgs(args) {
|
|
17
|
+
const options = {
|
|
18
|
+
port: 3000,
|
|
19
|
+
dir: process.cwd(),
|
|
20
|
+
root: null,
|
|
21
|
+
open: false,
|
|
22
|
+
init: false,
|
|
23
|
+
help: false,
|
|
24
|
+
version: false
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < args.length; i++) {
|
|
28
|
+
const arg = args[i];
|
|
29
|
+
|
|
30
|
+
if (arg === '--help' || arg === '-h') {
|
|
31
|
+
options.help = true;
|
|
32
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
33
|
+
options.version = true;
|
|
34
|
+
} else if (arg === '--open' || arg === '-o') {
|
|
35
|
+
options.open = true;
|
|
36
|
+
} else if (arg === '--init') {
|
|
37
|
+
options.init = true;
|
|
38
|
+
} else if (arg === '--port' || arg === '-p') {
|
|
39
|
+
const next = args[++i];
|
|
40
|
+
if (next && !next.startsWith('-')) {
|
|
41
|
+
options.port = parseInt(next, 10);
|
|
42
|
+
}
|
|
43
|
+
} else if (arg.startsWith('--port=')) {
|
|
44
|
+
options.port = parseInt(arg.split('=')[1], 10);
|
|
45
|
+
} else if (arg === '--dir' || arg === '-d') {
|
|
46
|
+
const next = args[++i];
|
|
47
|
+
if (next && !next.startsWith('-')) {
|
|
48
|
+
options.dir = next;
|
|
49
|
+
}
|
|
50
|
+
} else if (arg.startsWith('--dir=')) {
|
|
51
|
+
options.dir = arg.split('=')[1];
|
|
52
|
+
} else if (arg === '--root' || arg === '-r') {
|
|
53
|
+
const next = args[++i];
|
|
54
|
+
if (next && !next.startsWith('-')) {
|
|
55
|
+
options.root = next;
|
|
56
|
+
}
|
|
57
|
+
} else if (arg.startsWith('--root=')) {
|
|
58
|
+
options.root = arg.split('=')[1];
|
|
59
|
+
} else if (!arg.startsWith('-')) {
|
|
60
|
+
// Positional directory argument e.g. `htmlboard ./my-designs`
|
|
61
|
+
options.dir = arg;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return options;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function showHelp() {
|
|
69
|
+
console.log(`
|
|
70
|
+
htmlboard v${pkg.version}
|
|
71
|
+
Infinite canvas and design explorer for HTML artboards β designed for humans and LLMs
|
|
72
|
+
|
|
73
|
+
Usage:
|
|
74
|
+
htmlboard [directory] [options]
|
|
75
|
+
npx htmlboard [directory] [options]
|
|
76
|
+
htmlboard skills <install|uninstall> [options] Teach your AI agent to use HtmlBoard
|
|
77
|
+
|
|
78
|
+
Options:
|
|
79
|
+
-p, --port <number> Port to listen on (default: 3000)
|
|
80
|
+
-d, --dir <path> Directory to serve HTML artboards from (default: .)
|
|
81
|
+
-r, --root <path> Serve files from this parent folder, so artboards can link to
|
|
82
|
+
shared files outside the designs folder (default: the designs folder)
|
|
83
|
+
-o, --open Open the explorer automatically in your default browser
|
|
84
|
+
--init Scaffold starter designs.json and sample artboards in target directory
|
|
85
|
+
-v, --version Display the version
|
|
86
|
+
-h, --help Display this help message
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
htmlboard # Run in current directory on http://localhost:3000
|
|
90
|
+
htmlboard --open # Run and launch browser automatically
|
|
91
|
+
htmlboard ./designs -p 8080 # Serve ./designs on port 8080
|
|
92
|
+
htmlboard design --root . # Artboards in ./design that link to ../css/β¦
|
|
93
|
+
htmlboard --init # Initialize starter templates in current folder
|
|
94
|
+
htmlboard skills install # Add the HtmlBoard skill to this project's agents
|
|
95
|
+
|
|
96
|
+
On the canvas:
|
|
97
|
+
Drag, scroll Move around
|
|
98
|
+
Ctrl/Cmd + scroll Zoom (or pinch)
|
|
99
|
+
Double-click Fit a design (Shift + 1 fits everything)
|
|
100
|
+
/ Search
|
|
101
|
+
? All the shortcuts
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function openBrowser(url) {
|
|
106
|
+
const startCmd = process.platform === 'darwin'
|
|
107
|
+
? 'open'
|
|
108
|
+
: process.platform === 'win32'
|
|
109
|
+
? 'start'
|
|
110
|
+
: 'xdg-open';
|
|
111
|
+
|
|
112
|
+
exec(`${startCmd} ${url}`, (err) => {
|
|
113
|
+
if (err) {
|
|
114
|
+
console.warn(`[htmlboard] Unable to auto-launch browser: ${err.message}`);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function scaffoldProject(targetDir) {
|
|
120
|
+
const templatesDir = path.join(__dirname, '..', 'src', 'templates');
|
|
121
|
+
const targetConfig = path.join(targetDir, 'designs.json');
|
|
122
|
+
const targetArtboardsDir = path.join(targetDir, 'artboards');
|
|
123
|
+
|
|
124
|
+
console.log(`[htmlboard] Scaffolding starter project in ${targetDir}...`);
|
|
125
|
+
|
|
126
|
+
if (!fs.existsSync(targetDir)) {
|
|
127
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!fs.existsSync(targetArtboardsDir)) {
|
|
131
|
+
fs.mkdirSync(targetArtboardsDir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Copy designs.json if not present
|
|
135
|
+
if (!fs.existsSync(targetConfig)) {
|
|
136
|
+
const srcConfig = path.join(templatesDir, 'designs.json');
|
|
137
|
+
if (fs.existsSync(srcConfig)) {
|
|
138
|
+
fs.copyFileSync(srcConfig, targetConfig);
|
|
139
|
+
console.log(` β Created designs.json`);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
console.log(` βΉ designs.json already exists (skipped)`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Copy starter artboards
|
|
146
|
+
const starterFiles = ['mobile.html', 'desktop.html', 'components.html'];
|
|
147
|
+
for (const file of starterFiles) {
|
|
148
|
+
const srcFile = path.join(templatesDir, 'artboards', file);
|
|
149
|
+
const destFile = path.join(targetArtboardsDir, file);
|
|
150
|
+
if (!fs.existsSync(destFile) && fs.existsSync(srcFile)) {
|
|
151
|
+
fs.copyFileSync(srcFile, destFile);
|
|
152
|
+
console.log(` β Created artboards/${file}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log(`\nπ Starter templates created successfully!`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function showSkillsHelp() {
|
|
160
|
+
console.log(`
|
|
161
|
+
Usage:
|
|
162
|
+
htmlboard skills install [options] Copy the HtmlBoard skill into your agents' skill folders
|
|
163
|
+
htmlboard skills uninstall [options] Remove it
|
|
164
|
+
|
|
165
|
+
Options:
|
|
166
|
+
-g, --global Install for your user (~/.claude/skills, ~/.agents/skills) instead of this project
|
|
167
|
+
-a, --agent <name> claude (.claude/skills), agents (.agents/skills: Codex and other agents), or all (default)
|
|
168
|
+
-d, --dir <path> Project root (default: current directory)
|
|
169
|
+
-f, --force Replace a skill that was changed locally
|
|
170
|
+
-h, --help Display this help message
|
|
171
|
+
`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function runSkills(args) {
|
|
175
|
+
const [action, ...rest] = args;
|
|
176
|
+
const options = { agent: 'all', global: false, dir: process.cwd(), force: false };
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < rest.length; i++) {
|
|
179
|
+
const arg = rest[i];
|
|
180
|
+
if (arg === '--global' || arg === '-g') options.global = true;
|
|
181
|
+
else if (arg === '--force' || arg === '-f') options.force = true;
|
|
182
|
+
else if (arg === '--agent' || arg === '-a') options.agent = rest[++i];
|
|
183
|
+
else if (arg.startsWith('--agent=')) options.agent = arg.split('=')[1];
|
|
184
|
+
else if (arg === '--dir' || arg === '-d') options.dir = rest[++i];
|
|
185
|
+
else if (arg.startsWith('--dir=')) options.dir = arg.split('=')[1];
|
|
186
|
+
else if (arg === '--help' || arg === '-h') return showSkillsHelp();
|
|
187
|
+
else {
|
|
188
|
+
console.error(`[htmlboard] Unknown option: ${arg}`);
|
|
189
|
+
showSkillsHelp();
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (action !== 'install' && action !== 'uninstall') {
|
|
195
|
+
showSkillsHelp();
|
|
196
|
+
process.exit(action && action !== '--help' && action !== '-h' ? 1 : 0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let results;
|
|
200
|
+
try {
|
|
201
|
+
results = action === 'install' ? installSkills(options) : uninstallSkills(options);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
console.error(`[htmlboard] ${err.message}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const labels = {
|
|
208
|
+
installed: '\x1b[32mβ installed\x1b[0m',
|
|
209
|
+
updated: '\x1b[32mβ updated\x1b[0m',
|
|
210
|
+
unchanged: '\x1b[90m= up to date\x1b[0m',
|
|
211
|
+
skipped: '\x1b[33m! changed locally, kept (use --force to replace)\x1b[0m',
|
|
212
|
+
removed: '\x1b[32mβ removed\x1b[0m',
|
|
213
|
+
absent: '\x1b[90m- not installed\x1b[0m'
|
|
214
|
+
};
|
|
215
|
+
for (const { path: dest, status } of results) {
|
|
216
|
+
console.log(` ${labels[status]} ${path.relative(process.cwd(), dest) || dest}`);
|
|
217
|
+
}
|
|
218
|
+
if (action === 'install' && results.some((r) => r.status === 'installed' || r.status === 'updated')) {
|
|
219
|
+
console.log(`\n Restart your agent session to load the skill, then ask it for a design.`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function main() {
|
|
224
|
+
const args = process.argv.slice(2);
|
|
225
|
+
|
|
226
|
+
if (args[0] === 'skills') {
|
|
227
|
+
runSkills(args.slice(1));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const options = parseArgs(args);
|
|
232
|
+
|
|
233
|
+
if (options.help) {
|
|
234
|
+
showHelp();
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (options.version) {
|
|
239
|
+
console.log(`htmlboard v${pkg.version}`);
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const targetDir = path.resolve(options.dir);
|
|
244
|
+
|
|
245
|
+
if (options.init) {
|
|
246
|
+
scaffoldProject(targetDir);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// If directory is empty, auto-prompt or auto-scaffold
|
|
250
|
+
let entries = [];
|
|
251
|
+
try {
|
|
252
|
+
entries = fs.readdirSync(targetDir);
|
|
253
|
+
} catch (_) {
|
|
254
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const hasHtml = entries.some((e) => e.endsWith('.html')) ||
|
|
258
|
+
(fs.existsSync(path.join(targetDir, 'artboards')) &&
|
|
259
|
+
fs.readdirSync(path.join(targetDir, 'artboards')).some((e) => e.endsWith('.html')));
|
|
260
|
+
const hasDesignsJson = fs.existsSync(path.join(targetDir, 'designs.json'));
|
|
261
|
+
|
|
262
|
+
if (!hasHtml && !hasDesignsJson && entries.filter((e) => !e.startsWith('.')).length === 0) {
|
|
263
|
+
console.log(`[htmlboard] Directory is empty. Initializing starter artboards...`);
|
|
264
|
+
scaffoldProject(targetDir);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const { url, rootDir } = await startServer({
|
|
269
|
+
dir: targetDir,
|
|
270
|
+
root: options.root ? path.resolve(options.root) : undefined,
|
|
271
|
+
port: options.port
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
console.log(`
|
|
275
|
+
\x1b[90mhtml\x1b[0m\x1b[1mboard\x1b[0m \x1b[90mv${pkg.version}\x1b[0m
|
|
276
|
+
\x1b[90mInfinite canvas for HTML designs\x1b[0m
|
|
277
|
+
|
|
278
|
+
\x1b[32mβ\x1b[0m \x1b[1mLocal:\x1b[0m \x1b[36m${url}\x1b[0m
|
|
279
|
+
\x1b[32mβ\x1b[0m \x1b[1mTarget:\x1b[0m \x1b[90m${targetDir}\x1b[0m${rootDir !== targetDir ? `\n \x1b[32mβ\x1b[0m \x1b[1mRoot:\x1b[0m \x1b[90m${rootDir}\x1b[0m` : ''}
|
|
280
|
+
\x1b[32mβ\x1b[0m \x1b[1mLive:\x1b[0m \x1b[32mWatching files for hot live reload\x1b[0m
|
|
281
|
+
|
|
282
|
+
\x1b[90mPress \x1b[1mCtrl+C\x1b[0m\x1b[90m to stop.\x1b[0m
|
|
283
|
+
`);
|
|
284
|
+
|
|
285
|
+
if (options.open) {
|
|
286
|
+
openBrowser(url);
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error(`[htmlboard] Error starting server:`, err.message);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "htmlboard",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Infinite canvas and design explorer for HTML artboards β designed for humans and LLMs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"htmlboard": "bin/htmlboard.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./src/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"src",
|
|
13
|
+
"skills",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "node ./bin/htmlboard.js",
|
|
19
|
+
"dev": "node ./bin/htmlboard.js",
|
|
20
|
+
"test": "node --test"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"canvas",
|
|
24
|
+
"design",
|
|
25
|
+
"artboard",
|
|
26
|
+
"html",
|
|
27
|
+
"ui",
|
|
28
|
+
"explorer",
|
|
29
|
+
"infinite-canvas",
|
|
30
|
+
"llm",
|
|
31
|
+
"prototyping",
|
|
32
|
+
"live-reload",
|
|
33
|
+
"agent-skills",
|
|
34
|
+
"claude-code",
|
|
35
|
+
"codex"
|
|
36
|
+
],
|
|
37
|
+
"author": "Luiz Carlos (https://github.com/luizcarlos1405)",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/luizcarlos1405/htmlboard.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/luizcarlos1405/htmlboard#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/luizcarlos1405/htmlboard/issues"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
}
|
|
50
|
+
}
|