@legionworks/facet 1.10.0 → 1.10.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/dist/gallery/{chunk-48emrxg6.js → chunk-0by8qn4x.js} +15 -0
- package/dist/gallery/frame/artifact.css +3 -0
- package/dist/gallery/frame/chunks/{markdown-zazx9h4n.js → markdown-31ee7zgx.js} +1 -1
- package/dist/gallery/frame/chunks/{markdown-fvfncadf.js → markdown-7scxp44j.js} +9 -1
- package/dist/gallery/frame/chunks/{markdown-z1wc0v0d.js → markdown-mksxbxx8.js} +2 -2
- package/dist/gallery/frame/chunks/{markdown-d1wfbs70.js → markdown-n9xcxy80.js} +14 -9
- package/dist/gallery/frame/chunks/{markdown-rd8w0ng9.js → markdown-rkt612xp.js} +1 -1
- package/dist/gallery/frame/chunks/{mermaid-de7p4v0x.js → mermaid-b8fxhsgg.js} +3 -3
- package/dist/gallery/frame/frame.css +4 -24
- package/dist/gallery/frame/runtime/chart.js +3 -3
- package/dist/gallery/frame/runtime/html.js +3 -3
- package/dist/gallery/frame/runtime/markdown.js +8 -4
- package/dist/gallery/frame/runtime/mermaid.js +4 -4
- package/dist/gallery/frame/runtime/svg.js +3 -3
- package/dist/gallery/frame/runtime/tsx.js +3 -3
- package/dist/gallery/index.html +1 -1
- package/docs/reference/cli.md +1 -1
- package/docs/reference/html.md +3 -3
- package/docs/reference/mcp.md +15 -2
- package/docs/reference/tsx.md +3 -1
- package/docs/reference/validation.md +12 -4
- package/package.json +1 -1
- package/skills/facet/SKILL.md +3 -4
- package/src/gallery-web/app.ts +20 -1
- package/src/gallery-web/frame/renderers/markdown.ts +5 -1
- package/src/gallery-web/frame/renderers/registry.ts +17 -10
- package/src/gallery-web/frame/runtime.ts +7 -1
- package/src/gallery-web/frame/styles/artifact.css +3 -0
- package/src/gallery-web/frame/styles/frame.css +4 -24
- package/src/gallery-web/frame-html.ts +5 -1
- package/src/harness-adapters/mcp/server.ts +2 -2
- package/src/service/dispatcher.ts +12 -0
- package/src/shared/html/policy.ts +9 -0
- package/src/shared/html/style-vocabulary.ts +1 -0
- package/src/validation/tier0/html.ts +6 -9
- package/src/validation/tier0/markdown.ts +2 -12
- package/src/validation/tier1/isolated-probe.ts +20 -11
- package/src/validation/tier1/limits.ts +11 -6
- package/src/validation/tier1/protocol-probe.ts +60 -26
- package/src/validation/tier1/runner.ts +173 -10
- package/src/validation/tier1/verdict.ts +9 -0
- package/templates/capacity-report.tsx +1 -1
- package/templates/fleet-dashboard.html +1 -1
- package/templates/html-release-ledger.html +1 -1
- package/templates/html-status-report.html +1 -1
- package/templates/incident-console.tsx +1 -1
- package/templates/tsx-interactive-counter.tsx +1 -1
- package/templates/tsx-status-report.tsx +1 -1
|
@@ -40,6 +40,9 @@ function buildFrameAttributes(src = "/gallery/frame") {
|
|
|
40
40
|
src
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
function usesArtifactStylesheet(artifactType) {
|
|
44
|
+
return artifactType === "html" || artifactType === "tsx";
|
|
45
|
+
}
|
|
43
46
|
function isLoopbackHostname(hostname) {
|
|
44
47
|
return hostname === "127.0.0.1";
|
|
45
48
|
}
|
|
@@ -5190,6 +5193,13 @@ function createArtifactFrame(options) {
|
|
|
5190
5193
|
renderResult = handle;
|
|
5191
5194
|
return handle;
|
|
5192
5195
|
},
|
|
5196
|
+
setTheme(theme) {
|
|
5197
|
+
const api = raw.contentWindow?.__facetFrame;
|
|
5198
|
+
if (api === null || api === undefined || typeof api.setTheme !== "function") {
|
|
5199
|
+
throw new Error("frame theme API unavailable");
|
|
5200
|
+
}
|
|
5201
|
+
api.setTheme(theme);
|
|
5202
|
+
},
|
|
5193
5203
|
get renderResult() {
|
|
5194
5204
|
return renderResult;
|
|
5195
5205
|
}
|
|
@@ -5782,6 +5792,11 @@ async function startGallery(runtime = browserGalleryRuntime()) {
|
|
|
5782
5792
|
const canCommit = () => themeGeneration === generation && nextMode === requestedThemeMode;
|
|
5783
5793
|
if (!canCommit())
|
|
5784
5794
|
return;
|
|
5795
|
+
if (usesArtifactStylesheet(source.artifactType)) {
|
|
5796
|
+
current.setTheme(nextResolvedTheme);
|
|
5797
|
+
commitTheme(nextMode, nextResolvedTheme);
|
|
5798
|
+
return;
|
|
5799
|
+
}
|
|
5785
5800
|
const next = createArtifactFrame({
|
|
5786
5801
|
artifactType: source.artifactType,
|
|
5787
5802
|
renderer: source.renderer,
|
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
dispatchRender,
|
|
6
6
|
isRenderer,
|
|
7
7
|
isTsxExecutionMode
|
|
8
|
-
} from "./markdown-
|
|
8
|
+
} from "./markdown-n9xcxy80.js";
|
|
9
9
|
|
|
10
10
|
// src/gallery-web/theme.ts
|
|
11
11
|
function isResolvedGalleryTheme(value) {
|
|
12
12
|
return value === "dark" || value === "light";
|
|
13
13
|
}
|
|
14
|
+
function galleryDataTheme(theme) {
|
|
15
|
+
return theme === "light" ? "winter" : "night";
|
|
16
|
+
}
|
|
14
17
|
|
|
15
18
|
// src/gallery-web/frame/renderer-validation.ts
|
|
16
19
|
function validateRenderer(value) {
|
|
@@ -395,6 +398,11 @@ function installGalleryFrameApi(registry) {
|
|
|
395
398
|
setPointerEventsForAllChildren(container, mode === "panzoom" ? "none" : "");
|
|
396
399
|
};
|
|
397
400
|
const api = {
|
|
401
|
+
setTheme(theme) {
|
|
402
|
+
if (!isResolvedGalleryTheme(theme))
|
|
403
|
+
throw new FacetRenderError("frame theme is invalid", "invalid_request");
|
|
404
|
+
document.documentElement.dataset.theme = galleryDataTheme(theme);
|
|
405
|
+
},
|
|
398
406
|
async render(payload) {
|
|
399
407
|
if (rendered)
|
|
400
408
|
throw new FacetRenderError("frame already rendered", "invalid_request");
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
} from "./markdown-h7pzv34e.js";
|
|
11
11
|
import {
|
|
12
12
|
importSanitizedSvgText
|
|
13
|
-
} from "./markdown-
|
|
13
|
+
} from "./markdown-31ee7zgx.js";
|
|
14
14
|
import {
|
|
15
15
|
FacetRenderError,
|
|
16
16
|
decodeArtifactBytes
|
|
17
|
-
} from "./markdown-
|
|
17
|
+
} from "./markdown-n9xcxy80.js";
|
|
18
18
|
import {
|
|
19
19
|
dedent
|
|
20
20
|
} from "./markdown-qx3cngz2.js";
|
|
@@ -51,6 +51,15 @@ function isHtmlEventHandlerAttribute(name) {
|
|
|
51
51
|
function isHtmlInlineStyleAttribute(name) {
|
|
52
52
|
return name.toLowerCase() === "style";
|
|
53
53
|
}
|
|
54
|
+
function isExternalHttpsImageSource(src) {
|
|
55
|
+
if (typeof src !== "string")
|
|
56
|
+
return false;
|
|
57
|
+
try {
|
|
58
|
+
return new URL(src).protocol === "https:";
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
54
63
|
function isAllowedHtmlUrl(elementName, attributeName, value) {
|
|
55
64
|
const element = htmlUrlBearingElement(elementName);
|
|
56
65
|
if (element === null || !isHtmlUrlBearingAttribute(element, attributeName))
|
|
@@ -177,7 +186,8 @@ function countPageShim() {
|
|
|
177
186
|
const graphRoots = roots.filter((root) => root.getAttribute(RENDERER_GRAPH_ATTRIBUTE) === "true");
|
|
178
187
|
const allMarkedCandidates = safeSelectorElements(MARKED_ROOT_SELECTOR);
|
|
179
188
|
const markedSet = new Set(allMarkedCandidates);
|
|
180
|
-
const
|
|
189
|
+
const contentRoots = allMarkedCandidates.filter((root) => root.nodeName.toLowerCase() !== "svg" && !hasMarkedRootAncestor(root, markedSet));
|
|
190
|
+
const htmlRoots = contentRoots.filter((root) => root.getAttribute("data-facet-renderer-kind") !== "markdown");
|
|
181
191
|
const html = htmlRoots.length === 0 ? undefined : {
|
|
182
192
|
rendererRootCount: htmlRoots.length,
|
|
183
193
|
headingCount: 0,
|
|
@@ -193,24 +203,19 @@ function countPageShim() {
|
|
|
193
203
|
html.listCount += safeSelectorElementsWithin(root, HTML_STRUCTURAL_GROUPS.lists.join(",")).length;
|
|
194
204
|
const images = safeSelectorElementsWithin(root, HTML_STRUCTURAL_GROUPS.images.join(","));
|
|
195
205
|
html.imageCount += images.length;
|
|
196
|
-
html.externalImageCount += images.filter((image) =>
|
|
197
|
-
try {
|
|
198
|
-
return new URL(image.getAttribute("src") ?? "").protocol === "https:";
|
|
199
|
-
} catch {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
}).length;
|
|
206
|
+
html.externalImageCount += images.filter((image) => isExternalHttpsImageSource(image.getAttribute("src"))).length;
|
|
203
207
|
html.canvasCount += safeSelectorElementsWithin(root, HTML_STRUCTURAL_GROUPS.canvases.join(",")).length;
|
|
204
208
|
}
|
|
205
209
|
const mermaidNodeCount = graphRoots.reduce((count, root) => count + safeSelectorElementsWithin(root, "g.node").length, 0);
|
|
206
210
|
const opaqueRegionCount = safeSelectorElements("*").filter((element) => element.nodeName.toLowerCase() === "canvas").length;
|
|
211
|
+
const externalImageCount = contentRoots.reduce((count, root) => count + safeSelectorElementsWithin(root, HTML_STRUCTURAL_GROUPS.images.join(",")).filter((image) => isExternalHttpsImageSource(image.getAttribute("src"))).length, 0);
|
|
207
212
|
return {
|
|
208
213
|
rendererRootSvgCount: roots.length,
|
|
209
214
|
graphCount: graphRoots.length,
|
|
210
215
|
mermaidNodeCount,
|
|
211
216
|
visibleSvgCount: roots.filter(nonDegenerateViewBox).length,
|
|
212
217
|
opaqueRegionCount,
|
|
213
|
-
externalImageCount
|
|
218
|
+
externalImageCount,
|
|
214
219
|
errorCount: safeSelectorElements("[data-facet-error]").length,
|
|
215
220
|
...html === undefined ? {} : { html }
|
|
216
221
|
};
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
isHtmlInlineStyleAttribute,
|
|
7
7
|
isHtmlUrlAttributeName,
|
|
8
8
|
isHtmlUrlBearingAttribute
|
|
9
|
-
} from "./markdown-
|
|
9
|
+
} from "./markdown-n9xcxy80.js";
|
|
10
10
|
|
|
11
11
|
// src/gallery-web/frame/renderers/html.ts
|
|
12
12
|
function shouldKeepAttribute(elementName, name, value) {
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
mermaidInitializeConfig,
|
|
3
3
|
renderMermaidDocument,
|
|
4
4
|
renderMermaidInto
|
|
5
|
-
} from "./markdown-
|
|
5
|
+
} from "./markdown-mksxbxx8.js";
|
|
6
6
|
import"./markdown-6zz8efx2.js";
|
|
7
7
|
import"./markdown-rm2yxfjj.js";
|
|
8
8
|
import"./markdown-h7pzv34e.js";
|
|
9
|
-
import"./markdown-
|
|
10
|
-
import"./markdown-
|
|
9
|
+
import"./markdown-31ee7zgx.js";
|
|
10
|
+
import"./markdown-n9xcxy80.js";
|
|
11
11
|
import"./markdown-s1jatrnk.js";
|
|
12
12
|
import"./markdown-m7frq8x8.js";
|
|
13
13
|
import"./markdown-tvdpppw8.js";
|
|
@@ -78,26 +78,7 @@ body {
|
|
|
78
78
|
padding-inline: max(16px, calc((100% - 92ch) / 2));
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
/*
|
|
82
|
-
* Native dashboard artifacts (html, interactive tsx) read top-down
|
|
83
|
-
* like markdown — vertical stays start-aligned — but unlike markdown
|
|
84
|
-
* their width is whatever the fixture authored (often a fixed-width
|
|
85
|
-
* `max-w-*` container), not a fluid readable column. Left unstyled,
|
|
86
|
-
* that narrow content sits pinned to the container's left edge,
|
|
87
|
-
* stranding most of a wide desktop stage as dead space.
|
|
88
|
-
*
|
|
89
|
-
* The centering has to land on the renderer-ROOT element
|
|
90
|
-
* (`.facet-html-root` / `#facet-tsx-mount`), not `#artifact` itself:
|
|
91
|
-
* `renderHtml`/`renderTsx` always mount a single full-width wrapper
|
|
92
|
-
* as `#artifact`'s only child, and the fixture's own narrow content
|
|
93
|
-
* (e.g. `<main class="max-w-2xl">`) is a GRANDCHILD of `#artifact`
|
|
94
|
-
* inside that wrapper — centering `#artifact`'s single full-width
|
|
95
|
-
* child does nothing. `safe center` on the horizontal axis only —
|
|
96
|
-
* the same fallback-to-start mechanism the standalone-diagram rule
|
|
97
|
-
* above uses — centers content that fits and never clips content
|
|
98
|
-
* wider than the stage (it start-aligns and stays reachable by
|
|
99
|
-
* scroll instead).
|
|
100
|
-
*/
|
|
81
|
+
/* These selectors match usesArtifactStylesheet in frame-html.ts; CSS cannot import it. */
|
|
101
82
|
#artifact[data-facet-artifact-type="html"],
|
|
102
83
|
#artifact[data-facet-artifact-type="tsx"] {
|
|
103
84
|
display: flex;
|
|
@@ -106,10 +87,9 @@ body {
|
|
|
106
87
|
|
|
107
88
|
#artifact[data-facet-artifact-type="html"] > [data-facet-renderer-root],
|
|
108
89
|
#artifact[data-facet-artifact-type="tsx"] > [data-facet-renderer-root] {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
/* A full-height root plus a sibling runtime error makes capture bounds grow on every viewport resize. */
|
|
90
|
+
/* Block flow preserves authored sibling order while keeping the renderer root a flex item of the stage. */
|
|
91
|
+
display: block;
|
|
92
|
+
/* Filling the stage keeps a sibling runtime-error line outside capture bounds as the viewport resizes. */
|
|
113
93
|
flex: 1 0 auto;
|
|
114
94
|
}
|
|
115
95
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
importSanitizedSvgText
|
|
3
|
-
} from "../chunks/markdown-
|
|
3
|
+
} from "../chunks/markdown-31ee7zgx.js";
|
|
4
4
|
import {
|
|
5
5
|
installGalleryFrameApi
|
|
6
|
-
} from "../chunks/markdown-
|
|
6
|
+
} from "../chunks/markdown-7scxp44j.js";
|
|
7
7
|
import {
|
|
8
8
|
FacetRenderError,
|
|
9
9
|
createRendererRegistry,
|
|
10
10
|
decodeArtifactBytes
|
|
11
|
-
} from "../chunks/markdown-
|
|
11
|
+
} from "../chunks/markdown-n9xcxy80.js";
|
|
12
12
|
import {
|
|
13
13
|
Accent_default,
|
|
14
14
|
Dark2_default,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderHtml
|
|
3
|
-
} from "../chunks/markdown-
|
|
3
|
+
} from "../chunks/markdown-rkt612xp.js";
|
|
4
4
|
import {
|
|
5
5
|
installGalleryFrameApi
|
|
6
|
-
} from "../chunks/markdown-
|
|
6
|
+
} from "../chunks/markdown-7scxp44j.js";
|
|
7
7
|
import {
|
|
8
8
|
createRendererRegistry
|
|
9
|
-
} from "../chunks/markdown-
|
|
9
|
+
} from "../chunks/markdown-n9xcxy80.js";
|
|
10
10
|
import"../chunks/markdown-809k7q41.js";
|
|
11
11
|
|
|
12
12
|
// src/gallery-web/frame/entries/html.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installGalleryFrameApi
|
|
3
|
-
} from "../chunks/markdown-
|
|
3
|
+
} from "../chunks/markdown-7scxp44j.js";
|
|
4
4
|
import {
|
|
5
5
|
createRendererRegistry,
|
|
6
6
|
decodeArtifactBytes
|
|
7
|
-
} from "../chunks/markdown-
|
|
7
|
+
} from "../chunks/markdown-n9xcxy80.js";
|
|
8
8
|
import {
|
|
9
9
|
__require
|
|
10
10
|
} from "../chunks/markdown-809k7q41.js";
|
|
@@ -1380,7 +1380,7 @@ async function renderMarkdown(ctx, bytes) {
|
|
|
1380
1380
|
const template = document.createElement("template");
|
|
1381
1381
|
template.innerHTML = html;
|
|
1382
1382
|
const mermaidBlocks = Array.from(template.content.querySelectorAll("pre > code.language-mermaid"));
|
|
1383
|
-
const renderMermaidInto = mermaidBlocks.length === 0 ? null : (await import("../chunks/mermaid-
|
|
1383
|
+
const renderMermaidInto = mermaidBlocks.length === 0 ? null : (await import("../chunks/mermaid-b8fxhsgg.js")).renderMermaidInto;
|
|
1384
1384
|
for (const code of mermaidBlocks) {
|
|
1385
1385
|
const source = code.textContent ?? "";
|
|
1386
1386
|
const pre = code.parentElement;
|
|
@@ -1395,7 +1395,11 @@ async function renderMarkdown(ctx, bytes) {
|
|
|
1395
1395
|
else
|
|
1396
1396
|
pre.remove();
|
|
1397
1397
|
}
|
|
1398
|
-
|
|
1398
|
+
const root = document.createElement("div");
|
|
1399
|
+
root.setAttribute("data-facet-renderer-root", "true");
|
|
1400
|
+
root.setAttribute("data-facet-renderer-kind", "markdown");
|
|
1401
|
+
root.appendChild(template.content);
|
|
1402
|
+
ctx.container.appendChild(root);
|
|
1399
1403
|
}
|
|
1400
1404
|
|
|
1401
1405
|
// src/gallery-web/frame/entries/markdown.ts
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderMermaidDocument
|
|
3
|
-
} from "../chunks/markdown-
|
|
3
|
+
} from "../chunks/markdown-mksxbxx8.js";
|
|
4
4
|
import"../chunks/markdown-6zz8efx2.js";
|
|
5
5
|
import"../chunks/markdown-rm2yxfjj.js";
|
|
6
6
|
import"../chunks/markdown-h7pzv34e.js";
|
|
7
|
-
import"../chunks/markdown-
|
|
7
|
+
import"../chunks/markdown-31ee7zgx.js";
|
|
8
8
|
import {
|
|
9
9
|
installGalleryFrameApi
|
|
10
|
-
} from "../chunks/markdown-
|
|
10
|
+
} from "../chunks/markdown-7scxp44j.js";
|
|
11
11
|
import {
|
|
12
12
|
createRendererRegistry
|
|
13
|
-
} from "../chunks/markdown-
|
|
13
|
+
} from "../chunks/markdown-n9xcxy80.js";
|
|
14
14
|
import"../chunks/markdown-s1jatrnk.js";
|
|
15
15
|
import"../chunks/markdown-m7frq8x8.js";
|
|
16
16
|
import"../chunks/markdown-tvdpppw8.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderSvgDocument
|
|
3
|
-
} from "../chunks/markdown-
|
|
3
|
+
} from "../chunks/markdown-31ee7zgx.js";
|
|
4
4
|
import {
|
|
5
5
|
installGalleryFrameApi
|
|
6
|
-
} from "../chunks/markdown-
|
|
6
|
+
} from "../chunks/markdown-7scxp44j.js";
|
|
7
7
|
import {
|
|
8
8
|
createRendererRegistry
|
|
9
|
-
} from "../chunks/markdown-
|
|
9
|
+
} from "../chunks/markdown-n9xcxy80.js";
|
|
10
10
|
import"../chunks/markdown-809k7q41.js";
|
|
11
11
|
|
|
12
12
|
// src/gallery-web/frame/entries/svg.ts
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderHtml
|
|
3
|
-
} from "../chunks/markdown-
|
|
3
|
+
} from "../chunks/markdown-rkt612xp.js";
|
|
4
4
|
import {
|
|
5
5
|
installGalleryFrameApi
|
|
6
|
-
} from "../chunks/markdown-
|
|
6
|
+
} from "../chunks/markdown-7scxp44j.js";
|
|
7
7
|
import {
|
|
8
8
|
RENDER_ERROR_ATTRIBUTE,
|
|
9
9
|
appendRenderError,
|
|
10
10
|
createRendererRegistry
|
|
11
|
-
} from "../chunks/markdown-
|
|
11
|
+
} from "../chunks/markdown-n9xcxy80.js";
|
|
12
12
|
import"../chunks/markdown-809k7q41.js";
|
|
13
13
|
|
|
14
14
|
// src/gallery-web/frame/renderers/tsx.ts
|
package/dist/gallery/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<link rel="stylesheet" crossorigin href="./chunk-2dge4gwb.css"><script type="module" crossorigin src="./chunk-
|
|
11
|
+
<link rel="stylesheet" crossorigin href="./chunk-2dge4gwb.css"><script type="module" crossorigin src="./chunk-0by8qn4x.js"></script></head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="facet-shell">
|
|
14
14
|
<header id="facet-status">
|
package/docs/reference/cli.md
CHANGED
|
@@ -125,7 +125,7 @@ errors and next actions:
|
|
|
125
125
|
| code | next action |
|
|
126
126
|
| ---------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
127
127
|
| `artifact_not_found` | Check the artifact ID and list the project. |
|
|
128
|
-
| `revision_not_found` |
|
|
128
|
+
| `revision_not_found` | Check that the artifact has a revision; omit the SHA for latest or use an existing SHA. |
|
|
129
129
|
| `duplicate_revision` | Reuse `error.details.revisionSha`; no new revision was stored. |
|
|
130
130
|
| `output_unwritable` | Fix the output directory, permissions, or rename target. |
|
|
131
131
|
| `evidence_unavailable` | Run Tier 1 again or inspect retention and evidence paths. |
|
package/docs/reference/html.md
CHANGED
|
@@ -142,15 +142,15 @@ an included Tailwind utility outside this list can still render.
|
|
|
142
142
|
|
|
143
143
|
### Recommended Tailwind utilities
|
|
144
144
|
|
|
145
|
-
`block`, `flex`, `grid`, `inline-flex`, `flex-col`, `flex-wrap`, `items-center`, `justify-between`, `justify-center`, `grid-cols-1`, `grid-cols-2`, `grid-cols-3`, `gap-2`, `gap-3`, `gap-4`, `gap-6`, `p-2`, `p-3`, `p-4`, `p-6`, `px-3`, `px-4`, `py-2`, `py-3`, `m-0`, `mt-2`, `mt-4`, `mb-2`, `mb-4`, `w-full`, `max-w-prose`, `max-w-2xl`, `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `font-medium`, `font-semibold`, `font-bold`, `leading-relaxed`, `text-left`, `text-center`, `text-right`, `text-legion-ink`, `text-legion-muted`, `text-legion-cyan`, `bg-legion-paper`, `bg-legion-ink`, `bg-legion-cyan`, `border`, `border-2`, `border-legion-line`, `rounded`, `rounded-box`, `overflow-x-auto`, `table`, `table-zebra`.
|
|
145
|
+
`block`, `flex`, `grid`, `inline-flex`, `flex-col`, `flex-wrap`, `items-center`, `justify-between`, `justify-center`, `grid-cols-1`, `grid-cols-2`, `grid-cols-3`, `gap-2`, `gap-3`, `gap-4`, `gap-6`, `p-2`, `p-3`, `p-4`, `p-6`, `px-3`, `px-4`, `py-2`, `py-3`, `m-0`, `mt-2`, `mt-4`, `mb-2`, `mb-4`, `mx-auto`, `w-full`, `max-w-prose`, `max-w-2xl`, `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `font-medium`, `font-semibold`, `font-bold`, `leading-relaxed`, `text-left`, `text-center`, `text-right`, `text-legion-ink`, `text-legion-muted`, `text-legion-cyan`, `bg-legion-paper`, `bg-legion-ink`, `bg-legion-cyan`, `border`, `border-2`, `border-legion-line`, `rounded`, `rounded-box`, `overflow-x-auto`, `table`, `table-zebra`.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
60 recommended utilities.
|
|
148
148
|
|
|
149
149
|
### Recommended daisyUI components
|
|
150
150
|
|
|
151
151
|
`alert`, `badge`, `btn`, `card`, `stat`, `table`.
|
|
152
152
|
|
|
153
|
-
6 recommended components ·
|
|
153
|
+
6 recommended components · 65 documented recommendations.
|
|
154
154
|
|
|
155
155
|
<!-- VOCABULARY:END -->
|
|
156
156
|
|
package/docs/reference/mcp.md
CHANGED
|
@@ -11,9 +11,14 @@ bun add -g @legionworks/facet
|
|
|
11
11
|
facet-mcp
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Releases through `1.9.0` ship a `facet-mcp` bin that cannot launch
|
|
14
|
+
Releases through `1.9.0` ship a `facet-mcp` bin that cannot launch: its entry file has no `#!/usr/bin/env bun` line. Use `1.10.0` or newer. With `1.10.0` on Bun `1.4.0`, each of these answered JSON-RPC `initialize` and listed six tools:
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
- `bun add -g @legionworks/facet`, then `facet-mcp`
|
|
17
|
+
- `npm i -g @legionworks/facet`, then `facet-mcp`
|
|
18
|
+
- `npx -p @legionworks/facet facet-mcp`
|
|
19
|
+
- `bunx -p @legionworks/facet facet-mcp`
|
|
20
|
+
|
|
21
|
+
Run `npx -p` from outside a Facet checkout. Inside one, npx resolves the local project instead of the published package and fails with `facet-mcp: command not found`.
|
|
17
22
|
|
|
18
23
|
The adapter resolves the CLI in this order: `FACET_CLI`, then `bun <adapter-relative-repository>/src/cli/main.ts`, then `facet` on `PATH`. Set `FACET_CLI` to an absolute CLI executable when the adapter should use another installation.
|
|
19
24
|
|
|
@@ -69,6 +74,14 @@ Set `FACET_HOME` in the host configuration when the adapter must use a non-defau
|
|
|
69
74
|
|
|
70
75
|
Exactly one of `sourceText` or `file` is required. The adapter returns `invalid_request` when both or neither are supplied.
|
|
71
76
|
|
|
77
|
+
Tier 0 means structure was parsed and compiled, not rendered. Request
|
|
78
|
+
`facet_read_back` with `tier: "visual"` before reporting a render. Visual
|
|
79
|
+
evidence covers the first rendered state of an interactive TSX artifact;
|
|
80
|
+
later interaction is not validated and does not change the stored verdict.
|
|
81
|
+
When testing by hand with the MCP Inspector CLI, `--tool-arg key=value` passes
|
|
82
|
+
`\n` literally, so multi-line `sourceText` arrives as one line; pass `file`
|
|
83
|
+
instead.
|
|
84
|
+
|
|
72
85
|
The MCP surface has six artifact tools. Start a cold-home flow with `facet_create`, then call `facet_publish`, `facet_read_back`, and `facet_export`. Each tool advertises its input schema in `tools/list`; run `facet doctor` through the CLI.
|
|
73
86
|
|
|
74
87
|
## Result and error handling
|
package/docs/reference/tsx.md
CHANGED
|
@@ -74,7 +74,9 @@ has no host port or service capability.
|
|
|
74
74
|
The TSX verifier loads the vendored `artifact.css` used by the gallery frame.
|
|
75
75
|
A runtime error after initial render, such as one thrown by a click handler,
|
|
76
76
|
appears as a gallery signal in an `aria-live="polite"` status region. It does
|
|
77
|
-
not rewrite the immutable verdict
|
|
77
|
+
not rewrite the immutable verdict. It clears on revision swap. Switching the
|
|
78
|
+
theme keeps the frame mounted, including interactive component state and any
|
|
79
|
+
runtime error signal already displayed.
|
|
78
80
|
|
|
79
81
|
Revision source remains immutable. Compilation creates derived bytes recorded at
|
|
80
82
|
the run's `compiled_path`; TSX source export writes the original `.tsx` bytes.
|
|
@@ -14,7 +14,7 @@ Every other layer is bound to it through `VerdictSchema.status`.
|
|
|
14
14
|
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
15
15
|
| `ok` | Counts agree across protocol + shim + isolated worlds, layout observable, no discriminative errors. |
|
|
16
16
|
| `error` | Counts disagree with the lexical expectation OR the protocol surfaced a discriminative error. |
|
|
17
|
-
| `partial:layout_unverified` | Counts agree but the layout pass is unverified (no SVG rendered with a non-degenerate viewBox). MUST carry a screenshot path on the wire.
|
|
17
|
+
| `partial:layout_unverified` | Counts agree but the layout pass is unverified (no SVG rendered with a non-degenerate viewBox). Markdown with zero expected renderer roots and opaque regions instead reaches `ok` when protocol and isolated observations agree that its renderer root is non-empty. Empty Markdown remains unverified. MUST carry a screenshot path on the wire. |
|
|
18
18
|
| `partial:opaque_content` | An opaque DOM region was observed, so structural contents were not verified. MUST carry a screenshot path, or a typed `screenshotError` marker when capture fails transiently. |
|
|
19
19
|
| `partial:external_resources` | The artifact references external HTTPS images the no-egress verifier could not observe. MUST carry a screenshot path, or a typed `screenshotError` marker when capture fails transiently. |
|
|
20
20
|
| `partial:unstable` | TSX interactive mode: the structure observed at the render barrier differed from the structure observed after a bounded stability window. Deliberately NOT `tampered` — a legitimately animated or async-loading component also changes structure between observations, and branding that a forgery would manufacture the false-verdict class this project has spent three arcs eliminating. `tampered` stays reserved for channel divergence. MUST carry a screenshot path, or a typed `screenshotError` marker when capture fails transiently. |
|
|
@@ -48,7 +48,7 @@ ordering, top to bottom:
|
|
|
48
48
|
7. `error` (declared-opaque, observed-zero) — the artifact declared opaque regions and none were seen.
|
|
49
49
|
8. `partial:opaque_content` — single-snapshot claim: structure has opaque regions.
|
|
50
50
|
9. `partial:external_resources` — single-snapshot claim: structure has external HTTP references.
|
|
51
|
-
10. `partial:layout_unverified` — single-snapshot claim: visible SVG with zeroed viewBoxes.
|
|
51
|
+
10. `partial:layout_unverified` — single-snapshot claim: visible SVG with zeroed viewBoxes; it also applies to empty Markdown, while diagram-free Markdown with matching non-empty observations has no layout to verify.
|
|
52
52
|
11. `ok`.
|
|
53
53
|
|
|
54
54
|
The rule generalizing rows 4 and 6: a claim that depends on ONE observation
|
|
@@ -77,11 +77,19 @@ WebP; legacy PNG evidence remains readable and exportable. Before static
|
|
|
77
77
|
capture it emulates `prefers-reduced-motion: reduce` and awaits
|
|
78
78
|
`document.fonts.ready`; these pre-flights keep repeated captures byte-identical.
|
|
79
79
|
|
|
80
|
+
If resizing the capture viewport makes the artifact grow, Tier 1 restores the
|
|
81
|
+
1280×800 evidence viewport and captures the artifact's scroll area in tiles.
|
|
82
|
+
The tiles form one still WebP image, including for interactive or animated
|
|
83
|
+
artifacts. This avoids changing viewport-sized elements such as `100vh` while
|
|
84
|
+
retaining content below and to the right. Fixed and sticky elements repeat in
|
|
85
|
+
each tile. Content that grows while the tiles are captured gets
|
|
86
|
+
`screenshot_unavailable` instead of an incomplete image.
|
|
87
|
+
|
|
80
88
|
Tier 1 uses the same artifact-type layout rules as the gallery frame: Mermaid,
|
|
81
89
|
SVG, and chart roots are safely centered on both axes, with oversized content
|
|
82
90
|
remaining reachable by scrolling; Markdown is top-aligned in a horizontally
|
|
83
|
-
centered column capped at about 92ch; HTML and TSX
|
|
84
|
-
|
|
91
|
+
centered column capped at about 92ch; HTML and TSX use top-aligned document
|
|
92
|
+
flow, where a fixed-width layout can center itself with `mx-auto`. The renderer-parity gate
|
|
85
93
|
compares both the gallery and verifier renderer-module sets and CSS/stylesheet
|
|
86
94
|
sets.
|
|
87
95
|
|
package/package.json
CHANGED
package/skills/facet/SKILL.md
CHANGED
|
@@ -93,7 +93,7 @@ non-whitespace text; the verdict requires a screenshot.
|
|
|
93
93
|
| ------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
94
94
|
| `invalid_request` | Correct the command flags, artifact type, or source bytes, then retry. |
|
|
95
95
|
| `artifact_not_found` | List or create the artifact, then use its returned ID. |
|
|
96
|
-
| `revision_not_found` |
|
|
96
|
+
| `revision_not_found` | Check that the artifact has a revision; omit the SHA for latest, or replace it with a returned revision SHA. |
|
|
97
97
|
| `duplicate_revision` | Use `error.details.revisionSha` as the existing revision SHA; do not republish identical bytes. |
|
|
98
98
|
| `evidence_unavailable` | Request Tier 1 evidence for that revision before a render export, or export source instead. |
|
|
99
99
|
| `output_unwritable` | Choose a writable `--out` path, resolve collisions, or use `--force` deliberately. |
|
|
@@ -114,9 +114,8 @@ non-whitespace text; the verdict requires a screenshot.
|
|
|
114
114
|
- With shell access, the CLI is the integration — MCP is for structured-tool-only hosts, even when the host owns an MCP client. On that surface, `facet_open_url` is safe because it always uses `--no-launch`.
|
|
115
115
|
- For MCP registration, use `bun add -g @legionworks/facet` and the bare
|
|
116
116
|
`facet-mcp` command, or the no-install fallback `npx -p @legionworks/facet
|
|
117
|
-
facet-mcp
|
|
118
|
-
|
|
119
|
-
that as an environment-specific caveat, not a universal Bun claim.
|
|
117
|
+
facet-mcp` (`bunx -p` also works). Use `1.10.0` or newer; earlier releases
|
|
118
|
+
ship a `facet-mcp` bin that cannot launch.
|
|
120
119
|
|
|
121
120
|
## Deeper reference
|
|
122
121
|
|
package/src/gallery-web/app.ts
CHANGED
|
@@ -23,7 +23,12 @@
|
|
|
23
23
|
* surfaces an error badge.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
assertLoopbackHostname,
|
|
28
|
+
buildFrameAttributes,
|
|
29
|
+
usesArtifactStylesheet,
|
|
30
|
+
type FrameAttributes,
|
|
31
|
+
} from "./frame-html";
|
|
27
32
|
import { planSwap, type SwapPlanStep } from "./swap";
|
|
28
33
|
import { connectRevisionStream } from "./sse-client";
|
|
29
34
|
import type { VerdictObserved } from "../shared/contracts/validation";
|
|
@@ -376,6 +381,7 @@ export interface CreatedArtifactFrame {
|
|
|
376
381
|
payload: FrameRenderPayload,
|
|
377
382
|
timeoutMs: number,
|
|
378
383
|
) => Promise<FrameRenderResultHandle>;
|
|
384
|
+
readonly setTheme: (theme: ResolvedGalleryTheme) => void;
|
|
379
385
|
/** Handle from the last successful render (null before the first). */
|
|
380
386
|
readonly renderResult: FrameRenderResultHandle | null;
|
|
381
387
|
}
|
|
@@ -488,6 +494,14 @@ export function createArtifactFrame(options: CreateArtifactFrameOptions): Create
|
|
|
488
494
|
renderResult = handle;
|
|
489
495
|
return handle;
|
|
490
496
|
},
|
|
497
|
+
setTheme(theme) {
|
|
498
|
+
// oxlint-disable-next-line no-underscore-dangle
|
|
499
|
+
const api = raw.contentWindow?.__facetFrame;
|
|
500
|
+
if (api === null || api === undefined || typeof api.setTheme !== "function") {
|
|
501
|
+
throw new Error("frame theme API unavailable");
|
|
502
|
+
}
|
|
503
|
+
api.setTheme(theme);
|
|
504
|
+
},
|
|
491
505
|
get renderResult(): FrameRenderResultHandle | null {
|
|
492
506
|
return renderResult;
|
|
493
507
|
},
|
|
@@ -1309,6 +1323,11 @@ export async function startGallery(runtime = browserGalleryRuntime()): Promise<v
|
|
|
1309
1323
|
const canCommit = (): boolean =>
|
|
1310
1324
|
themeGeneration === generation && nextMode === requestedThemeMode;
|
|
1311
1325
|
if (!canCommit()) return;
|
|
1326
|
+
if (usesArtifactStylesheet(source.artifactType)) {
|
|
1327
|
+
current.setTheme(nextResolvedTheme);
|
|
1328
|
+
commitTheme(nextMode, nextResolvedTheme);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1312
1331
|
const next = createArtifactFrame({
|
|
1313
1332
|
artifactType: source.artifactType,
|
|
1314
1333
|
renderer: source.renderer,
|
|
@@ -77,5 +77,9 @@ export async function renderMarkdown(ctx: RenderContext, bytes: Uint8Array): Pro
|
|
|
77
77
|
if (region.firstElementChild !== null) pre.replaceWith(region);
|
|
78
78
|
else pre.remove();
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
const root = document.createElement("div");
|
|
81
|
+
root.setAttribute("data-facet-renderer-root", "true");
|
|
82
|
+
root.setAttribute("data-facet-renderer-kind", "markdown");
|
|
83
|
+
root.appendChild(template.content);
|
|
84
|
+
ctx.container.appendChild(root);
|
|
81
85
|
}
|