cbrowser 18.78.0 → 18.80.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/dist/analysis/accessibility-empathy.d.ts.map +1 -1
- package/dist/analysis/accessibility-empathy.js +395 -66
- package/dist/analysis/accessibility-empathy.js.map +1 -1
- package/dist/analysis/agent-ready-audit.d.ts.map +1 -1
- package/dist/analysis/agent-ready-audit.js +78 -0
- package/dist/analysis/agent-ready-audit.js.map +1 -1
- package/dist/analysis/page-understanding.d.ts +139 -1
- package/dist/analysis/page-understanding.d.ts.map +1 -1
- package/dist/analysis/page-understanding.js +179 -12
- package/dist/analysis/page-understanding.js.map +1 -1
- package/dist/artifact-store.d.ts +19 -5
- package/dist/artifact-store.d.ts.map +1 -1
- package/dist/artifact-store.js +41 -11
- package/dist/artifact-store.js.map +1 -1
- package/dist/browser.d.ts +9 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +81 -5
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +55 -10
- package/dist/cli.js.map +1 -1
- package/dist/cognitive/index.d.ts.map +1 -1
- package/dist/cognitive/index.js +3 -2
- package/dist/cognitive/index.js.map +1 -1
- package/dist/config.d.ts +57 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +234 -7
- package/dist/config.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +108 -4
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +62 -938
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/account-personas.d.ts +41 -0
- package/dist/mcp-tools/account-personas.d.ts.map +1 -0
- package/dist/mcp-tools/account-personas.js +98 -0
- package/dist/mcp-tools/account-personas.js.map +1 -0
- package/dist/mcp-tools/base/analysis-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/analysis-tools.js +12 -2
- package/dist/mcp-tools/base/analysis-tools.js.map +1 -1
- package/dist/mcp-tools/base/artifact-tools.d.ts +3 -0
- package/dist/mcp-tools/base/artifact-tools.d.ts.map +1 -0
- package/dist/mcp-tools/base/artifact-tools.js +78 -0
- package/dist/mcp-tools/base/artifact-tools.js.map +1 -0
- package/dist/mcp-tools/base/audit-tools.d.ts +11 -0
- package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +595 -340
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/mcp-tools/base/browser-management-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/browser-management-tools.js +18 -0
- package/dist/mcp-tools/base/browser-management-tools.js.map +1 -1
- package/dist/mcp-tools/base/bug-analysis-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/bug-analysis-tools.js +59 -3
- package/dist/mcp-tools/base/bug-analysis-tools.js.map +1 -1
- package/dist/mcp-tools/base/capture-tools.d.ts +37 -0
- package/dist/mcp-tools/base/capture-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/capture-tools.js +267 -6
- package/dist/mcp-tools/base/capture-tools.js.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.d.ts +12 -0
- package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.js +63 -15
- package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
- package/dist/mcp-tools/base/extraction-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/extraction-tools.js +33 -2
- package/dist/mcp-tools/base/extraction-tools.js.map +1 -1
- package/dist/mcp-tools/base/index.d.ts +2 -0
- package/dist/mcp-tools/base/index.d.ts.map +1 -1
- package/dist/mcp-tools/base/index.js +9 -0
- package/dist/mcp-tools/base/index.js.map +1 -1
- package/dist/mcp-tools/base/marketing-tools.js +3 -3
- package/dist/mcp-tools/base/marketing-tools.js.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.js +529 -40
- package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
- package/dist/mcp-tools/base/site-knowledge-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/site-knowledge-tools.js +36 -3
- package/dist/mcp-tools/base/site-knowledge-tools.js.map +1 -1
- package/dist/mcp-tools/base/values-tools.d.ts +113 -0
- package/dist/mcp-tools/base/values-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/values-tools.js +868 -78
- package/dist/mcp-tools/base/values-tools.js.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.js +130 -10
- package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
- package/dist/mcp-tools/index.d.ts +3 -1
- package/dist/mcp-tools/index.d.ts.map +1 -1
- package/dist/mcp-tools/index.js +3 -28
- package/dist/mcp-tools/index.js.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.d.ts.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.js +363 -20
- package/dist/mcp-tools/persona-creation-tools.js.map +1 -1
- package/dist/mcp-tools/persona-lifecycle-tools.d.ts +3 -0
- package/dist/mcp-tools/persona-lifecycle-tools.d.ts.map +1 -0
- package/dist/mcp-tools/persona-lifecycle-tools.js +406 -0
- package/dist/mcp-tools/persona-lifecycle-tools.js.map +1 -0
- package/dist/mcp-tools/screenshot-utils.d.ts.map +1 -1
- package/dist/mcp-tools/screenshot-utils.js +56 -10
- package/dist/mcp-tools/screenshot-utils.js.map +1 -1
- package/dist/mcp-tools/tier-gate.js +2 -2
- package/dist/mcp-tools/tier-gate.js.map +1 -1
- package/dist/mcp-tools/tool-categories.d.ts.map +1 -1
- package/dist/mcp-tools/tool-categories.js +4 -1
- package/dist/mcp-tools/tool-categories.js.map +1 -1
- package/dist/mcp-tools/ui-resources.d.ts +167 -0
- package/dist/mcp-tools/ui-resources.d.ts.map +1 -0
- package/dist/mcp-tools/ui-resources.js +707 -0
- package/dist/mcp-tools/ui-resources.js.map +1 -0
- package/dist/mcp-tools/widget-kit.d.ts +299 -0
- package/dist/mcp-tools/widget-kit.d.ts.map +1 -0
- package/dist/mcp-tools/widget-kit.js +2034 -0
- package/dist/mcp-tools/widget-kit.js.map +1 -0
- package/dist/mcp-ui-resources.d.ts.map +1 -1
- package/dist/mcp-ui-resources.js +14 -4
- package/dist/mcp-ui-resources.js.map +1 -1
- package/dist/persona-questionnaire.d.ts +69 -1
- package/dist/persona-questionnaire.d.ts.map +1 -1
- package/dist/persona-questionnaire.js +177 -76
- package/dist/persona-questionnaire.js.map +1 -1
- package/dist/persona-scope.d.ts +20 -0
- package/dist/persona-scope.d.ts.map +1 -0
- package/dist/persona-scope.js +59 -0
- package/dist/persona-scope.js.map +1 -0
- package/dist/personas.d.ts +51 -17
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +947 -286
- package/dist/personas.js.map +1 -1
- package/dist/recording/capture-player.d.ts +68 -0
- package/dist/recording/capture-player.d.ts.map +1 -0
- package/dist/recording/capture-player.js +298 -0
- package/dist/recording/capture-player.js.map +1 -0
- package/dist/recording/engine.d.ts +52 -0
- package/dist/recording/engine.d.ts.map +1 -1
- package/dist/recording/engine.js +241 -3
- package/dist/recording/engine.js.map +1 -1
- package/dist/recording/inline-player.d.ts +39 -0
- package/dist/recording/inline-player.d.ts.map +1 -0
- package/dist/recording/inline-player.js +272 -0
- package/dist/recording/inline-player.js.map +1 -0
- package/dist/recording/saliency-overlay.d.ts +133 -0
- package/dist/recording/saliency-overlay.d.ts.map +1 -0
- package/dist/recording/saliency-overlay.js +360 -0
- package/dist/recording/saliency-overlay.js.map +1 -0
- package/dist/recording/types.d.ts +32 -0
- package/dist/recording/types.d.ts.map +1 -1
- package/dist/recording/types.js +46 -0
- package/dist/recording/types.js.map +1 -1
- package/dist/site-model/manager.d.ts +20 -0
- package/dist/site-model/manager.d.ts.map +1 -1
- package/dist/site-model/manager.js +52 -0
- package/dist/site-model/manager.js.map +1 -1
- package/dist/trait-reference.d.ts +26 -2
- package/dist/trait-reference.d.ts.map +1 -1
- package/dist/trait-reference.js +129 -19
- package/dist/trait-reference.js.map +1 -1
- package/dist/types.d.ts +105 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/url-resolution.d.ts +58 -0
- package/dist/url-resolution.d.ts.map +1 -0
- package/dist/url-resolution.js +143 -0
- package/dist/url-resolution.js.map +1 -0
- package/dist/values/big-five-inference.d.ts +121 -0
- package/dist/values/big-five-inference.d.ts.map +1 -0
- package/dist/values/big-five-inference.js +241 -0
- package/dist/values/big-five-inference.js.map +1 -0
- package/dist/values/big-five-values.d.ts +76 -0
- package/dist/values/big-five-values.d.ts.map +1 -0
- package/dist/values/big-five-values.js +152 -0
- package/dist/values/big-five-values.js.map +1 -0
- package/dist/values/index.d.ts +1 -1
- package/dist/values/index.d.ts.map +1 -1
- package/dist/values/index.js +1 -1
- package/dist/values/index.js.map +1 -1
- package/dist/values/persona-completeness.d.ts +91 -0
- package/dist/values/persona-completeness.d.ts.map +1 -0
- package/dist/values/persona-completeness.js +114 -0
- package/dist/values/persona-completeness.js.map +1 -0
- package/dist/values/persona-diff.d.ts +50 -0
- package/dist/values/persona-diff.d.ts.map +1 -0
- package/dist/values/persona-diff.js +126 -0
- package/dist/values/persona-diff.js.map +1 -0
- package/dist/values/persona-values.d.ts +16 -0
- package/dist/values/persona-values.d.ts.map +1 -1
- package/dist/values/persona-values.js +33 -0
- package/dist/values/persona-values.js.map +1 -1
- package/dist/values/schwartz-values.d.ts +9 -1
- package/dist/values/schwartz-values.d.ts.map +1 -1
- package/dist/values/schwartz-values.js +10 -2
- package/dist/values/schwartz-values.js.map +1 -1
- package/dist/values/value-mappings.d.ts +41 -2
- package/dist/values/value-mappings.d.ts.map +1 -1
- package/dist/values/value-mappings.js +65 -8
- package/dist/values/value-mappings.js.map +1 -1
- package/dist/visual/attention-quality.d.ts +27 -0
- package/dist/visual/attention-quality.d.ts.map +1 -1
- package/dist/visual/attention-quality.js +164 -2
- package/dist/visual/attention-quality.js.map +1 -1
- package/dist/visual/attention-transport.d.ts +41 -2
- package/dist/visual/attention-transport.d.ts.map +1 -1
- package/dist/visual/attention-transport.js +98 -26
- package/dist/visual/attention-transport.js.map +1 -1
- package/dist/visual/barrier-overlay-html.d.ts +10 -0
- package/dist/visual/barrier-overlay-html.d.ts.map +1 -1
- package/dist/visual/barrier-overlay-html.js.map +1 -1
- package/dist/visual/cognitive-models.d.ts +109 -13
- package/dist/visual/cognitive-models.d.ts.map +1 -1
- package/dist/visual/cognitive-models.js +166 -3
- package/dist/visual/cognitive-models.js.map +1 -1
- package/dist/visual/cognitive-transport-chain.d.ts +149 -5
- package/dist/visual/cognitive-transport-chain.d.ts.map +1 -1
- package/dist/visual/cognitive-transport-chain.js +558 -30
- package/dist/visual/cognitive-transport-chain.js.map +1 -1
- package/dist/visual/cognitive-transport.d.ts +45 -3
- package/dist/visual/cognitive-transport.d.ts.map +1 -1
- package/dist/visual/cognitive-transport.js +126 -12
- package/dist/visual/cognitive-transport.js.map +1 -1
- package/dist/visual/fixation-corpus.d.ts +52 -0
- package/dist/visual/fixation-corpus.d.ts.map +1 -1
- package/dist/visual/fixation-corpus.js +95 -0
- package/dist/visual/fixation-corpus.js.map +1 -1
- package/dist/visual/llm-relevance.d.ts +193 -0
- package/dist/visual/llm-relevance.d.ts.map +1 -0
- package/dist/visual/llm-relevance.js +480 -0
- package/dist/visual/llm-relevance.js.map +1 -0
- package/dist/visual/perceptual-transport.d.ts +60 -0
- package/dist/visual/perceptual-transport.d.ts.map +1 -1
- package/dist/visual/perceptual-transport.js +228 -19
- package/dist/visual/perceptual-transport.js.map +1 -1
- package/dist/visual/persona-context.d.ts +23 -0
- package/dist/visual/persona-context.d.ts.map +1 -0
- package/dist/visual/persona-context.js +44 -0
- package/dist/visual/persona-context.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
import { buildWidget, widgetUri, MCP_APP_MIME as KIT_MIME } from "./widget-kit.js";
|
|
2
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
export const UI_PROBE_URI = "ui://cbrowser/probe";
|
|
4
|
+
export const CAPTURE_UI_PREFIX = "ui://cbrowser/capture/";
|
|
5
|
+
/**
|
|
6
|
+
* Built player HTML, keyed by capture slug.
|
|
7
|
+
*
|
|
8
|
+
* The host reads a ui:// resource in a request separate from the tool call, so
|
|
9
|
+
* the resource handler cannot know which capture is being asked about from the
|
|
10
|
+
* call itself -- the slug in the URI is the only link between them. Hence a
|
|
11
|
+
* store, and hence a per-capture URI rather than one fixed template URI.
|
|
12
|
+
*
|
|
13
|
+
* Bounded because these hold hundreds of KB of embedded frames each and a
|
|
14
|
+
* long-lived server would otherwise accumulate every capture it ever ran.
|
|
15
|
+
*/
|
|
16
|
+
const CAPTURE_HTML_LIMIT = 24;
|
|
17
|
+
const captureHtml = new Map();
|
|
18
|
+
/** Store a built player and return the resource URI that will serve it. */
|
|
19
|
+
export function publishCaptureUi(slug, html) {
|
|
20
|
+
captureHtml.set(slug, html);
|
|
21
|
+
while (captureHtml.size > CAPTURE_HTML_LIMIT) {
|
|
22
|
+
const oldest = captureHtml.keys().next().value;
|
|
23
|
+
if (oldest === undefined)
|
|
24
|
+
break;
|
|
25
|
+
captureHtml.delete(oldest);
|
|
26
|
+
}
|
|
27
|
+
return `${CAPTURE_UI_PREFIX}${slug}`;
|
|
28
|
+
}
|
|
29
|
+
export const MCP_APP_MIME = KIT_MIME;
|
|
30
|
+
const PROBE_HTML = `<!doctype html><meta charset="utf-8">
|
|
31
|
+
<style>
|
|
32
|
+
:root{color-scheme:light dark}
|
|
33
|
+
body{font:15px/1.55 ui-sans-serif,system-ui,sans-serif;margin:0;padding:18px}
|
|
34
|
+
.card{border:1px solid rgba(128,128,128,.35);border-radius:10px;padding:16px}
|
|
35
|
+
h1{font-size:1rem;margin:0 0 .5rem}
|
|
36
|
+
code{font-family:ui-monospace,monospace;font-size:.85em}
|
|
37
|
+
.ok{color:#1a7f4b;font-weight:600}
|
|
38
|
+
.note{color:#666;font-size:.86rem;margin-top:.8rem}
|
|
39
|
+
</style>
|
|
40
|
+
<div class="card">
|
|
41
|
+
<h1>CBrowser inline UI probe</h1>
|
|
42
|
+
<p><span class="ok">This rendered as HTML.</span> MCP Apps UI resources display for
|
|
43
|
+
this connector, so the capture player can be delivered the same way instead of as a link.</p>
|
|
44
|
+
<p class="note">Served as <code>text/html;profile=mcp-app</code> from <code>ui://cbrowser/probe</code>.</p>
|
|
45
|
+
<hr style="border:0;border-top:1px solid rgba(128,128,128,.3);margin:14px 0">
|
|
46
|
+
<p><strong>data: URI image test</strong> — a green bar should appear below.</p>
|
|
47
|
+
<img id="swatch" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAAoCAIAAAC6iKlyAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAo0lEQVR4nO3UQW0EARTD0MExlAxqqZdAr918VU8KAsvx836y9+8hPCi/X1EN6IDuP72N0QHdXENGNwcnHd2cRgd0cw0Z3RycdHRzGh3QzTVkdHNw0tHNaXRAN9eQ0c3BSUc3p9EB3VxDRjcHJx3dnEYHdHMNGd0cnHR0cxod0M01ZHRzcNLRzWl0QDfXkNHNwUlHN6fRAd1cQ0Y3BycdzZn+uh+XmLPx0sfXOwAAAABJRU5ErkJggg==" alt="data URI test swatch" width="120" height="40"
|
|
48
|
+
style="border-radius:4px;display:block">
|
|
49
|
+
<p class="note" id="verdict">If the bar is missing, this sandbox's CSP blocks
|
|
50
|
+
<code>data:</code> in <code>img-src</code>, and capture frames need another channel.</p>
|
|
51
|
+
<script>
|
|
52
|
+
// The capture player embeds frames as data: URIs -- nothing is fetched from an
|
|
53
|
+
// origin, because per-capture data cannot come from a static CDN and the
|
|
54
|
+
// sandbox allowlist does not include cbrowser.ai. Whether that works hinges
|
|
55
|
+
// entirely on img-src permitting data:, so the probe reports it rather than
|
|
56
|
+
// leaving it to be discovered after a player is built on the assumption.
|
|
57
|
+
var img = document.getElementById("swatch");
|
|
58
|
+
img.addEventListener("load", function () {
|
|
59
|
+
document.getElementById("verdict").textContent =
|
|
60
|
+
"data: URIs render here (" + img.naturalWidth + "x" + img.naturalHeight +
|
|
61
|
+
"). Capture frames can be embedded inline.";
|
|
62
|
+
});
|
|
63
|
+
img.addEventListener("error", function () {
|
|
64
|
+
document.getElementById("verdict").textContent =
|
|
65
|
+
"data: URI was BLOCKED by CSP. Capture frames need another channel.";
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
</div>`;
|
|
69
|
+
/**
|
|
70
|
+
* Servers that already have these resources.
|
|
71
|
+
*
|
|
72
|
+
* The call site is genuinely ambiguous: startRemoteMcpServer registers them for
|
|
73
|
+
* every server it builds, and the demo/enterprise entrypoints also need to
|
|
74
|
+
* register them because they are separate builds. Whichever one you delete, the
|
|
75
|
+
* other layer is wrong for some variant -- so registration is idempotent instead.
|
|
76
|
+
* The SDK throws on a duplicate URI, which crash-loops the process at boot.
|
|
77
|
+
*/
|
|
78
|
+
const registered = new WeakSet();
|
|
79
|
+
/**
|
|
80
|
+
* Render a status payload as a panel.
|
|
81
|
+
*
|
|
82
|
+
* Deliberately the first UI resource shipped, because it needs no subresources
|
|
83
|
+
* at all -- counts, versions, config strings. That isolates the ui:// delivery
|
|
84
|
+
* path (resource rides in the tool result, host injects, iframe renders) from
|
|
85
|
+
* the separate question of what the sandbox CSP will fetch. One unknown at a
|
|
86
|
+
* time; the capture player stacks two.
|
|
87
|
+
*
|
|
88
|
+
* The JSON block it accompanies is never replaced. status is read by the model
|
|
89
|
+
* at least as often as by a person, and it is read to diff numbers -- a tool
|
|
90
|
+
* that returned only a panel would leave the model able to report that a widget
|
|
91
|
+
* appeared and nothing else.
|
|
92
|
+
*/
|
|
93
|
+
/**
|
|
94
|
+
* The status view, as a spec rather than a template.
|
|
95
|
+
*
|
|
96
|
+
* This is the shape every other view now takes: name the fields, name the
|
|
97
|
+
* blocks, and let the kit supply chrome, hydration, theming and safety. What
|
|
98
|
+
* used to be 400 lines of markup and CSS per tool is the object below.
|
|
99
|
+
*/
|
|
100
|
+
export const STATUS_SPEC = {
|
|
101
|
+
id: "status",
|
|
102
|
+
// The docs page, not the /tools/ marketing route. Every tool has a docs page
|
|
103
|
+
// -- 128 of them, generated from the wiki, with When to Use / Parameters /
|
|
104
|
+
// Example / Tips -- whereas only six tools have a /tools/ page and the SPA
|
|
105
|
+
// answers 200 with the homepage for the rest, so a link there would look
|
|
106
|
+
// like it worked and silently land somewhere else. (2026-07-31)
|
|
107
|
+
toolPage: "https://cbrowser.ai/docs/tool-status/",
|
|
108
|
+
title: "Status",
|
|
109
|
+
hero: {
|
|
110
|
+
variant: "gradient",
|
|
111
|
+
subtitle: { field: "version" },
|
|
112
|
+
health: {
|
|
113
|
+
checks: [
|
|
114
|
+
{ field: "browsers", flag: "installed", failLabel: "browser not installed", nameKey: "name" },
|
|
115
|
+
{ field: "directories", flag: "exists", failLabel: "missing directory", nameKey: "name" },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
facts: [
|
|
119
|
+
{ field: "toolCount", label: "tools" },
|
|
120
|
+
{ field: "browsers", label: "browsers", countFlag: "installed", attnWhenShort: true },
|
|
121
|
+
{ field: "directories", label: "directories", countFlag: "exists", attnWhenShort: true },
|
|
122
|
+
{ field: "sessions", label: "sessions" },
|
|
123
|
+
{ field: "healCache.totalHeals", label: "self-heals" },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
blocks: [
|
|
127
|
+
{
|
|
128
|
+
type: "drawer",
|
|
129
|
+
title: "Details",
|
|
130
|
+
blocks: [
|
|
131
|
+
{ type: "kv", title: "Configuration", field: "config" },
|
|
132
|
+
{ type: "kv", title: "Self-healing cache", field: "healCache" },
|
|
133
|
+
{ type: "table", title: "Directories", field: "directories" },
|
|
134
|
+
{ type: "table", title: "Browsers", field: "browsers" },
|
|
135
|
+
{ type: "rest", title: "Counts and paths" },
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
footer: { label: "Data directory:", field: "dataDir" },
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Persona view: the three trait sections as the account editor draws them,
|
|
143
|
+
* each on its own colour ramp so the section is readable without the heading.
|
|
144
|
+
*/
|
|
145
|
+
export const PERSONA_SPEC = {
|
|
146
|
+
id: "persona",
|
|
147
|
+
toolPage: "https://cbrowser.ai/docs/tool-persona-lookup/",
|
|
148
|
+
title: "Persona",
|
|
149
|
+
// The name leads when there is one; "Persona" is the fallback for a payload
|
|
150
|
+
// that has not named itself.
|
|
151
|
+
titleField: "name",
|
|
152
|
+
hero: {
|
|
153
|
+
variant: "gradient",
|
|
154
|
+
subtitle: { field: "description" },
|
|
155
|
+
actions: [
|
|
156
|
+
{ label: "Customize this persona", url: "https://cbrowser.ai/account/personas" },
|
|
157
|
+
],
|
|
158
|
+
facts: [
|
|
159
|
+
{ field: "traits", label: "traits" },
|
|
160
|
+
{ field: "values", label: "values" },
|
|
161
|
+
{ field: "accessibility_traits", label: "accessibility" },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
blocks: [
|
|
165
|
+
// Editable. The sandbox has no network of its own, but callServerTool
|
|
166
|
+
// reaches the server that rendered this -- the same channel the image
|
|
167
|
+
// blocks already use -- so the sliders write straight back through
|
|
168
|
+
// persona_update rather than asking the reader to copy numbers into a
|
|
169
|
+
// separate tool call. (2026-08-01)
|
|
170
|
+
{ type: "editor", title: "Cognitive traits", field: "traits", personaField: "name", ramp: "trait", describe: true },
|
|
171
|
+
{ type: "traits", title: "Values", field: "values", ramp: "value" },
|
|
172
|
+
{ type: "traits", title: "Accessibility traits", field: "accessibility_traits", ramp: "accessibility", describe: true },
|
|
173
|
+
{
|
|
174
|
+
type: "drawer",
|
|
175
|
+
title: "Details",
|
|
176
|
+
blocks: [
|
|
177
|
+
{ type: "kv", title: "Demographics", field: "demographics" },
|
|
178
|
+
{ type: "rest", title: "Other fields" },
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* One interactive surface for persona CRUD.
|
|
185
|
+
*
|
|
186
|
+
* The roster, the selected persona's editable traits, save and delete, in a
|
|
187
|
+
* single view. Reads ship with the tool result so switching personas costs no
|
|
188
|
+
* round trip; writes go back through persona_update and persona_delete, which
|
|
189
|
+
* already handle both stores, the built-in guard and the description-drift
|
|
190
|
+
* check. This is a surface over those tools, not a second copy of them --
|
|
191
|
+
* nine duplicate tool families in this subsystem are what a second copy costs.
|
|
192
|
+
* (2026-08-01)
|
|
193
|
+
*/
|
|
194
|
+
export const PERSONA_MANAGER_SPEC = {
|
|
195
|
+
id: "persona-manager",
|
|
196
|
+
toolPage: "https://cbrowser.ai/docs/tool-persona-manager/",
|
|
197
|
+
title: "Personas",
|
|
198
|
+
hero: {
|
|
199
|
+
variant: "gradient",
|
|
200
|
+
subtitle: { field: "summary" },
|
|
201
|
+
facts: [
|
|
202
|
+
{ field: "personas", label: "personas" },
|
|
203
|
+
{ field: "editable", label: "editable" },
|
|
204
|
+
],
|
|
205
|
+
actions: [{ label: "Manage on cbrowser.ai", url: "https://cbrowser.ai/account/personas" }],
|
|
206
|
+
},
|
|
207
|
+
blocks: [
|
|
208
|
+
{ type: "manager", title: "Personas", field: "personas" },
|
|
209
|
+
{ type: "note", title: "Creating a persona", field: "createHint" },
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* A persona that was just created.
|
|
214
|
+
*
|
|
215
|
+
* The creation tools returned JSON and nothing else, so "what did I just make?"
|
|
216
|
+
* meant reading a trait map by eye. This shows the finished persona the way the
|
|
217
|
+
* lookup widget does, plus the two things only creation knows: which route
|
|
218
|
+
* produced the values, and which traits the caller declared the description
|
|
219
|
+
* could not support.
|
|
220
|
+
*
|
|
221
|
+
* Those declared-unsupported traits are the point. They sit at a population
|
|
222
|
+
* baseline and are indistinguishable from considered values in every later
|
|
223
|
+
* view; naming them at the moment of creation is the only place the
|
|
224
|
+
* distinction is free. (2026-08-01)
|
|
225
|
+
*/
|
|
226
|
+
export const PERSONA_CREATED_SPEC = {
|
|
227
|
+
id: "persona-created",
|
|
228
|
+
toolPage: "https://cbrowser.ai/docs/tool-persona-create-from-description/",
|
|
229
|
+
title: "Persona created",
|
|
230
|
+
titleField: "persona_name",
|
|
231
|
+
titlePrefix: "Created: ",
|
|
232
|
+
hero: {
|
|
233
|
+
variant: "gradient",
|
|
234
|
+
subtitle: { field: "description" },
|
|
235
|
+
facts: [
|
|
236
|
+
{ field: "traits", label: "traits" },
|
|
237
|
+
{ field: "valuesRoute", label: "values route" },
|
|
238
|
+
{ field: "unsupportedTraits", label: "unsupported" },
|
|
239
|
+
],
|
|
240
|
+
actions: [{ label: "Manage personas", url: "https://cbrowser.ai/account/personas" }],
|
|
241
|
+
},
|
|
242
|
+
blocks: [
|
|
243
|
+
{ type: "note", title: "What the trait vector says", field: "traitSummary" },
|
|
244
|
+
{ type: "traits", title: "Cognitive traits", field: "traits", ramp: "trait", describe: true },
|
|
245
|
+
{ type: "traits", title: "Values", field: "values", ramp: "value" },
|
|
246
|
+
{ type: "table", title: "Declared unsupported", field: "unsupportedDetail" },
|
|
247
|
+
{
|
|
248
|
+
type: "drawer",
|
|
249
|
+
title: "Details",
|
|
250
|
+
blocks: [
|
|
251
|
+
{ type: "kv", title: "Values route", field: "valuesDerivation" },
|
|
252
|
+
{ type: "kv", title: "Demographics", field: "demographics" },
|
|
253
|
+
{ type: "rest", title: "Other fields" },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* What an update changed.
|
|
260
|
+
*
|
|
261
|
+
* A diff rather than a snapshot, because the question after an edit is not
|
|
262
|
+
* "what is this persona" but "what did I just do to it" — and the second is not
|
|
263
|
+
* answerable from the first without having memorised the before.
|
|
264
|
+
*
|
|
265
|
+
* Carries the description-drift check: traits are updated and prose is not, so
|
|
266
|
+
* a persona ends up with a vector saying "impatient" under a description that
|
|
267
|
+
* still says "patient". The prose is never rewritten here; the contradiction is
|
|
268
|
+
* reported and the decision left alone. (2026-08-01)
|
|
269
|
+
*/
|
|
270
|
+
export const PERSONA_UPDATED_SPEC = {
|
|
271
|
+
id: "persona-updated",
|
|
272
|
+
toolPage: "https://cbrowser.ai/docs/tool-persona-update/",
|
|
273
|
+
title: "Persona updated",
|
|
274
|
+
titleField: "persona",
|
|
275
|
+
titlePrefix: "Updated: ",
|
|
276
|
+
hero: {
|
|
277
|
+
variant: "gradient",
|
|
278
|
+
subtitle: { field: "note" },
|
|
279
|
+
facts: [
|
|
280
|
+
{ field: "changed", label: "fields changed" },
|
|
281
|
+
{ field: "descriptionDrift", label: "description conflicts" },
|
|
282
|
+
],
|
|
283
|
+
actions: [{ label: "Manage personas", url: "https://cbrowser.ai/account/personas" }],
|
|
284
|
+
},
|
|
285
|
+
blocks: [
|
|
286
|
+
{ type: "table", title: "What changed", field: "changes" },
|
|
287
|
+
{ type: "table", title: "Description now contradicts the traits", field: "descriptionDrift" },
|
|
288
|
+
{ type: "note", title: "What the trait vector says now", field: "traitSummary" },
|
|
289
|
+
{ type: "kv", title: "Where the write landed", field: "stores" },
|
|
290
|
+
{
|
|
291
|
+
type: "drawer",
|
|
292
|
+
title: "Details",
|
|
293
|
+
blocks: [
|
|
294
|
+
{ type: "traits", title: "Traits after update", field: "traits", ramp: "trait", describe: true },
|
|
295
|
+
{ type: "rest", title: "Other fields" },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Attention heatmap view.
|
|
302
|
+
*
|
|
303
|
+
* The image is the finding here -- a JSON list of scores is a lossy
|
|
304
|
+
* description of where a persona actually looks. It is fetched through
|
|
305
|
+
* artifact_fetch rather than embedded or linked: the result cannot carry it
|
|
306
|
+
* past the host's size cap, and the sandbox will not load cbrowser.ai.
|
|
307
|
+
*/
|
|
308
|
+
export const ATTENTION_SPEC = {
|
|
309
|
+
id: "attention",
|
|
310
|
+
toolPage: "https://cbrowser.ai/docs/tool-attention-analysis/",
|
|
311
|
+
title: "Attention",
|
|
312
|
+
// Field names taken from a real tool result rather than assumed. The first
|
|
313
|
+
// pass guessed topElements/elementsAnalyzed/url; the payload actually carries
|
|
314
|
+
// topAttentionAreas, attentionQuality and no url at all, so every block was
|
|
315
|
+
// pointed at a field that did not exist.
|
|
316
|
+
titleField: "persona",
|
|
317
|
+
hero: {
|
|
318
|
+
variant: "gradient",
|
|
319
|
+
subtitle: { field: "attentionReasoning" },
|
|
320
|
+
facts: [
|
|
321
|
+
{ field: "topAttentionAreas", label: "areas" },
|
|
322
|
+
{ field: "attentionQuality", label: "quality" },
|
|
323
|
+
{ field: "alignmentScore", label: "alignment" },
|
|
324
|
+
{ field: "concentration", label: "concentration" },
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
blocks: [
|
|
328
|
+
{
|
|
329
|
+
type: "image",
|
|
330
|
+
title: "Predicted attention",
|
|
331
|
+
fetchTool: "artifact_fetch",
|
|
332
|
+
fetchArgs: { file: "heatmapFile" },
|
|
333
|
+
caption: "heatmapNote",
|
|
334
|
+
},
|
|
335
|
+
{ type: "table", title: "Where attention lands", field: "topAttentionAreas" },
|
|
336
|
+
{
|
|
337
|
+
type: "drawer",
|
|
338
|
+
title: "Details",
|
|
339
|
+
blocks: [{ type: "rest", title: "All fields" }],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Screenshot view.
|
|
345
|
+
*
|
|
346
|
+
* The image is the whole result here, so the view is mostly frame: the shot
|
|
347
|
+
* at full width, then the handful of facts that say what it is a picture of.
|
|
348
|
+
* Fetched through artifact_fetch rather than read from the result, because a
|
|
349
|
+
* compressed screenshot measured 144,731 base64 characters against a ~150k
|
|
350
|
+
* cap -- it fits today and stops fitting on a denser page, and a view that
|
|
351
|
+
* works until the page gets busy is worse than one that never did.
|
|
352
|
+
*/
|
|
353
|
+
export const SCREENSHOT_SPEC = {
|
|
354
|
+
id: "screenshot",
|
|
355
|
+
toolPage: "https://cbrowser.ai/docs/tool-screenshot/",
|
|
356
|
+
title: "Screenshot",
|
|
357
|
+
titleField: "url",
|
|
358
|
+
hero: {
|
|
359
|
+
variant: "gradient",
|
|
360
|
+
subtitle: { field: "title" },
|
|
361
|
+
facts: [
|
|
362
|
+
{ field: "viewport", label: "viewport" },
|
|
363
|
+
{ field: "viewport_forced", label: "forced to" },
|
|
364
|
+
{ field: "fullPage", label: "full page" },
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
blocks: [
|
|
368
|
+
{
|
|
369
|
+
type: "image",
|
|
370
|
+
title: "",
|
|
371
|
+
fetchTool: "artifact_fetch",
|
|
372
|
+
fetchArgs: { file: "screenshotFile" },
|
|
373
|
+
caption: "_screenshotNote",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
type: "drawer",
|
|
377
|
+
title: "Details",
|
|
378
|
+
blocks: [{ type: "rest", title: "All fields" }],
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
footer: { label: "Saved:", field: "screenshot" },
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Trait lookup view.
|
|
385
|
+
*
|
|
386
|
+
* The five levels are the answer: what a reader wants from "patience at 0.30"
|
|
387
|
+
* is where that sits relative to the other four bands and what it means
|
|
388
|
+
* behaviourally, which a JSON array of level objects does not convey.
|
|
389
|
+
*
|
|
390
|
+
* Bands use the same red-to-green ramp as the persona meters, so 0.30 reads
|
|
391
|
+
* identically in both views rather than being one colour here and another
|
|
392
|
+
* there.
|
|
393
|
+
*/
|
|
394
|
+
export const TRAIT_SPEC = {
|
|
395
|
+
// Served under a URI the host has never seen.
|
|
396
|
+
//
|
|
397
|
+
// The bisect proved the tool side is fine: pointed at ui://cbrowser/status
|
|
398
|
+
// it renders. So the fault is specific to this resource, which parses,
|
|
399
|
+
// registers and renders locally under test. The remaining explanation is a
|
|
400
|
+
// cached negative for the URI -- the host was told to render
|
|
401
|
+
// ui://cbrowser/trait while the resource had not yet deployed, and kept the
|
|
402
|
+
// failure. A fresh URI cannot have a stale entry.
|
|
403
|
+
id: "trait-v2",
|
|
404
|
+
toolPage: "https://cbrowser.ai/docs/tool-persona-trait-lookup/",
|
|
405
|
+
title: "Trait",
|
|
406
|
+
titleField: "trait",
|
|
407
|
+
hero: {
|
|
408
|
+
variant: "gradient",
|
|
409
|
+
subtitle: { field: "description" },
|
|
410
|
+
facts: [
|
|
411
|
+
{ field: "value", label: "value" },
|
|
412
|
+
{ field: "label", label: "level" },
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
blocks: [
|
|
416
|
+
{
|
|
417
|
+
type: "levels",
|
|
418
|
+
title: "Where this value sits",
|
|
419
|
+
field: "allLevels",
|
|
420
|
+
valueField: "value",
|
|
421
|
+
labelField: "label",
|
|
422
|
+
behaviorsField: "behaviors",
|
|
423
|
+
},
|
|
424
|
+
{ type: "note", title: "Research basis", field: "researchBasis" },
|
|
425
|
+
{
|
|
426
|
+
type: "drawer",
|
|
427
|
+
title: "Every level",
|
|
428
|
+
blocks: [{ type: "table", title: "Levels", field: "allLevels" }],
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* Bug hunt view. Field names read from a real run, not assumed:
|
|
434
|
+
* bugs[] carries type, severity, description, url, selector, recommendation.
|
|
435
|
+
*/
|
|
436
|
+
export const BUGS_SPEC = {
|
|
437
|
+
id: "bugs",
|
|
438
|
+
toolPage: "https://cbrowser.ai/docs/tool-hunt-bugs/",
|
|
439
|
+
title: "Bug hunt",
|
|
440
|
+
hero: {
|
|
441
|
+
variant: "gradient",
|
|
442
|
+
// No subtitle. duration is milliseconds, and rendered as a bare subtitle it
|
|
443
|
+
// read as "8563" under the title with nothing saying what it counted.
|
|
444
|
+
facts: [
|
|
445
|
+
// Distinct findings, not raw DOM occurrences: a carousel repeating five
|
|
446
|
+
// broken images across duplicated slides read as ten. The occurrence
|
|
447
|
+
// count is still in Run info for anyone who wants it.
|
|
448
|
+
{ field: "distinctBugs", label: "bugs" },
|
|
449
|
+
{ field: "pagesVisited", label: "pages crawled" },
|
|
450
|
+
{ field: "duration", label: "ms" },
|
|
451
|
+
],
|
|
452
|
+
},
|
|
453
|
+
blocks: [
|
|
454
|
+
// Grouped by page. A flat list across a multi-page crawl says nothing
|
|
455
|
+
// about which page a finding belongs to, and page attribution is exactly
|
|
456
|
+
// what was missing when the result was silently truncated to page one.
|
|
457
|
+
{ type: "tabs", title: "Findings by page", field: "bugs", groupBy: "url",
|
|
458
|
+
render: { type: "findings", title: "Findings, worst first", field: "bugs",
|
|
459
|
+
textKey: "description", severityKey: "severity", detailKey: "recommendation" } },
|
|
460
|
+
{ type: "drawer", title: "Details", blocks: [
|
|
461
|
+
// description and recommendation are shown in full by the findings list
|
|
462
|
+
// above; repeating them here is what made this table 1543px wide.
|
|
463
|
+
{ type: "table", title: "Every bug", field: "bugs",
|
|
464
|
+
columns: ["severity", "type", "url", "selector", "occurrences"] },
|
|
465
|
+
{ type: "rest", title: "Run info" },
|
|
466
|
+
] },
|
|
467
|
+
],
|
|
468
|
+
};
|
|
469
|
+
export function buildBugsTemplate() {
|
|
470
|
+
return buildWidget(BUGS_SPEC);
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Empathy audit view.
|
|
474
|
+
*
|
|
475
|
+
* Barriers ranked by severity is the artifact worth showing someone -- it is
|
|
476
|
+
* the output a product team acts on, and nested JSON buries the ordering that
|
|
477
|
+
* makes it actionable.
|
|
478
|
+
*/
|
|
479
|
+
export const EMPATHY_SPEC = {
|
|
480
|
+
id: "empathy",
|
|
481
|
+
toolPage: "https://cbrowser.ai/docs/tool-empathy-audit/",
|
|
482
|
+
title: "Empathy audit",
|
|
483
|
+
// The persona is the subject of the audit, so it belongs in the title. The
|
|
484
|
+
// bare view name did not say who was audited, and the tool tests exactly one
|
|
485
|
+
// persona per call.
|
|
486
|
+
titleField: "testedPersona",
|
|
487
|
+
titlePrefix: "Empathy Audit: ",
|
|
488
|
+
hero: {
|
|
489
|
+
variant: "gradient",
|
|
490
|
+
subtitle: { field: "scopeNote" },
|
|
491
|
+
facts: [
|
|
492
|
+
{ field: "overallScore", label: "score" },
|
|
493
|
+
{ field: "topBarriers", label: "barriers" },
|
|
494
|
+
{ field: "resultsSummary", label: "personas" },
|
|
495
|
+
{ field: "device", label: "device" },
|
|
496
|
+
],
|
|
497
|
+
},
|
|
498
|
+
blocks: [
|
|
499
|
+
// The overlay leads. It is what makes a score falsifiable by eye --
|
|
500
|
+
// barriers drawn on the real page at their real positions -- and the
|
|
501
|
+
// ranked list is the detail behind it.
|
|
502
|
+
{ type: "overlay", title: "Barriers on the page", field: "pageScreenshots",
|
|
503
|
+
fetchTool: "artifact_fetch" },
|
|
504
|
+
{ type: "findings", title: "Barriers, worst first", field: "topBarriers",
|
|
505
|
+
textKey: "description", severityKey: "severityForPersona", detailKey: "type" },
|
|
506
|
+
{ type: "drawer", title: "Details", blocks: [
|
|
507
|
+
{ type: "table", title: "Per persona", field: "resultsSummary" },
|
|
508
|
+
{ type: "table", title: "WCAG violations", field: "allWcagViolations" },
|
|
509
|
+
{ type: "table", title: "Remediation", field: "topRemediation" },
|
|
510
|
+
{ type: "rest", title: "Run info" },
|
|
511
|
+
] },
|
|
512
|
+
],
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Cognitive effort: the sequential transport chain, and what running it in
|
|
516
|
+
* sequence costs over running it in parallel.
|
|
517
|
+
*
|
|
518
|
+
* The JSON answers "how expensive is this page for this persona" with a number,
|
|
519
|
+
* and buries the reason inside `cognitiveTransportCost.chainCoefficient`.
|
|
520
|
+
* The chain block puts the reason on screen: one bar per layer with
|
|
521
|
+
* the bottleneck named, then the sum-of-parts against the actual sequential
|
|
522
|
+
* total. The gap between those last two is the claim the whole model rests on.
|
|
523
|
+
*/
|
|
524
|
+
export const EFFORT_SPEC = {
|
|
525
|
+
id: "effort",
|
|
526
|
+
toolPage: "https://cbrowser.ai/docs/tool-cognitive-effort/",
|
|
527
|
+
title: "Cognitive effort",
|
|
528
|
+
// Who was measured belongs in the title; the cost is meaningless without them.
|
|
529
|
+
titleField: "persona",
|
|
530
|
+
titlePrefix: "Cognitive Effort: ",
|
|
531
|
+
hero: {
|
|
532
|
+
variant: "gradient",
|
|
533
|
+
subtitle: { field: "interpretation" },
|
|
534
|
+
facts: [
|
|
535
|
+
{ field: "cognitiveTransportCost.total", label: "transport cost" },
|
|
536
|
+
{ field: "abandonmentRisk", label: "abandonment risk" },
|
|
537
|
+
{ field: "bottleneck", label: "bottleneck" },
|
|
538
|
+
{ field: "url", label: "page" },
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
blocks: [
|
|
542
|
+
// Leads, because it is the finding. Everything below is detail behind it.
|
|
543
|
+
// Title carries no layer COUNT on purpose. It named a count and went stale
|
|
544
|
+
// within the hour when readability was split into decoding and attention
|
|
545
|
+
// (2026-08-02), so the widget captioned seven bars as six. The bars state
|
|
546
|
+
// the count themselves; a number in prose is a second copy that drifts.
|
|
547
|
+
{ type: "chain", title: "The sequential transport chain", field: "layers",
|
|
548
|
+
nameKey: "name", costKey: "cost", capacityKey: "capacityConsumed",
|
|
549
|
+
bottleneckField: "bottleneck",
|
|
550
|
+
additiveField: "cognitiveTransportCost.additive",
|
|
551
|
+
totalField: "cognitiveTransportCost.total",
|
|
552
|
+
// Each bar opens into that layer's overlay on the real page. Layers with
|
|
553
|
+
// no overlay stay inert and say why.
|
|
554
|
+
overlaysField: "layerOverlays", fetchTool: "artifact_fetch" },
|
|
555
|
+
// Surfaced when the run was told, or forced, to treat the persona as new to
|
|
556
|
+
// the site. It changes every number above and is easy to miss in JSON.
|
|
557
|
+
{ type: "note", field: "familiarityWarning" },
|
|
558
|
+
{ type: "note", field: "languageWarning" },
|
|
559
|
+
// Everything below the chain is behind an accordion.
|
|
560
|
+
//
|
|
561
|
+
// The chain IS the finding; the rest is the evidence behind it, and eight
|
|
562
|
+
// stacked cards made the panel a scroll rather than an answer. Native
|
|
563
|
+
// <details>, so each one is keyboard operable and announced as an expander
|
|
564
|
+
// without any ARIA of our own.
|
|
565
|
+
//
|
|
566
|
+
// The two warnings above stay OUT of the accordions on purpose: a familiarity
|
|
567
|
+
// downgrade or a language mismatch changes how every number below should be
|
|
568
|
+
// read, and a warning behind a click is a warning nobody reads.
|
|
569
|
+
{ type: "drawer", title: "Where the cost sits", blocks: [
|
|
570
|
+
{ type: "kv", title: "Deficit vs surplus", field: "deficitVsSurplus" },
|
|
571
|
+
{ type: "kv", title: "Layer interactions", field: "interactions" },
|
|
572
|
+
] },
|
|
573
|
+
{ type: "drawer", title: "Motor accessibility", blocks: [
|
|
574
|
+
{ type: "kv", title: "Summary", fields: ["motorAccessibility.score", "motorAccessibility.barriers"] },
|
|
575
|
+
{ type: "table", title: "Hardest targets", field: "motorAccessibility.elements",
|
|
576
|
+
columns: ["element", "hitProbability", "movementTimeMs"] },
|
|
577
|
+
] },
|
|
578
|
+
{ type: "drawer", title: "Readability", blocks: [
|
|
579
|
+
// legibilityQuality, not score: high is GOOD here, while the readability
|
|
580
|
+
// LAYER above is a cost where high is bad. Same word, opposite direction.
|
|
581
|
+
{ type: "kv", title: "Summary", fields: ["readability.legibilityQuality", "readability.averageWPM"] },
|
|
582
|
+
{ type: "findings", title: "Hardest block", field: "readability.hardestBlock", textKey: "0" },
|
|
583
|
+
] },
|
|
584
|
+
{ type: "drawer", title: "How these numbers are built", blocks: [
|
|
585
|
+
{ type: "kv", title: "Chain", fields: ["cognitiveTransportCost.raw", "cognitiveTransportCost.chainCoefficient"] },
|
|
586
|
+
{ type: "note", field: "cognitiveTransportCost.chainNote" },
|
|
587
|
+
{ type: "note", field: "abandonmentBasis" },
|
|
588
|
+
{ type: "note", field: "siteFamiliarityEffect" },
|
|
589
|
+
] },
|
|
590
|
+
{ type: "drawer", title: "Everything else", blocks: [
|
|
591
|
+
{ type: "rest", title: "Remaining fields" },
|
|
592
|
+
] },
|
|
593
|
+
],
|
|
594
|
+
};
|
|
595
|
+
export function buildEffortTemplate() {
|
|
596
|
+
return buildWidget(EFFORT_SPEC);
|
|
597
|
+
}
|
|
598
|
+
export function buildEmpathyTemplate() {
|
|
599
|
+
return buildWidget(EMPATHY_SPEC);
|
|
600
|
+
}
|
|
601
|
+
export function buildTraitTemplate() {
|
|
602
|
+
return buildWidget(TRAIT_SPEC);
|
|
603
|
+
}
|
|
604
|
+
export function buildScreenshotTemplate() {
|
|
605
|
+
return buildWidget(SCREENSHOT_SPEC);
|
|
606
|
+
}
|
|
607
|
+
export function buildAttentionTemplate() {
|
|
608
|
+
return buildWidget(ATTENTION_SPEC);
|
|
609
|
+
}
|
|
610
|
+
export function buildPersonaTemplate() {
|
|
611
|
+
return buildWidget(PERSONA_SPEC);
|
|
612
|
+
}
|
|
613
|
+
export function buildPersonaManagerTemplate() {
|
|
614
|
+
return buildWidget(PERSONA_MANAGER_SPEC);
|
|
615
|
+
}
|
|
616
|
+
export function buildPersonaCreatedTemplate() {
|
|
617
|
+
return buildWidget(PERSONA_CREATED_SPEC);
|
|
618
|
+
}
|
|
619
|
+
export function buildPersonaUpdatedTemplate() {
|
|
620
|
+
return buildWidget(PERSONA_UPDATED_SPEC);
|
|
621
|
+
}
|
|
622
|
+
export function buildStatusTemplate() {
|
|
623
|
+
return buildWidget(STATUS_SPEC);
|
|
624
|
+
}
|
|
625
|
+
/** Read back a published capture panel by its resource URI. */
|
|
626
|
+
export function readCaptureUi(uri) {
|
|
627
|
+
return captureHtml.get(uri.startsWith(CAPTURE_UI_PREFIX) ? uri.slice(CAPTURE_UI_PREFIX.length) : uri);
|
|
628
|
+
}
|
|
629
|
+
/** Register the inline-UI resources on a server. Safe to call more than once. */
|
|
630
|
+
export function registerUiResources(server) {
|
|
631
|
+
if (registered.has(server))
|
|
632
|
+
return;
|
|
633
|
+
registered.add(server);
|
|
634
|
+
server.registerResource("cbrowser-ui-probe", UI_PROBE_URI, { description: "Inline UI render probe (MCP Apps)", mimeType: MCP_APP_MIME }, async () => ({
|
|
635
|
+
contents: [{ uri: UI_PROBE_URI, mimeType: MCP_APP_MIME, text: PROBE_HTML }],
|
|
636
|
+
}));
|
|
637
|
+
server.registerResource("cbrowser-status-ui", "ui://cbrowser/status", { description: "CBrowser environment status panel", mimeType: MCP_APP_MIME }, async () => ({
|
|
638
|
+
contents: [{
|
|
639
|
+
uri: "ui://cbrowser/status",
|
|
640
|
+
mimeType: MCP_APP_MIME,
|
|
641
|
+
// Read out of band there is no payload to render, so this stands in.
|
|
642
|
+
// The populated panel travels inside the status tool result itself.
|
|
643
|
+
text: buildStatusTemplate(),
|
|
644
|
+
}],
|
|
645
|
+
}));
|
|
646
|
+
server.registerResource("cbrowser-bugs-ui", widgetUri("bugs"), { description: "Bug hunt findings ranked by severity", mimeType: MCP_APP_MIME }, async () => ({
|
|
647
|
+
contents: [{ uri: widgetUri("bugs"), mimeType: MCP_APP_MIME, text: buildBugsTemplate() }],
|
|
648
|
+
}));
|
|
649
|
+
server.registerResource("cbrowser-empathy-ui", widgetUri("empathy"), { description: "Accessibility barriers ranked by severity", mimeType: MCP_APP_MIME }, async () => ({
|
|
650
|
+
contents: [{ uri: widgetUri("empathy"), mimeType: MCP_APP_MIME, text: buildEmpathyTemplate() }],
|
|
651
|
+
}));
|
|
652
|
+
server.registerResource("cbrowser-effort-ui", widgetUri("effort"), { description: "Sequential cognitive transport chain with its bottleneck", mimeType: MCP_APP_MIME }, async () => ({
|
|
653
|
+
contents: [{ uri: widgetUri("effort"), mimeType: MCP_APP_MIME, text: buildEffortTemplate() }],
|
|
654
|
+
}));
|
|
655
|
+
server.registerResource("cbrowser-trait-ui", widgetUri("trait-v2"), { description: "Trait level scale with behavioural bands", mimeType: MCP_APP_MIME }, async () => ({
|
|
656
|
+
contents: [{ uri: widgetUri("trait-v2"), mimeType: MCP_APP_MIME, text: buildTraitTemplate() }],
|
|
657
|
+
}));
|
|
658
|
+
server.registerResource("cbrowser-screenshot-ui", widgetUri("screenshot"), { description: "Screenshot with page and viewport context", mimeType: MCP_APP_MIME }, async () => ({
|
|
659
|
+
contents: [{ uri: widgetUri("screenshot"), mimeType: MCP_APP_MIME, text: buildScreenshotTemplate() }],
|
|
660
|
+
}));
|
|
661
|
+
server.registerResource("cbrowser-attention-ui", widgetUri("attention"), { description: "Attention heatmap with ranked elements", mimeType: MCP_APP_MIME }, async () => ({
|
|
662
|
+
contents: [{ uri: widgetUri("attention"), mimeType: MCP_APP_MIME, text: buildAttentionTemplate() }],
|
|
663
|
+
}));
|
|
664
|
+
server.registerResource("cbrowser-persona-ui", widgetUri("persona"), { description: "Persona traits, values and accessibility profile", mimeType: MCP_APP_MIME }, async () => ({
|
|
665
|
+
contents: [{ uri: widgetUri("persona"), mimeType: MCP_APP_MIME, text: buildPersonaTemplate() }],
|
|
666
|
+
}));
|
|
667
|
+
server.registerResource("cbrowser-persona-manager-ui", widgetUri("persona-manager"), { description: "Interactive persona CRUD: roster, inline trait editor, save and delete", mimeType: MCP_APP_MIME }, async () => ({
|
|
668
|
+
contents: [{ uri: widgetUri("persona-manager"), mimeType: MCP_APP_MIME, text: buildPersonaManagerTemplate() }],
|
|
669
|
+
}));
|
|
670
|
+
server.registerResource("cbrowser-persona-created-ui", widgetUri("persona-created"), { description: "A persona that was just created, with its values route and any traits declared unsupported", mimeType: MCP_APP_MIME }, async () => ({
|
|
671
|
+
contents: [{ uri: widgetUri("persona-created"), mimeType: MCP_APP_MIME, text: buildPersonaCreatedTemplate() }],
|
|
672
|
+
}));
|
|
673
|
+
server.registerResource("cbrowser-persona-updated-ui", widgetUri("persona-updated"), { description: "What a persona update changed, and whether the description still matches the traits", mimeType: MCP_APP_MIME }, async () => ({
|
|
674
|
+
contents: [{ uri: widgetUri("persona-updated"), mimeType: MCP_APP_MIME, text: buildPersonaUpdatedTemplate() }],
|
|
675
|
+
}));
|
|
676
|
+
server.registerResource("cbrowser-capture-ui",
|
|
677
|
+
// A template with no list callback never appears in resources/list, so a
|
|
678
|
+
// host that discovers views by listing would never find a capture panel --
|
|
679
|
+
// and it fails silently, the way a URI mismatch does. Published captures are
|
|
680
|
+
// enumerated instead, which is cheap because the store is bounded at 24.
|
|
681
|
+
new ResourceTemplate(`${CAPTURE_UI_PREFIX}{slug}`, {
|
|
682
|
+
list: async () => ({
|
|
683
|
+
resources: [...captureHtml.keys()].map((slug) => ({
|
|
684
|
+
uri: `${CAPTURE_UI_PREFIX}${slug}`,
|
|
685
|
+
name: `Capture player: ${slug}`,
|
|
686
|
+
mimeType: MCP_APP_MIME,
|
|
687
|
+
})),
|
|
688
|
+
}),
|
|
689
|
+
}), { description: "Inline capture player with embedded frames", mimeType: MCP_APP_MIME }, async (uri, vars) => {
|
|
690
|
+
const slug = String(Array.isArray(vars.slug) ? vars.slug[0] : vars.slug ?? "");
|
|
691
|
+
const html = captureHtml.get(slug);
|
|
692
|
+
return {
|
|
693
|
+
contents: [{
|
|
694
|
+
uri: uri.href,
|
|
695
|
+
mimeType: MCP_APP_MIME,
|
|
696
|
+
// An expired slug is normal, not an error: the store is bounded and a
|
|
697
|
+
// host may re-read an old panel. Say so plainly instead of throwing.
|
|
698
|
+
text: html ?? `<!doctype html><meta charset="utf-8">
|
|
699
|
+
<div style="font:15px/1.5 ui-sans-serif,system-ui,sans-serif;padding:18px">
|
|
700
|
+
<p>This capture is no longer held in memory. Re-run the capture, or open the full
|
|
701
|
+
player link from the original tool result.</p>
|
|
702
|
+
</div>`,
|
|
703
|
+
}],
|
|
704
|
+
};
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
//# sourceMappingURL=ui-resources.js.map
|