@cs4alhaider/screenbook 0.2.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/README.md +14 -9
- package/bridge.js +16 -2
- package/cli/postinstall.js +34 -0
- package/cli/screenbook.js +21 -10
- package/core/serve.js +1 -1
- 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} +42 -16
- package/docs/{getting-started.md → guides/getting-started.md} +7 -15
- 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} +7 -6
- 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/mcp/server.js +3 -1
- package/package.json +5 -5
- package/shell/screenhost.js +3 -0
- package/shell/selftest.js +12 -0
- package/shell/store.js +2 -0
- 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
- /package/docs/{mcp-agents.md → reference/mcp-agents.md} +0 -0
package/README.md
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://cs4alhaider.github.io/screenbook/">
|
|
3
|
+
<img src="docs/brand/readme-header.png" alt="ScreenBook — plain-file screens built by AI agents, reviewed by humans. Offline. Versioned. Owned by you." width="100%">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
|
-
**
|
|
7
|
+
**A design workspace for AI agents to present to humans — fully offline, agent-agnostic, and owned by you.**
|
|
4
8
|
|
|
5
|
-
ScreenBook turns a folder on your machine into
|
|
9
|
+
ScreenBook turns a folder on your machine into the design workspace your AI agents work in: every screen
|
|
6
10
|
of your product rendered inside real device frames — iPhone, Android, browser — with live mock
|
|
7
11
|
data, dark mode, right-to-left languages, guided demo walkthroughs, and a built-in design-review
|
|
8
12
|
workflow. AI agents build and revise the screens through a standard interface; you and your
|
|
9
13
|
stakeholders review, comment, and approve. When a screen is final, you hand that same file to
|
|
10
14
|
your agent and say: *now build the native version.*
|
|
11
15
|
|
|
12
|
-

|
|
16
|
+

|
|
13
17
|
|
|
14
18
|
```bash
|
|
15
19
|
npm install -g @cs4alhaider/screenbook
|
|
@@ -68,7 +72,7 @@ from *Regular morning* to *First visit* — the same screens now show the empty
|
|
|
68
72
|
Toggle dark mode. Switch to Arabic and watch the layout mirror right-to-left. Nothing is
|
|
69
73
|
staged; the "order ready by 08:44" timestamp is computed from *today* every time you demo.
|
|
70
74
|
|
|
71
|
-

|
|
75
|
+

|
|
72
76
|
|
|
73
77
|
**3. Your team comments on the pixels, not around them.** Turn on 📌 Review Mode and click
|
|
74
78
|
anywhere: each pin captures the screen, the data state, the theme, the mode, the direction,
|
|
@@ -91,7 +95,7 @@ The prototype you approved *is* the design document — no Figma-to-code guesswo
|
|
|
91
95
|
different in the mockup." The same works for Jetpack Compose, React Native, Flutter, or web
|
|
92
96
|
components.
|
|
93
97
|
|
|
94
|
-

|
|
95
99
|
|
|
96
100
|
## Two commands to your own studio
|
|
97
101
|
|
|
@@ -134,7 +138,7 @@ screenbook serve "$(npm root -g)/@cs4alhaider/screenbook/examples/tokens-lab" #
|
|
|
134
138
|
- **Theme Lab** — every design token visible and editable live against your screens; save
|
|
135
139
|
writes the JSON back to disk. Dark mode is a delta, not a fork.
|
|
136
140
|
|
|
137
|
-

|
|
141
|
+

|
|
138
142
|
- **Flows** — guided, captioned walkthroughs for stakeholder demos; every step is a shareable
|
|
139
143
|
URL that opens in the exact state (screen, data, theme, direction).
|
|
140
144
|
- **Review Mode** — pinned comments with full context, synced to a JSON file your agent reads
|
|
@@ -143,7 +147,7 @@ screenbook serve "$(npm root -g)/@cs4alhaider/screenbook/examples/tokens-lab" #
|
|
|
143
147
|
right-to-left is a first-class toggle, not an afterthought.
|
|
144
148
|
- **Embed existing prototypes** — already have a working single-page prototype? One command
|
|
145
149
|
mounts the *whole app* inside the studio without touching its files
|
|
146
|
-
(`screenbook app add legacy/index.html`) — see [App mode](docs/app-mode
|
|
150
|
+
(`screenbook app add legacy/index.html`) — see [App mode](docs/guides/app-mode.md).
|
|
147
151
|
- **A machine-verified quality gate** — `screenbook validate` renders every screen in every
|
|
148
152
|
theme × light/dark × LTR/RTL and fails on any console error. Green means done — for humans
|
|
149
153
|
and agents alike.
|
|
@@ -151,7 +155,8 @@ screenbook serve "$(npm root -g)/@cs4alhaider/screenbook/examples/tokens-lab" #
|
|
|
151
155
|
create screens, update data, restyle themes, take device-framed screenshots, run the gate.
|
|
152
156
|
|
|
153
157
|
Full guides: **[docs/](docs/README.md)** — getting started, the studio tour, the authoring
|
|
154
|
-
contract, CLI and agent references, troubleshooting
|
|
158
|
+
contract, CLI and agent references, troubleshooting — or browse them on the website:
|
|
159
|
+
**[cs4alhaider.github.io/screenbook](https://cs4alhaider.github.io/screenbook/)**.
|
|
155
160
|
|
|
156
161
|
## The shape of a project
|
|
157
162
|
|
package/bridge.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
(function () {
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
-
var VERSION = '0.2.
|
|
15
|
+
var VERSION = '0.2.1';
|
|
16
16
|
|
|
17
17
|
/* ------------------------------------------------------------------ *
|
|
18
18
|
* EngineKit — pure helpers shared by bridge (screens) and shell
|
|
@@ -139,7 +139,16 @@
|
|
|
139
139
|
none: { top: '0px', bottom: '0px' }
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
-
/*
|
|
142
|
+
/*
|
|
143
|
+
* Compact, stable-ish CSS path for review pins ("#cart > button.buy:nth-of-type(2)").
|
|
144
|
+
* Walks from the clicked element up towards <html>, one selector segment per hop:
|
|
145
|
+
* 1. An id ends the walk immediately — "#cart" is unique enough on its own.
|
|
146
|
+
* 2. Otherwise use the tag name plus at most two classes ("button.buy") so the
|
|
147
|
+
* segment stays readable even on utility-class-heavy markup.
|
|
148
|
+
* 3. If siblings share the tag, disambiguate with :nth-of-type(n).
|
|
149
|
+
* 4. Stop after 6 segments — deeper paths add noise, not identity; the pin also
|
|
150
|
+
* stores x/y percentages, so the path is a hint for humans/agents, not an address.
|
|
151
|
+
*/
|
|
143
152
|
function cssPath(el) {
|
|
144
153
|
var parts = [];
|
|
145
154
|
while (el && el.nodeType === 1 && el !== document.documentElement && parts.length < 6) {
|
|
@@ -317,6 +326,9 @@
|
|
|
317
326
|
});
|
|
318
327
|
}
|
|
319
328
|
|
|
329
|
+
/* Cross-screen sync: `storage` fires in every OTHER document on the origin
|
|
330
|
+
when one screen writes. e.key === null means localStorage.clear() — the
|
|
331
|
+
shell's ↺ reset — so every subscriber gets told its key is gone. */
|
|
320
332
|
window.addEventListener('storage', function (e) {
|
|
321
333
|
if (!e.key || e.key.indexOf(PREFIX) !== 0) {
|
|
322
334
|
if (e.key === null) Object.keys(stateEmitter).forEach(function (k) { emit(k, undefined); });
|
|
@@ -543,6 +555,8 @@
|
|
|
543
555
|
|
|
544
556
|
function standaloneInit() {
|
|
545
557
|
if (initDone) return;
|
|
558
|
+
/* Screens live at features/<feature>/screens/<id>.html — three levels below
|
|
559
|
+
the project root, so app.config.json and themes/ resolve from ../../.. */
|
|
546
560
|
var root = '../../..';
|
|
547
561
|
var config = null;
|
|
548
562
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* Printed by npm right after `npm install -g` — the one moment a new user is
|
|
3
|
+
guaranteed to be looking at the terminal and ready for instructions.
|
|
4
|
+
Must NEVER fail an install: everything is wrapped, exit code is always 0.
|
|
5
|
+
Skipped in CI (and via SCREENBOOK_SKIP_BANNER=1). */
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
if (!process.env.CI && !process.env.SCREENBOOK_SKIP_BANNER) {
|
|
9
|
+
const tty = !!process.stdout.isTTY;
|
|
10
|
+
const paint = (code) => (s) => (tty ? `\x1b[${code}m${s}\x1b[0m` : s);
|
|
11
|
+
const bold = paint('1');
|
|
12
|
+
const dim = paint('2');
|
|
13
|
+
const green = paint('32');
|
|
14
|
+
const accent = paint('36');
|
|
15
|
+
|
|
16
|
+
console.log(`
|
|
17
|
+
${bold('⧉ ScreenBook')} ${green('installed')} — a design workspace for AI agents, to present to humans.
|
|
18
|
+
|
|
19
|
+
${bold('Start in any project:')}
|
|
20
|
+
${accent('screenbook init')} ${dim('scaffolds .screenbook/ — brief.md, theme, starter screen, agent skill')}
|
|
21
|
+
${accent('screenbook serve')} ${dim('opens your studio at')} http://127.0.0.1:4600
|
|
22
|
+
|
|
23
|
+
${bold('Connect your AI agent')} ${dim('(Claude Code shown — any MCP client works):')}
|
|
24
|
+
claude mcp add screenbook -- npx -y @cs4alhaider/screenbook mcp --root .
|
|
25
|
+
|
|
26
|
+
${bold('Or explore a finished demo first:')}
|
|
27
|
+
screenbook serve "$(npm root -g)/@cs4alhaider/screenbook/examples/qahwa"
|
|
28
|
+
|
|
29
|
+
${dim('Docs & guides')} https://cs4alhaider.github.io/screenbook/
|
|
30
|
+
${dim('Bugs & ideas')} https://github.com/cs4alhaider/screenbook/issues
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
} catch { /* a banner must never break an install */ }
|
|
34
|
+
process.exit(0);
|
package/cli/screenbook.js
CHANGED
|
@@ -10,7 +10,7 @@ import { startServer } from '../core/serve.js';
|
|
|
10
10
|
import * as T from './templates.js';
|
|
11
11
|
|
|
12
12
|
const ENGINE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
13
|
-
const VERSION = '0.2.
|
|
13
|
+
const VERSION = '0.2.1';
|
|
14
14
|
|
|
15
15
|
/* ---------------- arg parsing ---------------- */
|
|
16
16
|
|
|
@@ -236,9 +236,13 @@ async function cmdApp() {
|
|
|
236
236
|
positional.splice(0, 2, positional[2]); // remaining positional = project dir
|
|
237
237
|
const root = await requireRoot();
|
|
238
238
|
|
|
239
|
-
/* Normalize src
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
/* Normalize src to something the server can actually mount. Relative paths
|
|
240
|
+
are taken relative to the project directory. Three cases:
|
|
241
|
+
1. inside the ScreenBook root itself → keep it root-relative
|
|
242
|
+
2. inside the user's project (.screenbook) → "project/<path>" — served
|
|
243
|
+
by the /project mount that maps one level above the root
|
|
244
|
+
3. anywhere else → keep the raw argument and let addApp's existence
|
|
245
|
+
check produce the error message */
|
|
242
246
|
const isDotRoot = path.basename(root) === '.screenbook';
|
|
243
247
|
const projectDir = isDotRoot ? path.dirname(root) : root;
|
|
244
248
|
const absSrc = path.isAbsolute(srcArg) ? srcArg : path.resolve(projectDir, srcArg);
|
|
@@ -292,21 +296,28 @@ async function cmdScreenshot() {
|
|
|
292
296
|
}
|
|
293
297
|
|
|
294
298
|
function help() {
|
|
295
|
-
console.log(`${bold('screenbook')} ${VERSION} —
|
|
299
|
+
console.log(`${bold('⧉ screenbook')} ${VERSION} — a design workspace for AI agents, to present to humans
|
|
296
300
|
|
|
297
|
-
${bold('
|
|
301
|
+
${bold('Start here')}
|
|
302
|
+
${ok('screenbook init')} scaffold .screenbook/ in your project — write brief.md first
|
|
303
|
+
${ok('screenbook serve')} your studio → http://127.0.0.1:4600
|
|
304
|
+
claude mcp add screenbook -- npx -y @cs4alhaider/screenbook mcp --root .
|
|
305
|
+
${dim('connect your AI agent (any MCP client works)')}
|
|
306
|
+
|
|
307
|
+
${bold('Commands')}
|
|
308
|
+
${bold('init')} [dir] [--name X] [--force] scaffold a consumer project (+ agent contract + skill)
|
|
298
309
|
[--app <src>] also register an existing SPA in that folder as an embedded app
|
|
299
310
|
${bold('app add')} <src> [dir] [--id --title --icon --device --chrome self|engine --cooperative]
|
|
300
311
|
embed an existing single-page app (driver scaffolded unless --cooperative)
|
|
301
312
|
${bold('serve')} [dir] [--port 4600] static server · live reload · manifest refresh · review sync
|
|
302
313
|
${bold('manifest')} [dir] rescan screens → regenerate features/*/manifest.json
|
|
303
|
-
${bold('validate')} [dir] [--static] [--json] static checks + headless browser self-test
|
|
304
|
-
${bold('mcp')} [--root dir] start the stdio MCP server (for agents):
|
|
305
|
-
claude mcp add screenbook -- npx -y @cs4alhaider/screenbook mcp --root .
|
|
314
|
+
${bold('validate')} [dir] [--static] [--json] static checks + headless browser self-test — the done gate
|
|
306
315
|
${bold('screenshot')} [dir] --screen f/s PNG of a screen in its device frame
|
|
307
316
|
[--mode light,dark] [--dir ltr,rtl] [--scenario id] [--theme id] [--lang xx] [--out dir]
|
|
317
|
+
${bold('mcp')} [--root dir] stdio MCP server — 18 authoring tools for agents
|
|
308
318
|
|
|
309
|
-
|
|
319
|
+
${dim('Docs & guides')} https://cs4alhaider.github.io/screenbook/
|
|
320
|
+
${dim('Bugs & ideas')} https://github.com/cs4alhaider/screenbook/issues`);
|
|
310
321
|
}
|
|
311
322
|
|
|
312
323
|
const commands = {
|
package/core/serve.js
CHANGED
|
@@ -102,7 +102,7 @@ export function startServer({ root, engineDir, port = 4600, quiet = false }) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (p === '/__sb/health') {
|
|
105
|
-
send(res, 200, JSON.stringify({ screenbook: true, version: '0.2.
|
|
105
|
+
send(res, 200, JSON.stringify({ screenbook: true, version: '0.2.1', root: path.basename(root) }),
|
|
106
106
|
{ 'Content-Type': 'application/json' });
|
|
107
107
|
return;
|
|
108
108
|
}
|
package/docs/README.md
CHANGED
|
@@ -3,19 +3,30 @@
|
|
|
3
3
|
Start here. Each guide is short, self-contained, and ordered by how you'll actually meet
|
|
4
4
|
the tool.
|
|
5
5
|
|
|
6
|
+
## Guides — using ScreenBook
|
|
7
|
+
|
|
6
8
|
| Guide | Read it when |
|
|
7
9
|
|-------|--------------|
|
|
8
|
-
| [Getting started](getting-started.md) | First contact — prerequisites, install, your first project in five minutes |
|
|
9
|
-
| [The studio](studio
|
|
10
|
-
| [Authoring](authoring
|
|
11
|
-
| [
|
|
12
|
-
| [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
| [Getting started](guides/getting-started.md) | First contact — prerequisites, install, your first project in five minutes |
|
|
11
|
+
| [The studio](guides/studio.md) | A tour of every control in the shell: sidebar, dropdowns, flows, review, Theme Lab, self-test |
|
|
12
|
+
| [Authoring](guides/authoring.md) | You're building screens by hand — the full contract for screens, data, scenarios, themes, flows, strings |
|
|
13
|
+
| [App mode](guides/app-mode.md) | Embedding an EXISTING single-page app instead of (or alongside) file-per-screen features |
|
|
14
|
+
| [Troubleshooting](guides/troubleshooting.md) | Something's red, blank, or missing |
|
|
15
|
+
|
|
16
|
+
## Reference — looking things up
|
|
17
|
+
|
|
18
|
+
| Reference | Covers |
|
|
19
|
+
|-----------|--------|
|
|
20
|
+
| [CLI](reference/cli.md) | Every command and flag: `init` · `serve` · `manifest` · `validate` · `screenshot` · `app add` · `mcp` |
|
|
21
|
+
| [AI agents & MCP](reference/mcp-agents.md) | Hooking ScreenBook to Claude Code (or any MCP client) so agents author screens for you |
|
|
22
|
+
|
|
23
|
+
## Contributing — hacking on ScreenBook itself
|
|
24
|
+
|
|
25
|
+
| Document | Covers |
|
|
26
|
+
|----------|--------|
|
|
27
|
+
| [Architecture](contributing/architecture.md) | How the engine works: the shell, the bridge protocol, the core, CLI, MCP server, and the self-test |
|
|
28
|
+
| [Testing map](contributing/testing.md) | Every test layer, what it covers, how to run it, and where the gaps are |
|
|
29
|
+
|
|
30
|
+
One more reference lives at the repo root: **[AGENTS.md](../AGENTS.md)** — the authoring
|
|
31
|
+
contract written for AI agents (scaffolded into every project by `init`; humans are welcome
|
|
32
|
+
to read it too).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Architecture — how the engine works
|
|
2
|
+
|
|
3
|
+
One mental model: **two worlds meeting at plain files.**
|
|
4
|
+
|
|
5
|
+
- **The studio** runs in the browser — plain ES modules, zero dependencies, no build step.
|
|
6
|
+
It reads the project's JSON + HTML over HTTP and renders screens in iframes.
|
|
7
|
+
- **The tooling** runs in Node — the CLI and the MCP server, sharing one core. Exactly one
|
|
8
|
+
dependency (`playwright-core`), used only for headless validation and screenshots.
|
|
9
|
+
|
|
10
|
+
Nothing is generated, bundled, or transpiled. What's in the repo is what executes.
|
|
11
|
+
|
|
12
|
+
## Repo map
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
bridge.js the ONLY file a screen includes — Engine API + sb:* protocol + EngineKit
|
|
16
|
+
app-bridge.js runs inside an embedded SPA — AppBridge API + sba:* protocol
|
|
17
|
+
index.html the studio page (loads shell/main.js)
|
|
18
|
+
shell/ the studio, one module per concern (see below)
|
|
19
|
+
core/ Node core shared by CLI + MCP: fs, meta, project API, server, browser
|
|
20
|
+
cli/screenbook.js command dispatcher: init · serve · manifest · validate · screenshot · app · mcp
|
|
21
|
+
cli/templates.js everything `init` scaffolds into a fresh project
|
|
22
|
+
mcp/server.js stdio JSON-RPC MCP server — 18 tools over the same core
|
|
23
|
+
skill/ the agent skill `init` copies into consumer projects
|
|
24
|
+
examples/ runnable demo projects that double as test fixtures
|
|
25
|
+
test/ the engine's own suite (node:test) — see the testing map
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## The bridge (`bridge.js`)
|
|
29
|
+
|
|
30
|
+
A screen is a standalone HTML document whose only obligation is a meta block and one
|
|
31
|
+
script include: the bridge. It boots in one of two modes:
|
|
32
|
+
|
|
33
|
+
- **Embedded** — the screen is inside the studio's iframe. The bridge sends `sb:hello`,
|
|
34
|
+
the shell answers `sb:init` with everything the screen needs (theme, mode, dir, lang,
|
|
35
|
+
merged data, i18n, review pins). Live changes arrive as `sb:update` / `sb:review`.
|
|
36
|
+
- **Standalone** — the screen was opened directly. The bridge self-loads
|
|
37
|
+
`app.config.json`, the theme, seed + scenarios, and i18n from their conventional
|
|
38
|
+
relative paths, honoring `?scenario=…&mode=dark&dir=rtl&lang=…` — so a screen file is
|
|
39
|
+
demoable with zero infrastructure. If an `sb:hello` goes unanswered for 600ms (iframed
|
|
40
|
+
by something that isn't the shell), it falls back to standalone.
|
|
41
|
+
|
|
42
|
+
The top half of the file is **EngineKit** — the pure logic both worlds must agree on:
|
|
43
|
+
deep merge, scenario overlay (`remove` dot-paths, then `patch`), `{{today±N}}` date
|
|
44
|
+
tokens, token flattening (`color.ink-2` → `--color-ink-2`), dark-over-light theme
|
|
45
|
+
resolution, i18n lookup, and the CSS-path builder for review pins. The shell loads
|
|
46
|
+
`bridge.js` too (with `window.__SCREENBOOK_SHELL__` set) purely to reuse this kit — the
|
|
47
|
+
merge/flatten/date logic exists exactly once. The Node unit tests load the same file in a
|
|
48
|
+
VM, so the browser and the tests exercise identical code.
|
|
49
|
+
|
|
50
|
+
What screens see: `Engine.ready(cb)` · `Engine.data` (frozen) · `Engine.env` ·
|
|
51
|
+
`Engine.state` (localStorage-backed KV that syncs across screens via `storage` events) ·
|
|
52
|
+
`Engine.go` / `data-go` · `Engine.t` · `Engine.toast`.
|
|
53
|
+
|
|
54
|
+
## The shell (`shell/`)
|
|
55
|
+
|
|
56
|
+
A tiny unidirectional loop, no framework:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
store.js → set(patch) → subscribers get the set of changed keys → each module
|
|
60
|
+
re-renders only what its keys touch → syncHash() mirrors state to the URL
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Module | Owns |
|
|
64
|
+
|--------|------|
|
|
65
|
+
| `store.js` | UI state, selectors, hash deep links (`#feature/screen?scenario=…&mode=…`) |
|
|
66
|
+
| `net.js` | project loading (config, features, themes), serve detection, SSE, review persistence |
|
|
67
|
+
| `main.js` | boot, toolbar wiring, keyboard, live reload, the change→render map |
|
|
68
|
+
| `screenhost.js` | the mounted iframe: builds frames, speaks sb:*/sba:*, pushes env updates |
|
|
69
|
+
| `frames.js` | device chrome (iPhone island/status bar, Android punch-hole, browser bar) + scale-to-fit |
|
|
70
|
+
| `sidebar.js` | searchable, grouped screen list |
|
|
71
|
+
| `toolbar.js` | dropdown/toggle/action chip widgets |
|
|
72
|
+
| `flowplayer.js` | walkthrough playback; steps can pin scenarios; restores state on exit |
|
|
73
|
+
| `review.js` | pins, the comments panel, disk/localStorage persistence |
|
|
74
|
+
| `themelab.js` | live token editing; dark stays a delta; save writes `themes/<id>.json` |
|
|
75
|
+
| `selftest.js` | the in-browser render matrix (below) |
|
|
76
|
+
|
|
77
|
+
Two decisions carry most of the weight here. **Screens live in iframes** — full style and
|
|
78
|
+
error isolation, which is what lets the gate say "zero console errors" and mean it. And
|
|
79
|
+
**state changes are described, not performed** — `set({mode:'dark'})` doesn't touch the
|
|
80
|
+
DOM; every interested module reacts, which is why deep links, flows, and the self-test can
|
|
81
|
+
drive the studio through the same door the toolbar uses.
|
|
82
|
+
|
|
83
|
+
## App mode (`app-bridge.js`)
|
|
84
|
+
|
|
85
|
+
An embedded SPA mounts once and is *driven* over `sba:*` instead of being remounted per
|
|
86
|
+
screen. The adapter (`screens()`, `go()`, optional `current()`/`set()`/`scenarios()`)
|
|
87
|
+
gets registered in one of two ways: **cooperative** — the app includes `app-bridge.js`
|
|
88
|
+
itself; **driven** — the shell injects the runtime plus a project-owned `driver.js` into
|
|
89
|
+
the (same-origin) iframe after load, so the app's own files stay byte-untouched.
|
|
90
|
+
|
|
91
|
+
## The Node core (`core/`)
|
|
92
|
+
|
|
93
|
+
| Module | Owns |
|
|
94
|
+
|--------|------|
|
|
95
|
+
| `fsx.js` | atomic writes (temp + rename), change-detecting JSON writes — every CLI/MCP write goes through here |
|
|
96
|
+
| `meta.js` | the screen-meta block: parse, rewrite, `updated` stamping, the screen lint, `data-go` extraction |
|
|
97
|
+
| `project.js` | **the one Project API** both CLI and MCP import: root resolution, feature scan, derived manifests, screen CRUD with lint-on-write, data/theme/flow/comment accessors, static validation |
|
|
98
|
+
| `serve.js` | the dev server: project root + `/screenbook/*` package mount + `/project/*` parent mount, no-cache everywhere, SSE live reload, watch → debounced manifest regen, review/theme write-back APIs |
|
|
99
|
+
| `browser.js` | headless side: playwright→system-Chrome resolution ladder (auto-download as last resort), the headless self-test wrapper, framed screenshots via the shell's `?solo=1` |
|
|
100
|
+
|
|
101
|
+
The CLI and the MCP server are both **thin faces over `project.js`** — same validation,
|
|
102
|
+
same atomic writes, same manifest regeneration, whether a human or an agent is typing.
|
|
103
|
+
`manifest.json` is always derived from the screens' meta blocks; it exists so the studio
|
|
104
|
+
can boot on any dumb static server, but the CLI/MCP/serve regenerate it — never edit it.
|
|
105
|
+
|
|
106
|
+
## The gate (`selftest.js` + `validate`)
|
|
107
|
+
|
|
108
|
+
`?selftest=1` renders **every screen × device variant × theme × light/dark × LTR/RTL** in
|
|
109
|
+
hidden iframes (a small worker pool), replaying the exact shell handshake per combo, and
|
|
110
|
+
requires `Engine.ready` + a meta title + zero console errors. Embedded apps must register
|
|
111
|
+
within 20s, report ≥1 screen, and survive a visit to every screen they report.
|
|
112
|
+
`screenbook validate` = static checks from `project.js` first, then this same matrix run
|
|
113
|
+
headlessly, with results read off `window.__selftestResult`. Green is the definition of
|
|
114
|
+
done — for humans and agents alike.
|
|
115
|
+
|
|
116
|
+
## The protocols
|
|
117
|
+
|
|
118
|
+
| Message | Direction | Meaning |
|
|
119
|
+
|---------|-----------|---------|
|
|
120
|
+
| `sb:hello` | screen → shell | bridge booted; carries the parsed meta |
|
|
121
|
+
| `sb:init` | shell → screen | full environment: theme, mode, dir, lang, device, scenario, data, i18n, review |
|
|
122
|
+
| `sb:update` | shell → screen | live env change (theme/mode/dir/lang) — no reload |
|
|
123
|
+
| `sb:review` | shell → screen | review state + pins to render |
|
|
124
|
+
| `sb:ready` | screen → shell | `Engine.ready` callbacks ran; hide the veil |
|
|
125
|
+
| `sb:go` | screen → shell | `data-go`/`Engine.go` navigation request |
|
|
126
|
+
| `sb:console-error` | screen → shell | forwarded error (also feeds the self-test) |
|
|
127
|
+
| `sb:pin-request` / `sb:pin-open` | screen → shell | review-mode clicks |
|
|
128
|
+
| `sba:hello` / `sba:screens` / `sba:current` | app → shell | registration, screen list, navigation echo |
|
|
129
|
+
| `sba:go` / `sba:env` | shell → app | drive navigation / apply environment live |
|
|
130
|
+
|
|
131
|
+
## Rules that keep it simple
|
|
132
|
+
|
|
133
|
+
1. **No build, ever.** The studio must run from a folder on disk. Adding a bundler is a
|
|
134
|
+
rejected feature, not a missing one.
|
|
135
|
+
2. **The viewer has zero dependencies; the tooling has one.** Anything heavier belongs in
|
|
136
|
+
a consumer project, not here.
|
|
137
|
+
3. **Screens are standalone documents.** Every screen must work opened directly — the
|
|
138
|
+
bridge's standalone mode is a contract, not a fallback.
|
|
139
|
+
4. **Derived files are never hand-edited** (`manifest.json`), and **every write is
|
|
140
|
+
atomic** (`fsx.js`) — agents and humans share the same safety rails.
|
|
141
|
+
5. **One core, two faces.** New authoring capability goes into `core/project.js` first;
|
|
142
|
+
the CLI command and the MCP tool are wrappers.
|
|
143
|
+
|
|
144
|
+
## Adding things
|
|
145
|
+
|
|
146
|
+
- **A CLI command** → implement against `core/project.js`, register it in the `commands`
|
|
147
|
+
map in `cli/screenbook.js`, document it in [the CLI reference](../reference/cli.md).
|
|
148
|
+
- **An MCP tool** → same core call, plus an entry in `TOOLS` in `mcp/server.js` (schema +
|
|
149
|
+
description are the agent's documentation — write them like you'd brief a new hire).
|
|
150
|
+
- **A studio control** → a widget in `main.js#buildControls()`, state in `store.js`, and a
|
|
151
|
+
reaction in `onChange`; if it must survive reload, add its key to the hash sync.
|
|
152
|
+
- **Anything** → update [the testing map](testing.md) and keep `npm test` +
|
|
153
|
+
`validate` green on the examples.
|
|
@@ -2,21 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
> The "don't forget" file: every test layer, what it covers, how to run it, and where the
|
|
4
4
|
> gaps are. Update this whenever a layer is added or a gap closes. Definition of done for
|
|
5
|
-
> any engine change: **`npm test` green AND `validate` green on
|
|
5
|
+
> any engine change: **`npm test` green AND `validate` green on the bundled examples.**
|
|
6
6
|
|
|
7
7
|
## How to run everything
|
|
8
8
|
|
|
9
|
+
From a checkout of this repo:
|
|
10
|
+
|
|
9
11
|
```bash
|
|
10
|
-
cd engine
|
|
11
12
|
npm i # once — playwright-core (drives your installed Chrome)
|
|
12
|
-
npm test # unit + E2E suites (node:test,
|
|
13
|
+
npm test # unit + E2E suites (node:test, 64 tests, ~60s incl. browser)
|
|
13
14
|
|
|
14
|
-
# full browser matrix on
|
|
15
|
-
node cli/screenbook.js validate .. # Rahhal (repo root)
|
|
15
|
+
# full browser matrix on the projects that ship in this repo:
|
|
16
16
|
node cli/screenbook.js validate examples/qahwa
|
|
17
17
|
node cli/screenbook.js validate examples/tokens-lab
|
|
18
|
+
node cli/screenbook.js validate examples/spa-demo
|
|
18
19
|
```
|
|
19
20
|
|
|
21
|
+
Docs screenshots (`docs/shots/`, light + dark variants of every subject) are regenerated in
|
|
22
|
+
one shot with `node tools/capture-shots.mjs` — it serves scratch copies of the examples, so
|
|
23
|
+
nothing in the repo is mutated by the capture run.
|
|
24
|
+
|
|
20
25
|
## Layers
|
|
21
26
|
|
|
22
27
|
| Layer | File(s) | Covers |
|
|
@@ -36,7 +41,7 @@ node cli/screenbook.js validate examples/tokens-lab
|
|
|
36
41
|
|
|
37
42
|
| Project | Exercises |
|
|
38
43
|
|---------|-----------|
|
|
39
|
-
|
|
|
44
|
+
| the maintainer's host project (not shipped) | iOS frame, AR/RTL/i18n screens, scenarios with `remove`, flows, `statusBar` override, shared.css/js — plus two certified, sealed prototypes embedded as **driven** apps (31 + 11 screens, drivers injected, app files byte-untouched) |
|
|
40
45
|
| `examples/qahwa` | iOS + **Android** + **web** frames in one project, in-screen sheet (EN-24), `Engine.state` cart, feature groups, device filter |
|
|
41
46
|
| `examples/tokens-lab` | **Multi-theme** (Theme ▾ + theme axis in the matrix), **bare `none` frame**, live token re-skin, MutationObserver pattern |
|
|
42
47
|
| `examples/spa-demo` | **Cooperative app mode** — a one-page notes app including `app-bridge.js` itself; engine chrome on an app |
|
|
@@ -50,5 +55,5 @@ node cli/screenbook.js validate examples/tokens-lab
|
|
|
50
55
|
- RTL screenshot diffing / visual regression (screenshots exist; no baseline comparison).
|
|
51
56
|
- Windows: paths are POSIX-tested only; `fs.watch` recursive + rename semantics unverified.
|
|
52
57
|
- `file://` standalone mode has no automated check (manual only; fallback tokens by design).
|
|
53
|
-
- Driven-app injection path in the shell suite uses the cooperative mini-app only;
|
|
54
|
-
|
|
58
|
+
- Driven-app injection path in the shell suite uses the cooperative mini-app only; real
|
|
59
|
+
driven-app drivers are exercised in the maintainer's host project, not by `npm test`.
|
|
@@ -5,20 +5,26 @@ App mode puts the WHOLE app inside the studio: its views appear in the sidebar,
|
|
|
5
5
|
work, dark/RTL/scenario controls drive it — and for sealed apps, **not one byte of the
|
|
6
6
|
app's files changes**.
|
|
7
7
|
|
|
8
|
-

|
|
8
|
+

|
|
9
9
|
|
|
10
10
|
## Register it
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
|
|
13
|
+
screenbook app add legacy/index.html --device ios --chrome self
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
That writes an app-type feature:
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
{
|
|
20
|
-
"
|
|
21
|
-
"
|
|
18
|
+
```jsonc
|
|
19
|
+
{
|
|
20
|
+
"id": "legacy", // feature id — the folder name under features/
|
|
21
|
+
"title": "Legacy app", // shown in the Feature ▾ dropdown
|
|
22
|
+
"type": "app", // app-type feature: one embedded SPA instead of file screens
|
|
23
|
+
"src": "legacy/index.html", // project-relative entry point the studio mounts
|
|
24
|
+
"device": "ios", // bezel: ios · android · web · none
|
|
25
|
+
"chrome": "self", // the app draws its own status/home bars — bezel only
|
|
26
|
+
"driver": "driver.js" // injected adapter (omitted for --cooperative apps)
|
|
27
|
+
}
|
|
22
28
|
```
|
|
23
29
|
|
|
24
30
|
- `device` picks the bezel; `chrome: "self"` means the app draws its own status/home bars
|
|
@@ -72,8 +78,9 @@ reported screen, and **a clean visit to every reported screen — zero console e
|
|
|
72
78
|
Driver bugs surface as `driver <fn>: <message>` rows. `ignoreErrors` patterns are for
|
|
73
79
|
narrow, documented third-party noise only — everything else still fails.
|
|
74
80
|
|
|
75
|
-
## Reference
|
|
81
|
+
## Reference material
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
`
|
|
83
|
+
For the **driven** path, `app add` scaffolds a fully commented `driver.js` template — its
|
|
84
|
+
TODOs walk you through `screens()`, `go()` and `set()` against your app's real globals.
|
|
85
|
+
For the **cooperative** path, `examples/spa-demo` inside the package is a complete working
|
|
86
|
+
app to copy from.
|
|
@@ -30,8 +30,14 @@ features/trip-planning/
|
|
|
30
30
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
31
31
|
<title>Home</title>
|
|
32
32
|
<script type="application/json" id="screen-meta">
|
|
33
|
-
{
|
|
34
|
-
"
|
|
33
|
+
{
|
|
34
|
+
"title": "Home",
|
|
35
|
+
"description": "Entry point.",
|
|
36
|
+
"order": 10,
|
|
37
|
+
"device": "ios",
|
|
38
|
+
"status": "wip",
|
|
39
|
+
"tags": ["core"]
|
|
40
|
+
}
|
|
35
41
|
</script>
|
|
36
42
|
<script src="../../../screenbook/bridge.js"></script>
|
|
37
43
|
<link rel="stylesheet" href="shared.css">
|
|
@@ -80,11 +86,19 @@ Rules that keep everything sane:
|
|
|
80
86
|
|
|
81
87
|
`seed.json` is the demo-complete state. `scenarios.json` layers named states over it:
|
|
82
88
|
|
|
83
|
-
```
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
```jsonc
|
|
90
|
+
{
|
|
91
|
+
"list": [
|
|
92
|
+
{ "id": "default", "icon": "🧪", "title": "Full demo" }, // baseline — the seed, untouched
|
|
93
|
+
{
|
|
94
|
+
"id": "new-user",
|
|
95
|
+
"icon": "🆕",
|
|
96
|
+
"title": "Brand-new user",
|
|
97
|
+
"remove": ["trips", "user.points"], // 1. delete these dot-paths from the seed…
|
|
98
|
+
"patch": { "user": { "isNew": true } } // 2. …then deep-merge this over what's left
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
88
102
|
```
|
|
89
103
|
|
|
90
104
|
`remove` deletes dot-paths, then `patch` deep-merges (arrays replace whole). Date tokens —
|
|
@@ -101,9 +115,11 @@ delta over light. Edit visually in the 🎨 Theme Lab and Save. Register extra t
|
|
|
101
115
|
|
|
102
116
|
`i18n.json`, key-first — every key shows all its languages side by side:
|
|
103
117
|
|
|
104
|
-
```
|
|
105
|
-
{
|
|
106
|
-
"home.
|
|
118
|
+
```jsonc
|
|
119
|
+
{
|
|
120
|
+
"home.title": { "en": "Trips", "ar": "الرحلات" },
|
|
121
|
+
"home.greet": { "en": "{name}, welcome back", "ar": "يا {name}، أهلاً بعودتك" } // {name} fills from Engine.t vars
|
|
122
|
+
}
|
|
107
123
|
```
|
|
108
124
|
|
|
109
125
|
`Engine.t('home.greet', {name})` — fallback: active language → `defaultLanguage` → the key.
|
|
@@ -111,10 +127,20 @@ Language switches reload the screen. Gaps show up as `validate` warnings, never
|
|
|
111
127
|
|
|
112
128
|
## Flows
|
|
113
129
|
|
|
114
|
-
```
|
|
115
|
-
{
|
|
116
|
-
"
|
|
117
|
-
|
|
130
|
+
```jsonc
|
|
131
|
+
{
|
|
132
|
+
"list": [
|
|
133
|
+
{
|
|
134
|
+
"id": "pitch",
|
|
135
|
+
"title": "The pitch",
|
|
136
|
+
"icon": "✨",
|
|
137
|
+
"steps": [
|
|
138
|
+
{ "screen": "010-home", "note": "Start empty.", "scenario": "new-user" }, // a step may pin a scenario
|
|
139
|
+
{ "screen": "020-detail", "note": "One tap deeper." } // …the note is presenter narration
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
118
144
|
```
|
|
119
145
|
|
|
120
146
|
Write notes as presenter narration; 3–7 steps; end on the money shot.
|
|
@@ -122,8 +148,8 @@ Write notes as presenter narration; 3–7 steps; end on the money shot.
|
|
|
122
148
|
## After editing by hand
|
|
123
149
|
|
|
124
150
|
```bash
|
|
125
|
-
|
|
126
|
-
|
|
151
|
+
screenbook manifest # if you added/renamed screens
|
|
152
|
+
screenbook validate # the done gate
|
|
127
153
|
```
|
|
128
154
|
|
|
129
155
|
(Under `serve`, manifests regenerate automatically on save. MCP writes do both for you.)
|
|
@@ -9,18 +9,10 @@
|
|
|
9
9
|
| **Google Chrome installed** | `validate` and `screenshot` (headless checks) | used through `playwright-core`; see below if you don't have Chrome |
|
|
10
10
|
| **Git** | recommended | ScreenBook writes plain files — git is your undo and your review tool |
|
|
11
11
|
|
|
12
|
-
Nothing else. No bundler, no framework, no
|
|
13
|
-
dependencies; the CLI
|
|
14
|
-
|
|
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
|
-
```
|
|
12
|
+
Nothing else. No bundler, no framework, no build step. The studio itself has **zero**
|
|
13
|
+
dependencies; the CLI carries exactly one (`playwright-core`), used only for headless
|
|
14
|
+
validation and screenshots — it comes along automatically with the install below. On a
|
|
15
|
+
machine without Chrome, the first `validate` downloads a headless browser once, by itself.
|
|
24
16
|
|
|
25
17
|
## Install
|
|
26
18
|
|
|
@@ -69,15 +61,15 @@ the sidebar. That's the whole loop.
|
|
|
69
61
|
Already have a single-page prototype? Embed it whole instead of rewriting:
|
|
70
62
|
|
|
71
63
|
```bash
|
|
72
|
-
|
|
64
|
+
screenbook init . --app path/to/index.html
|
|
73
65
|
```
|
|
74
66
|
|
|
75
|
-
(see [App mode](app-mode
|
|
67
|
+
(see [App mode](app-mode.md)).
|
|
76
68
|
|
|
77
69
|
## The definition of done
|
|
78
70
|
|
|
79
71
|
```bash
|
|
80
|
-
|
|
72
|
+
screenbook validate
|
|
81
73
|
```
|
|
82
74
|
|
|
83
75
|
Green means: every screen renders in every theme × light/dark × LTR/RTL with **zero console
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# The studio, control by control
|
|
2
2
|
|
|
3
|
-
Open `http://127.0.0.1:4600`
|
|
3
|
+
Open `http://127.0.0.1:4600` — `screenbook serve` redirects straight to the studio at
|
|
4
|
+
`/screenbook/`.
|
|
4
5
|
|
|
5
|
-

|
|
6
|
+

|
|
6
7
|
|
|
7
8
|
## Top bar
|
|
8
9
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Troubleshooting
|
|
2
2
|
|
|
3
3
|
**The studio is blank / "could not load this project".**
|
|
4
|
-
The
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
The studio expects `app.config.json` one level above where it's mounted. `screenbook serve`
|
|
5
|
+
wires this automatically (the studio lives at `/screenbook/`, your project at `/`). Under a
|
|
6
|
+
plain static server, serve the PROJECT root — never the studio folder itself.
|
|
7
7
|
|
|
8
8
|
**My new screen isn't in the sidebar.**
|
|
9
9
|
The sidebar reads the derived `features/<f>/manifest.json`. Under `serve` it regenerates on
|
|
10
|
-
save; under any other server run `
|
|
10
|
+
save; under any other server run `screenbook manifest`. Also check the
|
|
11
11
|
meta block parses (validate says exactly what's wrong).
|
|
12
12
|
|
|
13
13
|
**The screen shows "Loading…" forever.**
|
|
@@ -23,8 +23,10 @@ the console). Cooperative app: the include path to `engine/app-bridge.js` is wro
|
|
|
23
23
|
them cooperative.
|
|
24
24
|
|
|
25
25
|
**`validate` fails with "No headless browser available".**
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
The checks drive your installed Google Chrome via `playwright-core` (bundled with the
|
|
27
|
+
package), and on a Chrome-less machine they download a headless browser once, automatically.
|
|
28
|
+
If both routes failed (offline machine, blocked download), run
|
|
29
|
+
`npx playwright-core install chromium` when you're back online.
|
|
28
30
|
|
|
29
31
|
**`validate` is red on a screen that looks fine.**
|
|
30
32
|
The gate is zero console errors across every theme × mode × direction. The failure row
|
|
@@ -51,5 +53,6 @@ Python's server sends cache headers; hard-reload, or use `screenbook serve`
|
|
|
51
53
|
(no-store on everything — this problem is why).
|
|
52
54
|
|
|
53
55
|
**Something engine-side looks wrong.**
|
|
54
|
-
`?selftest=1` in the browser for the live matrix;
|
|
55
|
-
(`npm test`
|
|
56
|
+
`?selftest=1` in the browser for the live matrix; the [testing map](../contributing/testing.md)
|
|
57
|
+
covers every layer (`npm test` in a checkout of the ScreenBook repo runs the engine's own
|
|
58
|
+
64-test suite).
|
|
@@ -7,8 +7,9 @@ npm install -g @cs4alhaider/screenbook
|
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Alternatives that need no install: `npx -y @cs4alhaider/screenbook <command>`, or from a
|
|
10
|
-
checkout `node
|
|
11
|
-
directory (default: the current one; running from inside
|
|
10
|
+
checkout `node cli/screenbook.js <command>`. Every command takes an optional project
|
|
11
|
+
directory (default: the current one; running from inside `.screenbook/` resolves to its
|
|
12
|
+
parent project automatically).
|
|
12
13
|
|
|
13
14
|
## `init [dir] [--name X] [--force] [--app <src>] [--cooperative]`
|
|
14
15
|
|
|
@@ -20,8 +21,8 @@ embedded app (driver scaffolded unless `--cooperative`).
|
|
|
20
21
|
|
|
21
22
|
The dev server. Static hosting with:
|
|
22
23
|
|
|
23
|
-
-
|
|
24
|
-
|
|
24
|
+
- the studio runtime mounted at `/screenbook/*` straight from the installed package
|
|
25
|
+
(`/engine/*` kept as a compatibility alias) — your project never carries runtime code
|
|
25
26
|
- live reload over SSE — saving any project file refreshes the studio; screen edits reload
|
|
26
27
|
the current frame
|
|
27
28
|
- automatic `manifest.json` regeneration when screens change
|
|
@@ -57,8 +58,8 @@ Device-framed PNGs via headless Chrome. Options: `--mode light,dark` · `--dir l
|
|
|
57
58
|
|
|
58
59
|
Embed an existing single-page app as an app-type feature. Scaffolds an injectable
|
|
59
60
|
`driver.js` template (fill in `screens()` / `go()` / `set()`) unless `--cooperative`, in
|
|
60
|
-
which case the app is expected to include `
|
|
61
|
-
[App mode](app-mode
|
|
61
|
+
which case the app is expected to include the packaged `app-bridge.js` itself. See
|
|
62
|
+
[App mode](../guides/app-mode.md).
|
|
62
63
|
|
|
63
64
|
## `mcp [--root dir]`
|
|
64
65
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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.2.
|
|
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;
|
|
@@ -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.2.
|
|
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"
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"examples/",
|
|
37
37
|
"docs/",
|
|
38
38
|
"AGENTS.md",
|
|
39
|
-
"CLAUDE.md"
|
|
40
|
-
"TESTING.md"
|
|
39
|
+
"CLAUDE.md"
|
|
41
40
|
],
|
|
42
41
|
"dependencies": {
|
|
43
42
|
"playwright-core": "^1.45.0"
|
|
44
43
|
},
|
|
45
44
|
"scripts": {
|
|
45
|
+
"postinstall": "node cli/postinstall.js",
|
|
46
46
|
"test": "node --test",
|
|
47
47
|
"serve": "node cli/screenbook.js serve ..",
|
|
48
48
|
"validate": "node cli/screenbook.js validate .."
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"type": "git",
|
|
52
52
|
"url": "git+https://github.com/cs4alhaider/screenbook.git"
|
|
53
53
|
},
|
|
54
|
-
"homepage": "https://github.
|
|
54
|
+
"homepage": "https://cs4alhaider.github.io/screenbook/",
|
|
55
55
|
"bugs": {
|
|
56
56
|
"url": "https://github.com/cs4alhaider/screenbook/issues"
|
|
57
57
|
}
|
package/shell/screenhost.js
CHANGED
|
@@ -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();
|
|
@@ -236,6 +247,7 @@ export async function runSelftest() {
|
|
|
236
247
|
}
|
|
237
248
|
}
|
|
238
249
|
|
|
250
|
+
/* N workers drain one shared queue — at most CONCURRENCY iframes live at once. */
|
|
239
251
|
const queue = all.slice();
|
|
240
252
|
await Promise.all(Array.from({ length: CONCURRENCY }, async () => {
|
|
241
253
|
while (queue.length) {
|
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/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
|
|
File without changes
|