ketatlas 0.3.0 → 0.4.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 +7 -0
- package/README.md +36 -9
- package/bin/audit.js +15 -4
- package/bin/ketatlas.js +16 -6
- package/bin/renderer.js +189 -0
- package/bin/viewer.js +39 -7
- package/docs/agent-mockups.md +19 -13
- package/docs/architecture.md +21 -13
- package/docs/authoring.md +40 -2
- package/docs/cli.md +12 -2
- package/docs/configuration.md +21 -2
- package/docs/integration.md +4 -1
- package/docs/migration.md +13 -0
- package/package.json +9 -6
- package/renderer.schema.json +44 -0
- package/skills/ketatlas/SKILL.md +63 -16
- package/src/config.js +3 -3
- package/src/index.d.ts +2 -0
- package/src/index.js +5 -1
- package/templates/basic/README.md +5 -5
- package/templates/process/README.md +4 -4
- package/templates/web/README.md +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
- Add an opt-in two-port native renderer contract: KetAtlas serves the viewer while the declared React, Vue, KetJS, or other framework server owns screen HTML, assets, and same-origin behavior.
|
|
6
|
+
- Add `atlas.renderer.json`, its public schema, command-array validation, readiness checks, predictable renderer shutdown, and route-aware static audit.
|
|
7
|
+
- Add `screenBaseURL` to the browser API so screen routes can resolve on a different origin while external references continue to resolve beside `atlas.json`.
|
|
8
|
+
- Require the bundled agent skill to use the product's real framework and design-system components, share presenters/styles across atlases, and keep business data outside presentation components.
|
|
9
|
+
|
|
3
10
|
## 0.3.0
|
|
4
11
|
|
|
5
12
|
- Standardize discoverable projects as `<name>.ketatlas/atlas.json` bundles, add `discover`, accept bundle directories in Atlas commands, and teach the bundled skill to backfill legacy projects safely.
|
package/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# KetAtlas
|
|
2
2
|
|
|
3
|
-
**Scaffold, serve, and audit interactive
|
|
3
|
+
**Scaffold, serve, and audit interactive framework-native workflow maps.**
|
|
4
4
|
|
|
5
|
-
Turn a JSON file and your existing HTML
|
|
5
|
+
Turn a JSON file and screens from your existing HTML, React, Vue, KetJS, or other framework into a canvas you can drag, zoom, and explore. Connect screens with labelled arrows, model decisions and recovery paths, and open the real rendered page to try a step. The same tool supports mobile screens, web pages, and processes with no screens at all.
|
|
6
6
|
|
|
7
|
-
KetAtlas has an English UI
|
|
7
|
+
KetAtlas has an English UI and zero runtime npm dependencies. Node.js **22+** is needed for the CLI. Static projects need no build step. Framework-native projects reuse their product's own server and dependencies; KetAtlas supervises it on a separate localhost port.
|
|
8
8
|
|
|
9
9
|
## Demo
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Open the live map at **[atlas.ketsuite.com](https://atlas.ketsuite.com)**: drag, zoom, follow the
|
|
12
|
+
labelled arrows, and open a real HTML prototype from any screen. Nothing to install.
|
|
13
|
+
|
|
14
|
+
The same map recorded as a video, for a quick look:
|
|
12
15
|
|
|
13
16
|
https://github.com/user-attachments/assets/13f24fc8-7b8e-4bda-9e02-364c120ee163
|
|
14
17
|
|
|
@@ -32,11 +35,11 @@ ketatlas serve my-atlas.ketatlas
|
|
|
32
35
|
ketatlas audit my-atlas.ketatlas --strict
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
The consumer bundle is named `<name>.ketatlas/` and contains `atlas.json`, its schema,
|
|
38
|
+
The consumer bundle is named `<name>.ketatlas/` and contains `atlas.json`, its schema, documentation, and either static screen assets or an `atlas.renderer.json` sidecar. The stable directory suffix lets desktop tools discover atlases without parsing unrelated JSON. A bundle needs no KetAtlas package manifest, lockfile, `node_modules`, build step, or copy of the viewer. Framework dependencies and reusable UI stay in the product workspace, where multiple atlases can share one set of presenters, components, fixtures, and styles. The CLI supplies discovery, serving, validation, and audit. `audit` leaves project files unchanged unless an output file is explicitly requested. Viewing with `serve` does not write; explicit **Save progress** writes the sibling progress file. Use `--read-only` to disable editing.
|
|
36
39
|
|
|
37
|
-
Pin the version in run commands or the global installation for reproducible team workflows. Product scripts implement mock screen interactions; they are authored content, not a local installation of KetAtlas.
|
|
40
|
+
Pin the version in run commands or the global installation for reproducible team workflows. Product scripts implement mock screen interactions; they are authored content, not a local installation of KetAtlas. A native renderer reuses the product's framework tooling and shared UI source rather than copying markup into each atlas.
|
|
38
41
|
|
|
39
|
-
Edit the JSON and screen
|
|
42
|
+
Edit the JSON and product screen source, then refresh the browser. The default viewer address is **http://127.0.0.1:4178**.
|
|
40
43
|
|
|
41
44
|
## Create mockups with an agent
|
|
42
45
|
|
|
@@ -46,7 +49,31 @@ Install the KetAtlas skill in your product project:
|
|
|
46
49
|
npx skills add ketvietlab/ketatlas --skill ketatlas
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
Ask your agent to use the skill with a product brief: requested flows, target platforms, design references, and output directory. The agent creates
|
|
52
|
+
Ask your agent to use the skill with a product brief: requested flows, target platforms, design references, and output directory. The agent creates native framework routes (or static HTML for a static product), a version 1 `atlas.json`, and run instructions, then audits the result.
|
|
53
|
+
|
|
54
|
+
## Render with the product framework
|
|
55
|
+
|
|
56
|
+
KetAtlas 0.4.0 keeps the viewer and product renderer separate. Put `atlas.renderer.json` beside `atlas.json` when screens come from a framework:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"$schema": "https://unpkg.com/ketatlas@0.4.0/renderer.schema.json",
|
|
61
|
+
"version": 1,
|
|
62
|
+
"framework": "vue",
|
|
63
|
+
"command": ["npm", "run", "atlas:serve", "--", "--host", "{host}", "--port", "{port}"],
|
|
64
|
+
"cwd": "../..",
|
|
65
|
+
"readyPath": "/__atlas/ready",
|
|
66
|
+
"screenBasePath": "/__atlas/orders/"
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Then run:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npx --yes ketatlas@0.4.0 serve ./tasks/orders.ketatlas --renderer --port 60550 --html-port 60551
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The first port serves the map and progress API. The second is owned by the declared React/Vue/KetJS/etc. server and serves screen routes. `--renderer` is explicit because it executes the local command array. Static projects continue to use the one-port command without this flag. See [Native renderers](docs/authoring.md#framework-native-screens).
|
|
50
77
|
|
|
51
78
|
See [Agent mockups](docs/agent-mockups.md) for installation options, a ready-to-use request, and a reusable brief template. Agents without skill support can read the [single skill file](skills/ketatlas/SKILL.md) directly.
|
|
52
79
|
|
|
@@ -86,7 +113,7 @@ Open **Screens** to filter screen progress, review blockers and edit acceptance
|
|
|
86
113
|
| -------------------------- | -------------------------------------------------------------------------------- |
|
|
87
114
|
| `scaffold <name.ketatlas>` | Create a bundle from `basic`, `web`, or `process`. Refuses existing content. |
|
|
88
115
|
| `discover <directory>` | Find valid `*.ketatlas/atlas.json` bundles and report invalid bundles. |
|
|
89
|
-
| `serve <bundle\|json>` | Start the viewer
|
|
116
|
+
| `serve <bundle\|json>` | Start the viewer; optionally supervise a native renderer on a second port. |
|
|
90
117
|
| `audit <bundle\|json>` | Check configuration, reachability, local files, and literal HTML/CSS references. |
|
|
91
118
|
| `validate <bundle\|json>` | Validate configuration only, without reading screen files. |
|
|
92
119
|
|
package/bin/audit.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFile, realpath, stat } from "node:fs/promises";
|
|
|
3
3
|
import { resolve, dirname, relative, sep, extname } from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import { validateAtlas } from "../src/config.js";
|
|
6
|
+
import { readRendererConfig, rendererFileName } from "./renderer.js";
|
|
6
7
|
const within = (root, path) => {
|
|
7
8
|
const r = relative(root, path);
|
|
8
9
|
return r === "" || (!r.startsWith(".." + sep) && r !== ".." && !r.startsWith(sep));
|
|
@@ -29,6 +30,12 @@ export async function auditAtlas(file, { root: rootOption } = {}) {
|
|
|
29
30
|
warnings = [...result.warnings],
|
|
30
31
|
checked = new Set(),
|
|
31
32
|
remote = new Set();
|
|
33
|
+
let renderer;
|
|
34
|
+
try {
|
|
35
|
+
renderer = await readRendererConfig(resolve(dirname(absolute), rendererFileName));
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error.code !== "ENOENT") errors.push({ path: rendererFileName, message: error.message });
|
|
38
|
+
}
|
|
32
39
|
if (!within(root, absolute))
|
|
33
40
|
errors.push({ path: "file", message: "The atlas JSON must be inside the serve root." });
|
|
34
41
|
async function inspect(value, from, label) {
|
|
@@ -90,9 +97,12 @@ export async function auditAtlas(file, { root: rootOption } = {}) {
|
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
99
|
if (result.valid) {
|
|
93
|
-
|
|
100
|
+
if (!renderer)
|
|
101
|
+
for (const s of config.screens || []) await inspect(s.url, absolute, `screen:${s.id}`);
|
|
94
102
|
for (const f of config.flows)
|
|
95
|
-
for (const n of f.nodes)
|
|
103
|
+
for (const n of f.nodes)
|
|
104
|
+
if (n.url && (!renderer || (n.type || (n.screen ? "screen" : "note")) !== "screen"))
|
|
105
|
+
await inspect(n.url, absolute, `node:${f.id}/${n.id}`);
|
|
96
106
|
}
|
|
97
107
|
if (result.valid) {
|
|
98
108
|
try {
|
|
@@ -118,7 +128,8 @@ export async function auditAtlas(file, { root: rootOption } = {}) {
|
|
|
118
128
|
},
|
|
119
129
|
errors,
|
|
120
130
|
warnings,
|
|
121
|
-
scope:
|
|
122
|
-
|
|
131
|
+
scope: renderer
|
|
132
|
+
? `Configuration and ${renderer.data.framework} renderer contract. The renderer command, routes, JavaScript behavior and embedding are not executed.`
|
|
133
|
+
: "Static configuration and literal local HTML/CSS references. JavaScript behavior and remote embedding policies are not executed.",
|
|
123
134
|
};
|
|
124
135
|
}
|
package/bin/ketatlas.js
CHANGED
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
resolveAtlasFile,
|
|
16
16
|
} from "./discovery.js";
|
|
17
17
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
18
|
-
const help = `KetAtlas — scaffold, serve, and audit
|
|
18
|
+
const help = `KetAtlas — scaffold, serve, and audit framework-native workflow maps
|
|
19
19
|
|
|
20
20
|
ketatlas scaffold <name.ketatlas> [--template basic|web|process]
|
|
21
21
|
ketatlas discover <directory> [--json]
|
|
22
|
-
ketatlas serve <name.ketatlas|atlas.json> [--port 4178] [--root directory]
|
|
22
|
+
ketatlas serve <name.ketatlas|atlas.json> [--port 4178] [--root directory] [--renderer] [--html-port 4179]
|
|
23
23
|
ketatlas audit <name.ketatlas|atlas.json> [--root directory] [--json] [--strict]
|
|
24
24
|
ketatlas validate <name.ketatlas|atlas.json>
|
|
25
25
|
ketatlas progress <name.ketatlas|atlas.json> [--json] [--init]
|
|
@@ -32,7 +32,7 @@ Examples:
|
|
|
32
32
|
npx ketatlas serve my-atlas.ketatlas
|
|
33
33
|
npx ketatlas audit my-atlas.ketatlas --strict
|
|
34
34
|
|
|
35
|
-
Serve binds to 127.0.0.1.
|
|
35
|
+
Serve binds to 127.0.0.1. Static screens need no build; --renderer starts the project's declared framework server.
|
|
36
36
|
`;
|
|
37
37
|
function parse(args, allowed) {
|
|
38
38
|
const options = {},
|
|
@@ -196,10 +196,17 @@ async function main(args) {
|
|
|
196
196
|
port: "string",
|
|
197
197
|
root: "string",
|
|
198
198
|
"read-only": "boolean",
|
|
199
|
+
renderer: "boolean",
|
|
200
|
+
"html-port": "string",
|
|
199
201
|
}),
|
|
200
|
-
port = Number(options.port || 4178)
|
|
202
|
+
port = Number(options.port || 4178),
|
|
203
|
+
htmlPort = Number(options["html-port"] || (port < 65535 ? port + 1 : 0));
|
|
201
204
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
202
205
|
throw new Error("Port must be 1–65535.");
|
|
206
|
+
if (options.renderer && (!Number.isInteger(htmlPort) || htmlPort < 1 || htmlPort > 65535))
|
|
207
|
+
throw new Error("HTML port must be 1–65535.");
|
|
208
|
+
if (options.renderer && htmlPort === port)
|
|
209
|
+
throw new Error("Viewer and HTML ports must be different.");
|
|
203
210
|
const directory = (await stat(resolve(target))).isDirectory();
|
|
204
211
|
if (directory && options.root)
|
|
205
212
|
throw new Error("--root is only needed when serving an atlas JSON file.");
|
|
@@ -210,13 +217,16 @@ async function main(args) {
|
|
|
210
217
|
port,
|
|
211
218
|
root: options.root,
|
|
212
219
|
readOnly: options["read-only"],
|
|
220
|
+
renderer: options.renderer,
|
|
221
|
+
htmlPort,
|
|
213
222
|
});
|
|
214
223
|
console.log(
|
|
215
|
-
`KetAtlas: http://127.0.0.1:${server.address().port}\nServing ${resolve(target)}\nPress Ctrl+C to stop.`,
|
|
224
|
+
`KetAtlas: http://127.0.0.1:${server.address().port}${server.htmlOrigin ? `\nHTML: ${server.htmlOrigin}` : ""}\nServing ${resolve(target)}\nPress Ctrl+C to stop.`,
|
|
216
225
|
);
|
|
217
|
-
const close = () => {
|
|
226
|
+
const close = async () => {
|
|
218
227
|
server.close();
|
|
219
228
|
server.closeAllConnections();
|
|
229
|
+
await server.closeRenderer?.();
|
|
220
230
|
};
|
|
221
231
|
process.once("SIGINT", close);
|
|
222
232
|
process.once("SIGTERM", close);
|
package/bin/renderer.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createServer } from "node:net";
|
|
3
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
const text = (value) => typeof value === "string" && value.trim().length > 0;
|
|
8
|
+
|
|
9
|
+
export const rendererFileName = "atlas.renderer.json";
|
|
10
|
+
|
|
11
|
+
export function validateRendererConfig(input) {
|
|
12
|
+
const errors = [];
|
|
13
|
+
const error = (path, message) => errors.push({ path, message });
|
|
14
|
+
if (!object(input))
|
|
15
|
+
return {
|
|
16
|
+
valid: false,
|
|
17
|
+
errors: [{ path: "$", message: "Expected a renderer object." }],
|
|
18
|
+
};
|
|
19
|
+
for (const key of Object.keys(input))
|
|
20
|
+
if (
|
|
21
|
+
![
|
|
22
|
+
"$schema",
|
|
23
|
+
"version",
|
|
24
|
+
"framework",
|
|
25
|
+
"command",
|
|
26
|
+
"cwd",
|
|
27
|
+
"readyPath",
|
|
28
|
+
"screenBasePath",
|
|
29
|
+
"readyTimeoutMs",
|
|
30
|
+
].includes(key)
|
|
31
|
+
)
|
|
32
|
+
error(key, "Unknown property.");
|
|
33
|
+
if (input.version !== 1) error("version", "Expected version: 1.");
|
|
34
|
+
if (!text(input.framework)) error("framework", "Expected a non-empty framework name.");
|
|
35
|
+
if (!Array.isArray(input.command) || input.command.length === 0)
|
|
36
|
+
error("command", "Expected a non-empty command array.");
|
|
37
|
+
else
|
|
38
|
+
for (const [index, value] of input.command.entries())
|
|
39
|
+
if (!text(value)) error(`command[${index}]`, "Expected a non-empty argument.");
|
|
40
|
+
if (input.$schema !== undefined && typeof input.$schema !== "string")
|
|
41
|
+
error("$schema", "Expected text.");
|
|
42
|
+
if (input.cwd !== undefined && !text(input.cwd)) error("cwd", "Expected a directory path.");
|
|
43
|
+
for (const key of ["readyPath", "screenBasePath"])
|
|
44
|
+
if (input[key] !== undefined && (!text(input[key]) || !input[key].startsWith("/")))
|
|
45
|
+
error(key, "Expected an origin-relative path beginning with /.");
|
|
46
|
+
if (input.screenBasePath !== undefined && !input.screenBasePath.endsWith("/"))
|
|
47
|
+
error("screenBasePath", "Expected a path ending with /.");
|
|
48
|
+
if (
|
|
49
|
+
input.readyTimeoutMs !== undefined &&
|
|
50
|
+
(!Number.isInteger(input.readyTimeoutMs) ||
|
|
51
|
+
input.readyTimeoutMs < 100 ||
|
|
52
|
+
input.readyTimeoutMs > 120000)
|
|
53
|
+
)
|
|
54
|
+
error("readyTimeoutMs", "Expected an integer from 100 to 120000.");
|
|
55
|
+
return { valid: errors.length === 0, errors };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class RendererValidationError extends Error {
|
|
59
|
+
constructor(result) {
|
|
60
|
+
super(result.errors.map((issue) => `${issue.path}: ${issue.message}`).join("\n"));
|
|
61
|
+
this.name = "RendererValidationError";
|
|
62
|
+
this.errors = result.errors;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function readRendererConfig(file) {
|
|
67
|
+
const absolute = await realpath(resolve(file));
|
|
68
|
+
if (!(await stat(absolute)).isFile()) throw new Error("Expected a renderer JSON file.");
|
|
69
|
+
const data = JSON.parse(await readFile(absolute, "utf8"));
|
|
70
|
+
const result = validateRendererConfig(data);
|
|
71
|
+
if (!result.valid) throw new RendererValidationError(result);
|
|
72
|
+
const directory = dirname(absolute);
|
|
73
|
+
const cwd = await realpath(resolve(directory, data.cwd || "."));
|
|
74
|
+
if (!(await stat(cwd)).isDirectory()) throw new Error("Renderer cwd must be a directory.");
|
|
75
|
+
return {
|
|
76
|
+
file: absolute,
|
|
77
|
+
directory,
|
|
78
|
+
cwd,
|
|
79
|
+
data: {
|
|
80
|
+
...data,
|
|
81
|
+
cwd: data.cwd || ".",
|
|
82
|
+
readyPath: data.readyPath || "/",
|
|
83
|
+
screenBasePath: data.screenBasePath || "/",
|
|
84
|
+
readyTimeoutMs: data.readyTimeoutMs || 15000,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const reservePort = (host) =>
|
|
90
|
+
new Promise((resolvePort, reject) => {
|
|
91
|
+
const probe = createServer();
|
|
92
|
+
probe.once("error", reject);
|
|
93
|
+
probe.listen(0, host, () => {
|
|
94
|
+
const port = probe.address().port;
|
|
95
|
+
probe.close((error) => (error ? reject(error) : resolvePort(port)));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
function originPath(origin, value, name) {
|
|
100
|
+
const url = new URL(value, origin);
|
|
101
|
+
if (url.origin !== origin) throw new Error(`${name} must stay on the renderer origin.`);
|
|
102
|
+
return url.href;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function terminate(child) {
|
|
106
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
107
|
+
try {
|
|
108
|
+
if (process.platform === "win32") child.kill("SIGTERM");
|
|
109
|
+
else process.kill(-child.pid, "SIGTERM");
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function stop(child) {
|
|
114
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
115
|
+
terminate(child);
|
|
116
|
+
await Promise.race([
|
|
117
|
+
new Promise((done) => child.once("exit", done)),
|
|
118
|
+
new Promise((done) => setTimeout(done, 2000)),
|
|
119
|
+
]);
|
|
120
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
121
|
+
try {
|
|
122
|
+
if (process.platform === "win32") child.kill("SIGKILL");
|
|
123
|
+
else process.kill(-child.pid, "SIGKILL");
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function startRenderer(file, options = {}) {
|
|
129
|
+
const config = await readRendererConfig(file);
|
|
130
|
+
const host = options.host || "127.0.0.1";
|
|
131
|
+
const port = options.port || (await reservePort(host));
|
|
132
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
133
|
+
throw new Error("HTML port must be 1–65535.");
|
|
134
|
+
const values = {
|
|
135
|
+
host,
|
|
136
|
+
port: String(port),
|
|
137
|
+
atlasDirectory: config.directory,
|
|
138
|
+
};
|
|
139
|
+
const replace = (value) =>
|
|
140
|
+
value.replace(/\{(host|port|atlasDirectory)\}/g, (_, key) => values[key]);
|
|
141
|
+
const command = config.data.command.map(replace);
|
|
142
|
+
const origin = `http://${host}:${port}`;
|
|
143
|
+
const readyURL = originPath(origin, config.data.readyPath, "readyPath");
|
|
144
|
+
const screenBaseURL = originPath(origin, config.data.screenBasePath, "screenBasePath");
|
|
145
|
+
const child = spawn(command[0], command.slice(1), {
|
|
146
|
+
cwd: config.cwd,
|
|
147
|
+
detached: process.platform !== "win32",
|
|
148
|
+
env: {
|
|
149
|
+
...process.env,
|
|
150
|
+
KETATLAS_HOST: host,
|
|
151
|
+
KETATLAS_HTML_PORT: String(port),
|
|
152
|
+
KETATLAS_PROJECT_DIR: config.directory,
|
|
153
|
+
},
|
|
154
|
+
stdio: options.stdio || "inherit",
|
|
155
|
+
});
|
|
156
|
+
let spawnError;
|
|
157
|
+
child.once("error", (error) => {
|
|
158
|
+
spawnError = error;
|
|
159
|
+
});
|
|
160
|
+
const started = Date.now();
|
|
161
|
+
while (Date.now() - started < config.data.readyTimeoutMs) {
|
|
162
|
+
if (spawnError) throw spawnError;
|
|
163
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
164
|
+
throw new Error(`Renderer exited before ${readyURL} became ready.`);
|
|
165
|
+
try {
|
|
166
|
+
const response = await fetch(readyURL, { signal: AbortSignal.timeout(1000) });
|
|
167
|
+
if (response.ok) {
|
|
168
|
+
let closed = false;
|
|
169
|
+
return {
|
|
170
|
+
child,
|
|
171
|
+
config,
|
|
172
|
+
origin,
|
|
173
|
+
readyURL,
|
|
174
|
+
screenBaseURL,
|
|
175
|
+
async close() {
|
|
176
|
+
if (closed) return;
|
|
177
|
+
closed = true;
|
|
178
|
+
await stop(child);
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
} catch {}
|
|
183
|
+
await new Promise((done) => setTimeout(done, 100));
|
|
184
|
+
}
|
|
185
|
+
await stop(child);
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Renderer did not become ready at ${readyURL} within ${config.data.readyTimeoutMs}ms.`,
|
|
188
|
+
);
|
|
189
|
+
}
|
package/bin/viewer.js
CHANGED
|
@@ -5,6 +5,7 @@ import { resolve, dirname, relative, sep } from "node:path";
|
|
|
5
5
|
import { validateAtlas, AtlasValidationError } from "../src/config.js";
|
|
6
6
|
import { serve } from "./server.js";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { rendererFileName, startRenderer } from "./renderer.js";
|
|
8
9
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
10
|
export async function serveAtlas(file, options = {}) {
|
|
10
11
|
const absolute = await realpath(resolve(file));
|
|
@@ -18,11 +19,42 @@ export async function serveAtlas(file, options = {}) {
|
|
|
18
19
|
throw new Error("The JSON file must be inside --root.");
|
|
19
20
|
const configURL = "/" + rel.split(sep).map(encodeURIComponent).join("/");
|
|
20
21
|
const token = randomBytes(24).toString("hex");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
let renderer;
|
|
23
|
+
if (options.renderer) {
|
|
24
|
+
const rendererFile = resolve(options.rendererFile || dirname(absolute), rendererFileName);
|
|
25
|
+
const htmlPort =
|
|
26
|
+
options.htmlPort ?? (options.port && options.port < 65535 ? options.port + 1 : 0);
|
|
27
|
+
renderer = await startRenderer(rendererFile, {
|
|
28
|
+
host: options.host,
|
|
29
|
+
port: htmlPort,
|
|
30
|
+
stdio: options.rendererStdio,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const loadOptions = {
|
|
34
|
+
syncUrl: true,
|
|
35
|
+
progressEndpoint: "/__ketatlas__/progress",
|
|
36
|
+
progressToken: token,
|
|
37
|
+
...(renderer
|
|
38
|
+
? {
|
|
39
|
+
screenBaseURL: renderer.screenBaseURL,
|
|
40
|
+
sandbox: "allow-scripts allow-forms allow-same-origin",
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
43
|
+
};
|
|
44
|
+
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>KetAtlas</title><style>html,body{margin:0;height:100%}#atlas{height:100dvh}#error{font:16px system-ui;padding:24px;white-space:pre-wrap}</style></head><body><main id="atlas"></main><pre id="error" hidden></pre><script type="module">import {loadAtlas} from '/__ketatlas__/src/index.js';try{window.atlas=await loadAtlas(document.querySelector('#atlas'),${JSON.stringify(configURL).replaceAll("<", "\\u003c")},${JSON.stringify(loadOptions).replaceAll("<", "\\u003c")});document.title=${JSON.stringify(config.title).replaceAll("<", "\\u003c")}+' · KetAtlas';}catch(error){const el=document.querySelector('#error');el.hidden=false;el.textContent=error.message;}</script></body></html>`;
|
|
45
|
+
try {
|
|
46
|
+
const server = await serve(root, {
|
|
47
|
+
...options,
|
|
48
|
+
viewer: html,
|
|
49
|
+
packageRoot,
|
|
50
|
+
handler: progressHandler(absolute, absolute, token, !options.readOnly),
|
|
51
|
+
});
|
|
52
|
+
server.renderer = renderer;
|
|
53
|
+
server.htmlOrigin = renderer?.origin;
|
|
54
|
+
server.closeRenderer = () => renderer?.close();
|
|
55
|
+
return server;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
await renderer?.close();
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
28
60
|
}
|
package/docs/agent-mockups.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Create mockups with an agent
|
|
2
2
|
|
|
3
|
-
Give an agent the KetAtlas skill and a product brief. The agent produces
|
|
3
|
+
Give an agent the KetAtlas skill and a product brief. The agent produces native framework screen routes (or static HTML for a static product) plus `atlas.json`; KetAtlas opens them as an interactive workflow map. The brief describes the product, while the schemas and CLI audit check the output contracts.
|
|
4
4
|
|
|
5
5
|
## Install the skill once
|
|
6
6
|
|
|
@@ -26,7 +26,7 @@ If the agent does not support skill installation, attach that file or ask it to
|
|
|
26
26
|
For a short request, invoke the skill and include the product, target, design reference, and flows:
|
|
27
27
|
|
|
28
28
|
```text
|
|
29
|
-
Use $ketatlas to create one shared
|
|
29
|
+
Use $ketatlas to create one shared React mobile prototype for Northstar Tasks
|
|
30
30
|
in ./tasks/mobile. Use 390 × 844 screens, English product copy, and the design
|
|
31
31
|
system in ./design-system. Include sign-in, password recovery, task list,
|
|
32
32
|
task details, and task completion. Map entry points, actions, outcomes, and
|
|
@@ -36,11 +36,13 @@ and preview the result. Return the serve command and verification results.
|
|
|
36
36
|
|
|
37
37
|
`$ketatlas` is the explicit skill invocation in Codex. In other agents, use that agent's skill selector or explicitly ask it to use the installed KetAtlas skill. Keep the same product brief.
|
|
38
38
|
|
|
39
|
-
The deliverable is a `<name>.ketatlas/` bundle containing
|
|
39
|
+
The deliverable is a `<name>.ketatlas/` bundle containing JSON/schema, documentation, and either static screen assets or `atlas.renderer.json`. Do not add a package manifest, lockfile, node_modules, or copied viewer/test tooling inside the bundle. Native React/Vue/KetJS/etc. routes reuse the product's existing framework package and shared UI source outside the atlas folder. Use `ketatlas@0.4.0` for this contract. Browser checks can use the agent's existing tooling outside the atlas folder.
|
|
40
40
|
|
|
41
41
|
The agent should infer routine details and record assumptions. Provide an exact list when “all screens” means a defined inventory, so missing coverage can be checked against a source.
|
|
42
42
|
|
|
43
|
-
Before it creates or edits visual screens, the skill asks for one design-system choice: Auto (Két Design System), Két Design System, Carbon, GitHub Primer, Microsoft Fluent 2, no design system, or a repository URL/local path. A design system already named in the request counts as the answer. Auto uses [Két Design System](https://github.com/ketvietlab/ketjs/tree/develop/packages/design-system). The agent inspects
|
|
43
|
+
Before it creates or edits visual screens, the skill asks for one design-system choice: Auto (Két Design System), Két Design System, Carbon, GitHub Primer, Microsoft Fluent 2, no design system, or a repository URL/local path. A design system already named in the request counts as the answer. Auto uses [Két Design System](https://github.com/ketvietlab/ketjs/tree/develop/packages/design-system). The agent inspects its actual tokens, components, assets, patterns, and framework, then renders with that framework instead of duplicating markup. An incorrect canonical component is fixed at its source before Atlas work continues.
|
|
44
|
+
|
|
45
|
+
For several atlases in one workspace, the agent creates or reuses one shared framework module for screen presenters, design-system composition, fixture factories, route helpers, and styles. Individual bundles keep flow/state declarations and namespaced screen paths; they do not copy UI code from one another. Production routes and Atlas fixture routes call the same presenter so the mock and built product do not become two visual sources of truth.
|
|
44
46
|
|
|
45
47
|
## Use a saved brief for larger projects
|
|
46
48
|
|
|
@@ -55,7 +57,7 @@ Save this template as `mockup-brief.md`, fill in the relevant fields, and ask: *
|
|
|
55
57
|
- Target platforms and viewport sizes:
|
|
56
58
|
- Product content language:
|
|
57
59
|
- Design system choice (Auto/Két/Carbon/Primer/Fluent 2/none/custom source):
|
|
58
|
-
- Existing
|
|
60
|
+
- Existing application/framework and reference files/URLs:
|
|
59
61
|
- Requested flows and screen inventory:
|
|
60
62
|
- Relevant loading, empty, validation, error, and recovery states:
|
|
61
63
|
- Interactions to demonstrate and synthetic demo inputs:
|
|
@@ -63,15 +65,17 @@ Save this template as `mockup-brief.md`, fill in the relevant fields, and ask: *
|
|
|
63
65
|
|
|
64
66
|
## Expected delivery
|
|
65
67
|
|
|
66
|
-
Create
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
Create native framework routes, or static HTML only for a static product,
|
|
69
|
+
and one KetAtlas version 1 atlas.json. Reuse shared presenters and styles
|
|
70
|
+
across flows and atlases. Set explicit flow starts, outcomes, and labelled
|
|
71
|
+
transitions. Each named screen state must open directly.
|
|
69
72
|
|
|
70
73
|
Use the generated ketatlas.schema.json. Run KetAtlas audit, test the
|
|
71
74
|
important interactions in the viewer when browser automation is available,
|
|
72
75
|
and report results or limitations. Include a README with run commands,
|
|
73
|
-
flow coverage, demo inputs, and assumptions. Keep the
|
|
74
|
-
manifests, lockfiles, local dependencies,
|
|
76
|
+
flow coverage, demo inputs, and assumptions. Keep the atlas bundle free of
|
|
77
|
+
package manifests, lockfiles, local dependencies, duplicated component/style
|
|
78
|
+
implementations, and copied viewer/test tooling.
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
For an existing prototype, ask the agent to reuse its HTML and add or update the atlas instead of rebuilding it. For later changes, name the flow or screen IDs to preserve, for example: **“Add an expired-code recovery branch to sign-in; keep existing screen IDs and audit the updated project.”**
|
|
@@ -79,11 +83,13 @@ For an existing prototype, ask the agent to reuse its HTML and add or update the
|
|
|
79
83
|
## Review the result
|
|
80
84
|
|
|
81
85
|
```sh
|
|
82
|
-
npx ketatlas discover . --json
|
|
83
|
-
npx ketatlas serve ./tasks/mobile.ketatlas
|
|
84
|
-
npx ketatlas audit ./tasks/mobile.ketatlas --strict
|
|
86
|
+
npx --yes ketatlas@0.4.0 discover . --json
|
|
87
|
+
npx --yes ketatlas@0.4.0 serve ./tasks/mobile.ketatlas --renderer --port 60550 --html-port 60551
|
|
88
|
+
npx --yes ketatlas@0.4.0 audit ./tasks/mobile.ketatlas --strict
|
|
85
89
|
```
|
|
86
90
|
|
|
91
|
+
For a genuinely static project, omit `atlas.renderer.json`, `--renderer`, and `--html-port`.
|
|
92
|
+
|
|
87
93
|
Check the delivered flow coverage against the brief. Open **Try this screen** to test the actual HTML. A successful audit confirms structural and local-file checks, not visual quality, full product coverage, or working production integrations. Intentional remote URLs require separate verification and produce strict-audit warnings.
|
|
88
94
|
|
|
89
95
|
The skill lives in this repository so teams can review and evolve it alongside the schema and CLI. It also backfills legacy atlas directories into the discoverable bundle pattern before extending them.
|
package/docs/architecture.md
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
KetAtlas is a
|
|
3
|
+
KetAtlas is a browser viewer plus a Node.js command-line orchestrator. There is no database, account service or runtime npm dependency. Static screens can still use the built-in file server. Framework-native screens run through the product's own server on a second loopback origin, preserving the component implementation as the single source of truth. The localhost CLI has a narrow, protected API to read and save the progress sidecar; static hosting remains read-only.
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
|
-
<name>.ketatlas/atlas.json
|
|
6
|
+
<name>.ketatlas/atlas.json
|
|
7
7
|
│
|
|
8
8
|
├── discover → workspace bundle catalog
|
|
9
9
|
├── validate / audit → diagnostics and CI exit code
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
└── serve → viewer/control origin
|
|
11
|
+
├── config validation + deterministic map
|
|
12
|
+
├── progress API + Shadow DOM viewer
|
|
13
|
+
└── lazy iframes
|
|
14
|
+
├── static files on the viewer origin, or
|
|
15
|
+
└── native framework renderer origin
|
|
16
|
+
└── shared presenters + Atlas fixtures
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Consumer boundary
|
|
20
20
|
|
|
21
|
-
KetAtlas is installed globally or executed through npx. Consumers provide a `<name>.ketatlas/` bundle containing JSON, a schema,
|
|
21
|
+
KetAtlas is installed globally or executed through npx. Consumers provide a `<name>.ketatlas/` bundle containing JSON, a schema, documentation, and either static mock screens or `atlas.renderer.json`. The suffix is the discovery boundary: tools inspect only its direct `atlas.json`, not arbitrary workspace JSON. Consumers do not need a KetAtlas package, lockfile, development dependencies, viewer implementation, or build/test scripts. A native renderer deliberately reuses the product's existing framework package and lockfile outside the bundle.
|
|
22
|
+
|
|
23
|
+
The package owns the CLI, viewer, validation, renderer supervision, and contract verification. The product owns screen markup, components, styles, fixtures, and framework tests. Static audit does not execute renderer commands or simulate product interactions. Agent browser checks can run through external tooling without adding a test harness to the delivered atlas folder.
|
|
24
|
+
|
|
25
|
+
## Native renderer boundary
|
|
26
|
+
|
|
27
|
+
`atlas.renderer.json` declares a framework label, an argv array, a working directory, readiness route, and screen base path. It is separate from `atlas.json`: workflow format version 1 remains framework-neutral. `serve --renderer` is required to execute the command, making local code execution explicit. KetAtlas substitutes host/port tokens, waits for a 2xx readiness response, and terminates the renderer with the viewer.
|
|
28
|
+
|
|
29
|
+
The viewer and renderer use different loopback origins. Relative screen and screen-node URLs resolve against `screenBasePath` on the renderer origin. Relative external/note references still resolve beside `atlas.json`. Renderer iframes receive `allow-same-origin` because the separate origin prevents access to the viewer while enabling native module loading, assets, storage, and same-origin requests.
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
Multiple atlases should share one framework renderer source. Shared design-system compositions, presenters, fixture factories, and styles live in one product module; each atlas selects namespaced routes through `screenBasePath` and owns only its flow/state declarations. The core does not define a component DSL or translate component trees.
|
|
24
32
|
|
|
25
33
|
## Ownership
|
|
26
34
|
|
|
@@ -45,7 +53,7 @@ Flows are stacked on one canvas. A row and column grow to fit their largest node
|
|
|
45
53
|
|
|
46
54
|
Camera changes use an animation frame and a CSS transform. Drag, wheel, keyboard and pinch input use shared accelerated motion constants while zoom remains anchored under the pointer. Only the nearest visible screen cards receive iframes, up to the configured cap. Below the preview threshold, all cards show placeholders. This limits embedded-page cost; every node and edge still has a DOM element. There is no claim of unlimited graph size.
|
|
47
55
|
|
|
48
|
-
The inspector reuses the same screen URL at its declared viewport dimensions, with no extra side padding. Its iframe is removed when closed. Reopening a screen loads a fresh instance; editing a prototype is not persistent business state.
|
|
56
|
+
The inspector reuses the same screen URL at its declared viewport dimensions, with no extra side padding. Its iframe is removed when closed. Reopening a screen loads a fresh instance; editing a prototype is not persistent business state. Framework rendering and business simulation remain inside the renderer, never the viewer.
|
|
49
57
|
|
|
50
58
|
## Isolation and lifecycle
|
|
51
59
|
|
|
@@ -61,7 +69,7 @@ The default theme uses Inter and KetJS's canonical tokens and primitives. `prepa
|
|
|
61
69
|
|
|
62
70
|
## Version 1 boundaries
|
|
63
71
|
|
|
64
|
-
KetAtlas is a viewer and authoring toolkit, not a visual graph editor. Users edit JSON and
|
|
72
|
+
KetAtlas is a viewer and authoring toolkit, not a visual graph editor. Users edit JSON and product framework source in their normal tools. It does not record a graph by watching clicks, synthesize screens, translate React/Vue/KetJS components, calculate backend permissions, or execute the arrow graph as an automated test. Cross-flow edges, collaborative editing and browser-based project audits are outside the current contract.
|
|
65
73
|
|
|
66
74
|
## Delivery tracking
|
|
67
75
|
|
package/docs/authoring.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Authoring a useful atlas
|
|
2
2
|
|
|
3
|
-
Start with a user goal: sign in, approve a purchase, or deliver an order. Give each flow one clear start and a small number of outcomes. Screen
|
|
3
|
+
Start with a user goal: sign in, approve a purchase, or deliver an order. Give each flow one clear start and a small number of outcomes. Screen routes come from the product's actual framework; static HTML remains supported for products that are truly static.
|
|
4
4
|
|
|
5
5
|
## 1. Create a starting point
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ Open a card with a double-click. The preview is actual HTML; links and JavaScrip
|
|
|
13
13
|
|
|
14
14
|
## 2. Register your screens
|
|
15
15
|
|
|
16
|
-
Add each reusable screen once in `screens`. Use
|
|
16
|
+
Add each reusable screen once in `screens`. Use relative paths, including query parameters for prototype states. Static paths resolve beside `atlas.json`; framework paths resolve beneath the native renderer's `screenBasePath`.
|
|
17
17
|
|
|
18
18
|
For a desktop page:
|
|
19
19
|
|
|
@@ -28,6 +28,44 @@ For a desktop page:
|
|
|
28
28
|
|
|
29
29
|
HTML should include a viewport meta tag. Avoid adding a second device frame, reviewer sidebar or fixed outer margin inside the page being embedded; point to its clean preview route instead.
|
|
30
30
|
|
|
31
|
+
## Framework-native screens
|
|
32
|
+
|
|
33
|
+
Use the same implementation framework as the product and design system. React screens stay React, Vue screens stay Vue, and KetJS server components render through KetJS. Do not reproduce component output with copied HTML, string templates, DOM post-processing, or a parallel Atlas component format.
|
|
34
|
+
|
|
35
|
+
Keep one presentation path:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
business loader ─┐
|
|
39
|
+
├─ shared screen presenter ─ design-system components/styles
|
|
40
|
+
Atlas fixture ───┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The business loader supplies real data, permissions, and actions. The Atlas route supplies a deterministic fixture selected by its route/query state. Both call the same presenter. If several atlases need the same shell, field, table, or layout, move that composition and its styles into one shared product module rather than copying it between bundles.
|
|
44
|
+
|
|
45
|
+
Place this sidecar beside `atlas.json`:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"$schema": "https://unpkg.com/ketatlas@0.4.0/renderer.schema.json",
|
|
50
|
+
"version": 1,
|
|
51
|
+
"framework": "ketjs",
|
|
52
|
+
"command": ["npm", "run", "atlas:serve", "--", "--host", "{host}", "--port", "{port}"],
|
|
53
|
+
"cwd": "../..",
|
|
54
|
+
"readyPath": "/__atlas/ready",
|
|
55
|
+
"screenBasePath": "/__atlas/customer-care/"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Commands are argv arrays and do not run through a shell. `cwd` is relative to the bundle. The command may use `{host}`, `{port}`, and `{atlasDirectory}`; the same values are also available through `KETATLAS_HOST`, `KETATLAS_HTML_PORT`, and `KETATLAS_PROJECT_DIR`. `readyPath` must return 2xx. `screenBasePath` must start and end with `/`.
|
|
60
|
+
|
|
61
|
+
Run the two origins explicitly:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
npx --yes ketatlas@0.4.0 serve ./customer-journeys.ketatlas --renderer --port 60550 --html-port 60551
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The first origin owns only the map and progress API. The second origin owns HTML, framework modules, styles, assets, and screen-side requests. Audit validates the renderer contract and graph without executing the command; browser verification must exercise the combined result.
|
|
68
|
+
|
|
31
69
|
## 3. Connect actions and decisions
|
|
32
70
|
|
|
33
71
|
Use short labels such as **Submit request**, **Permission missing**, and **Try again**. Put the main journey on row 0, with explicit columns. Place recovery states on row 1 and give their arrows `kind: "recovery"`.
|
package/docs/cli.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Install once with `npm install --global ketatlas`, or prefix commands with `npx --yes ketatlas`. No dependency installation is required in the consumer directory. `node /path/to/ketatlas/bin/ketatlas.js` is also available to framework maintainers.
|
|
4
4
|
|
|
5
|
-
A consumer keeps
|
|
5
|
+
A consumer keeps its JSON/schema, documentation, and either static product assets or a native renderer sidecar. KetAtlas package files and browser test dependencies belong to the tool; framework package files remain in the product workspace. Normal `serve`, `validate`, and `audit` calls do not create files in the consumer; `audit --output` is an explicit exception.
|
|
6
6
|
|
|
7
7
|
## Scaffold
|
|
8
8
|
|
|
@@ -32,6 +32,7 @@ Legacy manifest paths remain valid command arguments, but discovery intentionall
|
|
|
32
32
|
```sh
|
|
33
33
|
ketatlas serve ./tasks/onboarding.ketatlas
|
|
34
34
|
ketatlas serve ./tasks/onboarding.ketatlas --port 4180
|
|
35
|
+
ketatlas serve ./tasks/onboarding.ketatlas --renderer --port 60550 --html-port 60551
|
|
35
36
|
ketatlas serve ./legacy/atlas.json --root .
|
|
36
37
|
```
|
|
37
38
|
|
|
@@ -41,6 +42,14 @@ Open the printed localhost URL. Changes appear after refreshing; there is no hot
|
|
|
41
42
|
|
|
42
43
|
The `/__ketatlas__/` route is reserved for viewer assets. Dotfiles and paths resolving outside the file root, including symlink escapes, are not served. Only GET/HEAD are supported. The server binds to `127.0.0.1`; it is a local preview server, not a production application server.
|
|
43
44
|
|
|
45
|
+
### Native renderer mode
|
|
46
|
+
|
|
47
|
+
`--renderer` reads `atlas.renderer.json` beside the manifest and executes its command array without a shell. This is opt-in because the project controls the command. The renderer gets `KETATLAS_HOST`, `KETATLAS_HTML_PORT`, and `KETATLAS_PROJECT_DIR`; command arguments can also use `{host}`, `{port}`, and `{atlasDirectory}` placeholders.
|
|
48
|
+
|
|
49
|
+
`--port` selects the viewer/control origin. `--html-port` selects the separate framework renderer origin and defaults to the following port. The ports must differ. KetAtlas waits for the configured `readyPath`, resolves relative screen URLs beneath `screenBasePath`, prints both origins, and terminates the renderer when the viewer receives SIGINT or SIGTERM.
|
|
50
|
+
|
|
51
|
+
The second origin lets trusted React/Vue/KetJS routes load their own modules, styles, assets, and same-origin requests while remaining isolated from the viewer origin. A shared product renderer can serve namespaced routes for several atlas bundles so they reuse the same presenters and styles.
|
|
52
|
+
|
|
44
53
|
`serve <directory>` also works as a plain static preview for an existing HTML integration. JSON mode is recommended for project planning.
|
|
45
54
|
|
|
46
55
|
## Audit
|
|
@@ -60,8 +69,9 @@ Audit checks:
|
|
|
60
69
|
- Local screen files and node URL overrides, using the same root boundary as the server.
|
|
61
70
|
- Literal resource references in HTML (`src`/`href`), links between HTML pages, and CSS imports/URLs.
|
|
62
71
|
- A viewport meta tag in HTML previews.
|
|
72
|
+
- A colocated native renderer contract when present. Its screen paths are treated as framework routes rather than local files.
|
|
63
73
|
|
|
64
|
-
Remote URLs are reported but not fetched. Audit does not execute JavaScript, discover dynamic imports/URLs, validate remote CSP or `X-Frame-Options`, simulate user actions, or verify backend/native behavior. Use your project's browser tests for those checks.
|
|
74
|
+
Remote URLs are reported but not fetched. Audit does not execute renderer commands or JavaScript, discover dynamic imports/URLs, validate remote CSP or `X-Frame-Options`, simulate user actions, or verify backend/native behavior. Use the two-port preview and your project's browser tests for those checks.
|
|
65
75
|
|
|
66
76
|
Exit codes:
|
|
67
77
|
|
package/docs/configuration.md
CHANGED
|
@@ -14,7 +14,26 @@ The [JSON Schema](../schema.json) defines version 1. A generated project contain
|
|
|
14
14
|
| `flows` | Yes | A nonempty array of workflows. |
|
|
15
15
|
| `$schema` | No | Schema URL/path for your editor. |
|
|
16
16
|
|
|
17
|
-
All viewer UI is English. Project titles, descriptions and labels can use any language. Search ignores case and combining accents.
|
|
17
|
+
All viewer UI is English. Project titles, descriptions and labels can use any language. Search ignores case and combining accents. Workflow format version 1 remains framework-neutral; native rendering is declared in a separate sidecar.
|
|
18
|
+
|
|
19
|
+
## Native renderer sidecar
|
|
20
|
+
|
|
21
|
+
`atlas.renderer.json` is optional and sits beside `atlas.json`. Its [schema](../renderer.schema.json) has these fields:
|
|
22
|
+
|
|
23
|
+
| Field | Required | Default / meaning |
|
|
24
|
+
| ---------------- | -------- | ------------------------------------------------------------------- |
|
|
25
|
+
| `version` | Yes | Renderer contract version; must be `1`. |
|
|
26
|
+
| `framework` | Yes | Human/tooling label such as `react`, `vue`, or `ketjs`. |
|
|
27
|
+
| `command` | Yes | Nonempty argv array executed directly, without a shell. |
|
|
28
|
+
| `cwd` | No | Working directory relative to the bundle; defaults to `.`. |
|
|
29
|
+
| `readyPath` | No | Origin-relative 2xx readiness route; defaults to `/`. |
|
|
30
|
+
| `screenBasePath` | No | Origin-relative screen route prefix ending in `/`; defaults to `/`. |
|
|
31
|
+
| `readyTimeoutMs` | No | Startup timeout from 100–120000 milliseconds; defaults to 15000. |
|
|
32
|
+
| `$schema` | No | Schema URL/path for editor completion. |
|
|
33
|
+
|
|
34
|
+
The command may interpolate `{host}`, `{port}`, and `{atlasDirectory}`. The corresponding environment values are `KETATLAS_HOST`, `KETATLAS_HTML_PORT`, and `KETATLAS_PROJECT_DIR`. The sidecar changes relative resolution for registered screens and screen-node URL overrides only; note/external references remain relative to `atlas.json`.
|
|
35
|
+
|
|
36
|
+
`audit` validates a discovered sidecar without running it. `serve --renderer` explicitly authorizes command execution and starts the framework on `--html-port`.
|
|
18
37
|
|
|
19
38
|
## Screen
|
|
20
39
|
|
|
@@ -22,7 +41,7 @@ All viewer UI is English. Project titles, descriptions and labels can use any la
|
|
|
22
41
|
| ------------- | -------- | --------------------------------------------------------------------- |
|
|
23
42
|
| `id` | Yes | Unique across the atlas. |
|
|
24
43
|
| `title` | Yes | Human-readable screen name. |
|
|
25
|
-
| `url` | Yes | Relative
|
|
44
|
+
| `url` | Yes | Relative or HTTP(S) URL for the actual rendered page. |
|
|
26
45
|
| `viewport` | No | Overrides the atlas viewport. Both dimensions are integers, 160–4096. |
|
|
27
46
|
| `description` | No | Context inherited by its nodes. |
|
|
28
47
|
| `badge` | No | Small card footer label; defaults to `Preview`. |
|
package/docs/integration.md
CHANGED
|
@@ -62,6 +62,7 @@ The browser implementation has no React dependency. Instantiate it after mount;
|
|
|
62
62
|
| Option | Default | Purpose |
|
|
63
63
|
| ------------------ | ------------------------------------------ | ------------------------------------------------------------------------------- |
|
|
64
64
|
| `baseURL` | Page URL; JSON response URL in `loadAtlas` | Resolve screen and node URLs. |
|
|
65
|
+
| `screenBaseURL` | `baseURL` | Resolve registered screens and screen-node URL overrides on another origin. |
|
|
65
66
|
| `assetBaseURL` | Package directory | Public directory containing `styles/` and `assets/`; must end in `/`. |
|
|
66
67
|
| `initialFlow` | First flow | Initial flow ID. |
|
|
67
68
|
| `theme` | `light` | `light` or `dark`. |
|
|
@@ -101,7 +102,9 @@ These are viewer events. KetAtlas does not inspect or synchronize navigation ins
|
|
|
101
102
|
|
|
102
103
|
## Embedding behavior
|
|
103
104
|
|
|
104
|
-
Iframe previews are sandboxed. Scripts and forms work by default, but
|
|
105
|
+
Iframe previews are sandboxed. Scripts and forms work by default, but a static page has an opaque origin: authenticated fetch, storage, some module imports, and other same-origin features can require additional permissions. Only add `allow-same-origin` for trusted prototypes. Combining it with scripts on a same-origin page weakens sandbox isolation.
|
|
106
|
+
|
|
107
|
+
The CLI's native renderer mode uses `screenBaseURL` and adds `allow-same-origin` automatically. Its HTML server is a separate loopback origin from the viewer, so framework routes can use native modules and same-origin resources without gaining same-origin access to the viewer/control page.
|
|
105
108
|
|
|
106
109
|
Remote pages can reject framing using CSP or `X-Frame-Options`. KetAtlas cannot override that. Use a permitted embedded route or the **Open in new tab** action. Test links/forms with the default sandbox before sharing a project.
|
|
107
110
|
|
package/docs/migration.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Migrating the KétSuite mobile map
|
|
2
2
|
|
|
3
|
+
## From copied HTML to a 0.4 native renderer
|
|
4
|
+
|
|
5
|
+
Version 0.4 does not require static projects to change. When an atlas copied or reconstructed markup from a React, Vue, KetJS, or other framework, remove that duplicate implementation instead:
|
|
6
|
+
|
|
7
|
+
1. Extract one shared screen presenter in the product's framework and compose canonical design-system components there.
|
|
8
|
+
2. Keep business loaders and Atlas fixtures separate; pass both into the same presenter shape.
|
|
9
|
+
3. Reuse that presenter and its styles across all atlases in the workspace.
|
|
10
|
+
4. Add namespaced Atlas routes and `atlas.renderer.json` beside each manifest.
|
|
11
|
+
5. Change screen URLs to paths relative to the declared `screenBasePath`.
|
|
12
|
+
6. Run `ketatlas@0.4.0 audit`, then use `serve --renderer` and verify both origins in a browser.
|
|
13
|
+
|
|
14
|
+
Do not migrate to a JSON component tree or another generated HTML layer. The framework component remains the screen source of truth.
|
|
15
|
+
|
|
3
16
|
## Bundle layout backfill
|
|
4
17
|
|
|
5
18
|
Current projects use `<name>.ketatlas/atlas.json`. For a legacy self-contained Atlas directory, rename the whole directory so relative screen, style and asset URLs remain unchanged. When `atlas.json` shares a directory with unrelated application code, create a sibling `<name>.ketatlas` bundle, move only Atlas-owned files, and update relative URLs for resources that intentionally remain outside it. Keep one canonical manifest, then run `ketatlas discover <workspace> --json`, `ketatlas validate <name>.ketatlas`, and `ketatlas audit <name>.ketatlas --strict`.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ketatlas",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Interactive
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Interactive framework-native maps for screens, workflows, and user journeys.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"import": "./src/index.js"
|
|
20
20
|
},
|
|
21
21
|
"./schema.json": "./schema.json",
|
|
22
|
-
"./progress.schema.json": "./progress.schema.json"
|
|
22
|
+
"./progress.schema.json": "./progress.schema.json",
|
|
23
|
+
"./renderer.schema.json": "./renderer.schema.json"
|
|
23
24
|
},
|
|
24
25
|
"bin": {
|
|
25
26
|
"ketatlas": "bin/ketatlas.js"
|
|
@@ -37,19 +38,21 @@
|
|
|
37
38
|
"skills",
|
|
38
39
|
"docs",
|
|
39
40
|
"CHANGELOG.md",
|
|
40
|
-
"progress.schema.json"
|
|
41
|
+
"progress.schema.json",
|
|
42
|
+
"renderer.schema.json"
|
|
41
43
|
],
|
|
42
44
|
"scripts": {
|
|
43
45
|
"dev": "node bin/ketatlas.js serve examples/atlas.json --root . --port 4178",
|
|
46
|
+
"build:demo": "node scripts/build-demo.mjs",
|
|
44
47
|
"validate": "node bin/ketatlas.js audit examples/atlas.json --root . && node bin/ketatlas.js audit templates/basic/atlas.json && node bin/ketatlas.js audit templates/web/atlas.json && node bin/ketatlas.js audit templates/process/atlas.json",
|
|
45
48
|
"test": "node --test tests/*.test.js",
|
|
46
|
-
"test:e2e": "node tests/browser.mjs && node tests/progress-browser.mjs",
|
|
49
|
+
"test:e2e": "node tests/browser.mjs && node tests/progress-browser.mjs && node tests/renderer-browser.mjs",
|
|
47
50
|
"check": "npm run validate && npm test && npm run test:e2e",
|
|
48
51
|
"format": "prettier --write src styles/ketatlas.css bin scripts docs examples starter templates skills tests '*.md' '*.json' .github/workflows",
|
|
49
52
|
"format:check": "prettier --check src styles/ketatlas.css bin scripts docs examples starter templates skills tests '*.md' '*.json' .github/workflows",
|
|
50
53
|
"prepare:assets": "node scripts/prepare-assets.mjs",
|
|
51
54
|
"prepack": "node scripts/verify-assets.mjs",
|
|
52
|
-
"prepare:templates": "node scripts/schema.mjs && node scripts/progress-schema.mjs && node scripts/prepare-templates.mjs",
|
|
55
|
+
"prepare:templates": "node scripts/schema.mjs && node scripts/progress-schema.mjs && node scripts/renderer-schema.mjs && node scripts/prepare-templates.mjs",
|
|
53
56
|
"test:package": "node tests/package.mjs"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "KetAtlas native renderer configuration",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"$schema": {
|
|
7
|
+
"type": "string"
|
|
8
|
+
},
|
|
9
|
+
"version": {
|
|
10
|
+
"const": 1
|
|
11
|
+
},
|
|
12
|
+
"framework": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"pattern": "\\S"
|
|
15
|
+
},
|
|
16
|
+
"command": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"minItems": 1,
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"pattern": "\\S"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"cwd": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "\\S"
|
|
27
|
+
},
|
|
28
|
+
"readyPath": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"pattern": "^/"
|
|
31
|
+
},
|
|
32
|
+
"screenBasePath": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"pattern": "^/.*/$|^/$"
|
|
35
|
+
},
|
|
36
|
+
"readyTimeoutMs": {
|
|
37
|
+
"type": "integer",
|
|
38
|
+
"minimum": 100,
|
|
39
|
+
"maximum": 120000
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"required": ["version", "framework", "command"],
|
|
43
|
+
"additionalProperties": false
|
|
44
|
+
}
|
package/skills/ketatlas/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ketatlas
|
|
3
|
-
description: Create or update interactive
|
|
3
|
+
description: Create or update interactive framework-native mockups and workflow maps in KetAtlas format. Use when a user requests KetAtlas output, an atlas.json project, or draggable flows connecting screens rendered by HTML, React, Vue, KetJS, or another product framework; also use to scaffold, serve, or audit an existing atlas.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# KetAtlas
|
|
7
7
|
|
|
8
|
-
Deliver an editable `<name>.ketatlas/` bundle
|
|
8
|
+
Deliver an editable `<name>.ketatlas/` bundle with a version 1 `atlas.json` that `npx ketatlas@0.4.0 serve` can open. KetAtlas supplies the draggable viewer; the selected product framework owns screen rendering. A screenshot gallery or a Mermaid diagram alone is not this deliverable.
|
|
9
9
|
|
|
10
10
|
## Read the brief and choose the scope
|
|
11
11
|
|
|
@@ -32,25 +32,68 @@ Before scaffolding or editing visual screens, ask the user to choose a design sy
|
|
|
32
32
|
|
|
33
33
|
Do not start visual screen implementation until the user answers. If the user explicitly delegates the choice to the agent, treat that as **Auto**. This selection prompt is still required for an existing atlas; its documented/current system may be offered as the likely choice. A request that already names a system or supplies a design-system source counts as an answer and must not be asked again.
|
|
34
34
|
|
|
35
|
-
Inspect the selected source, its usage documentation, tokens, components, icons,
|
|
35
|
+
Inspect the selected source, its usage documentation, tokens, components, icons, relevant product patterns, and implementation framework before mocking. Reuse its actual public components, assets, and composition patterns when available; do not merely imitate its color palette. Replace incompatible starter styling rather than layering multiple design systems. For one shared iOS/Android prototype, create one framework implementation with reusable presenters and states unless separate variants were requested.
|
|
36
36
|
|
|
37
|
-
Prefer a local adapter descriptor with `schemaVersion: "ketatlas.design-system-adapter.v1"` when the selected design system provides one. Discover it from the user-supplied path, a repository `design-system.atlas.json`, or the package's documented `./atlas/profile.json` export. Read the descriptor before using its assets or commands; do not assume capabilities it does not declare. If there is no adapter, use the design system through its documented
|
|
37
|
+
Prefer a local adapter descriptor with `schemaVersion: "ketatlas.design-system-adapter.v1"` when the selected design system provides one. Discover it from the user-supplied path, a repository `design-system.atlas.json`, or the package's documented `./atlas/profile.json` export. Read the descriptor before using its assets or commands; do not assume capabilities it does not declare. If there is no adapter, use the design system through its documented native framework interface and record that no reproducible adapter lock is available.
|
|
38
38
|
|
|
39
39
|
For Két Design System, prefer the descriptor exported at `@ketvietlab/design-system/atlas/profile.json` and its declared `ket-design-system-atlas` materializer. Read that descriptor for asset names, root attributes, slots, hooks, and state ownership instead of hard-coding them.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
## Use the product's native framework
|
|
42
|
+
|
|
43
|
+
Inspect the product and selected design-system source before choosing how screens render. Match their implementation framework exactly:
|
|
44
|
+
|
|
45
|
+
- React components render through the product's React runtime and routes.
|
|
46
|
+
- Vue components render through the product's Vue runtime and routes.
|
|
47
|
+
- KetJS server components render on a KetJS server. Do not reconstruct their output with client-side DOM builders or handwritten HTML strings.
|
|
48
|
+
- Apply the equivalent rule to any other declared framework. Use static HTML only when the product is actually static HTML, no framework exists, or the user explicitly requests static HTML.
|
|
49
|
+
|
|
50
|
+
Choose the existing product framework first. If there is no product implementation yet, use the selected design system's canonical framework. If neither declares a framework, static HTML is the fallback. Preserve the rendering model as well as the library name: server components must render on their server, while client components remain native client components. If the product and design system require incompatible frameworks and no documented adapter exists, pause for a design-system/framework decision instead of translating either implementation.
|
|
51
|
+
|
|
52
|
+
Do not create a second component representation for Atlas. In particular, do not translate framework components into a KetAtlas-specific component JSON DSL, duplicate their markup in `screens/`, or imitate them with local CSS. Fix an incorrect canonical design-system component or adapter at its source before continuing; do not hide the mismatch in an Atlas-only override.
|
|
53
|
+
|
|
54
|
+
Separate screen presentation from business behavior. A screen presenter receives a deterministic view model and composes the real design-system components. Atlas routes call that presenter with fixtures; production routes call the same presenter with real data, permissions, and actions. Atlas may select fixture states through routes or query parameters, but it does not own product markup.
|
|
55
|
+
|
|
56
|
+
When a workspace contains multiple atlases, put reusable presenters, component compositions, fixture factories, tokens, and Atlas route helpers in one shared framework module outside the individual `.ketatlas` bundles. Each bundle owns only its flow graph, per-atlas fixture/state declarations, renderer sidecar, and documentation. Never copy shared component or style files from one atlas to another. Prefer one native renderer application that exposes namespaced `screenBasePath` routes for all atlases.
|
|
57
|
+
|
|
58
|
+
Keep static atlas bundles self-contained and dependency-free. For framework-native atlases, reuse the product's existing package manifest, lockfile, framework server, and shared UI source outside the atlas bundle; do not add another package installation or bundler inside `.ketatlas`. Materialize or copy permitted assets only for a genuinely static product. Record the selected system, implementation framework, shared module location, source URL/path, pinned version or commit when known, adapter strategy, and any license or fidelity limitation in the bundle README.
|
|
59
|
+
|
|
60
|
+
### Declare the native renderer
|
|
61
|
+
|
|
62
|
+
Place `atlas.renderer.json` beside `atlas.json`. This sidecar is the executable renderer contract and does not change the version 1 workflow schema. Use an argument array, never a shell command string:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"$schema": "https://unpkg.com/ketatlas@0.4.0/renderer.schema.json",
|
|
67
|
+
"version": 1,
|
|
68
|
+
"framework": "react",
|
|
69
|
+
"command": ["npm", "run", "atlas:serve", "--", "--host", "{host}", "--port", "{port}"],
|
|
70
|
+
"cwd": "../..",
|
|
71
|
+
"readyPath": "/__atlas/ready",
|
|
72
|
+
"screenBasePath": "/__atlas/customer-care/"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`cwd` is relative to the bundle. KetAtlas substitutes `{host}`, `{port}`, and `{atlasDirectory}` and also provides `KETATLAS_HOST`, `KETATLAS_HTML_PORT`, and `KETATLAS_PROJECT_DIR`. `readyPath` must return HTTP 2xx when the framework server is ready. `screenBasePath` is origin-relative and ends in `/`; screen URLs in `atlas.json` resolve beneath it. Multiple atlases may use the same command and shared renderer source while choosing different namespaced base paths.
|
|
77
|
+
|
|
78
|
+
Start the viewer and HTML renderer on separate loopback ports:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
npx --yes ketatlas@0.4.0 serve ./tasks/mockups.ketatlas --renderer --port 60550 --html-port 60551
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`--renderer` is an explicit trust boundary because it executes the declared local command. The viewer remains on the first port; framework HTML, scripts, styles, assets, and same-origin requests stay on the second. Do not proxy or rebuild framework output in the viewer process.
|
|
42
85
|
|
|
43
86
|
## Scaffold or extend
|
|
44
87
|
|
|
45
88
|
Node.js 22 or newer is required. For a new, empty destination:
|
|
46
89
|
|
|
47
90
|
```sh
|
|
48
|
-
npx --yes ketatlas scaffold ./tasks/mockups.ketatlas --template basic
|
|
91
|
+
npx --yes ketatlas@0.4.0 scaffold ./tasks/mockups.ketatlas --template basic
|
|
49
92
|
```
|
|
50
93
|
|
|
51
94
|
Choose `basic` for mobile, `web` for desktop, or `process` for steps without UI. These are starting examples, not required product flows. Replace their sample content with the requested product.
|
|
52
95
|
|
|
53
|
-
For an existing atlas, read
|
|
96
|
+
For an existing atlas, read its JSON, renderer contract, and referenced framework source or static screen files. Preserve useful IDs and URLs; scaffold refuses a nonempty directory and has no `--force` option. Pin the CLI version in the README commands or use a global installation. A consumer atlas does not need a local KetAtlas installation.
|
|
54
97
|
|
|
55
98
|
### Backfill legacy projects
|
|
56
99
|
|
|
@@ -75,7 +118,9 @@ tasks/mockups.ketatlas/
|
|
|
75
118
|
README.md Run commands, flow coverage, assumptions, verification
|
|
76
119
|
```
|
|
77
120
|
|
|
78
|
-
|
|
121
|
+
A framework-native bundle replaces `screens/` and copied styles with `atlas.renderer.json`; its actual routes, shared presenters, fixtures, and styles stay in the product's framework source. Multiple bundles should point to the same shared renderer module instead of growing parallel component implementations.
|
|
122
|
+
|
|
123
|
+
For static mode, keep product assets inside the served directory when practical. For native mode, deliver JSON/schema, `atlas.renderer.json`, per-atlas fixtures or declarations, and documentation while changing shared framework source in its owning product module. Do not scaffold a `package.json`, lockfile, `node_modules`, asset build scripts, or a copied viewer/test harness in the atlas folder just to use KetAtlas. The installed CLI supplies scaffold, serve, validate, and audit. Browser verification can use the agent's external tooling. Preserve unrelated application tooling when extending an existing repository.
|
|
79
124
|
|
|
80
125
|
Do not create a wrapper viewer or a custom canvas: `serve ./tasks/mockups.ketatlas` provides it.
|
|
81
126
|
|
|
@@ -97,7 +142,7 @@ Core rules:
|
|
|
97
142
|
- Set `column` and `row` explicitly for branches. Both are integers from 0 to 100; no two nodes in a flow share a cell. This is a grid, not an automatic graph layout engine.
|
|
98
143
|
- Set the atlas `viewport` or a screen override to the intended layout size. The mobile default is 390 × 844; choose desktop dimensions from the brief. Width and height are integers from 160 to 4096.
|
|
99
144
|
|
|
100
|
-
Example structure
|
|
145
|
+
Example static structure; for native routes, replace the `.html` paths with paths relative to `screenBasePath`:
|
|
101
146
|
|
|
102
147
|
```json
|
|
103
148
|
{
|
|
@@ -144,23 +189,25 @@ Example structure, to adapt to the actual product and HTML files:
|
|
|
144
189
|
|
|
145
190
|
## Build the actual screens
|
|
146
191
|
|
|
147
|
-
|
|
192
|
+
For static HTML mode, make each screen a complete HTML document with a viewport meta tag and `body { margin: 0; }`. For framework-native mode, make every declared screen URL a directly addressable framework route whose response is a complete document at the selected state. Keep product content padding, but do not wrap the page in a second phone bezel, presentation frame, reviewer sidebar, or outer mockup margin. The iframe is the screen boundary.
|
|
148
193
|
|
|
149
|
-
Use shared
|
|
194
|
+
Use the shared framework presenters/styles and deterministic fixtures. States referenced by node URLs must render directly when opened or refreshed, without requiring a previous login or click. Implement the relevant buttons, forms, validation feedback, and navigation in the native framework: graph arrows do not wire screen interactions automatically.
|
|
150
195
|
|
|
151
|
-
|
|
196
|
+
Static screens use an opaque sandbox by default. Native renderer mode places screens on a distinct origin and enables `allow-same-origin` there so framework modules and same-origin assets work without granting access to the viewer origin. Keep framework routes trusted and loopback-only. Test in **Try this screen**, not just a standalone tab.
|
|
152
197
|
|
|
153
198
|
## Validate and hand off
|
|
154
199
|
|
|
155
200
|
Run from the user's project with the same root for audit and serve:
|
|
156
201
|
|
|
157
202
|
```sh
|
|
158
|
-
npx --yes ketatlas discover . --json
|
|
159
|
-
npx --yes ketatlas validate ./tasks/mockups.ketatlas
|
|
160
|
-
npx --yes ketatlas audit ./tasks/mockups.ketatlas --strict
|
|
161
|
-
npx --yes ketatlas serve ./tasks/mockups.ketatlas
|
|
203
|
+
npx --yes ketatlas@0.4.0 discover . --json
|
|
204
|
+
npx --yes ketatlas@0.4.0 validate ./tasks/mockups.ketatlas
|
|
205
|
+
npx --yes ketatlas@0.4.0 audit ./tasks/mockups.ketatlas --strict
|
|
206
|
+
npx --yes ketatlas@0.4.0 serve ./tasks/mockups.ketatlas --renderer --port 60550 --html-port 60551
|
|
162
207
|
```
|
|
163
208
|
|
|
209
|
+
Omit `--renderer` and `--html-port` only for a genuinely static atlas. Audit validates a discovered `atlas.renderer.json` and treats screen URLs as framework routes, but deliberately does not execute the command. The browser check must therefore prove the ready route, every requested screen state, framework scripts/styles, and important interactions through the two-port viewer.
|
|
210
|
+
|
|
164
211
|
Use `--root .` on both audit and serve if screens or assets intentionally live outside the atlas directory but inside the project. Use `--port 4180` or another free port when necessary. Refresh after file edits; there is no hot reload.
|
|
165
212
|
|
|
166
213
|
For local projects, fix structural errors, missing assets, and unreachable nodes until strict audit passes. Remote screen/assets URLs produce warnings and are not fetched by audit: when those are intentional, run the normal audit, report the warnings, and verify embedding separately rather than claiming a strict pass. With `audit --json --strict`, check the exit status and warnings as well as `valid`.
|
package/src/config.js
CHANGED
|
@@ -166,7 +166,7 @@ export class AtlasValidationError extends Error {
|
|
|
166
166
|
this.errors = result.errors;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
export function normalizeAtlas(input, baseURL) {
|
|
169
|
+
export function normalizeAtlas(input, baseURL, screenBaseURL = baseURL) {
|
|
170
170
|
const result = validateAtlas(input);
|
|
171
171
|
if (!result.valid) throw new AtlasValidationError(result);
|
|
172
172
|
const data = structuredClone(input);
|
|
@@ -174,7 +174,7 @@ export function normalizeAtlas(input, baseURL) {
|
|
|
174
174
|
data.viewport ??= { width: 390, height: 844 };
|
|
175
175
|
data.screens ??= [];
|
|
176
176
|
for (const s of data.screens) {
|
|
177
|
-
s.url = safeURL(s.url,
|
|
177
|
+
s.url = safeURL(s.url, screenBaseURL);
|
|
178
178
|
s.viewport ??= { ...data.viewport };
|
|
179
179
|
}
|
|
180
180
|
for (const f of data.flows) {
|
|
@@ -186,7 +186,7 @@ export function normalizeAtlas(input, baseURL) {
|
|
|
186
186
|
n.column ??= i;
|
|
187
187
|
n.row ??= 0;
|
|
188
188
|
n.type ??= n.screen ? "screen" : "note";
|
|
189
|
-
if (n.url) n.url = safeURL(n.url, baseURL);
|
|
189
|
+
if (n.url) n.url = safeURL(n.url, n.type === "screen" ? screenBaseURL : baseURL);
|
|
190
190
|
});
|
|
191
191
|
f.edges.forEach((edge) => {
|
|
192
192
|
edge.kind ??= "primary";
|
package/src/index.d.ts
CHANGED
|
@@ -114,6 +114,8 @@ export interface AtlasOptions {
|
|
|
114
114
|
progressToken?: string;
|
|
115
115
|
/** Relative screen URLs resolve against this URL. loadAtlas uses the JSON URL by default. */
|
|
116
116
|
baseURL?: string;
|
|
117
|
+
/** Override only relative screen and screen-node URLs, for a separate framework renderer. */
|
|
118
|
+
screenBaseURL?: string;
|
|
117
119
|
/** Public directory containing styles/ and assets/, ending in /. Needed when bundling the JS. */
|
|
118
120
|
assetBaseURL?: string;
|
|
119
121
|
initialFlow?: string;
|
package/src/index.js
CHANGED
|
@@ -26,7 +26,11 @@ const cameraMotion = Object.freeze({
|
|
|
26
26
|
export function createAtlas(container, input, options = {}) {
|
|
27
27
|
if (!(container instanceof HTMLElement))
|
|
28
28
|
throw new TypeError("createAtlas requires an HTML element.");
|
|
29
|
-
const config = normalizeAtlas(
|
|
29
|
+
const config = normalizeAtlas(
|
|
30
|
+
input,
|
|
31
|
+
options.baseURL || document.baseURI,
|
|
32
|
+
options.screenBaseURL || options.baseURL || document.baseURI,
|
|
33
|
+
);
|
|
30
34
|
const progressData = requireProgress(options.progress || emptyProgress(), config);
|
|
31
35
|
const maxPreviews = options.maxPreviews ?? 24,
|
|
32
36
|
previewThreshold = options.previewThreshold ?? 0.3;
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
This directory must keep its `.ketatlas` suffix. Run with Node.js 22 or newer:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npx ketatlas serve .
|
|
7
|
-
npx ketatlas audit . --strict
|
|
6
|
+
npx --yes ketatlas@0.4.0 serve .
|
|
7
|
+
npx --yes ketatlas@0.4.0 audit . --strict
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Edit `atlas.json` to change nodes, edges, and screen URLs. Screen URLs are relative to that file. Refresh the browser after editing. No wrapper HTML, package manifest, lockfile, node_modules, or build step is needed.
|
|
10
|
+
This starter is intentionally static. Edit `atlas.json` to change nodes, edges, and screen URLs. Screen URLs are relative to that file. Refresh the browser after editing. No wrapper HTML, package manifest, lockfile, node_modules, or build step is needed. For React, Vue, KetJS, or another framework, use atlas.renderer.json and shared framework presenters instead of copying this static implementation.
|
|
11
11
|
|
|
12
|
-
Edit the HTML files in screens/ to replace the sample product. styles/design-system.css is generated from the pinned KetJS design system; do not manually fork its tokens.
|
|
12
|
+
Edit the HTML files in screens/ to replace the sample static product. styles/design-system.css is generated from the pinned KetJS design system; do not manually fork its tokens.
|
|
13
13
|
|
|
14
|
-
Open **Screens** in the viewer to review or edit delivery progress. Saving writes atlas.progress.json beside the workflow; use --read-only to disable editing. You can initialize records with ketatlas progress . --init. Keep this directory in your own project repository. The schema provides editor completion. See https://github.com/ketvietlab/ketatlas for the full API, CLI, and configuration reference.
|
|
14
|
+
Open **Screens** in the viewer to review or edit delivery progress. Saving writes atlas.progress.json beside the workflow; use --read-only to disable editing. You can initialize records with npx --yes ketatlas@0.4.0 progress . --init. Keep this directory in your own project repository. The schema provides editor completion. See https://github.com/ketvietlab/ketatlas for the full API, CLI, and configuration reference.
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
This directory must keep its `.ketatlas` suffix. Run with Node.js 22 or newer:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npx ketatlas serve .
|
|
7
|
-
npx ketatlas audit . --strict
|
|
6
|
+
npx --yes ketatlas@0.4.0 serve .
|
|
7
|
+
npx --yes ketatlas@0.4.0 audit . --strict
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Edit `atlas.json` to change nodes, edges, and screen URLs. Screen URLs are relative to that file. Refresh the browser after editing. No wrapper HTML, package manifest, lockfile, node_modules, or build step is needed.
|
|
10
|
+
This starter is intentionally static. Edit `atlas.json` to change nodes, edges, and screen URLs. Screen URLs are relative to that file. Refresh the browser after editing. No wrapper HTML, package manifest, lockfile, node_modules, or build step is needed. For React, Vue, KetJS, or another framework, use atlas.renderer.json and shared framework presenters instead of copying this static implementation.
|
|
11
11
|
|
|
12
12
|
This template models a process without HTML screens. Add a screen registry and screen nodes when needed.
|
|
13
13
|
|
|
14
|
-
Open **Screens** in the viewer to review or edit delivery progress. Saving writes atlas.progress.json beside the workflow; use --read-only to disable editing. You can initialize records with ketatlas progress . --init. Keep this directory in your own project repository. The schema provides editor completion. See https://github.com/ketvietlab/ketatlas for the full API, CLI, and configuration reference.
|
|
14
|
+
Open **Screens** in the viewer to review or edit delivery progress. Saving writes atlas.progress.json beside the workflow; use --read-only to disable editing. You can initialize records with npx --yes ketatlas@0.4.0 progress . --init. Keep this directory in your own project repository. The schema provides editor completion. See https://github.com/ketvietlab/ketatlas for the full API, CLI, and configuration reference.
|
package/templates/web/README.md
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
This directory must keep its `.ketatlas` suffix. Run with Node.js 22 or newer:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npx ketatlas serve .
|
|
7
|
-
npx ketatlas audit . --strict
|
|
6
|
+
npx --yes ketatlas@0.4.0 serve .
|
|
7
|
+
npx --yes ketatlas@0.4.0 audit . --strict
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Edit `atlas.json` to change nodes, edges, and screen URLs. Screen URLs are relative to that file. Refresh the browser after editing. No wrapper HTML, package manifest, lockfile, node_modules, or build step is needed.
|
|
10
|
+
This starter is intentionally static. Edit `atlas.json` to change nodes, edges, and screen URLs. Screen URLs are relative to that file. Refresh the browser after editing. No wrapper HTML, package manifest, lockfile, node_modules, or build step is needed. For React, Vue, KetJS, or another framework, use atlas.renderer.json and shared framework presenters instead of copying this static implementation.
|
|
11
11
|
|
|
12
|
-
Edit the HTML files in screens/ to replace the sample product. styles/design-system.css is generated from the pinned KetJS design system; do not manually fork its tokens.
|
|
12
|
+
Edit the HTML files in screens/ to replace the sample static product. styles/design-system.css is generated from the pinned KetJS design system; do not manually fork its tokens.
|
|
13
13
|
|
|
14
|
-
Open **Screens** in the viewer to review or edit delivery progress. Saving writes atlas.progress.json beside the workflow; use --read-only to disable editing. You can initialize records with ketatlas progress . --init. Keep this directory in your own project repository. The schema provides editor completion. See https://github.com/ketvietlab/ketatlas for the full API, CLI, and configuration reference.
|
|
14
|
+
Open **Screens** in the viewer to review or edit delivery progress. Saving writes atlas.progress.json beside the workflow; use --read-only to disable editing. You can initialize records with npx --yes ketatlas@0.4.0 progress . --init. Keep this directory in your own project repository. The schema provides editor completion. See https://github.com/ketvietlab/ketatlas for the full API, CLI, and configuration reference.
|