ketatlas 0.2.4 → 0.3.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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ - 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.
6
+ - Accelerate drag, wheel, keyboard and pinch camera controls while preserving pointer-anchored zoom.
7
+ - Require agents to confirm a design system before visual mockup work, with Auto defaulting to Két Design System and explicit Carbon, Primer, Fluent 2, no-system, and custom-source options.
8
+
9
+ ## 0.2.5
10
+
11
+ - Show separate Checks and Implemented percentage badges per workflow. Implemented includes Verified screens; project verification remains separate on the top bar. Display a dash for Checks when no checklist is recorded.
12
+
3
13
  ## 0.2.4
4
14
 
5
15
  - Anchor the progress detail header directly inside the modal top border and scroll only the form beneath it.
package/README.md CHANGED
@@ -17,21 +17,22 @@ https://github.com/user-attachments/assets/13f24fc8-7b8e-4bda-9e02-364c120ee163
17
17
  [KetAtlas is available on npm](https://www.npmjs.com/package/ketatlas). Use Node.js 22+ and run it from any directory:
18
18
 
19
19
  ```sh
20
- npx --yes ketatlas@0.2.4 scaffold my-atlas --template web
21
- npx --yes ketatlas@0.2.4 serve my-atlas/atlas.json
22
- npx --yes ketatlas@0.2.4 audit my-atlas/atlas.json --strict
20
+ npx --yes ketatlas scaffold my-atlas.ketatlas --template web
21
+ npx --yes ketatlas discover . --json
22
+ npx --yes ketatlas serve my-atlas.ketatlas
23
+ npx --yes ketatlas audit my-atlas.ketatlas --strict
23
24
  ```
24
25
 
25
26
  Or install the CLI once for your user account:
26
27
 
27
28
  ```sh
28
- npm install --global ketatlas@0.2.4
29
- ketatlas scaffold my-atlas
30
- ketatlas serve my-atlas/atlas.json
31
- ketatlas audit my-atlas/atlas.json --strict
29
+ npm install --global ketatlas
30
+ ketatlas scaffold my-atlas.ketatlas
31
+ ketatlas serve my-atlas.ketatlas
32
+ ketatlas audit my-atlas.ketatlas --strict
32
33
  ```
33
34
 
34
- The consumer folder contains `atlas.json`, its schema, product HTML/CSS/JavaScript, local assets, and documentation. It needs no `package.json`, lockfile, `node_modules`, build step, or copy of the viewer. The CLI supplies scaffold, serving, and static audit from its own installation. `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.
35
+ The consumer bundle is named `<name>.ketatlas/` and contains `atlas.json`, its schema, product HTML/CSS/JavaScript, local assets, and documentation. The stable directory suffix lets desktop tools discover atlases without parsing unrelated JSON. A bundle needs no `package.json`, lockfile, `node_modules`, build step, or copy of the viewer. The CLI supplies scaffold, discovery, serving, and static audit from its own installation. `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.
35
36
 
36
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. Framework tooling and tests stay in the KetAtlas repository.
37
38
 
@@ -81,12 +82,13 @@ Open **Screens** to filter screen progress, review blockers and edit acceptance
81
82
 
82
83
  ## Commands
83
84
 
84
- | Command | Purpose |
85
- | ----------------------- | ------------------------------------------------------------------------------------------ |
86
- | `scaffold <directory>` | Create a project from `basic`, `web`, or `process`. Refuses to overwrite existing content. |
87
- | `serve <atlas.json>` | Start the viewer and serve local screens. Defaults to port 4178 on localhost. |
88
- | `audit <atlas.json>` | Check configuration, reachability, local files, and literal HTML/CSS references. |
89
- | `validate <atlas.json>` | Validate configuration only, without reading screen files. |
85
+ | Command | Purpose |
86
+ | -------------------------- | -------------------------------------------------------------------------------- |
87
+ | `scaffold <name.ketatlas>` | Create a bundle from `basic`, `web`, or `process`. Refuses existing content. |
88
+ | `discover <directory>` | Find valid `*.ketatlas/atlas.json` bundles and report invalid bundles. |
89
+ | `serve <bundle\|json>` | Start the viewer and serve local screens. Defaults to port 4178 on localhost. |
90
+ | `audit <bundle\|json>` | Check configuration, reachability, local files, and literal HTML/CSS references. |
91
+ | `validate <bundle\|json>` | Validate configuration only, without reading screen files. |
90
92
 
91
93
  Use `--help` for options, `--root` when assets live above the JSON directory, and `audit --json` for CI reports. [Full CLI reference →](docs/cli.md)
92
94
 
@@ -114,7 +116,7 @@ npm ci
114
116
  npm run dev
115
117
  ```
116
118
 
117
- The playground includes mobile sign-in, desktop approval, and a fulfilment process. These development dependencies are not required in consumer projects. To try an unpublished checkout, use `npx --yes --package ~/dev/ketatlas ketatlas serve /path/to/my-atlas/atlas.json`.
119
+ The playground includes mobile sign-in, desktop approval, and a fulfilment process. These development dependencies are not required in consumer projects. To try an unpublished checkout, use `npx --yes --package ~/dev/ketatlas ketatlas serve /path/to/my-atlas.ketatlas`.
118
120
 
119
121
  Stable version increases merged into `develop` are automatically published after CI verifies the package. See [Releasing](docs/releasing.md).
120
122
 
@@ -0,0 +1,90 @@
1
+ import { readFile, readdir, realpath, stat } from "node:fs/promises";
2
+ import { basename, join, resolve } from "node:path";
3
+ import { validateAtlas } from "../src/config.js";
4
+
5
+ export const atlasDirectorySuffix = ".ketatlas";
6
+ export const atlasManifestName = "atlas.json";
7
+
8
+ const excludedDirectories = new Set([
9
+ ".git",
10
+ ".cache",
11
+ ".next",
12
+ ".venv",
13
+ "build",
14
+ "dist",
15
+ "node_modules",
16
+ "vendor",
17
+ ]);
18
+
19
+ export function isAtlasDirectory(path) {
20
+ return basename(resolve(path)).toLowerCase().endsWith(atlasDirectorySuffix);
21
+ }
22
+
23
+ export function requireAtlasDirectory(path) {
24
+ const destination = resolve(path);
25
+ if (!isAtlasDirectory(destination))
26
+ throw new Error(`Atlas directories must end in ${atlasDirectorySuffix}.`);
27
+ return destination;
28
+ }
29
+
30
+ export async function resolveAtlasFile(target) {
31
+ const absolute = resolve(target);
32
+ const info = await stat(absolute);
33
+ if (!info.isDirectory()) return absolute;
34
+ if (!isAtlasDirectory(absolute))
35
+ throw new Error(`Atlas directories must end in ${atlasDirectorySuffix}.`);
36
+ return join(absolute, atlasManifestName);
37
+ }
38
+
39
+ async function describe(directory) {
40
+ const path = join(directory, atlasManifestName);
41
+ let config;
42
+ try {
43
+ config = JSON.parse(await readFile(path, "utf8"));
44
+ } catch (error) {
45
+ return { error: { path, message: error.message } };
46
+ }
47
+ const validation = validateAtlas(config);
48
+ if (!validation.valid)
49
+ return {
50
+ error: {
51
+ path,
52
+ message: validation.errors.map((issue) => `${issue.path}: ${issue.message}`).join("; "),
53
+ },
54
+ };
55
+ return {
56
+ atlas: {
57
+ path,
58
+ directory,
59
+ title: config.title,
60
+ screens: (config.screens || []).map(({ id, title, url }) => ({ id, title, url })),
61
+ flowCount: config.flows.length,
62
+ },
63
+ };
64
+ }
65
+
66
+ export async function discoverAtlases(root) {
67
+ const workspace = await realpath(resolve(root));
68
+ if (!(await stat(workspace)).isDirectory())
69
+ throw new Error("Discovery root must be a directory.");
70
+ const atlases = [],
71
+ errors = [],
72
+ directories = [workspace];
73
+ while (directories.length) {
74
+ const directory = directories.pop();
75
+ if (isAtlasDirectory(directory)) {
76
+ const result = await describe(directory);
77
+ if (result.atlas) atlases.push(result.atlas);
78
+ else errors.push(result.error);
79
+ continue;
80
+ }
81
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
82
+ if (!entry.isDirectory() || entry.isSymbolicLink() || excludedDirectories.has(entry.name))
83
+ continue;
84
+ directories.push(join(directory, entry.name));
85
+ }
86
+ }
87
+ atlases.sort((a, b) => a.path.localeCompare(b.path));
88
+ errors.sort((a, b) => a.path.localeCompare(b.path));
89
+ return { version: 1, root: workspace, atlases, errors };
90
+ }
package/bin/ketatlas.js CHANGED
@@ -8,21 +8,29 @@ import { validateAtlas } from "../src/config.js";
8
8
  import { serve } from "./server.js";
9
9
  import { serveAtlas } from "./viewer.js";
10
10
  import { auditAtlas } from "./audit.js";
11
+ import {
12
+ discoverAtlases,
13
+ isAtlasDirectory,
14
+ requireAtlasDirectory,
15
+ resolveAtlasFile,
16
+ } from "./discovery.js";
11
17
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
12
18
  const help = `KetAtlas — scaffold, serve, and audit HTML workflow maps
13
19
 
14
- ketatlas scaffold <directory> [--template basic|web|process]
15
- ketatlas serve <atlas.json> [--port 4178] [--root directory]
16
- ketatlas audit <atlas.json> [--root directory] [--json] [--strict]
17
- ketatlas validate <atlas.json>
18
- ketatlas progress <atlas.json> [--json] [--init]
19
- ketatlas progress <atlas.json> --set <screen-id> --record <record.json> --expect <revision>
20
+ ketatlas scaffold <name.ketatlas> [--template basic|web|process]
21
+ ketatlas discover <directory> [--json]
22
+ ketatlas serve <name.ketatlas|atlas.json> [--port 4178] [--root directory]
23
+ ketatlas audit <name.ketatlas|atlas.json> [--root directory] [--json] [--strict]
24
+ ketatlas validate <name.ketatlas|atlas.json>
25
+ ketatlas progress <name.ketatlas|atlas.json> [--json] [--init]
26
+ ketatlas progress <name.ketatlas|atlas.json> --set <screen-id> --record <record.json> --expect <revision>
20
27
  ketatlas --version
21
28
 
22
29
  Examples:
23
- npx ketatlas scaffold my-atlas
24
- npx ketatlas serve my-atlas/atlas.json
25
- npx ketatlas audit my-atlas/atlas.json --strict
30
+ npx ketatlas scaffold my-atlas.ketatlas
31
+ npx ketatlas discover . --json
32
+ npx ketatlas serve my-atlas.ketatlas
33
+ npx ketatlas audit my-atlas.ketatlas --strict
26
34
 
27
35
  Serve binds to 127.0.0.1. No HTML wrapper, build, account, or backend required.
28
36
  `;
@@ -61,7 +69,7 @@ async function main(args) {
61
69
  template = options.template || "basic";
62
70
  if (!["basic", "web", "process"].includes(template))
63
71
  throw new Error("Templates: basic, web, process.");
64
- const destination = resolve(target);
72
+ const destination = requireAtlasDirectory(target);
65
73
  let existing;
66
74
  try {
67
75
  existing = await readdir(destination);
@@ -79,10 +87,25 @@ async function main(args) {
79
87
  errorOnExist: true,
80
88
  });
81
89
  console.log(
82
- `Created ${destination}\n\nNext: npx ketatlas serve "${join(destination, "atlas.json")}"\nEdit atlas.json to make it yours.`,
90
+ `Created ${destination}\n\nNext: npx ketatlas serve "${destination}"\nEdit atlas.json to make it yours.`,
83
91
  );
84
92
  return;
85
93
  }
94
+ if (command === "discover") {
95
+ const { target, options } = parse(rest, { json: "boolean" }),
96
+ result = await discoverAtlases(target);
97
+ if (options.json) console.log(JSON.stringify(result, null, 2));
98
+ else {
99
+ for (const atlas of result.atlases)
100
+ console.log(`${atlas.title}\t${atlas.path}\t${atlas.screens.length} screens`);
101
+ for (const error of result.errors) console.error(`ERROR ${error.path}: ${error.message}`);
102
+ console.log(
103
+ `${result.atlases.length} atlas${result.atlases.length === 1 ? "" : "es"} in ${result.root}`,
104
+ );
105
+ }
106
+ if (result.errors.length) process.exitCode = 1;
107
+ return;
108
+ }
86
109
  if (command === "audit") {
87
110
  const { target, options } = parse(rest, {
88
111
  root: "string",
@@ -90,7 +113,7 @@ async function main(args) {
90
113
  strict: "boolean",
91
114
  output: "string",
92
115
  });
93
- const report = await auditAtlas(target, options),
116
+ const report = await auditAtlas(await resolveAtlasFile(target), options),
94
117
  pass = report.valid && (!options.strict || report.warnings.length === 0);
95
118
  if (options.output)
96
119
  await writeFile(resolve(options.output), JSON.stringify(report, null, 2) + "\n", {
@@ -109,7 +132,7 @@ async function main(args) {
109
132
  }
110
133
  if (command === "validate") {
111
134
  const { target } = parse(rest, {}),
112
- config = JSON.parse(await readFile(resolve(target), "utf8")),
135
+ config = JSON.parse(await readFile(await resolveAtlasFile(target), "utf8")),
113
136
  result = validateAtlas(config);
114
137
  for (const issue of result.errors) console.error(`ERROR ${issue.path}: ${issue.message}`);
115
138
  for (const issue of result.warnings) console.warn(`WARN ${issue.path}: ${issue.message}`);
@@ -130,7 +153,7 @@ async function main(args) {
130
153
  record: "string",
131
154
  expect: "string",
132
155
  });
133
- const file = resolve(target),
156
+ const file = await resolveAtlasFile(target),
134
157
  atlas = JSON.parse(await readFile(file, "utf8"));
135
158
  const valid = validateAtlas(atlas);
136
159
  if (!valid.valid) throw new Error(valid.errors.map((e) => e.message).join("; "));
@@ -180,9 +203,14 @@ async function main(args) {
180
203
  const directory = (await stat(resolve(target))).isDirectory();
181
204
  if (directory && options.root)
182
205
  throw new Error("--root is only needed when serving an atlas JSON file.");
183
- const server = directory
184
- ? await serve(target, { port })
185
- : await serveAtlas(target, { port, root: options.root, readOnly: options["read-only"] });
206
+ const server =
207
+ directory && !isAtlasDirectory(target)
208
+ ? await serve(target, { port })
209
+ : await serveAtlas(await resolveAtlasFile(target), {
210
+ port,
211
+ root: options.root,
212
+ readOnly: options["read-only"],
213
+ });
186
214
  console.log(
187
215
  `KetAtlas: http://127.0.0.1:${server.address().port}\nServing ${resolve(target)}\nPress Ctrl+C to stop.`,
188
216
  );
@@ -36,10 +36,12 @@ 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 data and product screen assets: JSON/schema, HTML/CSS/JavaScript, local assets, and documentation. Do not add a package manifest, lockfile, node_modules, or copied viewer/test tooling to make the atlas runnable. Use a global KetAtlas installation or a version-pinned npx command. Browser checks can use the agent's existing tooling outside the atlas folder.
39
+ The deliverable is a `<name>.ketatlas/` bundle containing data and product screen assets: JSON/schema, HTML/CSS/JavaScript, local assets, and documentation. Do not add a package manifest, lockfile, node_modules, or copied viewer/test tooling to make the atlas runnable. Use a global KetAtlas installation or a version-pinned npx command. 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 and uses the selected system's actual tokens, components, assets, and patterns, then records the source and integration strategy in the atlas README.
44
+
43
45
  ## Use a saved brief for larger projects
44
46
 
45
47
  Save this template as `mockup-brief.md`, fill in the relevant fields, and ask: **“Use the KetAtlas skill to implement mockup-brief.md.”** The brief stays Markdown; do not add its planning fields to `atlas.json`.
@@ -52,7 +54,8 @@ Save this template as `mockup-brief.md`, fill in the relevant fields, and ask: *
52
54
  - Output directory:
53
55
  - Target platforms and viewport sizes:
54
56
  - Product content language:
55
- - Design system, existing HTML, and reference files/URLs:
57
+ - Design system choice (Auto/Két/Carbon/Primer/Fluent 2/none/custom source):
58
+ - Existing HTML and reference files/URLs:
56
59
  - Requested flows and screen inventory:
57
60
  - Relevant loading, empty, validation, error, and recovery states:
58
61
  - Interactions to demonstrate and synthetic demo inputs:
@@ -76,10 +79,11 @@ For an existing prototype, ask the agent to reuse its HTML and add or update the
76
79
  ## Review the result
77
80
 
78
81
  ```sh
79
- npx ketatlas serve ./tasks/mobile/atlas.json
80
- npx ketatlas audit ./tasks/mobile/atlas.json --strict
82
+ npx ketatlas discover . --json
83
+ npx ketatlas serve ./tasks/mobile.ketatlas
84
+ npx ketatlas audit ./tasks/mobile.ketatlas --strict
81
85
  ```
82
86
 
83
87
  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.
84
88
 
85
- The skill lives in this repository so teams can review and evolve it alongside the schema and CLI. It works with the published `ketatlas@0.1.1` format; the skill itself can be distributed through GitHub without waiting for a new npm release.
89
+ 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.
@@ -3,8 +3,9 @@
3
3
  KetAtlas is a static browser viewer plus a Node.js command-line toolkit. There is no database, account service or runtime npm dependency. 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
- atlas.json + screen HTML
6
+ <name>.ketatlas/atlas.json + screen HTML
7
7
  │
8
+ ├── discover → workspace bundle catalog
8
9
  ├── validate / audit → diagnostics and CI exit code
9
10
  │
10
11
  └── serve → built-in viewer page
@@ -17,7 +18,7 @@ atlas.json + screen HTML
17
18
 
18
19
  ## Consumer boundary
19
20
 
20
- KetAtlas is installed globally or executed through npx. Consumers provide JSON, a schema, HTML/CSS/JavaScript mock screens, assets, and documentation. They do not need a Node package, lockfile, development dependencies, viewer implementation, or build/test scripts to scaffold, serve, or audit a map.
21
+ KetAtlas is installed globally or executed through npx. Consumers provide a `<name>.ketatlas/` bundle containing JSON, a schema, HTML/CSS/JavaScript mock screens, assets, and documentation. The suffix is the discovery boundary: tools inspect only its direct `atlas.json`, not arbitrary workspace JSON. Consumers do not need a Node package, lockfile, development dependencies, viewer implementation, or build/test scripts to scaffold, discover, serve, or audit a map.
21
22
 
22
23
  The package owns the CLI, viewer, validation, and framework verification. A product may have its own application tests elsewhere; those are independent of the map format. Static audit does not simulate product interactions. Agent browser checks can run through external tooling without adding a test harness to the delivered atlas folder.
23
24
 
@@ -32,7 +33,7 @@ The package owns the CLI, viewer, validation, and framework verification. A prod
32
33
  | `src/index.d.ts` | Public TypeScript declarations. |
33
34
  | `styles/` | Viewer layout and generated canonical design-system bundles. |
34
35
  | `assets/` | Local fonts, licenses and provenance manifest. |
35
- | `bin/` | Scaffold, JSON serving, validation and static audit. |
36
+ | `bin/` | Bundle discovery, scaffold, serving, validation and audit. |
36
37
  | `templates/` | Self-contained starting projects included in the npm package. |
37
38
  | `examples/` | Maintainer playground with mobile, web and process flows. |
38
39
  | `scripts/` | Reproducible assets, templates, schema and package checks. |
@@ -42,7 +43,7 @@ The package owns the CLI, viewer, validation, and framework verification. A prod
42
43
 
43
44
  Flows are stacked on one canvas. A row and column grow to fit their largest node, so mobile and desktop aspect ratios can coexist. The layout respects authored grid positions. Arrows use orthogonal routes and a small lane offset; this is not an obstacle-avoiding graph-layout engine. Use nearby grid cells and concise labels for readable large diagrams.
44
45
 
45
- Camera changes use an animation frame and a CSS transform. 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.
46
+ 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.
46
47
 
47
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.
48
49
 
package/docs/authoring.md CHANGED
@@ -5,8 +5,8 @@ Start with a user goal: sign in, approve a purchase, or deliver an order. Give e
5
5
  ## 1. Create a starting point
6
6
 
7
7
  ```sh
8
- ketatlas scaffold ./customer-journeys --template basic
9
- ketatlas serve ./customer-journeys/atlas.json
8
+ ketatlas scaffold ./customer-journeys.ketatlas --template basic
9
+ ketatlas serve ./customer-journeys.ketatlas
10
10
  ```
11
11
 
12
12
  Open a card with a double-click. The preview is actual HTML; links and JavaScript inside it can work independently of the map. Edit those files before adding more nodes.
@@ -41,7 +41,7 @@ Click a node to highlight incoming/outgoing arrows and follow a next-step button
41
41
  ## 5. Audit before sharing
42
42
 
43
43
  ```sh
44
- ketatlas audit ./customer-journeys/atlas.json --strict
44
+ ketatlas audit ./customer-journeys.ketatlas --strict
45
45
  ```
46
46
 
47
47
  Keep the JSON and screens in your project's repository. Review changes in pull requests. Share the directory or run the same serve command in the recipient's checkout. No hosted account or service state is required.
package/docs/cli.md CHANGED
@@ -1,30 +1,41 @@
1
1
  # CLI reference
2
2
 
3
- Install once with `npm install --global ketatlas@0.1.1`, or prefix commands with `npx --yes ketatlas@0.1.1`. No dependency installation is required in the consumer directory. `node /path/to/ketatlas/bin/ketatlas.js` is also available to framework maintainers.
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
5
  A consumer keeps only its JSON/schema, product HTML/CSS/JavaScript, assets, and documentation. The package manifest, lockfile, development scripts, and browser test dependencies belong to the tool. 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
 
9
9
  ```sh
10
- ketatlas scaffold ./journeys
11
- ketatlas scaffold ./approvals --template web
12
- ketatlas scaffold ./operations --template process
10
+ ketatlas scaffold ./journeys.ketatlas
11
+ ketatlas scaffold ./approvals.ketatlas --template web
12
+ ketatlas scaffold ./operations.ketatlas --template process
13
13
  ```
14
14
 
15
15
  `basic` is the default: two interactive mobile pages and one flow. `web` contains desktop purchase approval pages. `process` contains notes and an external handoff, without HTML screens. Every template includes a local JSON Schema for editor completion and a README. Product HTML and its styles belong to the new project.
16
16
 
17
- `init` is an alias for `scaffold`. The destination must be missing or empty. Existing files are never overwritten; there is no `--force` flag.
17
+ `init` is an alias for `scaffold`. The destination must end in `.ketatlas` and be missing or empty. Existing files are never overwritten; there is no `--force` flag.
18
+
19
+ ## Discover bundles
20
+
21
+ ```sh
22
+ ketatlas discover .
23
+ ketatlas discover . --json
24
+ ```
25
+
26
+ Discovery walks the selected workspace for exact `*.ketatlas/atlas.json` bundles. It skips dependency, generated, cache, Git and vendor directories; it does not parse unrelated JSON. The JSON report contains `version`, `root`, `atlases`, and `errors`, including each atlas title, path, directory, screens and flow count. Invalid bundle manifests are reported and produce exit code 1.
27
+
28
+ Legacy manifest paths remain valid command arguments, but discovery intentionally ignores them. Migrate a self-contained legacy directory by renaming it to `<name>.ketatlas`; see the bundled skill for mixed-directory migration safeguards.
18
29
 
19
30
  ## Serve a JSON file
20
31
 
21
32
  ```sh
22
- ketatlas serve ./tasks/onboarding/atlas.json
23
- ketatlas serve ./tasks/onboarding/atlas.json --port 4180
24
- ketatlas serve ./tasks/onboarding/atlas.json --root .
33
+ ketatlas serve ./tasks/onboarding.ketatlas
34
+ ketatlas serve ./tasks/onboarding.ketatlas --port 4180
35
+ ketatlas serve ./legacy/atlas.json --root .
25
36
  ```
26
37
 
27
- The tool supplies the viewer page. It validates the JSON at startup, serves your files, and mounts the atlas at `/`. The default file root is the JSON file's directory. Choose `--root` when a relative URL points to a sibling directory outside that default root. The JSON must be inside the selected root.
38
+ The tool supplies the viewer page. It validates the JSON at startup, serves your files, and mounts the atlas at `/`. Passing a `.ketatlas` directory resolves its `atlas.json` and uses the bundle as the file root. Explicit legacy JSON paths still support `--root` when a relative URL points outside the manifest directory.
28
39
 
29
40
  Open the printed localhost URL. Changes appear after refreshing; there is no hot reload or authoring server state. Use `?flow=your-flow-id` or `?screen=your-screen-id` to open a specific part of the map. An unknown ID falls back to the first flow.
30
41
 
@@ -35,10 +46,10 @@ The `/__ketatlas__/` route is reserved for viewer assets. Dotfiles and paths res
35
46
  ## Audit
36
47
 
37
48
  ```sh
38
- ketatlas audit ./atlas.json
39
- ketatlas audit ./atlas.json --root .. --strict
40
- ketatlas audit ./atlas.json --json
41
- ketatlas audit ./atlas.json --json --output audit-report.json
49
+ ketatlas audit ./tasks/onboarding.ketatlas
50
+ ketatlas audit ./tasks/onboarding.ketatlas --strict
51
+ ketatlas audit ./tasks/onboarding.ketatlas --json
52
+ ketatlas audit ./tasks/onboarding.ketatlas --json --output audit-report.json
42
53
  ```
43
54
 
44
55
  Audit checks:
@@ -64,7 +75,7 @@ Exit codes:
64
75
  ## Validate and version
65
76
 
66
77
  ```sh
67
- ketatlas validate ./atlas.json
78
+ ketatlas validate ./tasks/onboarding.ketatlas
68
79
  ketatlas --version
69
80
  ketatlas --help
70
81
  ```
@@ -73,4 +84,4 @@ ketatlas --help
73
84
 
74
85
  ## Screen progress
75
86
 
76
- `progress <atlas.json> --json` reads the progress record, content revision and unique-screen summary. `--init` creates a missing sidecar. `--set <screen-id> --record <record.json> --expect <revision>` replaces one record with conflict detection. `serve <atlas.json> --read-only` disables local progress writes. See [Screen progress](progress.md) for examples and persistence rules.
87
+ `progress <bundle|atlas.json> --json` reads the progress record, content revision and unique-screen summary. `--init` creates a missing sidecar. `--set <screen-id> --record <record.json> --expect <revision>` replaces one record with conflict detection. `serve <bundle|atlas.json> --read-only` disables local progress writes. See [Screen progress](progress.md) for examples and persistence rules.
package/docs/migration.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Migrating the KétSuite mobile map
2
2
 
3
+ ## Bundle layout backfill
4
+
5
+ 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`.
6
+
7
+ The CLI continues to accept a direct legacy JSON path so migration can be audited before and after the move. Discovery intentionally reports only the standard bundle pattern.
8
+
3
9
  The original mobile map remains in its project. KetAtlas does not copy its business data or change the mobile repository. The extraction generalizes the viewer; project migration is a separate configuration change.
4
10
 
5
11
  ## Field mapping
package/docs/progress.md CHANGED
@@ -6,9 +6,9 @@ Screen and state preview footers show the shared screen status, blockers and lin
6
6
 
7
7
  ## Sidebar and project completion
8
8
 
9
- Project-wide verified/checklist percentages and blocker counts appear in the top bar beside **Screens**. The sidebar shows one compact numeric badge after each workflow’s step count (`2 steps [30%]`). **Verified %** is the number of Verified screens divided by the total unique screens in that scope. Repeated nodes and error variants count once; note/external nodes do not count. Screenless flows have no badge. Percentages round down so unfinished work cannot appear as 100%.
9
+ Project-wide verified/checklist percentages and blocker counts appear in the top bar beside **Screens**. The sidebar shows two badges after each workflow’s step count: **Checks %** (completed recorded acceptance checks / all recorded checks for unique screens) and **Implemented %** (screens with Implemented or Verified status / all unique screens). The compact labels are `30% checks` and `20% impl.`; tooltips spell out the definitions. In progress and In review screens do not count as implemented. Repeated nodes and error variants count once; note/external nodes do not count. Screenless flows have no badge. Flows with no recorded checks show **—**, not a measured 0%. Percentages round down so unfinished checks cannot appear as 100%.
10
10
 
11
- The badge tooltip and accessible label include status counts, blockers and **Checks %**, which counts completed recorded acceptance checks over all recorded checks, with an explicit unscoped-screen count when checklists are missing. This is not an estimate of effort, and 100% of a partial checklist does not make a screen Verified. Saving or refreshing progress updates the sidebar; changing or searching workflows preserves the current progress.
11
+ The badge tooltip and accessible label include completed/total checks, status counts, blockers, verified-screen completion, and unscoped-screen counts when checklists are missing. Checklist completion is not an estimate of effort or verified screen completion: 100% of a partial checklist does not make a screen Verified. Verified % on the top bar remains the number of Verified screens divided by all unique project screens. Saving or refreshing progress updates the sidebar; changing or searching workflows preserves the current progress.
12
12
 
13
13
  ## Status and scope
14
14
 
@@ -71,11 +71,11 @@ Evidence kinds are `pr`, `test`, `release`, `reference`. Required fields: `id`,
71
71
  ## Editing locally and from agents
72
72
 
73
73
  ```sh
74
- npx --yes ketatlas@0.2.4 serve atlas.json
75
- npx --yes ketatlas@0.2.4 serve atlas.json --read-only
76
- npx --yes ketatlas@0.2.4 progress atlas.json --init
77
- npx --yes ketatlas@0.2.4 progress atlas.json --json
78
- npx --yes ketatlas@0.2.4 progress atlas.json --set sign-in --record record.json --expect REVISION_FROM_READ
74
+ npx --yes ketatlas serve ./account-access.ketatlas
75
+ npx --yes ketatlas serve ./account-access.ketatlas --read-only
76
+ npx --yes ketatlas progress ./account-access.ketatlas --init
77
+ npx --yes ketatlas progress ./account-access.ketatlas --json
78
+ npx --yes ketatlas progress ./account-access.ketatlas --set sign-in --record record.json --expect REVISION_FROM_READ
79
79
  ```
80
80
 
81
81
  `--init` explicitly creates Unassessed records and refuses an existing progress file. `--set` replaces one complete screen record; preserve existing checklist IDs, evidence and state references. Read the revision with `--json` first. Do not retry a stale revision by blindly substituting a new one: reload and reconcile changes. `updatedAt` is stamped on successful record writes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ketatlas",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Interactive HTML maps for screens, workflows, and user journeys.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -5,7 +5,7 @@ description: Create or update interactive HTML mockups and workflow maps in KetA
5
5
 
6
6
  # KetAtlas
7
7
 
8
- Deliver an editable project containing actual HTML screens and a version 1 `atlas.json` that `npx ketatlas serve` can open. KetAtlas supplies the draggable viewer; the agent authors the product mockups and their flow graph. A screenshot gallery or a Mermaid diagram alone is not this deliverable.
8
+ Deliver an editable `<name>.ketatlas/` bundle containing actual HTML screens and a version 1 `atlas.json` that `npx ketatlas serve` can open. KetAtlas supplies the draggable viewer; the agent authors the product mockups and their flow graph. 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
 
@@ -18,24 +18,56 @@ Use the user's requirements, existing project files, and design references to de
18
18
 
19
19
  A brief may be a chat message or a Markdown file; it is not another KetAtlas JSON format. If details are missing, use reasonable defaults and record assumptions. Ask only for information that materially blocks the requested result. Do not introduce unrelated flows or build a production backend to make a mockup work.
20
20
 
21
- Use the supplied product design system when one exists. The starter uses KetJS styles, but KetAtlas can embed product HTML using any design system. For one shared iOS/Android prototype, create one HTML implementation with reusable styles and states unless separate variants were requested.
21
+ ## Choose the design system
22
+
23
+ Before scaffolding or editing visual screens, ask the user to choose a design system unless the current request already makes the choice explicit. Present these options in one concise prompt:
24
+
25
+ 1. **Auto (recommended)** — use [Két Design System](https://github.com/ketvietlab/ketjs/tree/develop/packages/design-system).
26
+ 2. **Két Design System** — use the same canonical Két source explicitly.
27
+ 3. **Carbon Design System**.
28
+ 4. **GitHub Primer**.
29
+ 5. **Microsoft Fluent 2**.
30
+ 6. **No design system** — create product-specific shared CSS without claiming conformance to a named system.
31
+ 7. **Custom source** — ask for a repository URL or local design-system path if it was not included with the selection.
32
+
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
+
35
+ Inspect the selected source, its usage documentation, tokens, components, icons, and relevant product patterns before mocking. Reuse its actual public 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 HTML implementation with reusable styles and states unless separate variants were requested.
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 HTML/CSS interface and record that no reproducible adapter lock is available.
38
+
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
+
41
+ Keep the atlas self-contained and dependency-free. Do not add a consumer package installation or custom bundler just to use a design system. Materialize or copy permitted static assets only when the selected source supports it, preserve required notices, and do not rely on remote runtime assets when the requested mockup must work offline. Record the selected system, source URL/path, pinned version or commit when known, adapter/asset strategy, and any license or fidelity limitation in the bundle README.
22
42
 
23
43
  ## Scaffold or extend
24
44
 
25
45
  Node.js 22 or newer is required. For a new, empty destination:
26
46
 
27
47
  ```sh
28
- npx --yes ketatlas scaffold ./tasks/mockups --template basic
48
+ npx --yes ketatlas scaffold ./tasks/mockups.ketatlas --template basic
29
49
  ```
30
50
 
31
51
  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.
32
52
 
33
- For an existing atlas, read and edit its JSON and screen files directly. Preserve useful IDs and URLs; scaffold refuses a nonempty directory and has no `--force` option. Pin the CLI version in the README commands (for example `npx --yes ketatlas@0.1.1 serve atlas.json`) or use a global installation. A consumer atlas does not need a local package installation.
53
+ For an existing atlas, read and edit its JSON and screen files directly. 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 package installation.
54
+
55
+ ### Backfill legacy projects
56
+
57
+ Before changing an existing atlas, run `ketatlas discover <workspace> --json`. If its manifest is not located at `<name>.ketatlas/atlas.json`, migrate it as part of the task:
58
+
59
+ 1. Inventory the manifest, sibling progress file, schema, README, screens, styles and assets. Record every relative `$schema`, screen URL and node URL before moving anything.
60
+ 2. If the containing directory is a self-contained Atlas project, rename that directory to a concise `<name>.ketatlas`; moving the whole directory preserves relative URLs.
61
+ 3. If the manifest shares a directory with unrelated application code, create a sibling `<name>.ketatlas` bundle. Move only Atlas-owned files, preserve Git history when possible, and rewrite relative paths for resources that intentionally remain outside the bundle.
62
+ 4. Keep one canonical manifest. Do not leave a copied legacy `atlas.json` behind or delete files whose ownership is unclear.
63
+ 5. Run `ketatlas discover <workspace> --json`, `ketatlas validate <name>.ketatlas`, and `ketatlas audit <name>.ketatlas --strict` after migration. Fix discovery errors and broken paths before editing product flows.
64
+
65
+ Legacy JSON paths remain accepted by the CLI for migration, but new and updated deliverables must use the bundle pattern so desktop tools can discover them without scanning arbitrary JSON.
34
66
 
35
67
  A self-contained project typically has:
36
68
 
37
69
  ```text
38
- tasks/mockups/
70
+ tasks/mockups.ketatlas/
39
71
  atlas.json
40
72
  ketatlas.schema.json
41
73
  screens/ HTML pages and shared CSS/JavaScript
@@ -45,7 +77,7 @@ tasks/mockups/
45
77
 
46
78
  Keep product assets inside the served directory when practical. Deliver JSON/schema, screen HTML/CSS/JavaScript, assets, and documentation. 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.
47
79
 
48
- Do not create a wrapper viewer or a custom canvas: `serve atlas.json` provides it.
80
+ Do not create a wrapper viewer or a custom canvas: `serve ./tasks/mockups.ketatlas` provides it.
49
81
 
50
82
  ## Model the journey
51
83
 
@@ -123,9 +155,10 @@ The default iframe sandbox allows scripts and forms but gives the page an opaque
123
155
  Run from the user's project with the same root for audit and serve:
124
156
 
125
157
  ```sh
126
- npx --yes ketatlas validate ./tasks/mockups/atlas.json
127
- npx --yes ketatlas audit ./tasks/mockups/atlas.json --strict
128
- npx --yes ketatlas serve ./tasks/mockups/atlas.json
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
129
162
  ```
130
163
 
131
164
  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.
@@ -138,8 +171,8 @@ Update the project's README with flow-to-screen/state coverage, assumptions, dem
138
171
 
139
172
  ## Maintain screen delivery progress
140
173
 
141
- When implementing or reviewing screens, track progress in the sibling `<atlas-name>.progress.json`. Use KetAtlas 0.2.0 or newer. Keep version 1 workflow JSON unchanged. Read `ketatlas progress atlas.json --json`, preserve existing evidence/check IDs and state references, then update one record with `--set <screen-id> --record <record.json> --expect <revision>`. Re-read and reconcile conflicts instead of overwriting another writer. Run `audit` after updates.
174
+ When implementing or reviewing screens, track progress in `atlas.progress.json` beside the bundle manifest. Keep version 1 workflow JSON unchanged. Read `ketatlas progress <name>.ketatlas --json`, preserve existing evidence/check IDs and state references, then update one record with `--set <screen-id> --record <record.json> --expect <revision>`. Re-read and reconcile conflicts instead of overwriting another writer. Run `audit` after updates.
142
175
 
143
176
  Map task scope to screen IDs before assigning progress. Start unknown coverage at `unassessed`; a mockup is not implementation evidence. Use `planned`, `in_progress`, `in_review`, `implemented`, `verified` with a separate blocker reason/next action. Link PRs and exact merge commits, pin/release evidence, and test evidence at the revision/environment actually checked. Do not infer deployment, full screen coverage or verification from a PR merge or green aggregate CI. Completed acceptance checks need evidence; `verified` requires passed test evidence with revision and environment for every recorded check and no blocker. Track error/recovery states using check `nodes` references. Leave unrelated or unreviewed product surfaces unassessed and say why. See `docs/progress.md` in the package for the complete contract.
144
177
 
145
- Sidebar percentages distinguish Verified screens from completed recorded checks. Do not assign arbitrary weights to intermediate statuses or remove unknown/unscoped screens to inflate completion. Workflow counts deduplicate screen IDs, including variants; process-only flows have no screen percentage.
178
+ Sidebar badges show Checks (completed recorded checks / all recorded checks) and Implemented (Implemented + Verified screens / all unique screens). No recorded checks shows — for Checks. Verified-screen completion stays separate on the project top bar. Do not assign arbitrary weights to intermediate statuses or remove unknown/unscoped screens to inflate completion. Workflow counts deduplicate screen IDs, including variants; process-only flows have no screen percentage.
package/src/index.js CHANGED
@@ -12,6 +12,16 @@ import { template, escapeHTML as e } from "./template.js";
12
12
  import { icons } from "./icons.js";
13
13
  export { validateAtlas, AtlasValidationError } from "./config.js";
14
14
 
15
+ const cameraMotion = Object.freeze({
16
+ dragPan: 1.35,
17
+ wheelPan: 1.45,
18
+ wheelZoom: 0.0032,
19
+ pinchZoom: 1.2,
20
+ zoomStep: 1.3,
21
+ keyPan: 120,
22
+ keyPanLarge: 260,
23
+ });
24
+
15
25
  /** Mount an isolated viewer. Await atlas.ready before measuring or focusing it. */
16
26
  export function createAtlas(container, input, options = {}) {
17
27
  if (!(container instanceof HTMLElement))
@@ -136,7 +146,7 @@ export function createAtlas(container, input, options = {}) {
136
146
  ).includes(q),
137
147
  );
138
148
  return matches.length
139
- ? `<section><h2 class="flow-group-title">${e(group)}</h2>${matches.map((f) => `<button class="flow-link ${f === current ? "active" : ""}" data-flow="${f.id}" ${f === current ? 'aria-current="true"' : ""}><span class="flow-number">${String(f.index + 1).padStart(2, "0")}</span><span class="flow-link-copy"><strong>${e(f.title)}</strong><small>${f.nodes.length} steps <span class="flow-progress" data-progress-flow="${f.id}"></span></small></span></button>`).join("")}</section>`
149
+ ? `<section><h2 class="flow-group-title">${e(group)}</h2>${matches.map((f) => `<button class="flow-link ${f === current ? "active" : ""}" data-flow="${f.id}" ${f === current ? 'aria-current="true"' : ""}><span class="flow-number">${String(f.index + 1).padStart(2, "0")}</span><span class="flow-link-copy"><strong>${e(f.title)}</strong><small>${f.nodes.length} steps <span class="flow-progress-group" data-progress-flow="${f.id}"></span></small></span></button>`).join("")}</section>`
140
150
  : "";
141
151
  })
142
152
  .join("") || '<p class="mock-filter-empty">No matching workflows.</p>';
@@ -429,10 +439,8 @@ export function createAtlas(container, input, options = {}) {
429
439
  pointers.set(event.pointerId, point(event));
430
440
  if (pointers.size >= 2 && pinch) {
431
441
  const [a, b] = [...pointers.values()],
432
- z = Math.max(
433
- 0.18,
434
- Math.min(2.2, (pinch.z * Math.hypot(a.x - b.x, a.y - b.y)) / Math.max(1, pinch.distance)),
435
- );
442
+ ratio = Math.hypot(a.x - b.x, a.y - b.y) / Math.max(1, pinch.distance),
443
+ z = Math.max(0.18, Math.min(2.2, pinch.z * Math.pow(ratio, cameraMotion.pinchZoom)));
436
444
  view = { z, x: (a.x + b.x) / 2 - pinch.worldX * z, y: (a.y + b.y) / 2 - pinch.worldY * z };
437
445
  suppressClick = true;
438
446
  schedule();
@@ -445,8 +453,8 @@ export function createAtlas(container, input, options = {}) {
445
453
  if (Math.hypot(dx, dy) > 4) drag.moved = true;
446
454
  if (drag.moved) {
447
455
  viewport.classList.add("dragging");
448
- view.x = drag.x + dx;
449
- view.y = drag.y + dy;
456
+ view.x = drag.x + dx * cameraMotion.dragPan;
457
+ view.y = drag.y + dy * cameraMotion.dragPan;
450
458
  schedule();
451
459
  }
452
460
  });
@@ -477,10 +485,16 @@ export function createAtlas(container, input, options = {}) {
477
485
  event.preventDefault();
478
486
  if (event.ctrlKey || event.metaKey) {
479
487
  const p = point(event);
480
- zoomTo(view.z * Math.exp(-event.deltaY * 0.002), p.x, p.y);
488
+ zoomTo(view.z * Math.exp(-event.deltaY * cameraMotion.wheelZoom), p.x, p.y);
481
489
  } else {
482
- view.x -= event.deltaX;
483
- view.y -= event.deltaY;
490
+ const unit =
491
+ event.deltaMode === WheelEvent.DOM_DELTA_LINE
492
+ ? 16
493
+ : event.deltaMode === WheelEvent.DOM_DELTA_PAGE
494
+ ? viewport.clientHeight
495
+ : 1;
496
+ view.x -= event.deltaX * unit * cameraMotion.wheelPan;
497
+ view.y -= event.deltaY * unit * cameraMotion.wheelPan;
484
498
  syncCurrentFromPan();
485
499
  schedule();
486
500
  }
@@ -489,17 +503,17 @@ export function createAtlas(container, input, options = {}) {
489
503
  );
490
504
  viewport.addEventListener("keydown", (event) => {
491
505
  if (event.target.closest("button,a,input") || $("screen-dialog").open) return;
492
- const step = event.shiftKey ? 180 : 80;
506
+ const step = event.shiftKey ? cameraMotion.keyPanLarge : cameraMotion.keyPan;
493
507
  if (event.key === "ArrowLeft") view.x += step;
494
508
  else if (event.key === "ArrowRight") view.x -= step;
495
509
  else if (event.key === "ArrowUp") view.y += step;
496
510
  else if (event.key === "ArrowDown") view.y -= step;
497
511
  else if (["+", "="].includes(event.key)) {
498
- zoomTo(view.z * 1.2);
512
+ zoomTo(view.z * cameraMotion.zoomStep);
499
513
  event.preventDefault();
500
514
  return;
501
515
  } else if (event.key === "-") {
502
- zoomTo(view.z / 1.2);
516
+ zoomTo(view.z / cameraMotion.zoomStep);
503
517
  event.preventDefault();
504
518
  return;
505
519
  } else if (event.key.toLowerCase() === "f") {
@@ -566,8 +580,8 @@ export function createAtlas(container, input, options = {}) {
566
580
  if (button) progressUI.edit(button.dataset.editProgress);
567
581
  });
568
582
  $("flow-search").addEventListener("input", sidebar);
569
- $("zoom-in").onclick = () => zoomTo(view.z * 1.2);
570
- $("zoom-out").onclick = () => zoomTo(view.z / 1.2);
583
+ $("zoom-in").onclick = () => zoomTo(view.z * cameraMotion.zoomStep);
584
+ $("zoom-out").onclick = () => zoomTo(view.z / cameraMotion.zoomStep);
571
585
  $("zoom-reset").onclick = () => zoomTo(1);
572
586
  $("zoom-fit").onclick = fitFlow;
573
587
  $("minimap-fit").onclick = fitFlow;
@@ -58,15 +58,35 @@ export function mountProgress(root, config, initial, options, selectScreen) {
58
58
  .map(([key, label]) => `${summary.counts[key]} ${label.toLowerCase()}`)
59
59
  .join(" · ");
60
60
  const checks = summary.checks;
61
- const details = `${summary.verifiedPercent}% verified · ${summary.counts.verified}/${summary.total} screens. ${statuses}${summary.blocked ? ` · ${summary.blocked} blocked` : ""}. ${checks.total ? `Checks ${checks.percent}% · ${checks.done}/${checks.total}` : "No checks recorded"}${checks.unscoped ? ` · ${checks.unscoped} unscoped` : ""}.`;
61
+ const implemented = summary.counts.implemented + summary.counts.verified;
62
+ const implementedPercent = Math.floor((implemented / summary.total) * 100);
63
+ const details = `${checks.total ? `Checks ${checks.percent}% · ${checks.done}/${checks.total}. ` : "No checks recorded. "}${summary.verifiedPercent}% verified · ${summary.counts.verified}/${summary.total} screens. ${statuses}${summary.blocked ? ` · ${summary.blocked} blocked` : ""}${checks.unscoped ? ` · ${checks.unscoped} unscoped` : ""}.`;
62
64
  if (project) {
63
65
  el.innerHTML = `<span><b>${summary.verifiedPercent}%</b> verified</span><span><b>${checks.percent ?? "—"}${checks.percent === null ? "" : "%"}</b> checks</span>${summary.blocked ? `<span class="project-blocked"><b>${summary.blocked}</b> blocked</span>` : ""}`;
64
- } else el.textContent = `${summary.verifiedPercent}%`;
66
+ } else {
67
+ const metric = (name, label, percent, description) =>
68
+ `<span class="flow-progress" data-metric="${name}" data-tone="${summary.blocked ? "blocked" : percent === 100 ? "complete" : "pending"}" title="${e(description)}" aria-label="${e(description)}">${percent === null ? "—" : `${percent}%`} ${label}</span>`;
69
+ el.innerHTML =
70
+ metric(
71
+ "checks",
72
+ "checks",
73
+ checks.percent,
74
+ checks.total
75
+ ? `Checks ${checks.percent}%: ${checks.done}/${checks.total} recorded checks completed`
76
+ : "Checks: no recorded checklist",
77
+ ) +
78
+ metric(
79
+ "implemented",
80
+ "impl.",
81
+ implementedPercent,
82
+ `Implemented ${implementedPercent}%: ${implemented}/${summary.total} screens have Implemented or Verified status`,
83
+ );
84
+ }
65
85
  el.title = details;
66
86
  el.setAttribute("aria-label", details);
67
87
  el.dataset.tone = summary.blocked
68
88
  ? "blocked"
69
- : summary.verifiedPercent === 100
89
+ : (project ? summary.verifiedPercent : checks.percent) === 100
70
90
  ? "complete"
71
91
  : "pending";
72
92
  }
@@ -1440,3 +1440,13 @@ a svg {
1440
1440
  --progress-dialog-padding: var(--kv-space-3);
1441
1441
  }
1442
1442
  }
1443
+
1444
+ .flow-progress-group {
1445
+ display: inline-flex;
1446
+ align-items: center;
1447
+ gap: var(--kv-space-1);
1448
+ vertical-align: middle;
1449
+ }
1450
+ .flow-progress-group[hidden] {
1451
+ display: none;
1452
+ }
@@ -1,14 +1,14 @@
1
1
  # My KetAtlas project
2
2
 
3
- Run with Node.js 22 or newer:
3
+ This directory must keep its `.ketatlas` suffix. Run with Node.js 22 or newer:
4
4
 
5
5
  ```sh
6
- npx ketatlas serve atlas.json
7
- npx ketatlas audit atlas.json --strict
6
+ npx ketatlas serve .
7
+ npx ketatlas 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. Alternatively, install the CLI once with npm install --global ketatlas@0.2.4 and use ketatlas serve atlas.json.
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. Alternatively, install the CLI once with npm install --global ketatlas and use ketatlas serve .
11
11
 
12
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.
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 atlas.json --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 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.
@@ -1,14 +1,14 @@
1
1
  # My KetAtlas project
2
2
 
3
- Run with Node.js 22 or newer:
3
+ This directory must keep its `.ketatlas` suffix. Run with Node.js 22 or newer:
4
4
 
5
5
  ```sh
6
- npx ketatlas serve atlas.json
7
- npx ketatlas audit atlas.json --strict
6
+ npx ketatlas serve .
7
+ npx ketatlas 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. Alternatively, install the CLI once with npm install --global ketatlas@0.2.4 and use ketatlas serve atlas.json.
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. Alternatively, install the CLI once with npm install --global ketatlas and use ketatlas serve .
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 atlas.json --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 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.
@@ -1,14 +1,14 @@
1
1
  # My KetAtlas project
2
2
 
3
- Run with Node.js 22 or newer:
3
+ This directory must keep its `.ketatlas` suffix. Run with Node.js 22 or newer:
4
4
 
5
5
  ```sh
6
- npx ketatlas serve atlas.json
7
- npx ketatlas audit atlas.json --strict
6
+ npx ketatlas serve .
7
+ npx ketatlas 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. Alternatively, install the CLI once with npm install --global ketatlas@0.2.4 and use ketatlas serve atlas.json.
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. Alternatively, install the CLI once with npm install --global ketatlas and use ketatlas serve .
11
11
 
12
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.
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 atlas.json --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 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.