openink 0.1.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +164 -0
- package/bin/openink.js +4 -0
- package/docs/ai-assistants.md +35 -0
- package/docs/architecture.md +50 -0
- package/docs/blocks.md +604 -0
- package/docs/extending.md +86 -0
- package/docs/getting-started.md +97 -0
- package/docs/spec.md +215 -0
- package/package.json +38 -0
- package/schema/spec.schema.json +2641 -0
- package/src/build.js +82 -0
- package/src/cli.js +133 -0
- package/src/dev.js +53 -0
- package/src/export.js +68 -0
- package/src/icons.js +47 -0
- package/src/index.js +14 -0
- package/src/render/blocks/actions.js +68 -0
- package/src/render/blocks/content.js +86 -0
- package/src/render/blocks/data.js +24 -0
- package/src/render/blocks/feedback.js +29 -0
- package/src/render/blocks/forms.js +86 -0
- package/src/render/blocks/index.js +42 -0
- package/src/render/blocks/layout.js +102 -0
- package/src/render/blocks/media.js +65 -0
- package/src/render/blocks/navigation.js +67 -0
- package/src/render/blocks/overlay.js +18 -0
- package/src/render/blocks/shared.js +20 -0
- package/src/render/blocks/text.js +49 -0
- package/src/render/context.js +63 -0
- package/src/render/page.js +76 -0
- package/src/runtime/chart.js +85 -0
- package/src/runtime/dom.js +21 -0
- package/src/runtime/i18n.js +24 -0
- package/src/runtime/icon.js +28 -0
- package/src/runtime/index.js +64 -0
- package/src/runtime/navigation.js +27 -0
- package/src/runtime/placeholder.js +88 -0
- package/src/spec/docs.js +65 -0
- package/src/spec/schema.js +110 -0
- package/src/spec/validate.js +219 -0
- package/src/styles/openink.css +250 -0
- package/src/styles/themes/blueprint.css +31 -0
- package/src/styles/themes/color.css +28 -0
- package/src/styles/themes/dark.css +31 -0
- package/src/styles/themes/pastel.css +26 -0
- package/src/themes.js +10 -0
- package/templates/starter/AGENTS.md +18 -0
- package/templates/starter/README.md +11 -0
- package/templates/starter/gitignore +3 -0
- package/templates/starter/spec.yaml +55 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
## 1. Create a project
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx openink init my-wireframes
|
|
7
|
+
cd my-wireframes
|
|
8
|
+
npx openink dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Open <http://localhost:3000>. Edit `spec.yaml` and the page reloads on save.
|
|
12
|
+
|
|
13
|
+
**Skipping `npx`.** `npx openink` downloads the tool on first use. To pin a version per project, or to type just `openink`, install it:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g openink # anywhere on your machine
|
|
17
|
+
# or, per project:
|
|
18
|
+
npm install --save-dev openink
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Once installed globally, every `npx openink <command>` in these docs can be written as `openink <command>`.
|
|
22
|
+
|
|
23
|
+
You now have:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
my-wireframes/
|
|
27
|
+
├── spec.yaml ← the whole prototype
|
|
28
|
+
├── AGENTS.md ← instructions for AI coding assistants (see docs/ai-assistants.md)
|
|
29
|
+
├── README.md
|
|
30
|
+
└── .gitignore
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 2. Describe screens
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
name: Shop
|
|
37
|
+
|
|
38
|
+
nav:
|
|
39
|
+
- { label: Home, go: home }
|
|
40
|
+
|
|
41
|
+
screens:
|
|
42
|
+
- id: home
|
|
43
|
+
title: Home
|
|
44
|
+
blocks:
|
|
45
|
+
- { type: h1, text: Welcome }
|
|
46
|
+
- type: grid
|
|
47
|
+
cols: 3
|
|
48
|
+
children:
|
|
49
|
+
- type: card
|
|
50
|
+
go: product # clicking the card opens the "product" screen
|
|
51
|
+
children:
|
|
52
|
+
- { type: image, h: 120, label: Photo }
|
|
53
|
+
- { type: h3, text: Blue shirt }
|
|
54
|
+
- { type: text, muted: true, text: CHF 39 }
|
|
55
|
+
|
|
56
|
+
- id: product
|
|
57
|
+
title: Product page
|
|
58
|
+
blocks:
|
|
59
|
+
- { type: h1, text: Blue shirt }
|
|
60
|
+
- { type: button, label: Add to cart, primary: true, toast: Added }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- A **screen** is one page or state. `id` is what `go:` links point to.
|
|
64
|
+
- A **block** is `{ type: <name>, ...props }`. Blocks such as `grid`, `card` and `row` hold other blocks in `children:`.
|
|
65
|
+
- `go: <screen id>` on a button, card, table row or nav item navigates. `toast: "text"` shows a message.
|
|
66
|
+
|
|
67
|
+
Every block and its props: [blocks.md](blocks.md), or run `npx openink blocks`.
|
|
68
|
+
|
|
69
|
+
## 3. Check and share
|
|
70
|
+
|
|
71
|
+
| Goal | Command |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Catch mistakes (typos, dead links, unreachable screens) | `npx openink validate` |
|
|
74
|
+
| Static site for hosting | `npx openink build` → `dist/` |
|
|
75
|
+
| PDF, one screen per page | `npx openink pdf` → `dist/<name>.pdf` |
|
|
76
|
+
| One PNG per screen | `npx openink png` → `dist/png/` |
|
|
77
|
+
|
|
78
|
+
`dist/index.html` works when double-clicked and offline. To host it, upload `dist/` anywhere that serves static files (Netlify, Vercel, GitHub Pages, S3).
|
|
79
|
+
|
|
80
|
+
PDF and PNG export need Chrome, Chromium or Edge installed. Set `CHROME_PATH` if it is not found.
|
|
81
|
+
|
|
82
|
+
## Editor autocomplete
|
|
83
|
+
|
|
84
|
+
The first line of the starter spec points at the JSON Schema:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
# yaml-language-server: $schema=https://unpkg.com/openink/schema/spec.schema.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
With the YAML extension in VS Code (or any editor using yaml-language-server) you get completion for block types and props, hover docs, and inline errors.
|
|
91
|
+
|
|
92
|
+
## Next
|
|
93
|
+
|
|
94
|
+
- [Spec reference](spec.md): top-level fields, navigation, languages, theming, assets
|
|
95
|
+
- [Block reference](blocks.md)
|
|
96
|
+
- [Working with AI assistants](ai-assistants.md)
|
|
97
|
+
- [Extending Open Ink](extending.md)
|
package/docs/spec.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Spec reference
|
|
2
|
+
|
|
3
|
+
`spec.yaml` (or `spec.yml` / `spec.json`) sits at the root of a project.
|
|
4
|
+
|
|
5
|
+
## Top-level fields
|
|
6
|
+
|
|
7
|
+
| Field | Type | Description |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `name` * | string | Shown in the header and browser tab. Also names the PDF. |
|
|
10
|
+
| `screens` * | list | The screens. The first one is the start screen. |
|
|
11
|
+
| `nav` | list or object | Header buttons. See [Navigation](#navigation). |
|
|
12
|
+
| `modals` | list | Dialogs any screen can open. See [Modals](#modals). |
|
|
13
|
+
| `languages` | list | Language codes, e.g. `[en, de]`. The first is the default. See [Languages](#languages). |
|
|
14
|
+
| `theme` | string | A colour theme (`sketch`, `color`, `pastel`, `blueprint`, `dark`) or the path of your own `.css` file. See [Colour](#colour). |
|
|
15
|
+
| `colors` | object | Override single colours of the theme. See [Colour](#colour). |
|
|
16
|
+
| `footer` | string | Footer text (default: "Wireframe · not the final design"). |
|
|
17
|
+
| `x-*` | anything | Free-form; a place for YAML anchors. See [Reusing blocks](#reusing-blocks-yaml-anchors). |
|
|
18
|
+
|
|
19
|
+
\* required
|
|
20
|
+
|
|
21
|
+
## Screens
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
screens:
|
|
25
|
+
- id: dashboard # required, unique: letters, digits, "-" and "_", starting with a letter
|
|
26
|
+
title: Dashboard # shown in the tab title and above the page in the PDF
|
|
27
|
+
nav: owner # which nav set to show (default: "default")
|
|
28
|
+
note: "Only the owner sees this." # yellow sticky note at the top of the screen
|
|
29
|
+
width: narrow # narrow (~520px, phone-like) | medium (~760px) | wide (default, full width)
|
|
30
|
+
tone: blue # optional: tint the whole screen (see Colour)
|
|
31
|
+
blocks: [ ... ]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use `note:` (and the `note` block) for things a sketch cannot show: rules, states, open questions.
|
|
35
|
+
|
|
36
|
+
## Colour
|
|
37
|
+
|
|
38
|
+
Colour is optional and works at three levels. Mix them freely: a plain-sketch project with a few coloured parts, or a fully coloured one.
|
|
39
|
+
|
|
40
|
+
### 1. The whole project: `theme:` and `colors:`
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
theme: color # sketch (default) | color | pastel | blueprint | dark
|
|
44
|
+
colors: # optional: override single tokens on top of the theme
|
|
45
|
+
accent: "#f97316"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Theme | Look |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `sketch` | Pencil grey on warm paper with one orange accent. The default. |
|
|
51
|
+
| `color` | Indigo and rose on warm white. Filled buttons, coloured charts. |
|
|
52
|
+
| `pastel` | Soft pinks and lilacs. Friendly. |
|
|
53
|
+
| `blueprint` | White lines on blue graph paper. |
|
|
54
|
+
| `dark` | Dark background, light lines, coral accent. |
|
|
55
|
+
|
|
56
|
+
Try them without editing the spec: `openink dev --theme dark`.
|
|
57
|
+
|
|
58
|
+
`colors:` accepts `ink` (text and lines), `paper` (page background), `muted` (secondary text and outlines), `line` (dashed dividers), `accent` (pins, sliders, active states, "wait" badges), `note` (sticky notes) and `card` (card and dialog background). Values are any CSS colour.
|
|
59
|
+
|
|
60
|
+
For full control, point `theme:` at your own file (any value ending in `.css`); it is copied next to the page and loaded after the base styles:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
theme: brand.css
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
/* brand.css */
|
|
68
|
+
:root { --ink: #1e3a8a; --accent: #dc2626; --gap: 1.25rem; }
|
|
69
|
+
body { font-family: "Comic Neue", cursive; }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Also available: `--wired-toggle-on-color`, `--wired-slider-knob-color`, `--wired-progress-color`, `--card-bg`, `--ok` (green of "on" badges) and the tone palette `--blue`, `--blue-bg`, … (see `src/styles/openink.css`).
|
|
73
|
+
|
|
74
|
+
### 2. One part: `tone:` and `fill:` on any block
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
- { type: card, tone: pink, title: New, children: [...] } # pink outline, text and drawings
|
|
78
|
+
- { type: button, label: Delete, icon: trash, tone: red }
|
|
79
|
+
- { type: text, text: Saved, tone: green, fill: true } # plus a tinted background
|
|
80
|
+
- { type: chart, kind: bar, tone: purple }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`tone` is one of `blue`, `green`, `yellow`, `red`, `purple`, `pink`, `orange`, `teal`, `gray`. It recolours the block and everything inside it, including hand-drawn placeholders, charts and icons. `fill: true` adds a tinted background (a neutral tint if there is no tone). Primary buttons inside a tone become solid.
|
|
84
|
+
|
|
85
|
+
### 3. One screen: `tone:` on a screen
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
- id: promo
|
|
89
|
+
tone: orange
|
|
90
|
+
blocks: [ ... ]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The screen gets a tinted background and its blocks take the colour.
|
|
94
|
+
|
|
95
|
+
## Modals
|
|
96
|
+
|
|
97
|
+
A modal is a dialog that opens over the screen. Open it with `open: <id>` on any clickable block (`button`, `card`, `avatar`, `link`, nav and tab-bar items). Close it with a `close: true` button, the X, the Escape key, or a click outside.
|
|
98
|
+
|
|
99
|
+
Define modals once in the top-level `modals:` list so every screen can open them:
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
modals:
|
|
103
|
+
- id: share
|
|
104
|
+
title: Share
|
|
105
|
+
width: narrow # narrow | medium (default) | wide
|
|
106
|
+
children:
|
|
107
|
+
- { type: search, placeholder: Search people }
|
|
108
|
+
- { type: button, label: Send, primary: true, close: true, toast: Sent }
|
|
109
|
+
|
|
110
|
+
screens:
|
|
111
|
+
- id: feed
|
|
112
|
+
blocks:
|
|
113
|
+
- { type: button, icon: send, open: share }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A `{ type: modal, id: ... }` block inside a screen works too, but only from that screen. `validate` reports `open:` targets that don't exist.
|
|
117
|
+
|
|
118
|
+
## Reusing blocks (YAML anchors)
|
|
119
|
+
|
|
120
|
+
Top-level keys starting with `x-` are ignored by openink, so you can define a block once there with an anchor (`&name`) and reuse it anywhere with an alias (`*name`):
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
x-post-actions: &post-actions
|
|
124
|
+
type: row
|
|
125
|
+
children:
|
|
126
|
+
- { type: button, icon: heart }
|
|
127
|
+
- { type: button, icon: send }
|
|
128
|
+
|
|
129
|
+
screens:
|
|
130
|
+
- id: feed
|
|
131
|
+
blocks:
|
|
132
|
+
- *post-actions
|
|
133
|
+
- *post-actions
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
See `examples/photo-sharing` for a full example.
|
|
137
|
+
|
|
138
|
+
## Navigation
|
|
139
|
+
|
|
140
|
+
A single list of header buttons (each with a `label`, an `icon`, or both):
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
nav:
|
|
144
|
+
- { icon: home, label: Home, go: home }
|
|
145
|
+
- { label: Pricing, go: pricing }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or several **named sets**. A screen picks one with `nav:`; screens that don't choose use `default`. This is how you show different headers to different roles (an empty list `[]` hides the header buttons):
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
nav:
|
|
152
|
+
default:
|
|
153
|
+
- { label: Home, go: home }
|
|
154
|
+
- { label: Log in, go: login }
|
|
155
|
+
admin:
|
|
156
|
+
- { label: Dashboard, go: dashboard }
|
|
157
|
+
- { label: Users, go: users }
|
|
158
|
+
|
|
159
|
+
screens:
|
|
160
|
+
- id: dashboard
|
|
161
|
+
nav: admin
|
|
162
|
+
blocks: [ ... ]
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
For a mobile bottom bar, use the `tabbar` block inside a `device` frame instead.
|
|
166
|
+
|
|
167
|
+
## Linking
|
|
168
|
+
|
|
169
|
+
`go: <screen id>` works on `button`, `card`, `avatar`, `link`, `nextbar`, nav and tab-bar items, and table rows (`rows: [{ cells: [...], go: user }]`). `validate` and `build` report links to screens that don't exist and warn about screens nothing links to.
|
|
170
|
+
|
|
171
|
+
## Languages
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
languages: [en, de]
|
|
175
|
+
|
|
176
|
+
screens:
|
|
177
|
+
- id: home
|
|
178
|
+
title: { en: Home, de: Startseite }
|
|
179
|
+
blocks:
|
|
180
|
+
- { type: h1, text: { en: Welcome, de: Willkommen } }
|
|
181
|
+
- { type: input, placeholder: { en: Your name, de: Ihr Name } }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- Any text value may be a string or a `{ code: text }` object.
|
|
185
|
+
- The header gets a language switcher; the choice is remembered in the browser.
|
|
186
|
+
- A missing translation falls back to the first language, and `validate` warns about it.
|
|
187
|
+
- Language codes are 2 or 3 letters. There is no built-in list, so `pt`, `ja`, `gsw` all work.
|
|
188
|
+
|
|
189
|
+
## Icons
|
|
190
|
+
|
|
191
|
+
`icon` blocks, `button.icon`, nav items and tab-bar items use a built-in set of hand-drawn icons (`home`, `search`, `heart`, `comment`, `send`, `bell`, `user`, `camera`, …). The full list is at the end of the [block reference](blocks.md#icons). An unknown name is an error with a "did you mean" hint.
|
|
192
|
+
|
|
193
|
+
## Quoting text with commas
|
|
194
|
+
|
|
195
|
+
Inside `{ ... }` a comma ends the value, so YAML reads `{ type: text, text: Hello, world }` as text `Hello` plus a stray key `world`. Wrap such text in quotes: `text: "Hello, world"`. `validate` warns when it sees this.
|
|
196
|
+
|
|
197
|
+
## Assets
|
|
198
|
+
|
|
199
|
+
Put images, logos or fonts in `assets/` next to the spec; the folder is copied to `dist/assets/`. Reference them from your theme CSS (`url(assets/logo.svg)`). Wireframes normally use the drawn placeholders instead of real images.
|
|
200
|
+
|
|
201
|
+
## Output
|
|
202
|
+
|
|
203
|
+
`openink build` writes:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
dist/
|
|
207
|
+
├── index.html all screens in one page; navigation is client-side (#screen-id)
|
|
208
|
+
├── openink.js runtime, with wired-elements and RoughJS bundled in
|
|
209
|
+
├── openink.css
|
|
210
|
+
├── theme-<name>.css (if `theme:` is a preset other than sketch)
|
|
211
|
+
├── <your>.css (if `theme:` is your own file)
|
|
212
|
+
└── assets/ (if the folder exists)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Screens are addressable: `dist/index.html#dashboard` opens the dashboard directly, which is handy for links in emails and chat.
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openink",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Describe screens in YAML, get a clickable hand-drawn wireframe prototype (static HTML, PDF, PNG). Built on wired-elements and RoughJS.",
|
|
5
|
+
"keywords": ["wireframe", "prototype", "sketch", "mockup", "hand-drawn", "wired-elements", "roughjs", "ux", "static-site-generator"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Mediusware",
|
|
8
|
+
"repository": { "type": "git", "url": "git+https://github.com/mediuswareltd/openink.git" },
|
|
9
|
+
"homepage": "https://github.com/mediuswareltd/openink#readme",
|
|
10
|
+
"bugs": { "url": "https://github.com/mediuswareltd/openink/issues" },
|
|
11
|
+
"publishConfig": { "access": "public" },
|
|
12
|
+
"type": "module",
|
|
13
|
+
"bin": { "openink": "bin/openink.js" },
|
|
14
|
+
"main": "./src/index.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/index.js",
|
|
17
|
+
"./schema.json": "./schema/spec.schema.json",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": ["bin", "src", "templates", "schema", "docs", "!docs/img", "LICENSE", "README.md", "CHANGELOG.md"],
|
|
21
|
+
"engines": { "node": ">=20" },
|
|
22
|
+
"scripts": {
|
|
23
|
+
"generate": "node scripts/generate.js",
|
|
24
|
+
"test": "node --test \"test/*.test.js\"",
|
|
25
|
+
"prepare": "node scripts/setup-hooks.js",
|
|
26
|
+
"prepublishOnly": "npm test",
|
|
27
|
+
"lint:commit": "node scripts/commit-lint.js",
|
|
28
|
+
"screenshots": "node scripts/screenshots.js",
|
|
29
|
+
"dev": "node bin/openink.js dev examples/rental-portal"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"esbuild": "^0.28.2",
|
|
33
|
+
"puppeteer-core": "^25.12.0",
|
|
34
|
+
"roughjs": "4.3.1",
|
|
35
|
+
"wired-elements": "3.0.0-rc.6",
|
|
36
|
+
"yaml": "^2.5.0"
|
|
37
|
+
}
|
|
38
|
+
}
|