@cs4alhaider/screenbook 0.1.0 → 0.2.1
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/AGENTS.md +147 -0
- package/CLAUDE.md +4 -184
- package/README.md +146 -163
- package/bridge.js +16 -2
- package/cli/postinstall.js +34 -0
- package/cli/screenbook.js +84 -34
- package/cli/templates.js +54 -1
- package/core/browser.js +22 -7
- package/core/project.js +28 -7
- package/core/serve.js +22 -7
- package/docs/README.md +25 -14
- package/docs/brand/logo-mark.png +0 -0
- package/docs/brand/logo-transparent.png +0 -0
- package/docs/brand/logo.png +0 -0
- package/docs/brand/readme-header.png +0 -0
- package/docs/contributing/architecture.md +153 -0
- package/{TESTING.md → docs/contributing/testing.md} +13 -8
- package/docs/{app-mode-guide.md → guides/app-mode.md} +17 -10
- package/docs/{authoring-guide.md → guides/authoring.md} +43 -17
- package/docs/guides/getting-started.md +84 -0
- package/docs/{studio-guide.md → guides/studio.md} +3 -2
- package/docs/{troubleshooting.md → guides/troubleshooting.md} +11 -8
- package/docs/{cli.md → reference/cli.md} +15 -10
- package/docs/{mcp-agents.md → reference/mcp-agents.md} +3 -3
- package/docs/shots/android-dark.png +0 -0
- package/docs/shots/android-light.png +0 -0
- package/docs/shots/app-mode-dark.png +0 -0
- package/docs/shots/app-mode-light.png +0 -0
- package/docs/shots/hero-studio-dark.png +0 -0
- package/docs/shots/hero-studio-light.png +0 -0
- package/docs/shots/review-mode-dark.png +0 -0
- package/docs/shots/review-mode-light.png +0 -0
- package/docs/shots/rtl-arabic-dark.png +0 -0
- package/docs/shots/rtl-arabic-light.png +0 -0
- package/docs/shots/theme-lab-dark.png +0 -0
- package/docs/shots/theme-lab-light.png +0 -0
- package/docs/shots/theming-noir-dark.png +0 -0
- package/docs/shots/theming-noir-light.png +0 -0
- package/docs/shots/web-dashboard-dark.png +0 -0
- package/docs/shots/web-dashboard-light.png +0 -0
- package/examples/qahwa/features/order/screens/010-menu.html +1 -1
- package/examples/qahwa/features/order/screens/020-drink.html +1 -1
- package/examples/qahwa/features/order/screens/030-cart.html +1 -1
- package/examples/qahwa/features/order/screens/040-status.html +1 -1
- package/examples/qahwa/features/order/screens/050-status-android.html +1 -1
- package/examples/qahwa/features/roastery/screens/010-orders.html +1 -1
- package/examples/qahwa/features/roastery/screens/020-menu-editor.html +1 -1
- package/examples/spa-demo/app/index.html +1 -1
- package/examples/tokens-lab/features/lab/screens/010-swatches.html +1 -1
- package/examples/tokens-lab/features/lab/screens/020-typography.html +1 -1
- package/mcp/server.js +4 -2
- package/package.json +7 -6
- package/shell/screenhost.js +4 -1
- package/shell/selftest.js +18 -2
- package/shell/shell.css +17 -9
- package/shell/store.js +2 -0
- package/skill/SKILL.md +1 -1
- package/skill/references/cli-mcp.md +1 -1
- package/skill/references/screen-contract.md +1 -1
- package/docs/getting-started.md +0 -88
- package/docs/shots/app-mode.png +0 -0
- package/docs/shots/hero-studio.png +0 -0
- package/docs/shots/rtl-arabic.png +0 -0
- package/docs/shots/theme-lab.png +0 -0
- package/docs/shots/theming-noir.png +0 -0
- package/docs/shots/web-dashboard.png +0 -0
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"updated": "2026-07-18"
|
|
17
17
|
}
|
|
18
18
|
</script>
|
|
19
|
-
<script src="../../../
|
|
19
|
+
<script src="../../../screenbook/bridge.js"></script>
|
|
20
20
|
<link rel="stylesheet" href="shared.css">
|
|
21
21
|
<style>
|
|
22
22
|
/* Material-ish accents for the Android take */
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
</div>
|
|
63
63
|
|
|
64
64
|
<!-- the ONE line that makes this a cooperative ScreenBook app -->
|
|
65
|
-
<script src="../
|
|
65
|
+
<script src="../screenbook/app-bridge.js"></script>
|
|
66
66
|
<script>
|
|
67
67
|
var VIEWS = [
|
|
68
68
|
{ id: 'inbox', title: 'Inbox', description: 'The notes list — tap one to open it.' },
|
package/mcp/server.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from '../core/project.js';
|
|
17
17
|
|
|
18
18
|
const ENGINE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
-
const VERSION = '0.1
|
|
19
|
+
const VERSION = '0.2.1';
|
|
20
20
|
|
|
21
21
|
const rootFlagIx = process.argv.indexOf('--root');
|
|
22
22
|
const rootArg = rootFlagIx > -1 ? process.argv[rootFlagIx + 1] : undefined;
|
|
@@ -32,7 +32,7 @@ const SCREEN_CONTRACT =
|
|
|
32
32
|
'A screen is one full standalone HTML document: <!DOCTYPE html>, a required ' +
|
|
33
33
|
'<script type="application/json" id="screen-meta"> block ({title, description, order, ' +
|
|
34
34
|
'device: ios|android|web|none, status?, tags?, group?, statusBar?: light|dark, updated?}), and one ' +
|
|
35
|
-
'<script src="../../../
|
|
35
|
+
'<script src="../../../screenbook/bridge.js"></script> include. Use CSS vars from the active theme ' +
|
|
36
36
|
'(--color-*, --font-*, --radius-*, --space-*, --safe-top, --safe-bottom), bind mock data with ' +
|
|
37
37
|
'Engine.ready(cb → cb(data)), navigate with data-go="<screen-id>" attributes, share state via ' +
|
|
38
38
|
'Engine.state.get/set/subscribe, notify with Engine.toast(msg). Screens must render sensibly ' +
|
|
@@ -332,6 +332,8 @@ async function handle(msg) {
|
|
|
332
332
|
const tool = TOOLS.find((t) => t.name === params?.name);
|
|
333
333
|
if (!tool) return replyError(id, -32602, `unknown tool: ${params?.name}`);
|
|
334
334
|
try {
|
|
335
|
+
/* `extra` is a side channel: a tool can set extra.imagePath and the
|
|
336
|
+
file is attached to the reply as inline MCP image content. */
|
|
335
337
|
const extra = {};
|
|
336
338
|
const result = await tool.run(params.arguments || {}, extra);
|
|
337
339
|
const content = [{ type: 'text', text: JSON.stringify(result, null, 2) }];
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cs4alhaider/screenbook",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"description": "ScreenBook — a
|
|
7
|
+
"description": "ScreenBook — a design workspace for AI agents to present to humans. HTML screens + JSON data in real device frames, with review mode, flows, themes, self-tests, a CLI and an MCP server. Fully offline, no build step, and any MCP agent can drive it.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"bin": {
|
|
10
10
|
"screenbook": "cli/screenbook.js"
|
|
@@ -35,13 +35,14 @@
|
|
|
35
35
|
"skill/",
|
|
36
36
|
"examples/",
|
|
37
37
|
"docs/",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
38
|
+
"AGENTS.md",
|
|
39
|
+
"CLAUDE.md"
|
|
40
40
|
],
|
|
41
|
-
"
|
|
41
|
+
"dependencies": {
|
|
42
42
|
"playwright-core": "^1.45.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
+
"postinstall": "node cli/postinstall.js",
|
|
45
46
|
"test": "node --test",
|
|
46
47
|
"serve": "node cli/screenbook.js serve ..",
|
|
47
48
|
"validate": "node cli/screenbook.js validate .."
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
"type": "git",
|
|
51
52
|
"url": "git+https://github.com/cs4alhaider/screenbook.git"
|
|
52
53
|
},
|
|
53
|
-
"homepage": "https://github.
|
|
54
|
+
"homepage": "https://cs4alhaider.github.io/screenbook/",
|
|
54
55
|
"bugs": {
|
|
55
56
|
"url": "https://github.com/cs4alhaider/screenbook/issues"
|
|
56
57
|
}
|
package/shell/screenhost.js
CHANGED
|
@@ -30,7 +30,7 @@ export function injectAppRuntime(iframe, featureId, driverFile, onFail) {
|
|
|
30
30
|
}
|
|
31
31
|
const host = doc.head || doc.documentElement;
|
|
32
32
|
const runtime = doc.createElement('script');
|
|
33
|
-
runtime.src = url('
|
|
33
|
+
runtime.src = url('screenbook/app-bridge.js');
|
|
34
34
|
runtime.onload = () => {
|
|
35
35
|
const driver = doc.createElement('script');
|
|
36
36
|
driver.src = url(`features/${featureId}/${driverFile}`);
|
|
@@ -218,6 +218,9 @@ export function showScreen({ bust = false } = {}) {
|
|
|
218
218
|
return;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/* Same feature + same device chrome → keep the mounted frame and only swap
|
|
222
|
+
the iframe's document below; rebuilding the hardware around the screen on
|
|
223
|
+
every navigation would flash and drop the fit scale. */
|
|
221
224
|
const device = effectiveDevice(entry);
|
|
222
225
|
const sameFrame = current &&
|
|
223
226
|
current.featureId === store.ui.feature &&
|
package/shell/selftest.js
CHANGED
|
@@ -36,6 +36,17 @@ function combos(project) {
|
|
|
36
36
|
return list;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/*
|
|
40
|
+
* Render ONE (screen × device × theme × mode × dir) combination in a hidden
|
|
41
|
+
* iframe and judge it. The choreography mirrors what the real shell does:
|
|
42
|
+
* 1. Mount the screen at true device dimensions, parked far off-screen.
|
|
43
|
+
* 2. The bridge inside sends sb:hello → answer with a full sb:init payload
|
|
44
|
+
* (theme, mode, dir, default-scenario data) exactly like screenhost would.
|
|
45
|
+
* 3. Collect every sb:console-error the screen reports along the way.
|
|
46
|
+
* 4. On sb:ready, wait SETTLE_MS for late errors, then judge: a meta block
|
|
47
|
+
* with a title + zero console errors = green.
|
|
48
|
+
* 5. A screen that never reaches ready fails via READY_TIMEOUT.
|
|
49
|
+
*/
|
|
39
50
|
function runCombo(project, combo) {
|
|
40
51
|
return new Promise((resolve) => {
|
|
41
52
|
const started = performance.now();
|
|
@@ -200,11 +211,12 @@ export async function runSelftest() {
|
|
|
200
211
|
overlay.hidden = false;
|
|
201
212
|
overlay.innerHTML =
|
|
202
213
|
`<div class="st-card">
|
|
203
|
-
<div class="st-head"><h2>
|
|
214
|
+
<div class="st-head"><h2>Self-test</h2><span class="st-summary" id="st-summary">0 / ${all.length}</span></div>
|
|
204
215
|
<div class="st-progress"><i id="st-bar"></i></div>
|
|
216
|
+
<div class="st-now" id="st-now">warming up…</div>
|
|
205
217
|
<div class="st-rows" id="st-rows"></div>
|
|
206
218
|
<div class="st-foot">
|
|
207
|
-
<span class="st-note">
|
|
219
|
+
<span class="st-note">every screen × theme × mode × direction · zero console errors</span>
|
|
208
220
|
<button class="btn-ghost" onclick="location.search=''">Close</button>
|
|
209
221
|
</div>
|
|
210
222
|
</div>`;
|
|
@@ -217,11 +229,13 @@ export async function runSelftest() {
|
|
|
217
229
|
const results = [];
|
|
218
230
|
let done = 0;
|
|
219
231
|
|
|
232
|
+
const nowLine = document.getElementById('st-now');
|
|
220
233
|
function report(r) {
|
|
221
234
|
results.push(r);
|
|
222
235
|
done++;
|
|
223
236
|
summary.textContent = `${done}${done <= all.length ? ` / ${all.length}` : ' checks'}`;
|
|
224
237
|
bar.style.width = `${Math.min(100, (done / Math.max(1, all.length)) * 100)}%`;
|
|
238
|
+
nowLine.textContent = `${r.ok ? '✓' : '✕'} ${r.feature}/${r.screen} · ${r.theme}·${r.mode}·${r.dir}${r.device && r.device !== 'app' ? '·' + r.device : ''}`;
|
|
225
239
|
if (!r.ok) {
|
|
226
240
|
const row = document.createElement('div');
|
|
227
241
|
row.className = 'st-row is-fail';
|
|
@@ -233,6 +247,7 @@ export async function runSelftest() {
|
|
|
233
247
|
}
|
|
234
248
|
}
|
|
235
249
|
|
|
250
|
+
/* N workers drain one shared queue — at most CONCURRENCY iframes live at once. */
|
|
236
251
|
const queue = all.slice();
|
|
237
252
|
await Promise.all(Array.from({ length: CONCURRENCY }, async () => {
|
|
238
253
|
while (queue.length) {
|
|
@@ -245,6 +260,7 @@ export async function runSelftest() {
|
|
|
245
260
|
for (const f of project.features.values()) {
|
|
246
261
|
if (f.type !== 'app') continue;
|
|
247
262
|
summary.textContent = `app: ${f.id}…`;
|
|
263
|
+
nowLine.textContent = `booting embedded app "${f.id}" — visiting every screen it reports…`;
|
|
248
264
|
await runAppFeature(f, report);
|
|
249
265
|
}
|
|
250
266
|
|
package/shell/shell.css
CHANGED
|
@@ -868,19 +868,18 @@ body.solo .canvas { padding: 0; }
|
|
|
868
868
|
|
|
869
869
|
/* ---------------------------------------------------------------- selftest */
|
|
870
870
|
|
|
871
|
+
/* Self-test runs in a floating card — the studio stays fully visible. */
|
|
871
872
|
.selftest-overlay {
|
|
872
873
|
position: fixed;
|
|
873
|
-
|
|
874
|
+
right: 18px;
|
|
875
|
+
bottom: 18px;
|
|
874
876
|
z-index: 400;
|
|
875
|
-
|
|
876
|
-
backdrop-filter: blur(6px);
|
|
877
|
-
display: grid;
|
|
878
|
-
place-items: center;
|
|
879
|
-
padding: 30px;
|
|
877
|
+
max-width: min(520px, calc(100vw - 36px));
|
|
880
878
|
}
|
|
881
879
|
.st-card {
|
|
882
|
-
width:
|
|
883
|
-
max-
|
|
880
|
+
width: 480px;
|
|
881
|
+
max-width: 100%;
|
|
882
|
+
max-height: min(70vh, 640px);
|
|
884
883
|
display: flex;
|
|
885
884
|
flex-direction: column;
|
|
886
885
|
background: var(--sb-panel-2);
|
|
@@ -915,7 +914,16 @@ body.solo .canvas { padding: 0; }
|
|
|
915
914
|
border-radius: 2px;
|
|
916
915
|
transition: width .15s ease;
|
|
917
916
|
}
|
|
918
|
-
.st-
|
|
917
|
+
.st-now {
|
|
918
|
+
padding: 8px 18px 2px;
|
|
919
|
+
font-size: 11px;
|
|
920
|
+
font-family: var(--sb-mono);
|
|
921
|
+
color: var(--sb-ink-2);
|
|
922
|
+
white-space: nowrap;
|
|
923
|
+
overflow: hidden;
|
|
924
|
+
text-overflow: ellipsis;
|
|
925
|
+
}
|
|
926
|
+
.st-rows { flex: 1; overflow-y: auto; padding: 8px 12px 12px; min-height: 60px; }
|
|
919
927
|
.st-row {
|
|
920
928
|
display: flex;
|
|
921
929
|
align-items: baseline;
|
package/shell/store.js
CHANGED
|
@@ -68,6 +68,8 @@ export function activeScenario(featureId = store.ui.feature) {
|
|
|
68
68
|
/* ---------------- hash routing ---------------- */
|
|
69
69
|
|
|
70
70
|
const HASH_KEYS = ['scenario', 'mode', 'theme', 'dir', 'lang', 'flow'];
|
|
71
|
+
/* Re-entrancy guard: applyHash() → set() → syncHash() must not rewrite the
|
|
72
|
+
URL with half-applied state while we're still parsing it. */
|
|
71
73
|
let applyingHash = false;
|
|
72
74
|
|
|
73
75
|
export function parseHash() {
|
package/skill/SKILL.md
CHANGED
|
@@ -13,7 +13,7 @@ read or modify `engine/` internals.
|
|
|
13
13
|
|
|
14
14
|
- One screen = one full HTML document in `features/<f>/screens/<id>.html`, with a required
|
|
15
15
|
`<script type="application/json" id="screen-meta">` block and one bridge include
|
|
16
|
-
(`../../../
|
|
16
|
+
(`../../../screenbook/bridge.js`). It works opened alone AND inside the shell.
|
|
17
17
|
- Mock data = `features/<f>/data/seed.json`; named **scenarios** overlay it
|
|
18
18
|
(`patch` deep-merge, `remove` dot-paths, `{{today±N}}` date tokens).
|
|
19
19
|
- Look = design tokens in `themes/<id>.json`, flattened to CSS vars (`--color-accent`,
|
|
@@ -37,7 +37,7 @@ scenario?, theme?, lang?, return_image?}`
|
|
|
37
37
|
Register with Claude Code:
|
|
38
38
|
```
|
|
39
39
|
claude mcp add screenbook -- npx -y @cs4alhaider/screenbook mcp --root .
|
|
40
|
-
# or from a checkout:
|
|
40
|
+
# or from a checkout: node <path-to-package>/cli/screenbook.js mcp --root . # rarely needed
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Serve endpoints (for tooling)
|
|
@@ -29,7 +29,7 @@ fallback and keeps directory listings readable. Prefer ids like `010-home`, `020
|
|
|
29
29
|
</script>
|
|
30
30
|
|
|
31
31
|
<!-- 2. REQUIRED bridge — the ONLY engine include, exact relative path -->
|
|
32
|
-
<script src="../../../
|
|
32
|
+
<script src="../../../screenbook/bridge.js"></script>
|
|
33
33
|
|
|
34
34
|
<!-- 3. Optional shared feature look/helpers (explicit opt-in per screen) -->
|
|
35
35
|
<link rel="stylesheet" href="shared.css">
|
package/docs/getting-started.md
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# Getting started
|
|
2
|
-
|
|
3
|
-
## Prerequisites
|
|
4
|
-
|
|
5
|
-
| You need | For | Notes |
|
|
6
|
-
|----------|-----|-------|
|
|
7
|
-
| **Node.js ≥ 18** | the CLI, the dev server, the MCP server | the only hard requirement — check with `node --version` |
|
|
8
|
-
| **A modern browser** | the studio itself | Chrome, Safari, Edge, Firefox — the viewer is plain ES modules, no build |
|
|
9
|
-
| **Google Chrome installed** | `validate` and `screenshot` (headless checks) | used through `playwright-core`; see below if you don't have Chrome |
|
|
10
|
-
| **Git** | recommended | ScreenBook writes plain files — git is your undo and your review tool |
|
|
11
|
-
|
|
12
|
-
Nothing else. No bundler, no framework, no global installs. The viewer has **zero**
|
|
13
|
-
dependencies; the CLI has exactly one *dev* dependency (`playwright-core`) used only for
|
|
14
|
-
headless validation and screenshots.
|
|
15
|
-
|
|
16
|
-
One-time setup for the headless checks:
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
cd engine
|
|
20
|
-
npm i # installs playwright-core (~3s, no browser download)
|
|
21
|
-
# no Chrome on this machine? use a bundled browser instead:
|
|
22
|
-
npm i -D playwright && npx playwright install chromium
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Install
|
|
26
|
-
|
|
27
|
-
ScreenBook lives INSIDE your project as a folder named `engine/`:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
# today (from the repo):
|
|
31
|
-
git clone https://github.com/cs4alhaider/screenbook my-prototypes/engine
|
|
32
|
-
|
|
33
|
-
# after npm publish:
|
|
34
|
-
# npm i @cs4alhaider/screenbook (then copy/link node_modules/@cs4alhaider/screenbook → engine/)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The folder name matters: screens reference `../../../engine/bridge.js` by convention.
|
|
38
|
-
|
|
39
|
-
## Your first project — five minutes
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
cd my-prototypes
|
|
43
|
-
node engine/cli/screenbook.js init . --name "My product"
|
|
44
|
-
node engine/cli/screenbook.js serve .
|
|
45
|
-
# → open http://127.0.0.1:4600
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
You now have:
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
app.config.json project name · feature order · devices · themes · languages
|
|
52
|
-
themes/default.json design tokens (light + dark) — edit live in the 🎨 Theme Lab
|
|
53
|
-
features/welcome/ one starter feature with one screen
|
|
54
|
-
feature.json · flows.json · data/seed.json · data/scenarios.json
|
|
55
|
-
screens/010-hello.html ← copy this file to make your second screen
|
|
56
|
-
review/comments.json review pins land here
|
|
57
|
-
CLAUDE.md the authoring contract (for you AND your AI agents)
|
|
58
|
-
.claude/skills/screenbook/ packaged skill so Claude knows the rules instantly
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Edit `features/welcome/screens/010-hello.html`, save — the browser reloads itself.
|
|
62
|
-
Copy it to `020-something.html`, change the meta block's `title`/`order`, save — it's in
|
|
63
|
-
the sidebar. That's the whole loop.
|
|
64
|
-
|
|
65
|
-
Already have a single-page prototype? Embed it whole instead of rewriting:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
node engine/cli/screenbook.js init . --app path/to/index.html
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
(see [App mode](app-mode-guide.md)).
|
|
72
|
-
|
|
73
|
-
## The definition of done
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
node engine/cli/screenbook.js validate .
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Green means: every screen renders in every theme × light/dark × LTR/RTL with **zero console
|
|
80
|
-
errors**, all metadata is present, every navigation target exists. Make it your habit —
|
|
81
|
-
it's the same gate AI agents are held to.
|
|
82
|
-
|
|
83
|
-
## Serving without the CLI
|
|
84
|
-
|
|
85
|
-
Any static server works once `engine/` is copied into the project
|
|
86
|
-
(`python3 -m http.server`, nginx, GitHub Pages…). You lose the live extras — auto-reload,
|
|
87
|
-
manifest regeneration, review-comment disk sync, Theme-Lab save — but the studio, screens,
|
|
88
|
-
scenarios and flows all work. `/engine/index.html` is the entry point.
|
package/docs/shots/app-mode.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/shots/theme-lab.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|