ketatlas 0.2.0 → 0.2.2
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 +11 -0
- package/README.md +4 -4
- package/docs/progress.md +13 -5
- package/package.json +1 -1
- package/skills/ketatlas/SKILL.md +2 -0
- package/src/index.d.ts +9 -0
- package/src/index.js +11 -3
- package/src/progress-ui.js +69 -3
- package/src/progress.js +24 -0
- package/src/template.js +2 -2
- package/styles/ketatlas.css +122 -0
- package/templates/basic/README.md +1 -1
- package/templates/process/README.md +1 -1
- package/templates/web/README.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
- Show screen status, blockers, linked PR states and scoped state checks in preview footers, with direct access to progress details.
|
|
6
|
+
- Keep the footer visible when scrolling, refresh it after edits, and support read-only previews.
|
|
7
|
+
|
|
8
|
+
## 0.2.1
|
|
9
|
+
|
|
10
|
+
- Show compact numeric progress badges after workflow step counts, with status counts, checklist completion and blockers in tooltips.
|
|
11
|
+
- Place project-wide verified/checklist percentages and blockers next to Screens in the top bar.
|
|
12
|
+
- Deduplicate reused screens and variants, exclude process-only steps, and update the sidebar after saves, refreshes and flow navigation.
|
|
13
|
+
|
|
3
14
|
## 0.2.0
|
|
4
15
|
|
|
5
16
|
- Track delivery per screen in a separate progress JSON, with acceptance checks, blockers, ownership, task references and PR/test/release evidence.
|
package/README.md
CHANGED
|
@@ -17,15 +17,15 @@ 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.
|
|
21
|
-
npx --yes ketatlas@0.2.
|
|
22
|
-
npx --yes ketatlas@0.2.
|
|
20
|
+
npx --yes ketatlas@0.2.2 scaffold my-atlas --template web
|
|
21
|
+
npx --yes ketatlas@0.2.2 serve my-atlas/atlas.json
|
|
22
|
+
npx --yes ketatlas@0.2.2 audit my-atlas/atlas.json --strict
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Or install the CLI once for your user account:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
npm install --global ketatlas@0.2.
|
|
28
|
+
npm install --global ketatlas@0.2.2
|
|
29
29
|
ketatlas scaffold my-atlas
|
|
30
30
|
ketatlas serve my-atlas/atlas.json
|
|
31
31
|
ketatlas audit my-atlas/atlas.json --strict
|
package/docs/progress.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Open **Screens** to review delivery across the project. Counts use unique screen IDs, not repeated workflow nodes. Filter by status, workflow, blocker, or search names, IDs, tasks and owners. **View in map** focuses a screen; **Screen progress** opens its record from the node inspector. Mockup badges remain separate from implementation status.
|
|
4
4
|
|
|
5
|
+
Screen and state preview footers show the shared screen status, blockers and linked PRs with their recorded states. **Progress details** opens the same screen record. State-specific acceptance counts appear only when checks explicitly reference the previewed workflow/node; the shared screen status is not a separate claim that a state has been verified. Missing evidence displays **No linked PRs**. The footer stays visible while scrolling and updates after a save.
|
|
6
|
+
|
|
7
|
+
## Sidebar and project completion
|
|
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%.
|
|
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.
|
|
12
|
+
|
|
5
13
|
## Status and scope
|
|
6
14
|
|
|
7
15
|
| Status | Meaning |
|
|
@@ -63,11 +71,11 @@ Evidence kinds are `pr`, `test`, `release`, `reference`. Required fields: `id`,
|
|
|
63
71
|
## Editing locally and from agents
|
|
64
72
|
|
|
65
73
|
```sh
|
|
66
|
-
npx --yes ketatlas@0.2.
|
|
67
|
-
npx --yes ketatlas@0.2.
|
|
68
|
-
npx --yes ketatlas@0.2.
|
|
69
|
-
npx --yes ketatlas@0.2.
|
|
70
|
-
npx --yes ketatlas@0.2.
|
|
74
|
+
npx --yes ketatlas@0.2.2 serve atlas.json
|
|
75
|
+
npx --yes ketatlas@0.2.2 serve atlas.json --read-only
|
|
76
|
+
npx --yes ketatlas@0.2.2 progress atlas.json --init
|
|
77
|
+
npx --yes ketatlas@0.2.2 progress atlas.json --json
|
|
78
|
+
npx --yes ketatlas@0.2.2 progress atlas.json --set sign-in --record record.json --expect REVISION_FROM_READ
|
|
71
79
|
```
|
|
72
80
|
|
|
73
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
package/skills/ketatlas/SKILL.md
CHANGED
|
@@ -141,3 +141,5 @@ Update the project's README with flow-to-screen/state coverage, assumptions, dem
|
|
|
141
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.
|
|
142
142
|
|
|
143
143
|
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
|
+
|
|
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.
|
package/src/index.d.ts
CHANGED
|
@@ -170,3 +170,12 @@ export function loadAtlas(
|
|
|
170
170
|
url: string | URL,
|
|
171
171
|
options?: AtlasOptions,
|
|
172
172
|
): Promise<AtlasInstance>;
|
|
173
|
+
|
|
174
|
+
/** Unique-screen delivery and recorded-check completion for one workflow; screenless flows return null percentages. */
|
|
175
|
+
export function summarizeFlowProgress(
|
|
176
|
+
data: AtlasProgress,
|
|
177
|
+
flow: AtlasFlow,
|
|
178
|
+
): ReturnType<typeof summarizeProgress> & {
|
|
179
|
+
verifiedPercent: number | null;
|
|
180
|
+
checks: { done: number; total: number; unscoped: number; percent: number | null };
|
|
181
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { mountProgress } from "./progress-ui.js";
|
|
2
2
|
import { emptyProgress, requireProgress } from "./progress.js";
|
|
3
|
-
export {
|
|
3
|
+
export {
|
|
4
|
+
validateProgress,
|
|
5
|
+
summarizeProgress,
|
|
6
|
+
summarizeFlowProgress,
|
|
7
|
+
progressStatuses,
|
|
8
|
+
} from "./progress.js";
|
|
4
9
|
import { normalizeAtlas, safeURL } from "./config.js";
|
|
5
10
|
import { layoutAtlas, edgePath } from "./layout.js";
|
|
6
11
|
import { template, escapeHTML as e } from "./template.js";
|
|
@@ -117,6 +122,7 @@ export function createAtlas(container, input, options = {}) {
|
|
|
117
122
|
world.style.height = height + "px";
|
|
118
123
|
$("map-total").textContent = `${flows.length} flows · ${screens.size} screens`;
|
|
119
124
|
const groups = [...new Set(flows.map((f) => f.group))];
|
|
125
|
+
let progressUI;
|
|
120
126
|
function sidebar() {
|
|
121
127
|
const q = normalize($("flow-search").value);
|
|
122
128
|
$("flow-list").innerHTML =
|
|
@@ -130,10 +136,11 @@ export function createAtlas(container, input, options = {}) {
|
|
|
130
136
|
).includes(q),
|
|
131
137
|
);
|
|
132
138
|
return matches.length
|
|
133
|
-
? `<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><strong>${e(f.title)}</strong><small>${f.nodes.length} steps
|
|
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>`
|
|
134
140
|
: "";
|
|
135
141
|
})
|
|
136
142
|
.join("") || '<p class="mock-filter-empty">No matching workflows.</p>';
|
|
143
|
+
progressUI?.paintSidebar();
|
|
137
144
|
}
|
|
138
145
|
const defs = `<defs>${[
|
|
139
146
|
["main", "--kv-accent"],
|
|
@@ -372,6 +379,7 @@ export function createAtlas(container, input, options = {}) {
|
|
|
372
379
|
return;
|
|
373
380
|
}
|
|
374
381
|
previewNode = n;
|
|
382
|
+
progressUI.preview(n.screenId, n.flowId, n.id);
|
|
375
383
|
$("dialog-screen-id").textContent = n.screenId;
|
|
376
384
|
$("dialog-title").textContent = n.title;
|
|
377
385
|
$("dialog-open").href = n.url;
|
|
@@ -544,7 +552,7 @@ export function createAtlas(container, input, options = {}) {
|
|
|
544
552
|
const n = event.target.closest("[data-node]");
|
|
545
553
|
if (n && !event.target.closest("button")) openScreen(nodeByKey.get(n.dataset.node));
|
|
546
554
|
});
|
|
547
|
-
|
|
555
|
+
progressUI = mountProgress(root, config, progressData, options, (id) => {
|
|
548
556
|
const n = nodes.find((n) => n.screenId === id);
|
|
549
557
|
if (n) {
|
|
550
558
|
setCurrent(flowById.get(n.flowId));
|
package/src/progress-ui.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { escapeHTML as e } from "./template.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
progressFor,
|
|
4
|
+
progressStatuses,
|
|
5
|
+
summarizeProgress,
|
|
6
|
+
summarizeFlowProgress,
|
|
7
|
+
requireProgress,
|
|
8
|
+
} from "./progress.js";
|
|
3
9
|
import { safeURL } from "./config.js";
|
|
4
10
|
const optionsHTML = (values, selected) =>
|
|
5
11
|
Object.entries(values)
|
|
@@ -14,7 +20,8 @@ export function mountProgress(root, config, initial, options, selectScreen) {
|
|
|
14
20
|
editing,
|
|
15
21
|
draft,
|
|
16
22
|
baseline,
|
|
17
|
-
saving = false
|
|
23
|
+
saving = false,
|
|
24
|
+
preview;
|
|
18
25
|
const $ = (id) => root.getElementById(id);
|
|
19
26
|
const container = document.createElement("div");
|
|
20
27
|
container.innerHTML = `
|
|
@@ -32,7 +39,61 @@ export function mountProgress(root, config, initial, options, selectScreen) {
|
|
|
32
39
|
const badge = (r) =>
|
|
33
40
|
`<span class="progress-badge" data-status="${e(r.status)}">${e(progressStatuses[r.status])}</span>${r.blocker?.trim() ? '<span class="progress-blocked">Blocked</span>' : ""}`;
|
|
34
41
|
const record = (id) => progressFor(data, id);
|
|
42
|
+
function paintSidebar() {
|
|
43
|
+
for (const el of root.querySelectorAll("[data-progress-flow], #project-progress")) {
|
|
44
|
+
const project = el.id === "project-progress";
|
|
45
|
+
const flow = project
|
|
46
|
+
? { nodes: config.screens.map((s) => ({ screen: s.id })) }
|
|
47
|
+
: config.flows.find((f) => f.id === el.dataset.progressFlow);
|
|
48
|
+
const summary = summarizeFlowProgress(data, flow);
|
|
49
|
+
el.hidden = !summary.total;
|
|
50
|
+
if (!summary.total) {
|
|
51
|
+
el.textContent = "";
|
|
52
|
+
el.removeAttribute("title");
|
|
53
|
+
el.removeAttribute("aria-label");
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const statuses = Object.entries(progressStatuses)
|
|
57
|
+
.filter(([key]) => summary.counts[key])
|
|
58
|
+
.map(([key, label]) => `${summary.counts[key]} ${label.toLowerCase()}`)
|
|
59
|
+
.join(" · ");
|
|
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` : ""}.`;
|
|
62
|
+
if (project) {
|
|
63
|
+
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}%`;
|
|
65
|
+
el.title = details;
|
|
66
|
+
el.setAttribute("aria-label", details);
|
|
67
|
+
el.dataset.tone = summary.blocked
|
|
68
|
+
? "blocked"
|
|
69
|
+
: summary.verifiedPercent === 100
|
|
70
|
+
? "complete"
|
|
71
|
+
: "pending";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function paintPreview() {
|
|
75
|
+
if (!preview) return;
|
|
76
|
+
const r = record(preview.screenId);
|
|
77
|
+
const checks = (r.checks || []).filter((c) =>
|
|
78
|
+
c.nodes?.some((n) => n.flowId === preview.flowId && n.nodeId === preview.nodeId),
|
|
79
|
+
);
|
|
80
|
+
const prs = (r.evidence || [])
|
|
81
|
+
.filter((ev) => ev.kind === "pr")
|
|
82
|
+
.map((ev) => {
|
|
83
|
+
const url = safeURL(ev.url, options.baseURL || document.baseURI);
|
|
84
|
+
const parts = new URL(url, document.baseURI).pathname.match(/\/([^/]+)\/pull\/(\d+)\/?$/);
|
|
85
|
+
const title = parts ? `${parts[1]}#${parts[2]}` : ev.title;
|
|
86
|
+
return `<a href="${e(url)}" target="_blank" rel="noopener noreferrer" title="${e(ev.title)}">${e(title)}${ev.state ? ` <span>· ${e(ev.state)}</span>` : ""} ↗</a>`;
|
|
87
|
+
});
|
|
88
|
+
$("dialog-progress").innerHTML =
|
|
89
|
+
`<div class="preview-status"><span>Screen status</span>${badge(r)}${checks.length ? `<span>State checks: ${checks.filter((c) => c.done).length}/${checks.length}</span>` : ""}</div>${r.blocker?.trim() ? `<p class="preview-blocker">${e(r.blocker)}</p>` : ""}<div class="preview-prs" aria-label="Screen pull requests">${prs.length ? prs.join("") : "<span>No linked PRs</span>"}</div>`;
|
|
90
|
+
}
|
|
91
|
+
$("dialog-progress-details").onclick = () => {
|
|
92
|
+
if (preview) edit(preview.screenId);
|
|
93
|
+
};
|
|
35
94
|
function paint() {
|
|
95
|
+
paintSidebar();
|
|
96
|
+
paintPreview();
|
|
36
97
|
for (const el of root.querySelectorAll("[data-progress-screen]"))
|
|
37
98
|
el.innerHTML = badge(record(el.dataset.progressScreen));
|
|
38
99
|
const summary = summarizeProgress(data, config.screens);
|
|
@@ -235,7 +296,7 @@ export function mountProgress(root, config, initial, options, selectScreen) {
|
|
|
235
296
|
$("progress-error").textContent = err.message;
|
|
236
297
|
$("progress-reload-latest").hidden = !options.reloadProgress;
|
|
237
298
|
} finally {
|
|
238
|
-
saving = false;
|
|
299
|
+
((saving = false), preview);
|
|
239
300
|
$("progress-save").disabled = false;
|
|
240
301
|
}
|
|
241
302
|
};
|
|
@@ -243,6 +304,11 @@ export function mountProgress(root, config, initial, options, selectScreen) {
|
|
|
243
304
|
return {
|
|
244
305
|
edit,
|
|
245
306
|
paint,
|
|
307
|
+
paintSidebar,
|
|
308
|
+
preview(screenId, flowId, nodeId) {
|
|
309
|
+
preview = { screenId, flowId, nodeId };
|
|
310
|
+
paintPreview();
|
|
311
|
+
},
|
|
246
312
|
open() {
|
|
247
313
|
paint();
|
|
248
314
|
$("progress-screens").showModal();
|
package/src/progress.js
CHANGED
|
@@ -175,3 +175,27 @@ export function summarizeProgress(data, screens) {
|
|
|
175
175
|
}
|
|
176
176
|
return { total: screens.length, counts, blocked };
|
|
177
177
|
}
|
|
178
|
+
|
|
179
|
+
/** Delivery counts deduplicate reusable screens, including repeated error variants. */
|
|
180
|
+
export function summarizeFlowProgress(data, flow) {
|
|
181
|
+
const ids = [...new Set(flow.nodes.filter((n) => n.screen).map((n) => n.screen))];
|
|
182
|
+
const summary = summarizeProgress(
|
|
183
|
+
data,
|
|
184
|
+
ids.map((id) => ({ id })),
|
|
185
|
+
);
|
|
186
|
+
let done = 0,
|
|
187
|
+
total = 0,
|
|
188
|
+
unscoped = 0;
|
|
189
|
+
for (const id of ids) {
|
|
190
|
+
const checks = progressFor(data, id).checks || [];
|
|
191
|
+
if (!checks.length) unscoped++;
|
|
192
|
+
total += checks.length;
|
|
193
|
+
done += checks.filter((check) => check.done).length;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
...summary,
|
|
197
|
+
// Round down so unfinished work can never display as 100%.
|
|
198
|
+
verifiedPercent: ids.length ? Math.floor((summary.counts.verified * 100) / ids.length) : null,
|
|
199
|
+
checks: { done, total, unscoped, percent: total ? Math.floor((done * 100) / total) : null },
|
|
200
|
+
};
|
|
201
|
+
}
|
package/src/template.js
CHANGED
|
@@ -10,7 +10,7 @@ export function template(config) {
|
|
|
10
10
|
<button id="map-menu" class="icon-button" aria-label="Choose a flow" aria-expanded="false" aria-controls="map-sidebar"></button>
|
|
11
11
|
<div class="map-brand"><span class="brand-mark">K</span><span><strong>${escapeHTML(config.title)}</strong><small>KetAtlas · Workflow atlas</small></span></div>
|
|
12
12
|
<span id="map-total" class="map-total"></span>
|
|
13
|
-
<div class="map-header-actions"><button id="open-progress" data-ui="action" data-variant="secondary">Screens</button><button id="map-help" class="icon-button" aria-label="Help"></button></div>
|
|
13
|
+
<div class="map-header-actions"><div id="project-progress" class="project-progress" role="group" aria-label="Project progress"></div><button id="open-progress" data-ui="action" data-variant="secondary">Screens</button><button id="map-help" class="icon-button" aria-label="Help"></button></div>
|
|
14
14
|
</header>
|
|
15
15
|
<div class="map-layout">
|
|
16
16
|
<aside id="map-sidebar" class="map-sidebar">
|
|
@@ -30,7 +30,7 @@ export function template(config) {
|
|
|
30
30
|
<div class="map-minimap"><span>Current flow</span><svg id="minimap" viewBox="0 0 168 114" role="img" aria-label="Current viewport position"></svg><button id="minimap-fit">Fit entire flow</button></div>
|
|
31
31
|
</main>
|
|
32
32
|
</div>
|
|
33
|
-
<dialog id="screen-dialog" class="screen-dialog" aria-labelledby="dialog-title"><header><div><small id="dialog-screen-id"></small><h2 id="dialog-title"></h2></div><button id="close-screen" class="icon-button" aria-label="Close preview"></button></header><div id="dialog-preview"></div><footer><
|
|
33
|
+
<dialog id="screen-dialog" class="screen-dialog" aria-labelledby="dialog-title"><header><div><small id="dialog-screen-id"></small><h2 id="dialog-title"></h2></div><button id="close-screen" class="icon-button" aria-label="Close preview"></button></header><div id="dialog-preview"></div><footer><div id="dialog-progress" class="preview-progress"></div><div class="preview-actions"><button id="dialog-progress-details" data-ui="action" data-variant="secondary">Progress details</button><a id="dialog-open" data-ui="action" data-variant="secondary" target="_blank" rel="noopener noreferrer">Open in new tab</a></div></footer></dialog>
|
|
34
34
|
<dialog id="help-dialog" class="help-dialog" aria-labelledby="help-title"><h2 id="help-title">Explore the whole journey</h2><ul>
|
|
35
35
|
<li>Drag the canvas or a screen card to move around.</li><li>Use + / −, Ctrl/⌘ + scroll, or pinch to zoom.</li><li>Choose a workflow in the sidebar. Fit the flow to see every branch.</li><li>Click a node to follow its outgoing steps. Double-click a screen to interact.</li><li>Use arrow keys to pan, F to fit, 0 to return to the start, and Escape to close.</li>
|
|
36
36
|
</ul><p>Arrows describe the authored flow. Embedded screens keep their own behavior.</p><button id="close-help" data-ui="action" data-variant="primary">Got it</button></dialog>
|
package/styles/ketatlas.css
CHANGED
|
@@ -1284,3 +1284,125 @@ a svg {
|
|
|
1284
1284
|
color: var(--kv-text-muted);
|
|
1285
1285
|
font-weight: 400;
|
|
1286
1286
|
}
|
|
1287
|
+
|
|
1288
|
+
/* Compact verified-screen badges; detailed counts live in the tooltip. */
|
|
1289
|
+
.flow-link-copy {
|
|
1290
|
+
min-width: 0;
|
|
1291
|
+
flex: 1;
|
|
1292
|
+
}
|
|
1293
|
+
.flow-progress {
|
|
1294
|
+
display: inline-flex;
|
|
1295
|
+
align-items: center;
|
|
1296
|
+
justify-content: center;
|
|
1297
|
+
flex: none;
|
|
1298
|
+
min-width: 34px;
|
|
1299
|
+
height: 20px;
|
|
1300
|
+
padding: 0 var(--kv-space-1);
|
|
1301
|
+
border-radius: var(--kv-radius-full);
|
|
1302
|
+
background: var(--kv-surface-hover);
|
|
1303
|
+
color: var(--kv-text-muted);
|
|
1304
|
+
font-size: var(--kv-text-2xs);
|
|
1305
|
+
font-weight: var(--kv-weight-medium);
|
|
1306
|
+
font-variant-numeric: tabular-nums;
|
|
1307
|
+
line-height: 1;
|
|
1308
|
+
letter-spacing: normal;
|
|
1309
|
+
white-space: nowrap;
|
|
1310
|
+
vertical-align: middle;
|
|
1311
|
+
}
|
|
1312
|
+
.flow-progress[hidden] {
|
|
1313
|
+
display: none;
|
|
1314
|
+
}
|
|
1315
|
+
.flow-progress[data-tone="blocked"] {
|
|
1316
|
+
color: var(--kv-warning);
|
|
1317
|
+
}
|
|
1318
|
+
.flow-progress[data-tone="complete"] {
|
|
1319
|
+
color: var(--kv-positive);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
.project-progress {
|
|
1323
|
+
display: flex;
|
|
1324
|
+
align-items: center;
|
|
1325
|
+
gap: var(--kv-space-3);
|
|
1326
|
+
margin-right: var(--kv-space-2);
|
|
1327
|
+
color: var(--kv-text-muted);
|
|
1328
|
+
font-size: var(--kv-text-xs);
|
|
1329
|
+
font-variant-numeric: tabular-nums;
|
|
1330
|
+
white-space: nowrap;
|
|
1331
|
+
}
|
|
1332
|
+
.project-progress[hidden] {
|
|
1333
|
+
display: none;
|
|
1334
|
+
}
|
|
1335
|
+
.project-progress b {
|
|
1336
|
+
color: var(--kv-text-main);
|
|
1337
|
+
font-weight: var(--kv-weight-semibold);
|
|
1338
|
+
}
|
|
1339
|
+
.project-progress .project-blocked,
|
|
1340
|
+
.project-progress .project-blocked b {
|
|
1341
|
+
color: var(--kv-warning);
|
|
1342
|
+
}
|
|
1343
|
+
@container (max-width: 1000px) {
|
|
1344
|
+
.map-header {
|
|
1345
|
+
height: auto;
|
|
1346
|
+
min-height: 72px;
|
|
1347
|
+
flex-wrap: wrap;
|
|
1348
|
+
gap: var(--kv-space-2);
|
|
1349
|
+
}
|
|
1350
|
+
.map-header-actions {
|
|
1351
|
+
flex-basis: 100%;
|
|
1352
|
+
justify-content: flex-end;
|
|
1353
|
+
}
|
|
1354
|
+
.project-progress {
|
|
1355
|
+
margin-right: auto;
|
|
1356
|
+
gap: var(--kv-space-2);
|
|
1357
|
+
font-size: var(--kv-text-2xs);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/* Keep delivery context visible while scrolling a full-size screen preview. */
|
|
1362
|
+
.screen-dialog footer {
|
|
1363
|
+
position: sticky;
|
|
1364
|
+
bottom: 0;
|
|
1365
|
+
z-index: 1;
|
|
1366
|
+
background: var(--kv-panel-bg);
|
|
1367
|
+
flex-wrap: wrap;
|
|
1368
|
+
max-height: 40dvh;
|
|
1369
|
+
overflow: auto;
|
|
1370
|
+
}
|
|
1371
|
+
.preview-progress {
|
|
1372
|
+
flex: 1;
|
|
1373
|
+
min-width: min(240px, 100%);
|
|
1374
|
+
font-size: var(--kv-text-xs);
|
|
1375
|
+
}
|
|
1376
|
+
.preview-status,
|
|
1377
|
+
.preview-prs,
|
|
1378
|
+
.preview-actions {
|
|
1379
|
+
display: flex;
|
|
1380
|
+
flex-wrap: wrap;
|
|
1381
|
+
align-items: center;
|
|
1382
|
+
gap: var(--kv-space-2);
|
|
1383
|
+
}
|
|
1384
|
+
.preview-status {
|
|
1385
|
+
color: var(--kv-text-muted);
|
|
1386
|
+
}
|
|
1387
|
+
.preview-prs {
|
|
1388
|
+
margin-top: var(--kv-space-2);
|
|
1389
|
+
color: var(--kv-text-muted);
|
|
1390
|
+
}
|
|
1391
|
+
.preview-prs a {
|
|
1392
|
+
color: var(--kv-accent);
|
|
1393
|
+
overflow-wrap: anywhere;
|
|
1394
|
+
}
|
|
1395
|
+
.preview-prs a span {
|
|
1396
|
+
color: var(--kv-text-muted);
|
|
1397
|
+
}
|
|
1398
|
+
.screen-dialog .preview-blocker {
|
|
1399
|
+
color: var(--kv-warning);
|
|
1400
|
+
margin-top: var(--kv-space-2);
|
|
1401
|
+
overflow-wrap: anywhere;
|
|
1402
|
+
}
|
|
1403
|
+
.preview-actions {
|
|
1404
|
+
margin-left: auto;
|
|
1405
|
+
}
|
|
1406
|
+
.preview-actions [data-ui] {
|
|
1407
|
+
font-size: var(--kv-text-xs);
|
|
1408
|
+
}
|
|
@@ -7,7 +7,7 @@ npx ketatlas serve atlas.json
|
|
|
7
7
|
npx ketatlas audit atlas.json --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.
|
|
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.2 and use ketatlas serve atlas.json.
|
|
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
|
|
|
@@ -7,7 +7,7 @@ npx ketatlas serve atlas.json
|
|
|
7
7
|
npx ketatlas audit atlas.json --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.
|
|
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.2 and use ketatlas serve atlas.json.
|
|
11
11
|
|
|
12
12
|
This template models a process without HTML screens. Add a screen registry and screen nodes when needed.
|
|
13
13
|
|
package/templates/web/README.md
CHANGED
|
@@ -7,7 +7,7 @@ npx ketatlas serve atlas.json
|
|
|
7
7
|
npx ketatlas audit atlas.json --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.
|
|
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.2 and use ketatlas serve atlas.json.
|
|
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
|
|