@sapiom/harness 0.0.0 → 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.
- package/LICENSE +21 -0
- package/README.md +44 -43
- package/dist/cli/auth.d.ts +26 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +39 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +163 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/consent.d.ts +15 -0
- package/dist/cli/consent.d.ts.map +1 -0
- package/dist/cli/consent.js +81 -0
- package/dist/cli/consent.js.map +1 -0
- package/dist/cli/doctor.d.ts +27 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +74 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/machine-id.d.ts +19 -0
- package/dist/cli/machine-id.d.ts.map +1 -0
- package/dist/cli/machine-id.js +75 -0
- package/dist/cli/machine-id.js.map +1 -0
- package/dist/cli/paths.d.ts +3 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/paths.js +11 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/settings.d.ts +26 -0
- package/dist/cli/settings.d.ts.map +1 -0
- package/dist/cli/settings.js +123 -0
- package/dist/cli/settings.js.map +1 -0
- package/dist/core/adapters/claude-code.d.ts +41 -0
- package/dist/core/adapters/claude-code.d.ts.map +1 -0
- package/dist/core/adapters/claude-code.js +238 -0
- package/dist/core/adapters/claude-code.js.map +1 -0
- package/dist/core/adapters/codex.d.ts +59 -0
- package/dist/core/adapters/codex.d.ts.map +1 -0
- package/dist/core/adapters/codex.js +319 -0
- package/dist/core/adapters/codex.js.map +1 -0
- package/dist/core/canvas-body.d.ts +45 -0
- package/dist/core/canvas-body.d.ts.map +1 -0
- package/dist/core/canvas-body.js +94 -0
- package/dist/core/canvas-body.js.map +1 -0
- package/dist/core/canvas-cache.d.ts +23 -0
- package/dist/core/canvas-cache.d.ts.map +1 -0
- package/dist/core/canvas-cache.js +64 -0
- package/dist/core/canvas-cache.js.map +1 -0
- package/dist/core/canvas-enrich.d.ts +106 -0
- package/dist/core/canvas-enrich.d.ts.map +1 -0
- package/dist/core/canvas-enrich.js +178 -0
- package/dist/core/canvas-enrich.js.map +1 -0
- package/dist/core/canvas-enrichment.d.ts +144 -0
- package/dist/core/canvas-enrichment.d.ts.map +1 -0
- package/dist/core/canvas-enrichment.js +192 -0
- package/dist/core/canvas-enrichment.js.map +1 -0
- package/dist/core/canvas-graph.d.ts +71 -0
- package/dist/core/canvas-graph.d.ts.map +1 -0
- package/dist/core/canvas-graph.js +111 -0
- package/dist/core/canvas-graph.js.map +1 -0
- package/dist/core/canvas-index-classify.d.ts +41 -0
- package/dist/core/canvas-index-classify.d.ts.map +1 -0
- package/dist/core/canvas-index-classify.js +63 -0
- package/dist/core/canvas-index-classify.js.map +1 -0
- package/dist/core/canvas-interconnections.d.ts +24 -0
- package/dist/core/canvas-interconnections.d.ts.map +1 -0
- package/dist/core/canvas-interconnections.js +96 -0
- package/dist/core/canvas-interconnections.js.map +1 -0
- package/dist/core/canvas-manifest-check.d.ts +15 -0
- package/dist/core/canvas-manifest-check.d.ts.map +1 -0
- package/dist/core/canvas-manifest-check.js +90 -0
- package/dist/core/canvas-manifest-check.js.map +1 -0
- package/dist/core/canvas-render.d.ts +73 -0
- package/dist/core/canvas-render.d.ts.map +1 -0
- package/dist/core/canvas-render.js +145 -0
- package/dist/core/canvas-render.js.map +1 -0
- package/dist/core/canvas-svg.d.ts +50 -0
- package/dist/core/canvas-svg.d.ts.map +1 -0
- package/dist/core/canvas-svg.js +240 -0
- package/dist/core/canvas-svg.js.map +1 -0
- package/dist/core/canvas-template.d.ts +43 -0
- package/dist/core/canvas-template.d.ts.map +1 -0
- package/dist/core/canvas-template.js +365 -0
- package/dist/core/canvas-template.js.map +1 -0
- package/dist/core/canvas-watcher.d.ts +25 -0
- package/dist/core/canvas-watcher.d.ts.map +1 -0
- package/dist/core/canvas-watcher.js +144 -0
- package/dist/core/canvas-watcher.js.map +1 -0
- package/dist/core/collector/batcher.d.ts +57 -0
- package/dist/core/collector/batcher.d.ts.map +1 -0
- package/dist/core/collector/batcher.js +143 -0
- package/dist/core/collector/batcher.js.map +1 -0
- package/dist/core/collector/codex-tailer.d.ts +85 -0
- package/dist/core/collector/codex-tailer.d.ts.map +1 -0
- package/dist/core/collector/codex-tailer.js +270 -0
- package/dist/core/collector/codex-tailer.js.map +1 -0
- package/dist/core/collector/normalizer.d.ts +33 -0
- package/dist/core/collector/normalizer.d.ts.map +1 -0
- package/dist/core/collector/normalizer.js +94 -0
- package/dist/core/collector/normalizer.js.map +1 -0
- package/dist/core/collector/seq.d.ts +14 -0
- package/dist/core/collector/seq.d.ts.map +1 -0
- package/dist/core/collector/seq.js +20 -0
- package/dist/core/collector/seq.js.map +1 -0
- package/dist/core/collector/store.d.ts +16 -0
- package/dist/core/collector/store.d.ts.map +1 -0
- package/dist/core/collector/store.js +30 -0
- package/dist/core/collector/store.js.map +1 -0
- package/dist/core/collector/transcript.d.ts +38 -0
- package/dist/core/collector/transcript.d.ts.map +1 -0
- package/dist/core/collector/transcript.js +110 -0
- package/dist/core/collector/transcript.js.map +1 -0
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/example-seed.d.ts +34 -0
- package/dist/core/example-seed.d.ts.map +1 -0
- package/dist/core/example-seed.js +277 -0
- package/dist/core/example-seed.js.map +1 -0
- package/dist/core/inject/claude-settings.d.ts +34 -0
- package/dist/core/inject/claude-settings.d.ts.map +1 -0
- package/dist/core/inject/claude-settings.js +152 -0
- package/dist/core/inject/claude-settings.js.map +1 -0
- package/dist/core/inject/mcp-config.d.ts +26 -0
- package/dist/core/inject/mcp-config.d.ts.map +1 -0
- package/dist/core/inject/mcp-config.js +39 -0
- package/dist/core/inject/mcp-config.js.map +1 -0
- package/dist/core/inject/retention.d.ts +66 -0
- package/dist/core/inject/retention.d.ts.map +1 -0
- package/dist/core/inject/retention.js +119 -0
- package/dist/core/inject/retention.js.map +1 -0
- package/dist/core/inject/system-prompt.d.ts +11 -0
- package/dist/core/inject/system-prompt.d.ts.map +1 -0
- package/dist/core/inject/system-prompt.js +22 -0
- package/dist/core/inject/system-prompt.js.map +1 -0
- package/dist/core/macro-runner.d.ts +36 -0
- package/dist/core/macro-runner.d.ts.map +1 -0
- package/dist/core/macro-runner.js +66 -0
- package/dist/core/macro-runner.js.map +1 -0
- package/dist/core/macros.d.ts +10 -0
- package/dist/core/macros.d.ts.map +1 -0
- package/dist/core/macros.js +59 -0
- package/dist/core/macros.js.map +1 -0
- package/dist/core/path-safety.d.ts +33 -0
- package/dist/core/path-safety.d.ts.map +1 -0
- package/dist/core/path-safety.js +63 -0
- package/dist/core/path-safety.js.map +1 -0
- package/dist/core/paths.d.ts +27 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +43 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/port-detector.d.ts +69 -0
- package/dist/core/port-detector.d.ts.map +1 -0
- package/dist/core/port-detector.js +121 -0
- package/dist/core/port-detector.js.map +1 -0
- package/dist/core/session-manager.d.ts +275 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +729 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/task-manager.d.ts +138 -0
- package/dist/core/task-manager.d.ts.map +1 -0
- package/dist/core/task-manager.js +286 -0
- package/dist/core/task-manager.js.map +1 -0
- package/dist/core/task-stream.d.ts +32 -0
- package/dist/core/task-stream.d.ts.map +1 -0
- package/dist/core/task-stream.js +87 -0
- package/dist/core/task-stream.js.map +1 -0
- package/dist/core/workflow-registry.d.ts +36 -0
- package/dist/core/workflow-registry.d.ts.map +1 -0
- package/dist/core/workflow-registry.js +220 -0
- package/dist/core/workflow-registry.js.map +1 -0
- package/dist/core/workspace-context.d.ts +43 -0
- package/dist/core/workspace-context.d.ts.map +1 -0
- package/dist/core/workspace-context.js +108 -0
- package/dist/core/workspace-context.js.map +1 -0
- package/dist/core/workspace-watcher.d.ts +26 -0
- package/dist/core/workspace-watcher.d.ts.map +1 -0
- package/dist/core/workspace-watcher.js +174 -0
- package/dist/core/workspace-watcher.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/profiles/canvas-guidelines.d.ts +20 -0
- package/dist/profiles/canvas-guidelines.d.ts.map +1 -0
- package/dist/profiles/canvas-guidelines.js +48 -0
- package/dist/profiles/canvas-guidelines.js.map +1 -0
- package/dist/profiles/default.d.ts +10 -0
- package/dist/profiles/default.d.ts.map +1 -0
- package/dist/profiles/default.js +70 -0
- package/dist/profiles/default.js.map +1 -0
- package/dist/server/auth.d.ts +9 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/canvas-render.d.ts +18 -0
- package/dist/server/canvas-render.d.ts.map +1 -0
- package/dist/server/canvas-render.js +24 -0
- package/dist/server/canvas-render.js.map +1 -0
- package/dist/server/canvas.d.ts +12 -0
- package/dist/server/canvas.d.ts.map +1 -0
- package/dist/server/canvas.js +174 -0
- package/dist/server/canvas.js.map +1 -0
- package/dist/server/events-ws.d.ts +11 -0
- package/dist/server/events-ws.d.ts.map +1 -0
- package/dist/server/events-ws.js +24 -0
- package/dist/server/events-ws.js.map +1 -0
- package/dist/server/fs.d.ts +4 -0
- package/dist/server/fs.d.ts.map +1 -0
- package/dist/server/fs.js +88 -0
- package/dist/server/fs.js.map +1 -0
- package/dist/server/index.d.ts +83 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/ingest.d.ts +74 -0
- package/dist/server/ingest.d.ts.map +1 -0
- package/dist/server/ingest.js +74 -0
- package/dist/server/ingest.js.map +1 -0
- package/dist/server/macros.d.ts +32 -0
- package/dist/server/macros.d.ts.map +1 -0
- package/dist/server/macros.js +76 -0
- package/dist/server/macros.js.map +1 -0
- package/dist/server/rest.d.ts +75 -0
- package/dist/server/rest.d.ts.map +1 -0
- package/dist/server/rest.js +235 -0
- package/dist/server/rest.js.map +1 -0
- package/dist/server/static.d.ts +8 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +36 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/terminal-ws.d.ts +9 -0
- package/dist/server/terminal-ws.d.ts.map +1 -0
- package/dist/server/terminal-ws.js +59 -0
- package/dist/server/terminal-ws.js.map +1 -0
- package/dist/server/ws-router.d.ts +16 -0
- package/dist/server/ws-router.d.ts.map +1 -0
- package/dist/server/ws-router.js +26 -0
- package/dist/server/ws-router.js.map +1 -0
- package/dist/shared/types.d.ts +485 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +80 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/web/assets/index-B9R4TzH7.css +32 -0
- package/dist/web/assets/index-Dh97QAgq.js +192 -0
- package/dist/web/index.html +27 -0
- package/package.json +84 -7
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deterministic render pipeline: given a session bound to a workflow,
|
|
3
|
+
* extracts that workflow's graph (core/canvas-cache.ts — cached, so an
|
|
4
|
+
* unchanged workflow never pays for a second child process), builds the SVG +
|
|
5
|
+
* panel markup (core/canvas-svg.ts, core/canvas-body.ts), wraps it through
|
|
6
|
+
* the shared document shell (core/canvas-template.ts's
|
|
7
|
+
* `renderCanvasDocument`), and writes it to the workflow's own render file,
|
|
8
|
+
* `<cwd>/.sapiom/canvas/renders/<slug>.html`. Zero LLM involvement —
|
|
9
|
+
* extraction failure degrades to an honest error panel, never a crash.
|
|
10
|
+
*
|
|
11
|
+
* Renders are per-WORKFLOW files (not a shared index.html) so switching the
|
|
12
|
+
* binding never rewrites anything another binding depends on — the server
|
|
13
|
+
* (src/server/canvas.ts) resolves the session's current binding at request
|
|
14
|
+
* time and serves the matching render. `index.html` remains the
|
|
15
|
+
* agent-authored/custom canvas and is never touched here. An unbound session
|
|
16
|
+
* renders nothing at all (no extraction, no write): the server serves the
|
|
17
|
+
* existing empty-state/custom canvas for it.
|
|
18
|
+
*
|
|
19
|
+
* The write alone is enough to hot-reload an open canvas pane —
|
|
20
|
+
* CanvasWatcherManager already watches the whole session cwd and treats any
|
|
21
|
+
* change under `.sapiom/canvas/` as a reload signal, regardless of who wrote
|
|
22
|
+
* it.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import * as fs from "node:fs/promises";
|
|
26
|
+
import * as path from "node:path";
|
|
27
|
+
import { CANVAS_CACHE_DIR, CANVAS_RENDERS_DIR } from "../shared/types.js";
|
|
28
|
+
import { renderCanvasDocument } from "./canvas-template.js";
|
|
29
|
+
import { extractWorkflowGraphCached } from "./canvas-cache.js";
|
|
30
|
+
import { readEnrichmentCacheFile } from "./canvas-enrichment.js";
|
|
31
|
+
import { usedKinds } from "./canvas-svg.js";
|
|
32
|
+
import { assembleCanvasBody, buildErrorPanelHtml, buildLegendHtml, buildWorkflowPanelHtml, } from "./canvas-body.js";
|
|
33
|
+
/**
|
|
34
|
+
* Stable, filesystem-safe render-file name for a workflow: its directory
|
|
35
|
+
* basename (readable in `ls`) plus a short path hash (two same-named
|
|
36
|
+
* workflows at different paths can never collide). Shared by the writer here
|
|
37
|
+
* and the server's request-time resolution — must stay deterministic.
|
|
38
|
+
*/
|
|
39
|
+
export function slugForWorkflowPath(workflowPath) {
|
|
40
|
+
const base = path
|
|
41
|
+
.basename(workflowPath)
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
44
|
+
.replace(/^-+|-+$/g, "") || "workflow";
|
|
45
|
+
const hash = createHash("sha256").update(path.resolve(workflowPath)).digest("hex").slice(0, 8);
|
|
46
|
+
return `${base}-${hash}`;
|
|
47
|
+
}
|
|
48
|
+
/** Absolute path of `workflowPath`'s render file under `cwd`'s canvas dir. */
|
|
49
|
+
export function renderFileFor(cwd, workflowPath) {
|
|
50
|
+
return path.join(cwd, CANVAS_RENDERS_DIR, `${slugForWorkflowPath(workflowPath)}.html`);
|
|
51
|
+
}
|
|
52
|
+
/** Absolute path of `workflowPath`'s enrichment cache file under `cwd`'s
|
|
53
|
+
* canvas dir — same slug scheme as the render file. */
|
|
54
|
+
export function enrichmentCacheFileFor(cwd, workflowPath) {
|
|
55
|
+
return path.join(cwd, CANVAS_CACHE_DIR, `${slugForWorkflowPath(workflowPath)}.json`);
|
|
56
|
+
}
|
|
57
|
+
function badgesFor(workflow) {
|
|
58
|
+
return [workflow.definitionId != null ? "deployed" : "local only"];
|
|
59
|
+
}
|
|
60
|
+
function buildSingleBody(workflow, graph, reason, panelEnrichment) {
|
|
61
|
+
if (!graph) {
|
|
62
|
+
return assembleCanvasBody({
|
|
63
|
+
panels: [buildErrorPanelHtml(workflow.name, reason ?? "unknown extraction failure")],
|
|
64
|
+
legend: "",
|
|
65
|
+
note: "Static preview — this workflow has a build error; regenerate once it's fixed.",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const used = usedKinds(graph);
|
|
69
|
+
return assembleCanvasBody({
|
|
70
|
+
panels: [buildWorkflowPanelHtml(graph, { title: workflow.name, badges: badgesFor(workflow) }, panelEnrichment)],
|
|
71
|
+
legend: buildLegendHtml(used.nodeKinds, used.edgeKinds),
|
|
72
|
+
note: "Static preview — re-renders automatically when the workflow changes.",
|
|
73
|
+
...(panelEnrichment?.enrichment.notes ? { notes: panelEnrichment.enrichment.notes } : {}),
|
|
74
|
+
...(panelEnrichment?.enrichment.crossWorkflow
|
|
75
|
+
? { crossWorkflow: panelEnrichment.enrichment.crossWorkflow }
|
|
76
|
+
: {}),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Renders `session`'s bound workflow to its per-workflow render file. Never
|
|
81
|
+
* throws — every failure mode (extraction, filesystem) is captured in the
|
|
82
|
+
* returned outcome instead. Unbound sessions are a cheap no-op: no
|
|
83
|
+
* extraction, no write (`mode: "empty"`).
|
|
84
|
+
*/
|
|
85
|
+
export async function renderCanvasForSession(session, workflows, options = {}) {
|
|
86
|
+
const bound = session.boundWorkflowPath ? workflows.find((w) => w.path === session.boundWorkflowPath) : undefined;
|
|
87
|
+
if (!bound) {
|
|
88
|
+
return { mode: "empty", extractionFailed: [] };
|
|
89
|
+
}
|
|
90
|
+
return renderWorkflowRenderFile(session.cwd, bound, options);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Renders ONE workflow to its render file under `cwd`, merging in any cached
|
|
94
|
+
* enrichment (fresh or stale — stale gets the chip; the base structure is
|
|
95
|
+
* always freshly extracted either way). This is the write path shared by
|
|
96
|
+
* bind/create/macro renders (via `renderCanvasForSession`) and by the
|
|
97
|
+
* enrichment task's own completion re-render (core/canvas-enrich.ts), which
|
|
98
|
+
* targets a workflow directly — the session may have switched bindings while
|
|
99
|
+
* the task ran, and the render file is per-workflow anyway.
|
|
100
|
+
*/
|
|
101
|
+
export async function renderWorkflowRenderFile(cwd, bound, options = {}) {
|
|
102
|
+
const { result, cached, fingerprint } = await extractWorkflowGraphCached(bound.path);
|
|
103
|
+
const failed = !result.ok;
|
|
104
|
+
// The enrichment cache only ever decorates a successful extraction — an
|
|
105
|
+
// error panel with AI annotations for steps it can't show would be noise.
|
|
106
|
+
let panelEnrichment = null;
|
|
107
|
+
if (result.ok) {
|
|
108
|
+
const entry = await readEnrichmentCacheFile(enrichmentCacheFileFor(cwd, bound.path));
|
|
109
|
+
if (entry) {
|
|
110
|
+
panelEnrichment = { enrichment: entry.enrichment, stale: entry.sourceFingerprint !== fingerprint };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const body = buildSingleBody(bound, result.ok ? result.graph : null, result.ok ? null : result.reason, panelEnrichment);
|
|
114
|
+
const renderPath = renderFileFor(cwd, bound.path);
|
|
115
|
+
const outcome = {
|
|
116
|
+
mode: "single",
|
|
117
|
+
workflowPath: bound.path,
|
|
118
|
+
renderPath,
|
|
119
|
+
extractionFailed: failed ? [bound.name] : [],
|
|
120
|
+
cachedExtraction: cached,
|
|
121
|
+
...(panelEnrichment ? { enrichmentApplied: true, enrichmentStale: panelEnrichment.stale } : {}),
|
|
122
|
+
};
|
|
123
|
+
// An unprompted render that failed to extract must not destroy an existing
|
|
124
|
+
// (possibly good) diagram for this workflow — e.g. a project that merely
|
|
125
|
+
// hasn't run `npm install` yet after a fresh clone.
|
|
126
|
+
if (options.preserveExistingOnFailure && failed) {
|
|
127
|
+
const exists = await fs
|
|
128
|
+
.access(renderPath)
|
|
129
|
+
.then(() => true)
|
|
130
|
+
.catch(() => false);
|
|
131
|
+
if (exists) {
|
|
132
|
+
outcome.preservedExisting = true;
|
|
133
|
+
return outcome;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
await fs.mkdir(path.dirname(renderPath), { recursive: true });
|
|
138
|
+
await fs.writeFile(renderPath, renderCanvasDocument(body), "utf8");
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
outcome.writeError = err instanceof Error ? err.message : String(err);
|
|
142
|
+
}
|
|
143
|
+
return outcome;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=canvas-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-render.js","sourceRoot":"","sources":["../../src/core/canvas-render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side SVG layout for a `CanvasGraph` (see core/canvas-graph.ts),
|
|
3
|
+
* emitting markup against the classes `core/canvas-template.ts`'s CSS shell
|
|
4
|
+
* already defines (node--entry/step/pause/terminal-success/terminal-warn/
|
|
5
|
+
* launched-workflow, canvas-edge/--success/--warn/--cross/--launch). No DOM,
|
|
6
|
+
* no client-side script — the
|
|
7
|
+
* whole diagram is a static string built once, at render time.
|
|
8
|
+
*
|
|
9
|
+
* Layout: longest-path-from-roots layering (a Kahn's-algorithm variant),
|
|
10
|
+
* robust to reconverging branches (diamonds) and defensively bounded against
|
|
11
|
+
* a cycle (a malformed/adversarial manifest should degrade, not hang). Ported
|
|
12
|
+
* from the layout math in an earlier client-side renderer (see git history of
|
|
13
|
+
* canvas-template.ts, commit "canvas kit — LLM writes data, not HTML") — the
|
|
14
|
+
* geometry is unchanged, only where it runs (server, not browser).
|
|
15
|
+
*/
|
|
16
|
+
import type { CanvasEdge, CanvasEdgeKind, CanvasGraph, CanvasNode, CanvasNodeKind } from "./canvas-graph.js";
|
|
17
|
+
import type { CanvasEnrichment, CanvasLayoutHints } from "./canvas-enrichment.js";
|
|
18
|
+
export declare const NODE_W = 176;
|
|
19
|
+
export declare const NODE_H = 64;
|
|
20
|
+
export interface GraphLayout {
|
|
21
|
+
pos: Record<string, {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
}>;
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
}
|
|
28
|
+
/** Assigns each node the longest-path layer reachable from a root (an
|
|
29
|
+
* in-degree-0 node). Nodes never reached by a root (only possible on a
|
|
30
|
+
* disconnected/cyclic fragment) fall to the layer just past the deepest
|
|
31
|
+
* known one, so they still render instead of vanishing. */
|
|
32
|
+
export declare function computeLayers(nodes: readonly CanvasNode[], edges: readonly CanvasEdge[]): Map<string, number>;
|
|
33
|
+
/** Computes (x, y) for every node and the overall canvas size. Nodes in the
|
|
34
|
+
* same layer are centered as a row; layers stack top to bottom. An
|
|
35
|
+
* enrichment's `laneOrder` hint reorders nodes WITHIN their computed layer
|
|
36
|
+
* only — layer assignment itself stays purely structural. */
|
|
37
|
+
export declare function layoutGraph(graph: CanvasGraph, laneOrder?: CanvasLayoutHints["laneOrder"]): GraphLayout;
|
|
38
|
+
/** Renders one graph's `<svg>` diagram, merging in any enrichment content
|
|
39
|
+
* (node sublabels/descriptions, edge labels, group bands, lane order) — all
|
|
40
|
+
* of it pre-bounded plain strings (core/canvas-enrichment.ts), escaped here
|
|
41
|
+
* like every other text. Assumes the shared `<defs>` (glow filter + arrow
|
|
42
|
+
* markers) are already present once at the document level — see
|
|
43
|
+
* `renderCanvasDocument`'s `SVG_DEFS` — so this never emits its own. */
|
|
44
|
+
export declare function renderGraphSvg(graph: CanvasGraph, enrichment?: CanvasEnrichment | null): string;
|
|
45
|
+
/** Every node/edge kind actually used, for the shared legend footer. */
|
|
46
|
+
export declare function usedKinds(graph: CanvasGraph): {
|
|
47
|
+
nodeKinds: Set<CanvasNodeKind>;
|
|
48
|
+
edgeKinds: Set<CanvasEdgeKind>;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=canvas-svg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-svg.d.ts","sourceRoot":"","sources":["../../src/core/canvas-svg.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC7G,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAElF,eAAO,MAAM,MAAM,MAAM,CAAC;AAC1B,eAAO,MAAM,MAAM,KAAK,CAAC;AAOzB,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;4DAG4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqC7G;AAED;;;8DAG8D;AAC9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,iBAAiB,CAAC,WAAW,CAAC,GAAG,WAAW,CAyBvG;AAkFD;;;;;yEAKyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CA4E/F;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG;IAAE,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,CAKhH"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
export const NODE_W = 176;
|
|
2
|
+
export const NODE_H = 64;
|
|
3
|
+
const LAYER_GAP = 72;
|
|
4
|
+
const COL_GAP = 32;
|
|
5
|
+
const MARGIN = 40;
|
|
6
|
+
/** How far a group band extends past its member nodes' boxes. */
|
|
7
|
+
const GROUP_PAD = 10;
|
|
8
|
+
/** Assigns each node the longest-path layer reachable from a root (an
|
|
9
|
+
* in-degree-0 node). Nodes never reached by a root (only possible on a
|
|
10
|
+
* disconnected/cyclic fragment) fall to the layer just past the deepest
|
|
11
|
+
* known one, so they still render instead of vanishing. */
|
|
12
|
+
export function computeLayers(nodes, edges) {
|
|
13
|
+
const ids = nodes.map((n) => n.id);
|
|
14
|
+
const idSet = new Set(ids);
|
|
15
|
+
const indeg = new Map(ids.map((id) => [id, 0]));
|
|
16
|
+
const adj = new Map(ids.map((id) => [id, []]));
|
|
17
|
+
for (const e of edges) {
|
|
18
|
+
if (!idSet.has(e.from) || !idSet.has(e.to))
|
|
19
|
+
continue;
|
|
20
|
+
adj.get(e.from).push(e.to);
|
|
21
|
+
indeg.set(e.to, (indeg.get(e.to) ?? 0) + 1);
|
|
22
|
+
}
|
|
23
|
+
const layer = new Map();
|
|
24
|
+
let queue = ids.filter((id) => indeg.get(id) === 0);
|
|
25
|
+
const seen = new Set(queue);
|
|
26
|
+
for (const id of queue)
|
|
27
|
+
layer.set(id, 0);
|
|
28
|
+
const maxIterations = ids.length * 2 + 2;
|
|
29
|
+
let iterations = 0;
|
|
30
|
+
while (queue.length > 0 && iterations++ < maxIterations) {
|
|
31
|
+
const next = [];
|
|
32
|
+
for (const id of queue) {
|
|
33
|
+
for (const child of adj.get(id) ?? []) {
|
|
34
|
+
const candidate = (layer.get(id) ?? 0) + 1;
|
|
35
|
+
if (!layer.has(child) || candidate > layer.get(child))
|
|
36
|
+
layer.set(child, candidate);
|
|
37
|
+
if (!seen.has(child)) {
|
|
38
|
+
seen.add(child);
|
|
39
|
+
next.push(child);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
queue = next;
|
|
44
|
+
}
|
|
45
|
+
let maxLayer = 0;
|
|
46
|
+
for (const l of layer.values())
|
|
47
|
+
if (l > maxLayer)
|
|
48
|
+
maxLayer = l;
|
|
49
|
+
for (const id of ids)
|
|
50
|
+
if (!layer.has(id))
|
|
51
|
+
layer.set(id, maxLayer + 1);
|
|
52
|
+
return layer;
|
|
53
|
+
}
|
|
54
|
+
/** Computes (x, y) for every node and the overall canvas size. Nodes in the
|
|
55
|
+
* same layer are centered as a row; layers stack top to bottom. An
|
|
56
|
+
* enrichment's `laneOrder` hint reorders nodes WITHIN their computed layer
|
|
57
|
+
* only — layer assignment itself stays purely structural. */
|
|
58
|
+
export function layoutGraph(graph, laneOrder) {
|
|
59
|
+
const layer = computeLayers(graph.nodes, graph.edges);
|
|
60
|
+
const byLayer = new Map();
|
|
61
|
+
for (const n of graph.nodes) {
|
|
62
|
+
const l = layer.get(n.id) ?? 0;
|
|
63
|
+
const arr = byLayer.get(l) ?? [];
|
|
64
|
+
arr.push(n.id);
|
|
65
|
+
byLayer.set(l, arr);
|
|
66
|
+
}
|
|
67
|
+
applyLaneOrder(byLayer, graph, laneOrder);
|
|
68
|
+
const layers = [...byLayer.keys()].sort((a, b) => a - b);
|
|
69
|
+
const maxCols = layers.reduce((m, l) => Math.max(m, byLayer.get(l).length), 1);
|
|
70
|
+
const width = MARGIN * 2 + maxCols * NODE_W + (maxCols - 1) * COL_GAP;
|
|
71
|
+
const pos = {};
|
|
72
|
+
for (const l of layers) {
|
|
73
|
+
const idsInLayer = byLayer.get(l);
|
|
74
|
+
const rowWidth = idsInLayer.length * NODE_W + (idsInLayer.length - 1) * COL_GAP;
|
|
75
|
+
const startX = (width - rowWidth) / 2;
|
|
76
|
+
idsInLayer.forEach((id, i) => {
|
|
77
|
+
pos[id] = { x: startX + i * (NODE_W + COL_GAP), y: MARGIN + l * (NODE_H + LAYER_GAP) };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const height = MARGIN * 2 + (layers.length - 1) * (NODE_H + LAYER_GAP) + NODE_H;
|
|
81
|
+
return { pos, width, height: Math.max(height, MARGIN * 2 + NODE_H) };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Reorders each hinted layer's nodes in place. A hint entry is applied only
|
|
85
|
+
* when every node id it lists exists in the graph (the schema's contract —
|
|
86
|
+
* a stale enrichment must degrade to "no hint", never to a broken layout);
|
|
87
|
+
* listed ids that live in a DIFFERENT layer are simply not in this layer's
|
|
88
|
+
* row and are ignored, since laneOrder can never move a node between layers.
|
|
89
|
+
*/
|
|
90
|
+
function applyLaneOrder(byLayer, graph, laneOrder) {
|
|
91
|
+
if (!laneOrder)
|
|
92
|
+
return;
|
|
93
|
+
const nodeIds = new Set(graph.nodes.map((n) => n.id));
|
|
94
|
+
for (const [layerKey, order] of Object.entries(laneOrder)) {
|
|
95
|
+
const layerIndex = Number(layerKey);
|
|
96
|
+
const row = byLayer.get(layerIndex);
|
|
97
|
+
if (!row || !order.every((id) => nodeIds.has(id)))
|
|
98
|
+
continue;
|
|
99
|
+
const inRow = new Set(row);
|
|
100
|
+
const ordered = order.filter((id) => inRow.has(id));
|
|
101
|
+
const rest = row.filter((id) => !ordered.includes(id));
|
|
102
|
+
byLayer.set(layerIndex, [...ordered, ...rest]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function esc(s) {
|
|
106
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
107
|
+
}
|
|
108
|
+
function edgeColorClass(nodesById, edge) {
|
|
109
|
+
const target = nodesById.get(edge.to);
|
|
110
|
+
if (target?.kind === "terminal-success")
|
|
111
|
+
return "--success";
|
|
112
|
+
if (target?.kind === "terminal-warn")
|
|
113
|
+
return "--warn";
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
function arrowMarker(colorSuffix) {
|
|
117
|
+
if (colorSuffix === "--success")
|
|
118
|
+
return "url(#canvas-arrow-success)";
|
|
119
|
+
if (colorSuffix === "--warn")
|
|
120
|
+
return "url(#canvas-arrow-warn)";
|
|
121
|
+
return "url(#canvas-arrow)";
|
|
122
|
+
}
|
|
123
|
+
function edgePath(kind, x1, y1, x2, y2) {
|
|
124
|
+
if (kind === "branching") {
|
|
125
|
+
const midY = (y1 + y2) / 2;
|
|
126
|
+
return `M${x1},${y1} C${x1},${midY} ${x2},${midY} ${x2},${y2}`;
|
|
127
|
+
}
|
|
128
|
+
return `M${x1},${y1} L${x2},${y2}`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Group-hint background bands: one subtle rect per group whose member nodes
|
|
132
|
+
* ALL exist, sized to their bounding box, labeled at its top-left. Rendered
|
|
133
|
+
* before edges/nodes so it sits behind everything.
|
|
134
|
+
*/
|
|
135
|
+
function groupBandMarkup(groups, graph, layout) {
|
|
136
|
+
if (!groups || groups.length === 0)
|
|
137
|
+
return "";
|
|
138
|
+
const nodeIds = new Set(graph.nodes.map((n) => n.id));
|
|
139
|
+
return groups
|
|
140
|
+
.map((group) => {
|
|
141
|
+
if (!group.nodeIds.every((id) => nodeIds.has(id)))
|
|
142
|
+
return "";
|
|
143
|
+
const positions = group.nodeIds.map((id) => layout.pos[id]).filter(Boolean);
|
|
144
|
+
if (positions.length === 0)
|
|
145
|
+
return "";
|
|
146
|
+
const minX = Math.min(...positions.map((p) => p.x)) - GROUP_PAD;
|
|
147
|
+
const minY = Math.min(...positions.map((p) => p.y)) - GROUP_PAD;
|
|
148
|
+
const maxX = Math.max(...positions.map((p) => p.x + NODE_W)) + GROUP_PAD;
|
|
149
|
+
const maxY = Math.max(...positions.map((p) => p.y + NODE_H)) + GROUP_PAD;
|
|
150
|
+
return (`<rect class="canvas-group-band" x="${minX}" y="${minY}" width="${maxX - minX}" height="${maxY - minY}" rx="18" />` +
|
|
151
|
+
`<text class="canvas-group-label" x="${minX + 8}" y="${minY - 4}">${esc(group.label)}</text>`);
|
|
152
|
+
})
|
|
153
|
+
.filter(Boolean)
|
|
154
|
+
.join("\n");
|
|
155
|
+
}
|
|
156
|
+
/** Renders one graph's `<svg>` diagram, merging in any enrichment content
|
|
157
|
+
* (node sublabels/descriptions, edge labels, group bands, lane order) — all
|
|
158
|
+
* of it pre-bounded plain strings (core/canvas-enrichment.ts), escaped here
|
|
159
|
+
* like every other text. Assumes the shared `<defs>` (glow filter + arrow
|
|
160
|
+
* markers) are already present once at the document level — see
|
|
161
|
+
* `renderCanvasDocument`'s `SVG_DEFS` — so this never emits its own. */
|
|
162
|
+
export function renderGraphSvg(graph, enrichment) {
|
|
163
|
+
const layout = layoutGraph(graph, enrichment?.layoutHints?.laneOrder);
|
|
164
|
+
const nodesById = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
165
|
+
const bandMarkup = groupBandMarkup(enrichment?.layoutHints?.groups, graph, layout);
|
|
166
|
+
const edgeMarkup = graph.edges
|
|
167
|
+
.map((edge) => {
|
|
168
|
+
const from = layout.pos[edge.from];
|
|
169
|
+
const to = layout.pos[edge.to];
|
|
170
|
+
if (!from || !to)
|
|
171
|
+
return "";
|
|
172
|
+
const x1 = from.x + NODE_W / 2;
|
|
173
|
+
const y1 = from.y + NODE_H;
|
|
174
|
+
const x2 = to.x + NODE_W / 2;
|
|
175
|
+
const y2 = to.y;
|
|
176
|
+
// Cross (signal/handoff) and launch edges are always dashed-neutral,
|
|
177
|
+
// never colored by their destination.
|
|
178
|
+
const dashedClass = edge.kind === "cross" ? "canvas-edge--cross" : edge.kind === "launch" ? "canvas-edge--launch" : null;
|
|
179
|
+
const colorSuffix = dashedClass ? "" : edgeColorClass(nodesById, edge);
|
|
180
|
+
const classes = ["canvas-edge", dashedClass ?? (colorSuffix && `canvas-edge${colorSuffix}`)]
|
|
181
|
+
.filter(Boolean)
|
|
182
|
+
.join(" ");
|
|
183
|
+
const d = edgePath(edge.kind, x1, y1, x2, y2);
|
|
184
|
+
const marker = dashedClass ? "url(#canvas-arrow)" : arrowMarker(colorSuffix);
|
|
185
|
+
const path = `<path class="${classes}" d="${d}" marker-end="${marker}" />`;
|
|
186
|
+
// An enriched label (an intent/condition name the AI read out of the
|
|
187
|
+
// step body) wins over the structural default (e.g. "launch()").
|
|
188
|
+
const labelText = enrichment?.edgeLabels?.[`${edge.from}->${edge.to}`] ?? edge.label;
|
|
189
|
+
if (!labelText)
|
|
190
|
+
return path;
|
|
191
|
+
const dx = x2 - x1;
|
|
192
|
+
const anchor = dx > 4 ? "start" : dx < -4 ? "end" : "middle";
|
|
193
|
+
const labelX = x1 + (dx > 0 ? 10 : dx < 0 ? -10 : 0);
|
|
194
|
+
const label = `<text class="canvas-edge-label" x="${labelX}" y="${y1 + 20}" text-anchor="${anchor}">${esc(labelText)}</text>`;
|
|
195
|
+
return path + label;
|
|
196
|
+
})
|
|
197
|
+
.join("\n");
|
|
198
|
+
const nodeMarkup = graph.nodes
|
|
199
|
+
.map((node) => {
|
|
200
|
+
const p = layout.pos[node.id];
|
|
201
|
+
if (!p)
|
|
202
|
+
return "";
|
|
203
|
+
const details = enrichment?.nodeDetails?.[node.id];
|
|
204
|
+
const sublabel = details?.sublabel ?? node.sublabel;
|
|
205
|
+
const titleY = sublabel ? 26 : NODE_H / 2;
|
|
206
|
+
const sub = sublabel
|
|
207
|
+
? `<text class="canvas-node-sub" x="${NODE_W / 2}" y="44">${esc(sublabel)}</text>`
|
|
208
|
+
: "";
|
|
209
|
+
// SVG <title> = native hover tooltip; the only enrichment slot with
|
|
210
|
+
// room for a full sentence.
|
|
211
|
+
const description = details?.description ? `<title>${esc(details.description)}</title>` : "";
|
|
212
|
+
return (`<g class="canvas-node node--${node.kind}" filter="url(#canvas-glow)" transform="translate(${p.x},${p.y})">` +
|
|
213
|
+
description +
|
|
214
|
+
`<rect class="canvas-node-rect" width="${NODE_W}" height="${NODE_H}" rx="14" />` +
|
|
215
|
+
`<text class="canvas-node-title" x="${NODE_W / 2}" y="${titleY}">${esc(node.label)}</text>` +
|
|
216
|
+
sub +
|
|
217
|
+
`</g>`);
|
|
218
|
+
})
|
|
219
|
+
.join("\n");
|
|
220
|
+
// Explicit width/height pin the diagram to its NATURAL (1×) size. Without
|
|
221
|
+
// them, the CSS `width:100%` used to stretch a single-column workflow's
|
|
222
|
+
// tiny viewBox to fill the pane, ballooning every node and its font (~4×).
|
|
223
|
+
// The CSS now only centers the svg (`margin: 0 auto`); a graph wider than
|
|
224
|
+
// the pane keeps full size and scrolls (`.canvas-diagram-panel`'s
|
|
225
|
+
// overflow-x) rather than shrinking to an illegible size.
|
|
226
|
+
return (`<svg viewBox="0 0 ${layout.width} ${layout.height}" width="${layout.width}" height="${layout.height}" class="canvas-graph-svg">\n` +
|
|
227
|
+
(bandMarkup ? bandMarkup + "\n" : "") +
|
|
228
|
+
edgeMarkup +
|
|
229
|
+
"\n" +
|
|
230
|
+
nodeMarkup +
|
|
231
|
+
`\n</svg>`);
|
|
232
|
+
}
|
|
233
|
+
/** Every node/edge kind actually used, for the shared legend footer. */
|
|
234
|
+
export function usedKinds(graph) {
|
|
235
|
+
return {
|
|
236
|
+
nodeKinds: new Set(graph.nodes.map((n) => n.kind)),
|
|
237
|
+
edgeKinds: new Set(graph.edges.map((e) => e.kind)),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=canvas-svg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-svg.js","sourceRoot":"","sources":["../../src/core/canvas-svg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas kit: a prewritten template — CSS (light + dark, following the
|
|
3
|
+
* app's own theme) and a documented set of markup building blocks — that an
|
|
4
|
+
* agent clones and fills in with real content, instead of hand-writing a
|
|
5
|
+
* canvas from scratch every time. This is deliberately NOT a JSON-data +
|
|
6
|
+
* JS-renderer scheme: the agent authors real HTML/SVG using the template's
|
|
7
|
+
* classes, so it keeps full expressive freedom over the graph itself while
|
|
8
|
+
* the CSS (and therefore the visual language: colors, glow, edge styling,
|
|
9
|
+
* legend markers) is locked and can't drift.
|
|
10
|
+
*
|
|
11
|
+
* `renderCanvasDocument(bodyHtml)` is the single shared shell (CSS + theme
|
|
12
|
+
* switch) — `TEMPLATE_HTML` (the pristine, empty-state document) and
|
|
13
|
+
* `scripts/seed-example.mjs` (prefilled with real content) both wrap their
|
|
14
|
+
* body through it, so the kit and the seed can't drift from each other.
|
|
15
|
+
*/
|
|
16
|
+
/** Lives alongside index.html, in the same CANVAS_DIR — a pristine copy of
|
|
17
|
+
* the template, written once and never touched again, so "clone the
|
|
18
|
+
* template" always has a clean, un-filled-in source to clone from. */
|
|
19
|
+
export declare const CANVAS_TEMPLATE_FILE = ".sapiom/canvas/_template.html";
|
|
20
|
+
/**
|
|
21
|
+
* Wraps `bodyHtml` in the shared canvas document shell: doctype, the theme
|
|
22
|
+
* switch script, and every CSS class an agent's markup can use. This is the
|
|
23
|
+
* single source both the pristine template and `scripts/seed-example.mjs`'s
|
|
24
|
+
* prefilled instance render through, so they can't drift from each other.
|
|
25
|
+
*/
|
|
26
|
+
export declare function renderCanvasDocument(bodyHtml: string): string;
|
|
27
|
+
/** The exact document written to both `_template.html` and the initial
|
|
28
|
+
* `index.html` — a friendly empty state plus the patterns reference. */
|
|
29
|
+
export declare const TEMPLATE_HTML: string;
|
|
30
|
+
/**
|
|
31
|
+
* Backfill-only: ensures both `<cwd>/.sapiom/canvas/_template.html` (a
|
|
32
|
+
* pristine copy, so "clone the template" always has a clean source — see
|
|
33
|
+
* the visualize macro's prompt) and `<cwd>/.sapiom/canvas/index.html` (the
|
|
34
|
+
* live canvas, seeded with the same empty-state content) exist. Never
|
|
35
|
+
* clobbers either file if something's already there — an earlier session,
|
|
36
|
+
* or the agent's own edits. Called from SessionManager's create()/resume()
|
|
37
|
+
* (see its `ensureCanvasTemplate` option) so the canvas pane never opens to
|
|
38
|
+
* a completely empty iframe. Best-effort, like the sibling
|
|
39
|
+
* `workspace-context.ts` writer: a session's cwd could be unwritable, and
|
|
40
|
+
* that must never fail session creation itself.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ensureCanvasTemplate(cwd: string): Promise<void>;
|
|
43
|
+
//# sourceMappingURL=canvas-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-template.d.ts","sourceRoot":"","sources":["../../src/core/canvas-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH;;uEAEuE;AACvE,eAAO,MAAM,oBAAoB,kCAAiC,CAAC;AAiOnE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAsB7D;AAgCD;yEACyE;AACzE,eAAO,MAAM,aAAa,QAAsC,CAAC;AAiBjE;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrE"}
|