htmlboard 0.3.0 → 0.5.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/README.md +170 -5
- package/bin/htmlboard.js +152 -0
- package/package.json +1 -1
- package/skills/htmlboard/SKILL.md +52 -7
- package/src/catalog.js +449 -0
- package/src/check.js +173 -0
- package/src/client/check.js +349 -0
- package/src/client/explorer.js +68 -2
- package/src/client/probe.js +41 -0
- package/src/components.js +95 -0
- package/src/designs.js +63 -15
- package/src/index.js +18 -0
- package/src/server.js +167 -11
- package/src/templates/01-screens/01-dashboard.html +112 -0
- package/src/templates/01-screens/02-customers.html +77 -0
- package/src/templates/01-screens/{02-mobile.html → 03-mobile.html} +35 -182
- package/src/templates/02-ui-kit/01-overview.html +89 -0
- package/src/templates/components/app-sidebar.html +67 -0
- package/src/templates/components/app-topbar.html +19 -0
- package/src/templates/components/stat-card.html +14 -0
- package/src/templates/components/tx-item.html +33 -0
- package/src/templates/components.js +180 -0
- package/src/templates/styles.css +155 -0
- package/src/templates/01-screens/01-desktop.html +0 -399
- package/src/templates/02-components/01-ui-kit.html +0 -201
package/README.md
CHANGED
|
@@ -31,8 +31,11 @@ Modern AI agents and developers generate dozens of HTML mockups, layout variatio
|
|
|
31
31
|
|
|
32
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
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.
|
|
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 component, 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
35
|
- 📂 **Designs That Describe Themselves**: Every HTML file is a design, each folder a section, number prefixes the order. A file says its own size, category and variants in a `<meta>` tag or two, so adding a design means writing one file — nothing to keep in sync.
|
|
36
|
+
- 🧩 **Components**: Each file in `components/` is a custom element named after it (`tx-item.html` is `<tx-item>`), so the sidebar every screen shares is written once and used as one tag: attributes, slots and scoped CSS, built in the light DOM with your `styles.css`, no build step.
|
|
37
|
+
- 🗂️ **A Catalog for Agents**: `htmlboard catalog` reads the project and prints what the designs share in a few lines: each stylesheet's variables and classes, the scripts, every component with its attributes, an example and where it's used. An agent learns your styles and components without reading every file, and since it's read from the files, it is never out of date.
|
|
38
|
+
- 🩺 **Checks for Agents**: `htmlboard check` has the canvas open in your browser load each design again and report, as text, what went wrong: script errors, missing files and components, fonts that didn't load, content cut at the frame's edge, text cut off or spilling out of its box. An agent checks its work without screenshots.
|
|
36
39
|
- 📐 **Optional `designs.json`**: Curates when you want it (section titles, order, overrides); designs it doesn't list still show at the end, or don't (`"unlisted": "hide"`). `htmlboard migrate` moves an existing one into the files.
|
|
37
40
|
- 📱 **Device Filters**: **Mobile**, **Tablet** and **Desktop** (and **Components**, or your own categories) lay out just those designs, side by side, to compare them.
|
|
38
41
|
- 🔎 **Search**: Find designs by name, file, tags or section (case and accents don't matter); the others fade out in place.
|
|
@@ -59,7 +62,7 @@ htmlboard
|
|
|
59
62
|
|
|
60
63
|
### Scaffold a Starter Project
|
|
61
64
|
|
|
62
|
-
To start with sample designs (a folder of screens and a
|
|
65
|
+
To start with sample designs (a folder of screens and a UI kit, each file describing itself, all sharing one `styles.css` and four components):
|
|
63
66
|
|
|
64
67
|
```bash
|
|
65
68
|
npx htmlboard --init
|
|
@@ -75,6 +78,9 @@ Usage:
|
|
|
75
78
|
npx htmlboard [directory] [options]
|
|
76
79
|
htmlboard skills <install|uninstall> [options] Teach your AI agent to use HtmlBoard
|
|
77
80
|
htmlboard migrate [directory] [--write] Move designs.json's sizes, titles and variants into the files
|
|
81
|
+
htmlboard components [directory] [--force] Add components.js: files in components/ become custom elements
|
|
82
|
+
htmlboard catalog [directory] What the designs share: stylesheets, scripts, components
|
|
83
|
+
htmlboard check [directory or file] [-p <port>] What went wrong in the designs, from the canvas open in a browser
|
|
78
84
|
|
|
79
85
|
Options:
|
|
80
86
|
-p, --port <number> Port to listen on (default: 3000)
|
|
@@ -105,6 +111,15 @@ htmlboard design --root .
|
|
|
105
111
|
# Start a new folder with sample designs
|
|
106
112
|
htmlboard ./my-new-ui --init --open
|
|
107
113
|
|
|
114
|
+
# Let the designs in ./design use components
|
|
115
|
+
htmlboard components design
|
|
116
|
+
|
|
117
|
+
# What the designs in ./design share, in a few lines (what the agent skill reads)
|
|
118
|
+
htmlboard catalog design
|
|
119
|
+
|
|
120
|
+
# What went wrong in them: script errors, missing files, cut-off text (the canvas must be open)
|
|
121
|
+
htmlboard check design
|
|
122
|
+
|
|
108
123
|
# See what moving ./design/designs.json into its files would do, then do it
|
|
109
124
|
htmlboard migrate design
|
|
110
125
|
htmlboard migrate design --write
|
|
@@ -150,11 +165,70 @@ Running `install` again after upgrading HtmlBoard updates the skill; a copy you
|
|
|
150
165
|
3. **Watch the canvas update live:**
|
|
151
166
|
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.
|
|
152
167
|
|
|
168
|
+
### What the designs share: `htmlboard catalog`
|
|
169
|
+
|
|
170
|
+
Before an agent writes a new design, it needs to know what the others use: the stylesheet's variables and classes, the components and how to write them. Reading every file for that costs tokens in every session. `htmlboard catalog` reads them instead and prints a summary (the skill runs it). On the samples, shortened:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
$ npx htmlboard catalog designs
|
|
174
|
+
designs: 4 designs. Paths below are from this folder.
|
|
175
|
+
|
|
176
|
+
Stylesheets
|
|
177
|
+
styles.css · all 4 designs
|
|
178
|
+
:root { --bg: #0b0d13; --surface: #141724; --surface-2: #1c2030; --border: #23283c;
|
|
179
|
+
--text: #f1f5f9; --text-muted: #94a3b8; --text-subtle: #64748b; --accent: #6366f1; … }
|
|
180
|
+
classes .muted .label .row .page-title .page-sub .app .app-main .app-content .card .btn
|
|
181
|
+
.btn-primary .btn-secondary .btn-outline .btn-danger .badge .badge-dot .badge-emerald …
|
|
182
|
+
|
|
183
|
+
Scripts
|
|
184
|
+
components.js · all 4 designs
|
|
185
|
+
|
|
186
|
+
Components (components/)
|
|
187
|
+
<stat-card label value trend [warn]> · 2 designs: 01-screens/01-dashboard.html, 02-ui-kit/01-overview.html
|
|
188
|
+
e.g. <stat-card label="Monthly Recurring Revenue" value="$184,290" trend="▲ +12.4% vs last mo"></stat-card> …
|
|
189
|
+
<tx-item icon name meta amount [positive]> · 2 designs: 01-screens/03-mobile.html, 02-ui-kit/01-overview.html
|
|
190
|
+
e.g. <tx-item icon="💼" name="Client Invoice #1042" meta="Yesterday • Wire Transfer" amount="+$3,450.00" positive></tx-item> …
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
It describes whatever the project has and asks for nothing:
|
|
194
|
+
- **Stylesheets** with any name, in any folder: the ones the designs link or `@import` (CDN ones by their URL), and the ones at the top of the folder that no design links yet.
|
|
195
|
+
- **Variables** under the selector that declares them, so a dark theme's values show too.
|
|
196
|
+
- **Components**: their attributes come from their markup (`{{name}}`), their script and their CSS (`&[warn]` is a flag, `&[size="lg"]` a value).
|
|
197
|
+
|
|
198
|
+
On the samples it is 2 KB instead of the 9 KB of `styles.css` and `components/`, and the gap grows with the project: a stylesheet's rules don't show, only what they define.
|
|
199
|
+
|
|
200
|
+
### Checking the designs: `htmlboard check`
|
|
201
|
+
|
|
202
|
+
An agent can't see the canvas, and a screenshot costs it many tokens, when it can take one at all. `htmlboard check` asks the canvas open in your browser to look instead: each design loads again, out of sight, in a frame of its size, and what went wrong comes back as text:
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
$ npx htmlboard check designs
|
|
206
|
+
Checked 6 designs in designs (the canvas at http://localhost:3000): 2 with problems, 1 with notes.
|
|
207
|
+
|
|
208
|
+
01-screens/02-customers.html · Customers
|
|
209
|
+
error TypeError: Cannot read properties of null (reading 'append') (01-screens/02-customers.html:88)
|
|
210
|
+
error <org-cel> isn't built: there is no components/org-cel.html
|
|
211
|
+
error missing file: img/acme.svg (<img>)
|
|
212
|
+
layout cut at the right edge (the frame is 1280 px wide): table.table reaches 1342
|
|
213
|
+
layout text cut off by p.plan (overflow: hidden): "Enterprise annual plan"
|
|
214
|
+
|
|
215
|
+
01-screens/03-mobile.html · Mobile
|
|
216
|
+
layout text spills out of .btn: "Confirm and continue"
|
|
217
|
+
|
|
218
|
+
02-print/01-flyer.html · Flyer
|
|
219
|
+
note content goes on below the frame, down to 1460 px (the frame is 1350 px tall)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- **What it finds:** script errors and `console.error`; files that aren't there (a stylesheet's fonts and images, a fetch's data too); components that weren't built; fonts that didn't load; a page that shows nothing; content cut at the frame's right edge; text cut off by a box that hides its overflow, or spilling out of its box. A note says when content goes on below a frame of fixed height: fine for a screen that scrolls, not for a flyer.
|
|
223
|
+
- **What it leaves alone:** what a design means to do, such as text that ends in an ellipsis or is clamped, boxes that scroll, and content hidden or moved off the page on purpose. It doesn't judge looks (colors, spacing, alignment).
|
|
224
|
+
- **It runs in your browser**, so the canvas must be open there. Each check loads the files as they are on disk, so it can run right after a save. Pages shown in frames get a small script first in their `<head>` (`/__explorer/probe.js`) to hear what goes wrong from the start; it only listens in the frames the check loads, and a design opened on its own doesn't get it.
|
|
225
|
+
- A file or a folder checks just those designs: `htmlboard check designs/01-screens/02-customers.html`. It finds the canvas on ports 3000 to 3020 (`-p` for another) and exits with 1 when there are problems, 2 when it can't check. `--json` gives the report as data.
|
|
226
|
+
|
|
153
227
|
---
|
|
154
228
|
|
|
155
229
|
## 📂 How Designs Are Found
|
|
156
230
|
|
|
157
|
-
Every `.html` file under the folder is a design (hidden folders, `node_modules`, `dist` and `
|
|
231
|
+
Every `.html` file under the folder is a design (hidden folders, `node_modules`, `dist`, `build` and the [components](#-components) in `components/` aside):
|
|
158
232
|
|
|
159
233
|
- **Sections are folders.** Each folder is a row on the canvas, named after the folder; the files at the top of the folder make a first row without a title.
|
|
160
234
|
- **Number prefixes set the order** of files and folders: `01-…`, `02-…`, `10-…` (up to three digits; `2` comes before `10`). They don't show in names or in links, so reordering breaks nothing.
|
|
@@ -180,6 +254,56 @@ Every `.html` file under the folder is a design (hidden folders, `node_modules`,
|
|
|
180
254
|
|
|
181
255
|
**Categories** drive the filters. Left out, they come from words in the path, then from the size when it is a screen's (a phone's width, a tablet's 3:4, a desktop's landscape); flyers, boards and other formats get none and show under **All**.
|
|
182
256
|
|
|
257
|
+
**Shared styles.** Designs link one stylesheet for what they share (tokens, base styles, classes like `.btn` and `.card`) and keep only their own CSS in their `<style>`, so they stay consistent and each new design is shorter to write. The samples use a `styles.css` at the top of the folder (`<link rel="stylesheet" href="../styles.css">`), and the agent skill tells LLMs to look it up (`htmlboard catalog`), use it and grow it. Editing it reloads every design that links it.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 🧩 Components
|
|
262
|
+
|
|
263
|
+
Designs repeat markup too: the same sidebar on every screen, a dozen rows of a list. Written as a component, it is one tag in each design, and one file to change. Every file in `components/` (at the top of the designs folder) is a custom element named after it, built by `components.js`, which designs load once next to their stylesheet:
|
|
264
|
+
|
|
265
|
+
```html
|
|
266
|
+
<link rel="stylesheet" href="../styles.css">
|
|
267
|
+
<script type="module" src="../components.js"></script>
|
|
268
|
+
…
|
|
269
|
+
<app-sidebar active="Customers"></app-sidebar>
|
|
270
|
+
<tx-item icon="☕" name="Blue Bottle Coffee" meta="Oct 26 • Food & Beverage" amount="-$6.75"></tx-item>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
A component is a piece of HTML, `components/tx-item.html`:
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<!-- <tx-item icon="💼" name="Client Invoice #1042" meta="Yesterday" amount="+$3,450.00" positive></tx-item> -->
|
|
277
|
+
<div class="icon">{{icon}}</div>
|
|
278
|
+
<div class="what">
|
|
279
|
+
<div class="name">{{name}}</div>
|
|
280
|
+
<div class="muted">{{meta}}</div>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="amount">{{amount}}</div>
|
|
283
|
+
|
|
284
|
+
<style>
|
|
285
|
+
& { display: flex; align-items: center; gap: 14px; }
|
|
286
|
+
.icon { width: 40px; height: 40px; border-radius: 12px; background: var(--surface-2); }
|
|
287
|
+
&[positive] .amount { color: var(--emerald); }
|
|
288
|
+
</style>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
| In the file | What it does |
|
|
292
|
+
| :--- | :--- |
|
|
293
|
+
| A comment first | An example of use: the component's documentation, for people and agents, and its preview on the canvas. |
|
|
294
|
+
| `{{name}}` | The element's `name="…"` attribute, as text. |
|
|
295
|
+
| `<slot>`, `<slot name="x">` | The element's content; its children with `slot="x"`. What a slot holds shows when nothing fills it. |
|
|
296
|
+
| `<style>` | CSS for this element only (`&` is the element, a block box by default), added once to the pages that use it. |
|
|
297
|
+
| `<script>` | Runs for each element once it is built, with `this` as the element: for the little that markup and CSS can't do, like marking the current link. |
|
|
298
|
+
|
|
299
|
+
- **They work with your stylesheet.** Components are built in the light DOM, with no shadow root in the way, so `styles.css` reaches inside: they use the same classes as the designs (`card`, `btn`, `muted`). A component's CSS comes after `styles.css` and before the design's own `<style>`, so a design can still adjust one element.
|
|
300
|
+
- **Variants are attributes styled in CSS** (`&[positive]`). There is no template logic to learn; a list is several elements.
|
|
301
|
+
- **Relative links start from the component's file**, so they work from every design.
|
|
302
|
+
- **`components.js` is a file in your project**, so designs work on any static server. `--init` adds it; `htmlboard components [dir]` adds it to a designs folder. After upgrading htmlboard, `htmlboard components [dir] --force` replaces an older copy (and any edits you made to it).
|
|
303
|
+
- **Saving a component reloads the designs that use it.** Component files aren't designs; `designs.json` can show them on the canvas (below).
|
|
304
|
+
- **`htmlboard catalog` lists them** with their attributes, the example each one starts with and the designs that use them.
|
|
305
|
+
- HTML has no self-closing custom elements: write `<tx-item …></tx-item>`, not `<tx-item />`. And a component can't be a table's row or cell itself (the browser moves it out of the `<table>`), but it works inside a cell: `<td><org-cell …></org-cell></td>`.
|
|
306
|
+
|
|
183
307
|
---
|
|
184
308
|
|
|
185
309
|
## 📋 `designs.json` (Optional)
|
|
@@ -204,6 +328,7 @@ A `designs.json` in the folder curates the canvas when folders aren't enough: se
|
|
|
204
328
|
- Sections come in their order, designs in theirs. An entry is a file name, or `{ "file", … }` with values that win over the file's (`title`, `description`, `width`, `height`, `category`, `tags`, `id`). A `file` may carry a query string to place a variant itself.
|
|
205
329
|
- A file's variants follow it unless designs.json lists them. `"folder"` brings a whole folder (in prefix order).
|
|
206
330
|
- **Designs it doesn't list still show**, after the ones it does: in the section that holds their folder, else in their folder's own section (the files at the top in "Unlisted"). `"unlisted": "hide"` shows only what it lists.
|
|
331
|
+
- **Components show on the canvas with `"components": "show"`**, each alone in its frame, from the example its file starts with, in a "components" section after the designs. They are 480 wide, unless the component's file says `<meta name="htmlboard" content="width=1040">`. A section can also list them (`"components/tx-item.html"`) or bring them all (`"folder": "components"`). A `designs.json` that only says `{ "components": "show" }` leaves the rest of the canvas as the files make it.
|
|
207
332
|
- `name` and `artboards` are synonyms of `title` and `designs`.
|
|
208
333
|
|
|
209
334
|
### Moving an existing `designs.json` into the files
|
|
@@ -277,6 +402,38 @@ installSkills({ dir: process.cwd(), agent: 'all' });
|
|
|
277
402
|
// → [{ skill: 'htmlboard', path: '…/.claude/skills/htmlboard', status: 'installed' }, …]
|
|
278
403
|
```
|
|
279
404
|
|
|
405
|
+
And so is the components installer:
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
import { installComponents } from 'htmlboard';
|
|
409
|
+
|
|
410
|
+
installComponents({ dir: './designs' });
|
|
411
|
+
// → { path: '…/designs/components.js', status: 'installed' }
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
And the catalog, as data or as the text `htmlboard catalog` prints:
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
import { buildCatalog, formatCatalog } from 'htmlboard';
|
|
418
|
+
|
|
419
|
+
const catalog = buildCatalog('./designs'); // { root } for links that start with "/", like --root
|
|
420
|
+
// → { designs: ['01-screens/01-dashboard.html', …],
|
|
421
|
+
// stylesheets: [{ path: 'styles.css', users: [{ design: '01-screens/01-dashboard.html' }, …], variables, classes, keyframes, fonts }, …],
|
|
422
|
+
// scripts: [{ path: 'components.js', users }],
|
|
423
|
+
// components: [{ tag: 'tx-item', attributes: [{ name: 'icon' }, …, { name: 'positive', flag: true }], slots, example, users }, …] }
|
|
424
|
+
console.log(formatCatalog(catalog));
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
And the check, which needs the canvas open in a browser (the server answers it at `GET /api/check`, `?only=<file or folder>` for some of the designs):
|
|
428
|
+
|
|
429
|
+
```javascript
|
|
430
|
+
import { runCheck, formatReport } from 'htmlboard';
|
|
431
|
+
|
|
432
|
+
const { canvas, report } = await runCheck('./designs'); // { port } when it isn't on 3000 to 3020
|
|
433
|
+
// → report: { dir, root, designs: [{ file, title, width, height, problems: [{ level: 'error', kind: 'missing', path: '/img/logo.png', tag: 'img' }, …] }, …] }
|
|
434
|
+
console.log(formatReport(report, { url: canvas.url }));
|
|
435
|
+
```
|
|
436
|
+
|
|
280
437
|
---
|
|
281
438
|
|
|
282
439
|
## 📂 Project Structure
|
|
@@ -289,17 +446,25 @@ htmlboard/
|
|
|
289
446
|
│ ├── index.js # Library entry point & API exports
|
|
290
447
|
│ ├── server.js # HTTP server, SSE live reload
|
|
291
448
|
│ ├── designs.js # Which designs a folder holds: files, their tags, designs.json
|
|
449
|
+
│ ├── components.js # Components: their previews, `htmlboard components`
|
|
450
|
+
│ ├── catalog.js # What the designs share: `htmlboard catalog`
|
|
451
|
+
│ ├── check.js # `htmlboard check`: asks the canvas, prints its report
|
|
292
452
|
│ ├── migrate.js # `htmlboard migrate`
|
|
293
453
|
│ ├── skills.js # `htmlboard skills` installer
|
|
294
454
|
│ ├── client/ # The canvas
|
|
295
455
|
│ │ ├── index.html # Explorer shell: list, canvas, toolbar, shortcuts
|
|
296
456
|
│ │ ├── explorer.css # Dark chrome, dot grid
|
|
297
457
|
│ │ ├── explorer.js # Layout, pan/zoom, search, live reload
|
|
458
|
+
│ │ ├── check.js # Checks the designs in frames out of sight, for `htmlboard check`
|
|
459
|
+
│ │ ├── probe.js # Notes script errors and failed loads in the frames the check loads
|
|
298
460
|
│ │ ├── favicon.svg, logo.svg, mark.svg
|
|
299
461
|
│ │ └── fonts/ # Inter and Bricolage Grotesque (OFL)
|
|
300
462
|
│ └── templates/ # The sample designs `--init` adds
|
|
301
|
-
│ ├──
|
|
302
|
-
│
|
|
463
|
+
│ ├── styles.css # Their shared tokens and classes
|
|
464
|
+
│ ├── components.js # Turns components/*.html into custom elements (copied into projects)
|
|
465
|
+
│ ├── components/ # Sidebar, top bar, stat card, transaction row
|
|
466
|
+
│ ├── 01-screens/ # Dashboard, customers, mobile feed
|
|
467
|
+
│ └── 02-ui-kit/
|
|
303
468
|
├── skills/
|
|
304
469
|
│ └── htmlboard/ # Agent skill (SKILL.md) copied by `htmlboard skills install`
|
|
305
470
|
├── brand/ # The logo's files (from design/tools/logo.py)
|
package/bin/htmlboard.js
CHANGED
|
@@ -7,6 +7,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
import { startServer } from '../src/server.js';
|
|
8
8
|
import { installSkills, uninstallSkills } from '../src/skills.js';
|
|
9
9
|
import { planMigration, checkMigration, applyMigration } from '../src/migrate.js';
|
|
10
|
+
import { installComponents, COMPONENTS_FOLDER, COMPONENTS_SCRIPT } from '../src/components.js';
|
|
11
|
+
import { buildCatalog, formatCatalog } from '../src/catalog.js';
|
|
12
|
+
import { runCheck, formatReport, hasProblems, CheckError } from '../src/check.js';
|
|
10
13
|
|
|
11
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
15
|
const __dirname = path.dirname(__filename);
|
|
@@ -76,6 +79,9 @@ Usage:
|
|
|
76
79
|
npx htmlboard [directory] [options]
|
|
77
80
|
htmlboard skills <install|uninstall> [options] Teach your AI agent to use HtmlBoard
|
|
78
81
|
htmlboard migrate [directory] [--write] Move designs.json's sizes, titles and variants into the files
|
|
82
|
+
htmlboard components [directory] [--force] Add components.js: files in components/ become custom elements
|
|
83
|
+
htmlboard catalog [directory] What the designs share: stylesheets, scripts, components
|
|
84
|
+
htmlboard check [directory or file] [-p <port>] What went wrong in the designs, from the canvas open in a browser
|
|
79
85
|
|
|
80
86
|
Options:
|
|
81
87
|
-p, --port <number> Port to listen on (default: 3000)
|
|
@@ -94,6 +100,9 @@ Examples:
|
|
|
94
100
|
htmlboard design --root . # Artboards in ./design that link to ../css/…
|
|
95
101
|
htmlboard --init # Add sample designs to the current folder
|
|
96
102
|
htmlboard skills install # Add the HtmlBoard skill to this project's agents
|
|
103
|
+
htmlboard components designs # Let ./designs use components (components/tx-item.html is <tx-item>)
|
|
104
|
+
htmlboard catalog designs # What ./designs share, in a few lines (for your AI agent)
|
|
105
|
+
htmlboard check designs # Script errors, missing files, cut-off text in ./designs
|
|
97
106
|
|
|
98
107
|
On the canvas:
|
|
99
108
|
Drag, scroll Move around
|
|
@@ -206,6 +215,134 @@ function runSkills(args) {
|
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
|
|
218
|
+
function runComponents(args) {
|
|
219
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
220
|
+
console.log(`
|
|
221
|
+
Usage:
|
|
222
|
+
htmlboard components [directory] [--force]
|
|
223
|
+
|
|
224
|
+
Adds ${COMPONENTS_SCRIPT} and a ${COMPONENTS_FOLDER}/ folder to the designs folder (default: the current one).
|
|
225
|
+
Each file in ${COMPONENTS_FOLDER}/ is then a custom element named after it (${COMPONENTS_FOLDER}/tx-item.html is
|
|
226
|
+
<tx-item>) in the designs that load ${COMPONENTS_SCRIPT}, once, beside their stylesheet:
|
|
227
|
+
|
|
228
|
+
<script type="module" src="../${COMPONENTS_SCRIPT}"></script>
|
|
229
|
+
|
|
230
|
+
A ${COMPONENTS_SCRIPT} that differs from this version's (an older one, or one you edited) is kept;
|
|
231
|
+
--force replaces it.
|
|
232
|
+
`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const unknown = args.find((arg) => arg.startsWith('-') && arg !== '--force' && arg !== '-f');
|
|
236
|
+
if (unknown) {
|
|
237
|
+
console.error(`[htmlboard] Unknown option: ${unknown} (see htmlboard components --help)`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
const dirArg = args.find((arg) => !arg.startsWith('-'));
|
|
241
|
+
const { path: dest, status } = installComponents({
|
|
242
|
+
dir: dirArg || process.cwd(),
|
|
243
|
+
force: args.includes('--force') || args.includes('-f')
|
|
244
|
+
});
|
|
245
|
+
const labels = {
|
|
246
|
+
installed: '\x1b[32m✓ added\x1b[0m',
|
|
247
|
+
updated: '\x1b[32m✓ updated\x1b[0m',
|
|
248
|
+
unchanged: '\x1b[90m= up to date\x1b[0m',
|
|
249
|
+
skipped: '\x1b[33m! differs from this version, kept (use --force to replace)\x1b[0m'
|
|
250
|
+
};
|
|
251
|
+
console.log(` ${labels[status]} ${path.relative(process.cwd(), dest) || dest}`);
|
|
252
|
+
if (status === 'installed') {
|
|
253
|
+
const folder = path.relative(process.cwd(), path.join(path.dirname(dest), COMPONENTS_FOLDER)) || COMPONENTS_FOLDER;
|
|
254
|
+
console.log(`\n Write components in ${folder}/ (tx-item.html is <tx-item>) and load ${COMPONENTS_SCRIPT} in the designs.`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// A folder as the user would type it: from the current folder, unless that means going up
|
|
259
|
+
function shownPath(dir) {
|
|
260
|
+
const rel = path.relative(process.cwd(), dir);
|
|
261
|
+
return !rel ? '.' : rel.startsWith('..') ? dir : rel;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function runCatalog(args) {
|
|
265
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
266
|
+
console.log(`
|
|
267
|
+
Usage:
|
|
268
|
+
htmlboard catalog [directory] [--root <path>]
|
|
269
|
+
|
|
270
|
+
Prints what the designs in the folder (default: the current one) share, read from the files: the
|
|
271
|
+
stylesheets and scripts they load, each local stylesheet's variables, classes, @keyframes and
|
|
272
|
+
fonts, and the components in ${COMPONENTS_FOLDER}/ with their attributes, slots, example and where
|
|
273
|
+
they are used. A few lines instead of every file, for the AI agent that writes the designs.
|
|
274
|
+
|
|
275
|
+
Options:
|
|
276
|
+
-r, --root <path> The folder the canvas serves (htmlboard … --root), for links that start
|
|
277
|
+
with "/" (default: the designs folder)
|
|
278
|
+
`);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
let root;
|
|
282
|
+
const dirs = [];
|
|
283
|
+
for (let i = 0; i < args.length; i++) {
|
|
284
|
+
const arg = args[i];
|
|
285
|
+
if (arg === '--root' || arg === '-r') root = args[++i];
|
|
286
|
+
else if (arg.startsWith('--root=')) root = arg.slice('--root='.length);
|
|
287
|
+
else if (arg.startsWith('-')) {
|
|
288
|
+
console.error(`[htmlboard] Unknown option: ${arg} (see htmlboard catalog --help)`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
} else dirs.push(arg);
|
|
291
|
+
}
|
|
292
|
+
let catalog;
|
|
293
|
+
try {
|
|
294
|
+
catalog = buildCatalog(dirs[0] || process.cwd(), { root });
|
|
295
|
+
} catch (err) {
|
|
296
|
+
console.error(`[htmlboard] ${err.message}`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
process.stdout.write(formatCatalog(catalog, { name: shownPath(catalog.dir) }));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function runCheckCommand(args) {
|
|
303
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
304
|
+
console.log(`
|
|
305
|
+
Usage:
|
|
306
|
+
htmlboard check [directory or file] [-p <port>] [--json]
|
|
307
|
+
|
|
308
|
+
Has the canvas running for the designs (open in a browser: the check runs there) load each one
|
|
309
|
+
afresh and prints what went wrong, in a few lines of text: script errors and console.error, files
|
|
310
|
+
that aren't there, components that weren't built, fonts that didn't load, content cut at the
|
|
311
|
+
frame's edge, text cut off or spilling out of its box. A directory checks the designs in it, a
|
|
312
|
+
file just that design (default: the current directory).
|
|
313
|
+
|
|
314
|
+
Exits with 1 when it finds problems, 2 when it can't check (no canvas running, or none open).
|
|
315
|
+
|
|
316
|
+
Options:
|
|
317
|
+
-p, --port <number> The canvas's port (default: found on 3000 to 3020)
|
|
318
|
+
--json The report as JSON
|
|
319
|
+
`);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
let port;
|
|
323
|
+
let asJson = false;
|
|
324
|
+
const targets = [];
|
|
325
|
+
for (let i = 0; i < args.length; i++) {
|
|
326
|
+
const arg = args[i];
|
|
327
|
+
if (arg === '--port' || arg === '-p') port = parseInt(args[++i], 10);
|
|
328
|
+
else if (arg.startsWith('--port=')) port = parseInt(arg.slice('--port='.length), 10);
|
|
329
|
+
else if (arg === '--json') asJson = true;
|
|
330
|
+
else if (arg.startsWith('-')) {
|
|
331
|
+
console.error(`[htmlboard] Unknown option: ${arg} (see htmlboard check --help)`);
|
|
332
|
+
process.exit(2);
|
|
333
|
+
} else targets.push(arg);
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const { canvas, report } = await runCheck(targets[0], { port });
|
|
337
|
+
if (asJson) console.log(JSON.stringify(report, null, 2));
|
|
338
|
+
else process.stdout.write(formatReport(report, { name: shownPath(report.dir), url: canvas.url }));
|
|
339
|
+
process.exitCode = hasProblems(report) ? 1 : 0;
|
|
340
|
+
} catch (err) {
|
|
341
|
+
console.error(err instanceof CheckError ? err.message : `[htmlboard] ${err.message}`);
|
|
342
|
+
process.exitCode = 2;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
209
346
|
function runMigrate(args) {
|
|
210
347
|
const write = args.includes('--write');
|
|
211
348
|
const dirArg = args.find((arg) => !arg.startsWith('-'));
|
|
@@ -266,11 +403,26 @@ async function main() {
|
|
|
266
403
|
return;
|
|
267
404
|
}
|
|
268
405
|
|
|
406
|
+
if (args[0] === 'components') {
|
|
407
|
+
runComponents(args.slice(1));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
269
411
|
if (args[0] === 'migrate') {
|
|
270
412
|
runMigrate(args.slice(1));
|
|
271
413
|
return;
|
|
272
414
|
}
|
|
273
415
|
|
|
416
|
+
if (args[0] === 'catalog') {
|
|
417
|
+
runCatalog(args.slice(1));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (args[0] === 'check') {
|
|
422
|
+
await runCheckCommand(args.slice(1));
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
274
426
|
const options = parseArgs(args);
|
|
275
427
|
|
|
276
428
|
if (options.help) {
|
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@ npx htmlboard designs --open
|
|
|
23
23
|
|
|
24
24
|
## Write an artboard
|
|
25
25
|
|
|
26
|
-
One design per file,
|
|
26
|
+
One design per file, with its facts in the `<head>`, linking the shared stylesheet and components (see below):
|
|
27
27
|
|
|
28
28
|
```html
|
|
29
29
|
<!DOCTYPE html>
|
|
@@ -33,9 +33,10 @@ One design per file, self-contained so it renders on its own, with its facts in
|
|
|
33
33
|
<title>Checkout · one page</title> <!-- its name on the canvas -->
|
|
34
34
|
<meta name="description" content="Address and payment on one screen">
|
|
35
35
|
<meta name="htmlboard" content="width=390, height=844"> <!-- its size -->
|
|
36
|
+
<link rel="stylesheet" href="../styles.css"> <!-- shared tokens and classes -->
|
|
37
|
+
<script type="module" src="../components.js"></script> <!-- shared components -->
|
|
36
38
|
<style>
|
|
37
|
-
|
|
38
|
-
.screen { width: 390px; height: 844px; /* … */ }
|
|
39
|
+
.screen { height: 844px; /* only what this design needs */ }
|
|
39
40
|
</style>
|
|
40
41
|
</head>
|
|
41
42
|
<body>
|
|
@@ -47,9 +48,20 @@ One design per file, self-contained so it renders on its own, with its facts in
|
|
|
47
48
|
- Always give the `width`: it is the frame's viewport, and the page's CSS needs it. Give the `height` for fixed sizes (a phone screen, a flyer: the frame cuts at it); leave it out for pages that should grow with their content.
|
|
48
49
|
- Design at the real size of the target: phone 390×844, tablet 768×1024, desktop 1280×800 (or 1440×900), and the exact size for print or social formats (e.g. 1080×1350).
|
|
49
50
|
- Use real content in the user's language, not lorem ipsum.
|
|
50
|
-
- Relative links to
|
|
51
|
+
- Relative links to CSS, images, scripts and fonts work (`assets/logo.svg`; `../css/theme.css` with `--root`).
|
|
51
52
|
- No build step: plain HTML, CSS and browser JavaScript (ES modules and CDN scripts work).
|
|
52
53
|
|
|
54
|
+
## Shared styles
|
|
55
|
+
|
|
56
|
+
Put what the designs share in one stylesheet instead of repeating it in every file: it keeps the designs consistent and spares writing the same CSS again.
|
|
57
|
+
|
|
58
|
+
- The designs folder has a `styles.css` at its top (sample projects start with one). Before writing a design, see what the designs already share: `npx htmlboard catalog <designs folder>` prints it in a few lines, read from the files (each stylesheet's variables and classes, the scripts, the components with their attributes and an example). Use what fits (`var(--accent)`, `.btn`, `<tx-item>`) and add what's missing; open a file only for what the list doesn't say.
|
|
59
|
+
- If there is none and the project already has a stylesheet or design tokens, link those. Otherwise create `styles.css` with your first design: colors, type and spacing as custom properties, the base styles, and the components you'll repeat.
|
|
60
|
+
- Link it with a relative path: `styles.css` from the top of the folder, `../styles.css` from a section folder.
|
|
61
|
+
- A design's own `<style>` holds only what is specific to it. When a second design needs the same thing, move it to `styles.css`.
|
|
62
|
+
- Split it when it grows (`forms.css`, `charts.css`) and link the extra files where they are used.
|
|
63
|
+
- Editing `styles.css` restyles every design that links it, which is the point for a product's screens. For one-off explorations that should keep their look (a different style, logo options), style them in their own file instead.
|
|
64
|
+
|
|
53
65
|
More tags, all optional:
|
|
54
66
|
|
|
55
67
|
- `category=…` in the `htmlboard` tag files the design under the explorer's filters: `mobile`, `tablet`, `desktop`, `components` or your own word. Usually not needed: it comes from words in the path, then from the size when it is a screen's.
|
|
@@ -57,6 +69,38 @@ More tags, all optional:
|
|
|
57
69
|
- `<meta name="htmlboard:label" content="light">` adds a label to the name of the file's own frame ("Checkout · light"), to tell it from its variants.
|
|
58
70
|
- `<meta name="htmlboard" content="skip">` leaves a file out (partials, templates, a page that isn't a design).
|
|
59
71
|
|
|
72
|
+
## Components
|
|
73
|
+
|
|
74
|
+
Markup that designs repeat (a sidebar, a tab bar, a list row, a card with content) goes in a component, so a design writes one tag instead of the block: `<tx-item icon="☕" name="Blue Bottle" amount="-$6.75"></tx-item>`.
|
|
75
|
+
|
|
76
|
+
- `components/tx-item.html`, at the top of the designs folder, is `<tx-item>`: the file name is the tag, with a hyphen and no number prefix. Designs load `components.js` once (above). If the folder has no `components.js`, run `npx htmlboard components <designs folder>`.
|
|
77
|
+
- The catalog (above) lists them, with their attributes, an example and the designs that use them. Use them; don't copy their markup into designs.
|
|
78
|
+
- Make one when a second design repeats a block (or a design repeats it three times). Not for what a `styles.css` class already does (`<button class="btn btn-primary">` is short), nor in one-off explorations.
|
|
79
|
+
|
|
80
|
+
A component file is a piece of HTML (no `<!DOCTYPE>`, `<html>`, `<head>` or `<body>`):
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<!-- <tx-item icon="💼" name="Invoice #1042" meta="Yesterday" amount="+$3,450.00" positive></tx-item> -->
|
|
84
|
+
<div class="icon">{{icon}}</div>
|
|
85
|
+
<div class="what"><b>{{name}}</b> <span class="muted">{{meta}}</span></div>
|
|
86
|
+
<div class="amount">{{amount}}</div>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
& { display: flex; align-items: center; gap: 14px; } /* & is the element itself */
|
|
90
|
+
.icon { width: 40px; height: 40px; border-radius: 12px; background: var(--surface-2); }
|
|
91
|
+
&[positive] .amount { color: var(--emerald); }
|
|
92
|
+
</style>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- First line: a comment with an example of use, with real values, every attribute and its variants side by side. The catalog shows it, and it is the component's preview on the canvas.
|
|
96
|
+
- `{{name}}` is the element's `name="…"` attribute, as text. Content goes in slots: `<slot></slot>` takes the element's children, `<slot name="actions"></slot>` its children with `slot="actions"`, and what a slot holds shows when nothing fills it.
|
|
97
|
+
- No logic in the markup: variants are attributes styled in CSS (`&[positive]`), and a list is several elements.
|
|
98
|
+
- Its `<style>` only reaches inside the element, which is a block box unless `&` says otherwise. `styles.css` applies inside too: build with its classes (`card`, `btn`, `muted`) and name the component's own parts with names it doesn't use. A design's own `<style>` comes after and can restyle one element, but its selectors reach inside components too, so keep them specific.
|
|
99
|
+
- `<script>` runs for each element once it is built, with `this` as the element. Use it sparingly, a few lines for what markup and CSS can't do, like marking the current link (`<app-sidebar active="Customers">`) or a toggle. No fetching, data or frameworks.
|
|
100
|
+
- Relative links in a component start from its file (`../01-screens/02-customers.html`).
|
|
101
|
+
- Close the tag: `<tx-item …></tx-item>`, never `<tx-item />`. A component can't be a table's row or cell itself (the browser moves it out of the `<table>`), but it works inside a cell: `<td><org-cell …></org-cell></td>`. Where it sits (fixed, margins, the grid around it) belongs to the design.
|
|
102
|
+
- Editing a component changes every design that uses it, like `styles.css`.
|
|
103
|
+
|
|
60
104
|
## Group and order
|
|
61
105
|
|
|
62
106
|
- **A folder is a section** (a row on the canvas), named after the folder. Put related designs in a folder: `designs/03-checkout/`.
|
|
@@ -74,16 +118,17 @@ More tags, all optional:
|
|
|
74
118
|
}
|
|
75
119
|
```
|
|
76
120
|
|
|
77
|
-
An entry is a file name (its variants follow it), or `{ "file", "title", "width", "height", "category", "description" }` to override the file. `"folder"` brings a whole folder. `npx htmlboard migrate <folder>` moves an old designs.json's sizes, names and variants into the files (`--write` to apply; without it, it only shows the plan).
|
|
121
|
+
An entry is a file name (its variants follow it), or `{ "file", "title", "width", "height", "category", "description" }` to override the file. `"folder"` brings a whole folder. Components aren't on the canvas unless designs.json says `"components": "show"` (or lists `components/<tag>.html`): each shows alone, from its first-line example, 480 wide unless its file has `<meta name="htmlboard" content="width=…">`. `npx htmlboard migrate <folder>` moves an old designs.json's sizes, names and variants into the files (`--write` to apply; without it, it only shows the plan).
|
|
78
122
|
|
|
79
123
|
## Live reload
|
|
80
124
|
|
|
81
125
|
- Saving an artboard's HTML reloads just that frame; the user's view stays put. A new name or size in its `<head>` updates in place.
|
|
82
|
-
- Saving a file artboards use (a stylesheet, a script, an image, data) reloads the artboards that loaded it.
|
|
126
|
+
- Saving a file artboards use (a stylesheet, a component, a script, an image, data) reloads the artboards that loaded it.
|
|
83
127
|
- Adding, removing or moving artboards, or saving `designs.json`, rebuilds the canvas.
|
|
84
128
|
|
|
85
129
|
## Working with the user
|
|
86
130
|
|
|
87
131
|
- Put variants in separate files next to each other (`01-hero-a.html`, `02-hero-b.html`) instead of overwriting the current design, so they can be compared on the canvas. Keep the current one as the reference until the user picks.
|
|
88
132
|
- Say which files you created or changed and what to look at; the user is watching the canvas, not the code. Link straight to an artboard with `http://localhost:3000/#<id>`: the id is the file's path without prefixes and `.html`, other symbols turned into `-` (`03-checkout/02-steps.html` → `checkout-steps`; a variant adds its query: `checkout-steps-theme-dark`).
|
|
89
|
-
-
|
|
133
|
+
- Before reporting back, run `npx htmlboard check <designs folder>` (or a design's file). The canvas open in the user's browser loads each design again and lists, as text, what went wrong: script errors, missing files and components, fonts that didn't load, content cut at the frame's edge, text cut off or spilling out of its box. Fix what it lists and run it again. A note that content goes on below a frame of fixed height is fine for a screen that scrolls, not for a flyer. The check runs in the browser: if it says the canvas isn't open, ask the user to open its URL.
|
|
134
|
+
- The check can't judge looks (colors, spacing, alignment). If you can take screenshots, also look at the design at its real size (`http://localhost:3000/<file>`).
|