ketatlas 0.1.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +10 -0
  4. package/README.md +130 -0
  5. package/assets/INTER-LICENSE +93 -0
  6. package/assets/KETJS-LICENSE +21 -0
  7. package/assets/LUCIDE-LICENSE +43 -0
  8. package/assets/design-system.lock.json +56 -0
  9. package/assets/inter-latin-wght-normal.woff2 +0 -0
  10. package/assets/inter-vietnamese-wght-normal.woff2 +0 -0
  11. package/bin/audit.js +116 -0
  12. package/bin/ketatlas.js +148 -0
  13. package/bin/server.js +93 -0
  14. package/bin/viewer.js +20 -0
  15. package/docs/architecture.md +57 -0
  16. package/docs/authoring.md +49 -0
  17. package/docs/cli.md +70 -0
  18. package/docs/configuration.md +65 -0
  19. package/docs/integration.md +108 -0
  20. package/docs/migration.md +35 -0
  21. package/docs/releasing.md +40 -0
  22. package/package.json +59 -0
  23. package/schema.json +187 -0
  24. package/src/config.js +196 -0
  25. package/src/icons.js +23 -0
  26. package/src/index.d.ts +106 -0
  27. package/src/index.js +679 -0
  28. package/src/layout.js +103 -0
  29. package/src/template.js +38 -0
  30. package/styles/design-system.css +2838 -0
  31. package/styles/design-system.document.css +2838 -0
  32. package/styles/fonts.css +17 -0
  33. package/styles/ketatlas.css +992 -0
  34. package/templates/basic/README.md +14 -0
  35. package/templates/basic/atlas.json +45 -0
  36. package/templates/basic/ketatlas.schema.json +187 -0
  37. package/templates/basic/screens/done.html +18 -0
  38. package/templates/basic/screens/screen.css +48 -0
  39. package/templates/basic/screens/welcome.html +21 -0
  40. package/templates/basic/styles/LICENSE +21 -0
  41. package/templates/basic/styles/design-system.css +2838 -0
  42. package/templates/process/README.md +14 -0
  43. package/templates/process/atlas.json +83 -0
  44. package/templates/process/ketatlas.schema.json +187 -0
  45. package/templates/web/README.md +14 -0
  46. package/templates/web/atlas.json +77 -0
  47. package/templates/web/ketatlas.schema.json +187 -0
  48. package/templates/web/screens/demo.css +255 -0
  49. package/templates/web/screens/portal.html +41 -0
  50. package/templates/web/styles/LICENSE +21 -0
  51. package/templates/web/styles/design-system.css +2838 -0
package/schema.json ADDED
@@ -0,0 +1,187 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "KetAtlas configuration",
4
+ "type": "object",
5
+ "properties": {
6
+ "$schema": {
7
+ "type": "string"
8
+ },
9
+ "version": {
10
+ "const": 1
11
+ },
12
+ "title": {
13
+ "type": "string",
14
+ "pattern": "\\S"
15
+ },
16
+ "description": {
17
+ "type": "string"
18
+ },
19
+ "viewport": {
20
+ "type": "object",
21
+ "properties": {
22
+ "width": {
23
+ "type": "integer",
24
+ "minimum": 160,
25
+ "maximum": 4096
26
+ },
27
+ "height": {
28
+ "type": "integer",
29
+ "minimum": 160,
30
+ "maximum": 4096
31
+ }
32
+ },
33
+ "required": ["width", "height"],
34
+ "additionalProperties": false
35
+ },
36
+ "screens": {
37
+ "type": "array",
38
+ "items": {
39
+ "type": "object",
40
+ "properties": {
41
+ "id": {
42
+ "type": "string",
43
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
44
+ },
45
+ "title": {
46
+ "type": "string",
47
+ "pattern": "\\S"
48
+ },
49
+ "url": {
50
+ "type": "string",
51
+ "pattern": "\\S"
52
+ },
53
+ "viewport": {
54
+ "type": "object",
55
+ "properties": {
56
+ "width": {
57
+ "type": "integer",
58
+ "minimum": 160,
59
+ "maximum": 4096
60
+ },
61
+ "height": {
62
+ "type": "integer",
63
+ "minimum": 160,
64
+ "maximum": 4096
65
+ }
66
+ },
67
+ "required": ["width", "height"],
68
+ "additionalProperties": false
69
+ },
70
+ "description": {
71
+ "type": "string"
72
+ },
73
+ "badge": {
74
+ "type": "string"
75
+ }
76
+ },
77
+ "required": ["id", "title", "url"],
78
+ "additionalProperties": false
79
+ }
80
+ },
81
+ "flows": {
82
+ "type": "array",
83
+ "minItems": 1,
84
+ "items": {
85
+ "type": "object",
86
+ "properties": {
87
+ "id": {
88
+ "type": "string",
89
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
90
+ },
91
+ "title": {
92
+ "type": "string",
93
+ "pattern": "\\S"
94
+ },
95
+ "group": {
96
+ "type": "string"
97
+ },
98
+ "description": {
99
+ "type": "string"
100
+ },
101
+ "start": {
102
+ "type": "string",
103
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
104
+ },
105
+ "ends": {
106
+ "type": "array",
107
+ "items": {
108
+ "type": "string",
109
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
110
+ }
111
+ },
112
+ "nodes": {
113
+ "type": "array",
114
+ "minItems": 1,
115
+ "items": {
116
+ "type": "object",
117
+ "properties": {
118
+ "id": {
119
+ "type": "string",
120
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
121
+ },
122
+ "screen": {
123
+ "type": "string",
124
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
125
+ },
126
+ "type": {
127
+ "enum": ["screen", "note", "external"]
128
+ },
129
+ "title": {
130
+ "type": "string"
131
+ },
132
+ "description": {
133
+ "type": "string"
134
+ },
135
+ "url": {
136
+ "type": "string",
137
+ "pattern": "\\S"
138
+ },
139
+ "column": {
140
+ "type": "integer",
141
+ "minimum": 0,
142
+ "maximum": 100
143
+ },
144
+ "row": {
145
+ "type": "integer",
146
+ "minimum": 0,
147
+ "maximum": 100
148
+ }
149
+ },
150
+ "required": ["id"],
151
+ "additionalProperties": false
152
+ }
153
+ },
154
+ "edges": {
155
+ "type": "array",
156
+ "items": {
157
+ "type": "object",
158
+ "properties": {
159
+ "from": {
160
+ "type": "string",
161
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
162
+ },
163
+ "to": {
164
+ "type": "string",
165
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"
166
+ },
167
+ "label": {
168
+ "type": "string",
169
+ "pattern": "\\S"
170
+ },
171
+ "kind": {
172
+ "enum": ["primary", "conditional", "recovery"]
173
+ }
174
+ },
175
+ "required": ["from", "to", "label"],
176
+ "additionalProperties": false
177
+ }
178
+ }
179
+ },
180
+ "required": ["id", "title", "nodes", "edges"],
181
+ "additionalProperties": false
182
+ }
183
+ }
184
+ },
185
+ "required": ["version", "title", "flows"],
186
+ "additionalProperties": false
187
+ }
package/src/config.js ADDED
@@ -0,0 +1,196 @@
1
+ /** Validate project data without a DOM, build tool, or runtime dependency. */
2
+ const ID = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
3
+ const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
4
+ const string = (value) => typeof value === "string" && value.trim().length > 0;
5
+ export function safeURL(value, baseURL = "https://ketatlas.invalid/") {
6
+ if (!string(value)) throw new TypeError("Expected a non-empty URL");
7
+ const url = new URL(value, baseURL);
8
+ if (!["http:", "https:"].includes(url.protocol))
9
+ throw new TypeError("Use an HTTP(S) or relative URL");
10
+ if (url.username || url.password) throw new TypeError("URLs must not contain credentials");
11
+ return url.href;
12
+ }
13
+ export function validateAtlas(input) {
14
+ const errors = [],
15
+ warnings = [];
16
+ const error = (path, message) => errors.push({ path, message });
17
+ const keys = (value, allowed, path) => {
18
+ for (const key of Object.keys(value))
19
+ if (!allowed.includes(key)) error(path ? `${path}.${key}` : key, "Unknown property.");
20
+ };
21
+ const id = (value, path) => {
22
+ if (!string(value) || !ID.test(value))
23
+ error(
24
+ path,
25
+ "Use letters, numbers, dots, underscores or hyphens; start with a letter or number.",
26
+ );
27
+ };
28
+ const text = (value, path, required = false) => {
29
+ if ((required && !string(value)) || (value !== undefined && typeof value !== "string"))
30
+ error(path, "Expected text.");
31
+ };
32
+ const url = (value, path) => {
33
+ try {
34
+ safeURL(value);
35
+ } catch (e) {
36
+ error(path, e.message);
37
+ }
38
+ };
39
+ const viewport = (value, path) => {
40
+ if (value === undefined) return;
41
+ if (!object(value)) return error(path, "Expected { width, height }.");
42
+ keys(value, ["width", "height"], path);
43
+ for (const key of ["width", "height"])
44
+ if (!Number.isInteger(value[key]) || value[key] < 160 || value[key] > 4096)
45
+ error(`${path}.${key}`, "Expected an integer from 160 to 4096.");
46
+ };
47
+ if (!object(input))
48
+ return {
49
+ valid: false,
50
+ errors: [{ path: "$", message: "Expected an atlas object." }],
51
+ warnings,
52
+ };
53
+ keys(input, ["$schema", "version", "title", "description", "viewport", "screens", "flows"], "");
54
+ if (input.version !== 1) error("version", "Expected version: 1.");
55
+ text(input.title, "title", true);
56
+ text(input.description, "description");
57
+ text(input.$schema, "$schema");
58
+ viewport(input.viewport, "viewport");
59
+ const screens = new Set();
60
+ if (input.screens !== undefined && !Array.isArray(input.screens))
61
+ error("screens", "Expected an array.");
62
+ for (const [i, s] of (Array.isArray(input.screens) ? input.screens : []).entries()) {
63
+ const p = `screens[${i}]`;
64
+ if (!object(s)) {
65
+ error(p, "Expected a screen object.");
66
+ continue;
67
+ }
68
+ keys(s, ["id", "title", "url", "viewport", "description", "badge"], p);
69
+ id(s.id, `${p}.id`);
70
+ text(s.title, `${p}.title`, true);
71
+ url(s.url, `${p}.url`);
72
+ viewport(s.viewport, `${p}.viewport`);
73
+ text(s.description, `${p}.description`);
74
+ text(s.badge, `${p}.badge`);
75
+ if (screens.has(s.id)) error(`${p}.id`, `Duplicate screen "${s.id}".`);
76
+ screens.add(s.id);
77
+ }
78
+ if (!Array.isArray(input.flows) || !input.flows.length) error("flows", "Add at least one flow.");
79
+ const flowIds = new Set();
80
+ for (const [i, f] of (Array.isArray(input.flows) ? input.flows : []).entries()) {
81
+ const p = `flows[${i}]`;
82
+ if (!object(f)) {
83
+ error(p, "Expected a flow object.");
84
+ continue;
85
+ }
86
+ keys(f, ["id", "title", "group", "description", "start", "ends", "nodes", "edges"], p);
87
+ id(f.id, `${p}.id`);
88
+ text(f.title, `${p}.title`, true);
89
+ text(f.group, `${p}.group`);
90
+ text(f.description, `${p}.description`);
91
+ if (flowIds.has(f.id)) error(`${p}.id`, `Duplicate flow "${f.id}".`);
92
+ flowIds.add(f.id);
93
+ if (!Array.isArray(f.nodes) || !f.nodes.length) error(`${p}.nodes`, "Add at least one node.");
94
+ const nodeIds = new Set(),
95
+ cells = new Set();
96
+ for (const [j, n] of (Array.isArray(f.nodes) ? f.nodes : []).entries()) {
97
+ const q = `${p}.nodes[${j}]`;
98
+ if (!object(n)) {
99
+ error(q, "Expected a node object.");
100
+ continue;
101
+ }
102
+ keys(n, ["id", "screen", "type", "title", "description", "url", "column", "row"], q);
103
+ id(n.id, `${q}.id`);
104
+ if (nodeIds.has(n.id)) error(`${q}.id`, `Duplicate node "${n.id}".`);
105
+ nodeIds.add(n.id);
106
+ if (n.screen !== undefined && !screens.has(n.screen))
107
+ error(`${q}.screen`, `Unknown screen "${n.screen}".`);
108
+ const type = n.type || (n.screen ? "screen" : "note");
109
+ if (!["screen", "note", "external"].includes(type))
110
+ error(`${q}.type`, "Use screen, note or external.");
111
+ if (type === "screen" && !n.screen)
112
+ error(`${q}.screen`, "A screen node needs a screen reference.");
113
+ if (type !== "screen" && n.screen !== undefined)
114
+ error(`${q}.screen`, "Only screen nodes may reference a screen.");
115
+ text(n.title, `${q}.title`, type !== "screen");
116
+ text(n.description, `${q}.description`);
117
+ if (n.url !== undefined) url(n.url, `${q}.url`);
118
+ for (const key of ["column", "row"])
119
+ if (n[key] !== undefined && (!Number.isInteger(n[key]) || n[key] < 0 || n[key] > 100))
120
+ error(`${q}.${key}`, "Expected an integer from 0 to 100.");
121
+ const cell = `${n.column ?? j}:${n.row ?? 0}`;
122
+ if (cells.has(cell)) error(q, `Two nodes occupy grid cell ${cell}.`);
123
+ cells.add(cell);
124
+ }
125
+ const start = f.start ?? f.nodes?.[0]?.id;
126
+ if (!nodeIds.has(start)) error(`${p}.start`, "Start must reference a node in this flow.");
127
+ if (f.ends !== undefined && !Array.isArray(f.ends))
128
+ error(`${p}.ends`, "Expected an array of node IDs.");
129
+ for (const key of Array.isArray(f.ends) ? f.ends : [])
130
+ if (!nodeIds.has(key)) error(`${p}.ends`, `Unknown node "${key}".`);
131
+ if (!Array.isArray(f.edges))
132
+ error(`${p}.edges`, "Expected an array (use [] for a single step).");
133
+ for (const [j, edge] of (Array.isArray(f.edges) ? f.edges : []).entries()) {
134
+ const q = `${p}.edges[${j}]`;
135
+ if (!object(edge)) {
136
+ error(q, "Expected an edge object.");
137
+ continue;
138
+ }
139
+ keys(edge, ["from", "to", "label", "kind"], q);
140
+ for (const key of ["from", "to"])
141
+ if (!nodeIds.has(edge[key])) error(`${q}.${key}`, `Unknown node "${edge[key]}".`);
142
+ text(edge.label, `${q}.label`, true);
143
+ if (edge.kind !== undefined && !["primary", "conditional", "recovery"].includes(edge.kind))
144
+ error(`${q}.kind`, "Use primary, conditional or recovery.");
145
+ }
146
+ const reached = new Set([start]);
147
+ let size = -1;
148
+ while (size !== reached.size) {
149
+ size = reached.size;
150
+ for (const edge of Array.isArray(f.edges) ? f.edges : [])
151
+ if (object(edge) && reached.has(edge.from)) reached.add(edge.to);
152
+ }
153
+ for (const key of nodeIds)
154
+ if (!reached.has(key))
155
+ warnings.push({
156
+ path: `${p}.nodes`,
157
+ message: `Node "${key}" is not reachable from "${start}".`,
158
+ });
159
+ }
160
+ return { valid: errors.length === 0, errors, warnings };
161
+ }
162
+ export class AtlasValidationError extends Error {
163
+ constructor(result) {
164
+ super(result.errors.map((e) => `${e.path}: ${e.message}`).join("\n"));
165
+ this.name = "AtlasValidationError";
166
+ this.errors = result.errors;
167
+ }
168
+ }
169
+ export function normalizeAtlas(input, baseURL) {
170
+ const result = validateAtlas(input);
171
+ if (!result.valid) throw new AtlasValidationError(result);
172
+ const data = structuredClone(input);
173
+ data.description ??= "";
174
+ data.viewport ??= { width: 390, height: 844 };
175
+ data.screens ??= [];
176
+ for (const s of data.screens) {
177
+ s.url = safeURL(s.url, baseURL);
178
+ s.viewport ??= { ...data.viewport };
179
+ }
180
+ for (const f of data.flows) {
181
+ f.group ??= "";
182
+ f.description ??= "";
183
+ f.start ??= f.nodes[0].id;
184
+ f.ends ??= f.nodes.filter((n) => !f.edges.some((edge) => edge.from === n.id)).map((n) => n.id);
185
+ f.nodes.forEach((n, i) => {
186
+ n.column ??= i;
187
+ n.row ??= 0;
188
+ n.type ??= n.screen ? "screen" : "note";
189
+ if (n.url) n.url = safeURL(n.url, baseURL);
190
+ });
191
+ f.edges.forEach((edge) => {
192
+ edge.kind ??= "primary";
193
+ });
194
+ }
195
+ return data;
196
+ }
package/src/icons.js ADDED
@@ -0,0 +1,23 @@
1
+ /* Generated from lucide-static 1.40.0; see assets/LUCIDE-LICENSE. */
2
+ export const icons = {
3
+ circle:
4
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <circle cx="12" cy="12" r="10" />\n</svg>\n',
5
+ "circle-dot":
6
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <circle cx="12" cy="12" r="1" />\n <circle cx="12" cy="12" r="10" />\n</svg>\n',
7
+ "circle-check":
8
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <circle cx="12" cy="12" r="10" />\n <path d="m16 9-5.5 5.5L8 12" />\n</svg>\n',
9
+ "arrow-up-right":
10
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <path d="M7 7h10v10" />\n <path d="M7 17 17 7" />\n</svg>\n',
11
+ "layout-grid":
12
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <rect width="7" height="7" x="3" y="3" rx="1" />\n <rect width="7" height="7" x="14" y="3" rx="1" />\n <rect width="7" height="7" x="14" y="14" rx="1" />\n <rect width="7" height="7" x="3" y="14" rx="1" />\n</svg>\n',
13
+ "circle-help":
14
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <circle cx="12" cy="12" r="10" />\n <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />\n <path d="M12 17h.01" />\n</svg>\n',
15
+ plus: '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <path d="M5 12h14" />\n <path d="M12 5v14" />\n</svg>\n',
16
+ minus:
17
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <path d="M5 12h14" />\n</svg>\n',
18
+ expand:
19
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <path d="m15 15 6 6" />\n <path d="m15 9 6-6" />\n <path d="M21 16v5h-5" />\n <path d="M21 8V3h-5" />\n <path d="M3 16v5h5" />\n <path d="m3 21 6-6" />\n <path d="M3 8V3h5" />\n <path d="M9 9 3 3" />\n</svg>\n',
20
+ x: '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <path d="M18 6 6 18" />\n <path d="m6 6 12 12" />\n</svg>\n',
21
+ "file-text":
22
+ '<!-- @license lucide-static v1.40.0 - ISC -->\n<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">\n <path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />\n <path d="M14 2v5a1 1 0 0 0 1 1h5" />\n <path d="M10 9H8" />\n <path d="M16 13H8" />\n <path d="M16 17H8" />\n</svg>\n',
23
+ };
package/src/index.d.ts ADDED
@@ -0,0 +1,106 @@
1
+ export interface Viewport {
2
+ width: number;
3
+ height: number;
4
+ }
5
+ export interface AtlasScreen {
6
+ id: string;
7
+ title: string;
8
+ url: string;
9
+ viewport?: Viewport;
10
+ description?: string;
11
+ badge?: string;
12
+ }
13
+ export interface AtlasNode {
14
+ id: string;
15
+ screen?: string;
16
+ type?: "screen" | "note" | "external";
17
+ title?: string;
18
+ description?: string;
19
+ url?: string;
20
+ column?: number;
21
+ row?: number;
22
+ }
23
+ export interface AtlasEdge {
24
+ from: string;
25
+ to: string;
26
+ label: string;
27
+ kind?: "primary" | "conditional" | "recovery";
28
+ }
29
+ export interface AtlasFlow {
30
+ id: string;
31
+ title: string;
32
+ group?: string;
33
+ description?: string;
34
+ start?: string;
35
+ ends?: string[];
36
+ nodes: AtlasNode[];
37
+ edges: AtlasEdge[];
38
+ }
39
+ export interface AtlasConfig {
40
+ $schema?: string;
41
+ version: 1;
42
+ title: string;
43
+ description?: string;
44
+ viewport?: Viewport;
45
+ screens?: AtlasScreen[];
46
+ flows: AtlasFlow[];
47
+ }
48
+ export interface AtlasOptions {
49
+ /** Relative screen URLs resolve against this URL. loadAtlas uses the JSON URL by default. */
50
+ baseURL?: string;
51
+ /** Public directory containing styles/ and assets/, ending in /. Needed when bundling the JS. */
52
+ assetBaseURL?: string;
53
+ initialFlow?: string;
54
+ theme?: "light" | "dark";
55
+ /** Opt in only for one viewer per page; preserves unrelated URL parameters. */
56
+ syncUrl?: boolean;
57
+ maxPreviews?: number;
58
+ previewThreshold?: number;
59
+ /** Defaults to "allow-scripts allow-forms". Only add permissions for trusted content. */
60
+ sandbox?: string;
61
+ signal?: AbortSignal;
62
+ }
63
+ export interface AtlasState {
64
+ flowId: string;
65
+ selectedNodeId: string | null;
66
+ zoom: number;
67
+ pan: { x: number; y: number };
68
+ flowCount: number;
69
+ nodeCount: number;
70
+ screenCount: number;
71
+ destroyed: boolean;
72
+ }
73
+ export interface AtlasInstance {
74
+ element: HTMLDivElement;
75
+ ready: Promise<void>;
76
+ goToFlow(id: string): void;
77
+ focusNode(flowId: string, nodeId: string): void;
78
+ openNode(flowId: string, nodeId: string): void;
79
+ fit(): void;
80
+ zoomTo(value: number): void;
81
+ getState(): AtlasState;
82
+ destroy(): void;
83
+ }
84
+ export interface ValidationIssue {
85
+ path: string;
86
+ message: string;
87
+ }
88
+ export interface ValidationResult {
89
+ valid: boolean;
90
+ errors: ValidationIssue[];
91
+ warnings: ValidationIssue[];
92
+ }
93
+ export class AtlasValidationError extends Error {
94
+ errors: ValidationIssue[];
95
+ }
96
+ export function validateAtlas(input: unknown): ValidationResult;
97
+ export function createAtlas(
98
+ container: HTMLElement,
99
+ config: AtlasConfig,
100
+ options?: AtlasOptions,
101
+ ): AtlasInstance;
102
+ export function loadAtlas(
103
+ container: HTMLElement,
104
+ url: string | URL,
105
+ options?: AtlasOptions,
106
+ ): Promise<AtlasInstance>;