@reqvire-org/ads 0.3.8 → 0.3.10
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/atomic-trace.mjs +3 -198
- package/dist/bootstrap-ds/scripts/build-mcp-previews.mjs +2 -1
- package/dist/bootstrap-ds/vite.config.ts +2 -1
- package/dist/check.mjs +12 -3
- package/dist/linaria/atomic-css.cjs +2 -0
- package/dist/linaria/atomic-styled.cjs +2 -0
- package/dist/linaria/preserve-values.cjs +32 -0
- package/dist/linaria.d.mts +1 -0
- package/dist/linaria.mjs +9 -0
- package/dist/mcp/preview-widget.bundle.js +1 -1
- package/dist/mcp/resource-loader.js +5 -2
- package/dist/mcp/server.mjs +3 -1
- package/dist/preview-resources.mjs +2 -1
- package/dist/scenario-trace.mjs +149 -0
- package/dist/scenarios-compiler.mjs +2 -1
- package/dist/scenarios.mjs +13 -6
- package/dist/skills/ads-break-ui/SKILL.md +9 -2
- package/dist/skills/ads-break-ui/references/atomic-styles.md +50 -0
- package/dist/skills/ads-break-ui/references/scenarios.md +98 -0
- package/dist/skills/ads-designer/SKILL.md +12 -3
- package/dist/skills/ads-designer/assets/bootstrap-ds/scripts/build-mcp-previews.mjs +2 -1
- package/dist/skills/ads-designer/assets/bootstrap-ds/vite.config.ts +2 -1
- package/dist/skills/ads-designer/references/atomic-styles.md +50 -0
- package/dist/skills/ads-designer/references/scenarios.md +98 -0
- package/dist/skills/ads-import/SKILL.md +9 -2
- package/dist/skills/ads-import/references/atomic-styles.md +50 -0
- package/dist/skills/ads-import/references/scenarios.md +98 -0
- package/dist/skills/ads-migrate/SKILL.md +9 -2
- package/dist/skills/ads-migrate/references/atomic-styles.md +50 -0
- package/dist/skills/ads-migrate/references/scenarios.md +98 -0
- package/dist/skills/ads-motion/SKILL.md +9 -2
- package/dist/skills/ads-motion/references/atomic-styles.md +50 -0
- package/dist/skills/ads-motion/references/scenarios.md +98 -0
- package/dist/source-values.mjs +247 -0
- package/dist/verification/browser.mjs +21 -12
- package/dist/verification/playwright-reporter.cjs +6 -2
- package/dist/verification.mjs +13 -4
- package/docs/ADS.md +20 -5
- package/docs/ARCHITECTURE.md +18 -0
- package/docs/BEHAVIORS.md +11 -1
- package/docs/CHANGELOG.md +27 -1
- package/docs/MCP.md +16 -0
- package/package.json +10 -1
package/dist/atomic-trace.mjs
CHANGED
|
@@ -1,204 +1,9 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
import {
|
|
3
|
-
import { uiOperation } from "./ui-framework.mjs";
|
|
2
|
+
import { createSourceValues, reference, literal, unknown, isFunction } from "./source-values.mjs";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
const literal = value => ({ kind: "literal", value });
|
|
7
|
-
const reference = (node, env = new Map(), via = []) => node ? { kind: "node", node, env, via } : literal(undefined);
|
|
8
|
-
const union = values => values.length === 1 ? values[0] : { kind: "union", values };
|
|
9
|
-
const isFunction = node => node && (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node));
|
|
10
|
-
const nameOf = node => node && (ts.isIdentifier(node) || ts.isStringLiteralLike(node) || ts.isNumericLiteral(node)) ? node.text : undefined;
|
|
11
|
-
function unwrap(node) {
|
|
12
|
-
while (node && (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node))) node = node.expression;
|
|
13
|
-
return node;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Bounded symbolic source traversal. No project code is imported or executed. */
|
|
4
|
+
/** Bounded symbolic composition review, sharing source evaluation with scenarios. */
|
|
17
5
|
export function createAtomicTrace(program, options, { entries, isAtomic, isCx }) {
|
|
18
|
-
const
|
|
19
|
-
const symbol = node => checker.getSymbolAtLocation(ts.isPropertyAccessExpression(node) ? node.name : node);
|
|
20
|
-
function declaration(node) {
|
|
21
|
-
let value = symbol(node);
|
|
22
|
-
if (value?.flags & ts.SymbolFlags.Alias) value = checker.getAliasedSymbol(value);
|
|
23
|
-
return value?.valueDeclaration;
|
|
24
|
-
}
|
|
25
|
-
const state = () => ({ budget: { steps: 0 }, depth: 0, calls: new Set() });
|
|
26
|
-
function next(context) { return { ...context, depth: context.depth + 1 }; }
|
|
27
|
-
function exhausted(context) { return ++context.budget.steps > 4000 || context.depth > 48; }
|
|
28
|
-
function at(node, kind, name) {
|
|
29
|
-
const source = node.getSourceFile(), position = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
30
|
-
return { file: source.fileName, line: position.line + 1, column: position.character + 1, kind, name: name.slice(0, 100) };
|
|
31
|
-
}
|
|
32
|
-
function withVia(value, via) {
|
|
33
|
-
if (value.kind === "union") return { ...value, values: value.values.map(item => withVia(item, via)) };
|
|
34
|
-
return value.kind === "node" ? { ...value, via: [...new Map([...via, ...value.via].map(item => [JSON.stringify(item), item])).values()].slice(0, 16) } : value;
|
|
35
|
-
}
|
|
36
|
-
function withDefault(value, fallback, context) {
|
|
37
|
-
if (!fallback) return value;
|
|
38
|
-
value = resolve(value, next(context));
|
|
39
|
-
if (value.kind === "literal") return value.value === undefined ? fallback : value;
|
|
40
|
-
if (value.kind === "unknown") return union([value, fallback]);
|
|
41
|
-
if (value.kind === "union") return union(value.values.map(item => withDefault(item, fallback, next(context))));
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
|
-
function bind(pattern, value, env, via, context) {
|
|
45
|
-
if (ts.isIdentifier(pattern)) { env.set(symbol(pattern), value); return; }
|
|
46
|
-
if (ts.isObjectBindingPattern(pattern)) {
|
|
47
|
-
const excluded = new Set();
|
|
48
|
-
for (const part of pattern.elements) {
|
|
49
|
-
const key = nameOf(part.propertyName ?? part.name);
|
|
50
|
-
if (part.dotDotDotToken) bind(part.name, { kind: "rest", value, excluded: new Set(excluded) }, env, via, next(context));
|
|
51
|
-
else {
|
|
52
|
-
if (key === undefined) continue;
|
|
53
|
-
excluded.add(key);
|
|
54
|
-
bind(part.name, withDefault(get(value, key, next(context)), part.initializer && reference(part.initializer, env, via), context), env, via, next(context));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
} else if (ts.isArrayBindingPattern(pattern)) {
|
|
58
|
-
pattern.elements.forEach((part, index) => {
|
|
59
|
-
if (ts.isBindingElement(part) && !part.dotDotDotToken) bind(part.name, withDefault(get(value, String(index), next(context)), part.initializer && reference(part.initializer, env, via), context), env, via, next(context));
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function boundElement(node, env, via, context) {
|
|
64
|
-
let owner = node;
|
|
65
|
-
while (ts.isBindingElement(owner) || ts.isObjectBindingPattern(owner) || ts.isArrayBindingPattern(owner)) owner = owner.parent;
|
|
66
|
-
if (!ts.isVariableDeclaration(owner) || !owner.initializer || !(owner.parent.flags & ts.NodeFlags.Const)) return unknown();
|
|
67
|
-
const local = new Map(env);
|
|
68
|
-
bind(owner.name, reference(owner.initializer, env, via), local, via, next(context));
|
|
69
|
-
return local.get(symbol(node.name)) ?? unknown();
|
|
70
|
-
}
|
|
71
|
-
function get(value, key, context) {
|
|
72
|
-
value = resolve(value, next(context));
|
|
73
|
-
if (value.kind === "union") return union(value.values.map(item => get(item, key, next(context))));
|
|
74
|
-
if (value.kind === "rest") return value.excluded.has(key) ? literal(undefined) : get(value.value, key, next(context));
|
|
75
|
-
const layers = value.kind === "object" ? value.layers : [];
|
|
76
|
-
if (value.kind === "node" && ts.isObjectLiteralExpression(value.node)) {
|
|
77
|
-
for (const property of value.node.properties) {
|
|
78
|
-
if (ts.isSpreadAssignment(property)) layers.push(reference(property.expression, value.env, value.via));
|
|
79
|
-
else {
|
|
80
|
-
const name = nameOf(property.name);
|
|
81
|
-
if (name !== undefined) layers.push(new Map([[name, reference(ts.isShorthandPropertyAssignment(property) ? property.name : ts.isMethodDeclaration(property) ? property : property.initializer, value.env, value.via)]]));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} else if (value.kind === "node" && ts.isArrayLiteralExpression(value.node)) {
|
|
85
|
-
return reference(value.node.elements[Number(key)], value.env, value.via);
|
|
86
|
-
} else if (value.kind !== "object") return unknown(value.reason);
|
|
87
|
-
for (let index = layers.length - 1; index >= 0; index--) {
|
|
88
|
-
const layer = layers[index];
|
|
89
|
-
if (layer instanceof Map) { if (layer.has(key)) return layer.get(key); }
|
|
90
|
-
else {
|
|
91
|
-
const selected = get(layer, key, next(context));
|
|
92
|
-
if (selected.kind === "literal" && selected.value === undefined) continue;
|
|
93
|
-
// An opaque later spread may replace an earlier explicit property.
|
|
94
|
-
if (selected.kind === "unknown" || selected.kind === "union") return union([selected, get({ kind: "object", layers: layers.slice(0, index) }, key, next(context))]);
|
|
95
|
-
return selected;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return literal(undefined);
|
|
99
|
-
}
|
|
100
|
-
function keys(value, context) {
|
|
101
|
-
value = resolve(value, next(context));
|
|
102
|
-
if (value.kind === "union") return [...new Set(value.values.flatMap(item => keys(item, next(context))))];
|
|
103
|
-
if (value.kind === "node" && ts.isObjectLiteralExpression(value.node)) return value.node.properties.flatMap(property => ts.isSpreadAssignment(property) ? keys(reference(property.expression, value.env, value.via), next(context)) : nameOf(property.name) ?? []);
|
|
104
|
-
if (value.kind === "object") return [...new Set(value.layers.flatMap(layer => layer instanceof Map ? [...layer.keys()] : keys(layer, next(context))))];
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
function returns(fn, args, context, kind = "helper") {
|
|
108
|
-
if (context.calls.has(fn.node)) return [unknown("Recursive local call could not be fully traced.")];
|
|
109
|
-
if (!fn.node.body || fn.node.asteriskToken || fn.node.modifiers?.some(item => item.kind === ts.SyntaxKind.AsyncKeyword)) return [unknown("Async, generator or declaration-only calls are outside atomic source tracing.")];
|
|
110
|
-
const env = new Map(fn.env), via = [...fn.via, at(fn.node, kind, fn.node.name?.getText() ?? "local function")];
|
|
111
|
-
const inner = { ...next(context), calls: new Set([...context.calls, fn.node]) };
|
|
112
|
-
for (const [index, parameter] of fn.node.parameters.entries()) {
|
|
113
|
-
if (parameter.dotDotDotToken) { bind(parameter.name, { kind: "array", values: args.slice(index) }, env, via, inner); break; }
|
|
114
|
-
bind(parameter.name, withDefault(resolve(args[index] ?? literal(undefined), inner), parameter.initializer && reference(parameter.initializer, env, via), inner), env, via, inner);
|
|
115
|
-
}
|
|
116
|
-
const outputs = [];
|
|
117
|
-
function statement(node) {
|
|
118
|
-
if (ts.isReturnStatement(node)) { outputs.push(reference(node.expression, env, via)); return true; }
|
|
119
|
-
if (ts.isBlock(node)) { for (const child of node.statements) if (statement(child)) return true; }
|
|
120
|
-
else if (ts.isIfStatement(node)) {
|
|
121
|
-
const condition = resolve(reference(node.expression, env, via), inner);
|
|
122
|
-
if (condition.kind === "literal") return condition.value ? statement(node.thenStatement) : node.elseStatement ? statement(node.elseStatement) : false;
|
|
123
|
-
const left = statement(node.thenStatement), right = node.elseStatement ? statement(node.elseStatement) : false;
|
|
124
|
-
return left && right;
|
|
125
|
-
} else if (ts.isThrowStatement(node)) return true;
|
|
126
|
-
else if (ts.isIterationStatement(node, false) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) outputs.push(unknown("Loop, switch or try control flow could not be fully traced."));
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
if (ts.isBlock(fn.node.body)) statement(fn.node.body);
|
|
130
|
-
else outputs.push(reference(fn.node.body, env, via));
|
|
131
|
-
// Keep call stacks with lazy return references, including nested helpers.
|
|
132
|
-
return outputs.length ? outputs.map(value => ({ ...value, context: inner })) : [literal(undefined)];
|
|
133
|
-
}
|
|
134
|
-
function resolve(value, context) {
|
|
135
|
-
if (!value) return literal(undefined);
|
|
136
|
-
context = value.context ?? context;
|
|
137
|
-
if (exhausted(context)) return unknown("Atomic source tracing reached its depth or work limit.");
|
|
138
|
-
if (value.kind !== "node") return value;
|
|
139
|
-
const node = unwrap(value.node), { env, via } = value;
|
|
140
|
-
if (ts.isStringLiteralLike(node) || ts.isNumericLiteral(node)) return literal(ts.isNumericLiteral(node) ? Number(node.text) : node.text);
|
|
141
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword) return literal(node.kind === ts.SyntaxKind.TrueKeyword);
|
|
142
|
-
if (node.kind === ts.SyntaxKind.NullKeyword) return literal(null);
|
|
143
|
-
if (ts.isIdentifier(node)) {
|
|
144
|
-
if (env.has(symbol(node))) return resolve(env.get(symbol(node)), next(context));
|
|
145
|
-
const owner = declaration(node);
|
|
146
|
-
if (owner && isInside(options.dsRoot, owner.getSourceFile().fileName)) {
|
|
147
|
-
if (isFunction(owner)) return reference(owner, env, via);
|
|
148
|
-
if (ts.isVariableDeclaration(owner) && owner.initializer && owner.parent.flags & ts.NodeFlags.Const) return resolve(reference(owner.initializer, env, via), next(context));
|
|
149
|
-
if (ts.isBindingElement(owner)) return resolve(boundElement(owner, env, via, context), next(context));
|
|
150
|
-
}
|
|
151
|
-
return node.text === "undefined" && !owner ? literal(undefined) : unknown();
|
|
152
|
-
}
|
|
153
|
-
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
|
|
154
|
-
const key = ts.isPropertyAccessExpression(node) ? literal(node.name.text) : resolve(reference(node.argumentExpression, env, via), next(context));
|
|
155
|
-
const object = reference(node.expression, env, via);
|
|
156
|
-
if (key.kind === "literal") {
|
|
157
|
-
const selected = get(object, String(key.value), next(context));
|
|
158
|
-
const owner = declaration(node);
|
|
159
|
-
if (selected.kind === "unknown" && owner && isInside(options.dsRoot, owner.getSourceFile().fileName)) {
|
|
160
|
-
if (isFunction(owner)) return reference(owner, env, via);
|
|
161
|
-
if (ts.isVariableDeclaration(owner) && owner.initializer && owner.parent.flags & ts.NodeFlags.Const) return resolve(reference(owner.initializer, env, via), next(context));
|
|
162
|
-
}
|
|
163
|
-
return resolve(selected, next(context));
|
|
164
|
-
}
|
|
165
|
-
const names = keys(object, next(context));
|
|
166
|
-
return names.length ? union(names.map(name => get(object, name, next(context)))) : unknown();
|
|
167
|
-
}
|
|
168
|
-
if (ts.isConditionalExpression(node)) {
|
|
169
|
-
const condition = resolve(reference(node.condition, env, via), next(context));
|
|
170
|
-
return condition.kind === "literal" ? resolve(reference(condition.value ? node.whenTrue : node.whenFalse, env, via), next(context)) : union([reference(node.whenTrue, env, via), reference(node.whenFalse, env, via)]);
|
|
171
|
-
}
|
|
172
|
-
if (ts.isBinaryExpression(node)) {
|
|
173
|
-
const left = resolve(reference(node.left, env, via), next(context)), right = reference(node.right, env, via), operator = node.operatorToken.kind;
|
|
174
|
-
if (operator === ts.SyntaxKind.AmpersandAmpersandToken) return left.kind === "literal" ? left.value ? right : literal(undefined) : union([literal(undefined), right]);
|
|
175
|
-
if (operator === ts.SyntaxKind.BarBarToken) return left.kind === "literal" ? left.value ? left : right : union([left, right]);
|
|
176
|
-
if (operator === ts.SyntaxKind.QuestionQuestionToken) return left.kind === "literal" ? left.value == null ? right : left : union([left, right]);
|
|
177
|
-
const other = resolve(right, next(context));
|
|
178
|
-
if (left.kind === "literal" && other.kind === "literal" && [ts.SyntaxKind.EqualsEqualsEqualsToken, ts.SyntaxKind.ExclamationEqualsEqualsToken].includes(operator)) return literal(operator === ts.SyntaxKind.EqualsEqualsEqualsToken ? left.value === other.value : left.value !== other.value);
|
|
179
|
-
}
|
|
180
|
-
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
181
|
-
const operand = resolve(reference(node.operand, env, via), next(context));
|
|
182
|
-
return operand.kind === "literal" ? literal(!operand.value) : unknown();
|
|
183
|
-
}
|
|
184
|
-
if (ts.isCallExpression(node) && !isCx(node.expression)) {
|
|
185
|
-
if (["memo", "forwardRef"].includes(uiOperation(node.expression, checker))) return resolve(reference(node.arguments[0], env, via), next(context));
|
|
186
|
-
const fn = resolve(reference(node.expression, env, via), next(context));
|
|
187
|
-
if (fn.kind === "node" && isFunction(fn.node)) {
|
|
188
|
-
const outputs = returns(fn, node.arguments.map(argument => reference(argument, env, via)), next(context));
|
|
189
|
-
return withVia(resolve(union(outputs), next(context)), outputs.flatMap(item => item.via ?? []));
|
|
190
|
-
}
|
|
191
|
-
return unknown(`Call ${node.expression.getText().slice(0, 80)} could not be traced to a local function.`);
|
|
192
|
-
}
|
|
193
|
-
return { ...value, node };
|
|
194
|
-
}
|
|
195
|
-
function arrays(value, context) {
|
|
196
|
-
value = resolve(value, next(context));
|
|
197
|
-
if (value.kind === "array") return value.values;
|
|
198
|
-
if (value.kind === "node" && ts.isArrayLiteralExpression(value.node)) return value.node.elements.map(node => reference(node, value.env, value.via));
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
|
|
6
|
+
const { state, next, at, resolve, get, arrays, returns } = createSourceValues(program, options, { preserveCall: isCx });
|
|
202
7
|
return function review(source, { compare, limit }) {
|
|
203
8
|
const maxBranches = 32;
|
|
204
9
|
const unique = branches => [...new Map(branches.map(branch => [JSON.stringify(branch.map(entry => [entry.file, entry.line, entry.column])), branch])).values()];
|
|
@@ -4,6 +4,7 @@ import { execFile } from "node:child_process";
|
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { build } from "vite";
|
|
6
6
|
import wyw from "@wyw-in-js/vite";
|
|
7
|
+
import { linariaTagResolver } from "@reqvire-org/ads/linaria";
|
|
7
8
|
import react from "@vitejs/plugin-react";
|
|
8
9
|
import { runScenarioCompiler } from "@reqvire-org/ads/scenarios/compiler";
|
|
9
10
|
import { createPreviewResources, previewEntriesPlugin } from "@reqvire-org/ads/scenarios/vite";
|
|
@@ -22,7 +23,7 @@ await runScenarioCompiler(root, {
|
|
|
22
23
|
async buildGraph(entries, options, track) {
|
|
23
24
|
const mount = path.join(options.dsRoot, "showcase/mcp/main.tsx");
|
|
24
25
|
const output = await build({ configFile: false, root, base: "./", publicDir: false,
|
|
25
|
-
plugins: [previewEntriesPlugin(entries.map(entry => ({ ...entry, root })), mount), wyw({ include: ["**/*.{ts,tsx}"], exclude: ["**/node_modules/**"] }), react(), track],
|
|
26
|
+
plugins: [previewEntriesPlugin(entries.map(entry => ({ ...entry, root })), mount), wyw({ tagResolver: linariaTagResolver, include: ["**/*.{ts,tsx}"], exclude: ["**/node_modules/**"] }), react(), track],
|
|
26
27
|
resolve: { alias: { "@ds": path.join(options.dsRoot, options.config.publicEntry) } },
|
|
27
28
|
worker: { format: "iife" },
|
|
28
29
|
build: { write: false, cssCodeSplit: true, assetsInlineLimit: 0, modulePreload: false,
|
|
@@ -2,9 +2,10 @@ import path from "node:path";
|
|
|
2
2
|
import { defineConfig } from "vite";
|
|
3
3
|
import react from "@vitejs/plugin-react";
|
|
4
4
|
import wyw from "@wyw-in-js/vite";
|
|
5
|
+
import { linariaTagResolver } from "@reqvire-org/ads/linaria";
|
|
5
6
|
|
|
6
7
|
export default defineConfig({
|
|
7
|
-
plugins: [wyw(), react()],
|
|
8
|
+
plugins: [wyw({ tagResolver: linariaTagResolver }), react()],
|
|
8
9
|
resolve: {
|
|
9
10
|
alias: { "@ds": path.resolve(import.meta.dirname, "design-system/index.ts") },
|
|
10
11
|
},
|
package/dist/check.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { numericCssUnit, tokenLengthLiteral } from "./css-values.mjs";
|
|
|
20
20
|
import { foundationDeclaration, foundationSelector } from "./foundation.mjs";
|
|
21
21
|
import { scopedTokens, scopedTokenOwner } from "./scoped-tokens.mjs";
|
|
22
22
|
import { createAtomicCompositionReview } from "./atomic-composition.mjs";
|
|
23
|
+
import { createScenarioTrace } from "./scenario-trace.mjs";
|
|
23
24
|
|
|
24
25
|
const scenarioLayoutGuidance = "Put demo-only sizing and framing in a public review/layout pattern. Keep required behavior in the runtime owner; add component props for deliberate product API needs.";
|
|
25
26
|
const inlineStyleGuidance = `Own visual styles in component/pattern Linaria. ${scenarioLayoutGuidance} Reserve inlineStyleFiles for documented measured geometry, not demo layout.`;
|
|
@@ -52,13 +53,14 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
52
53
|
const program = ts.createProgram(scriptFiles, { ...compiler, paths: { ...compiler.paths, [config.publicAlias]: [path.join(dsRoot, config.publicEntry)] }, allowJs: true, noResolve: true, noLib: true, types: [], target: ts.ScriptTarget.Latest, module: ts.ModuleKind.ESNext, jsx: ts.JsxEmit.Preserve });
|
|
53
54
|
const checker = program.getTypeChecker();
|
|
54
55
|
const atomicCompositionReview = createAtomicCompositionReview(program, options);
|
|
56
|
+
const scenarioTrace = createScenarioTrace(program, options);
|
|
55
57
|
function report(file, rule, message, line = 1, column = 1) {
|
|
56
58
|
diagnostics.push({ file: path.relative(root, file).replaceAll("\\", "/"), rule, message, line, column });
|
|
57
59
|
}
|
|
58
60
|
inspectStyleBoundaries(options, program, report);
|
|
59
61
|
inspectShowcasePages(options, program, resolveImport, report);
|
|
60
62
|
inspectShowcaseCoverage(options, program, report);
|
|
61
|
-
inspectScenarioCoverage(options, program, report, (file, rule, message) => advisories.push({ file: path.relative(root, file).replaceAll("\\", "/"), rule, message, line: 1, column: 1 }));
|
|
63
|
+
inspectScenarioCoverage(options, program, report, (file, rule, message) => advisories.push({ file: path.relative(root, file).replaceAll("\\", "/"), rule, message, line: 1, column: 1 }), scenarioTrace);
|
|
62
64
|
const behaviors = inspectBehaviors(options, program);
|
|
63
65
|
diagnostics.push(...behaviors.diagnostics);
|
|
64
66
|
inspectControllers(options, program, report, behaviors.contracts);
|
|
@@ -162,6 +164,10 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
162
164
|
const position = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
163
165
|
report(file, rule, message, position.line + 1, position.character + 1);
|
|
164
166
|
}
|
|
167
|
+
function scenarioClass(node, expression, property = false) {
|
|
168
|
+
const proof = scenarioTrace.classValue(expression, property);
|
|
169
|
+
if (proof.present && !proof.ok) nodeReport(node, "showcase-class", `Scenarios define no custom class names. A public class helper must return runtime-owned Linaria styles; stable hooks authored in that owner may accompany real styles. Arbitrary strings, private styles and forwarded consumer classes do not qualify.${proof.reasons.length ? ` ${proof.reasons.join(" ")}` : ""} ${scenarioLayoutGuidance} See ads://guides/scenarios.`);
|
|
170
|
+
}
|
|
165
171
|
if (!showcase && (isInside(path.join(dsRoot, "components"), file) || isInside(path.join(dsRoot, "patterns"), file))) inspectPublicProps(source, checker, nodeReport);
|
|
166
172
|
function isLinariaTag(tag) {
|
|
167
173
|
if (ts.isCallExpression(tag)) tag = tag.expression;
|
|
@@ -213,8 +219,10 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
213
219
|
if (property.name.text === "style" && (!inlineAllowed || showcase)) nodeReport(property, "no-inline-style", showcase ? `Scenarios define no inline styles. ${scenarioLayoutGuidance}` : inlineStyleGuidance);
|
|
214
220
|
if (property.name.text === "style") inspectProperty(property.initializer);
|
|
215
221
|
if (property.name.text === "className") inspectClass(property.initializer);
|
|
216
|
-
if (showcase && property.name.text === "className")
|
|
222
|
+
if (showcase && property.name.text === "className") scenarioClass(property, property.initializer);
|
|
217
223
|
}
|
|
224
|
+
if (showcase && props && !ts.isObjectLiteralExpression(props)) scenarioClass(props, props, true);
|
|
225
|
+
if (showcase) for (const property of properties) if (ts.isSpreadAssignment(property)) scenarioClass(property, property.expression, true);
|
|
218
226
|
if (showcase && tag && ts.isStringLiteralLike(tag)) {
|
|
219
227
|
const role = properties.find(property => ts.isPropertyAssignment(property) && (ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)) && property.name.text === "role")?.initializer;
|
|
220
228
|
if (showcaseControls.has(tag.text) || role && ts.isStringLiteralLike(role) && widgetRoles.has(role.text)) nodeReport(node, "showcase-control", `Scenario controls must render public ADS components for actual interaction. ${scenarioLayoutGuidance}`);
|
|
@@ -230,7 +238,8 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
230
238
|
const roleValue = role && ts.isStringLiteralLike(role) ? role.text : role && ts.isJsxExpression(role) && role.expression && ts.isStringLiteralLike(role.expression) ? role.expression.text : null;
|
|
231
239
|
if (showcaseControls.has(node.tagName.text) || widgetRoles.has(roleValue)) nodeReport(node, "showcase-control", `Scenario controls must render public ADS components for actual interaction. ${scenarioLayoutGuidance}`);
|
|
232
240
|
}
|
|
233
|
-
if (showcase && ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && node.name.text === "className")
|
|
241
|
+
if (showcase && ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && node.name.text === "className") scenarioClass(node, node.initializer && ts.isJsxExpression(node.initializer) ? node.initializer.expression : node.initializer);
|
|
242
|
+
if (showcase && ts.isJsxSpreadAttribute(node)) scenarioClass(node, node.expression, true);
|
|
234
243
|
let specifier;
|
|
235
244
|
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) specifier = node.moduleSpecifier.text;
|
|
236
245
|
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments[0] && ts.isStringLiteral(node.arguments[0])) specifier = node.arguments[0].text;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { createHash } = require("node:crypto");
|
|
2
|
+
const postcss = require("postcss");
|
|
3
|
+
|
|
4
|
+
/** Stylis must not tokenize an unevaluated custom-property value as ordinary CSS.
|
|
5
|
+
* Keep the project's atomizer, selectors and priorities; restore opaque values
|
|
6
|
+
* after its nesting pass and before the bundler's CSS optimizer sees them. */
|
|
7
|
+
module.exports = function preserveCustomProperties(Processor) {
|
|
8
|
+
return class PreservedCustomProperties extends Processor {
|
|
9
|
+
extractRules(valueCache, cssText, loc) {
|
|
10
|
+
const source = postcss.parse(cssText);
|
|
11
|
+
const values = new Map();
|
|
12
|
+
source.walkDecls(declaration => {
|
|
13
|
+
if (!declaration.prop.startsWith("--")) return;
|
|
14
|
+
const value = declaration.raws.value?.raw ?? declaration.value;
|
|
15
|
+
const marker = `ads-preserved-${createHash("sha256").update(value).digest("hex")}`;
|
|
16
|
+
values.set(marker, value);
|
|
17
|
+
declaration.value = marker;
|
|
18
|
+
delete declaration.raws.value;
|
|
19
|
+
});
|
|
20
|
+
if (!values.size) return super.extractRules(valueCache, cssText, loc);
|
|
21
|
+
const rules = super.extractRules(valueCache, source.toString(), loc);
|
|
22
|
+
return Object.fromEntries(Object.entries(rules).map(([key, rule]) => {
|
|
23
|
+
const output = postcss.parse(rule.cssText);
|
|
24
|
+
output.walkDecls(declaration => {
|
|
25
|
+
if (declaration.prop.startsWith("--") && values.has(declaration.value)) declaration.value = values.get(declaration.value);
|
|
26
|
+
});
|
|
27
|
+
const restored = output.toString();
|
|
28
|
+
return [rule.atom ? restored : key, { ...rule, cssText: restored }];
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function linariaTagResolver(source: string, tag: string): string | null;
|
package/dist/linaria.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
|
|
3
|
+
/** WyW's supported tag resolver; non-atomic imports retain their normal processor.
|
|
4
|
+
* Import this in every project build that extracts atomic Linaria, including
|
|
5
|
+
* the product build and its independently configured MCP preview builder. */
|
|
6
|
+
export function linariaTagResolver(source, tag) {
|
|
7
|
+
if (source !== "@linaria/atomic" || !["css", "styled"].includes(tag)) return null;
|
|
8
|
+
return fileURLToPath(new URL(`./linaria/atomic-${tag}.cjs`, import.meta.url));
|
|
9
|
+
}
|
|
@@ -100,7 +100,7 @@ Boolean requesting whether a visible border and background is provided by the ho
|
|
|
100
100
|
- omitted: host decides border`)}),Bx=d.object({method:d.literal("ui/request-display-mode"),params:d.object({mode:da.describe("The display mode being requested.")})}),Dv=d.object({mode:da.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough(),Vv=d.union([d.literal("model"),d.literal("app")]).describe("Tool visibility scope - who can access the tool."),Wx=d.object({resourceUri:d.string().optional(),visibility:d.array(Vv).optional().describe(`Who can access this tool. Default: ["model", "app"]
|
|
101
101
|
- "model": Tool visible to and callable by the agent
|
|
102
102
|
- "app": Tool callable by the app from this server only`),csp:d.never().optional(),permissions:d.never().optional()}),Gx=d.object({mimeTypes:d.array(d.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')}),Kx=d.object({method:d.literal("ui/download-file"),params:d.object({contents:d.array(d.union([go,So])).describe("Resource contents to download \u2014 embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})}),Yx=d.object({method:d.literal("ui/message"),params:d.object({role:d.literal("user").describe('Message role, currently only "user" is supported.'),content:d.array(Fe).describe("Message content blocks (text, image, etc.).")})}),Qx=d.object({method:d.literal("ui/notifications/sandbox-resource-ready"),params:d.object({html:d.string().describe("HTML content to load into the inner iframe."),sandbox:d.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:_f.optional().describe("CSP configuration from resource metadata."),permissions:yf.optional().describe("Sandbox permissions from resource metadata.")})}),Hv=d.object({method:d.literal("ui/notifications/tool-result"),params:Ot.describe("Standard MCP tool execution result.")}),x_=d.object({toolInfo:d.object({id:Ne.optional().describe("JSON-RPC id of the tools/call request."),tool:It.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:Tv.optional().describe("Current color theme preference."),styles:Av.optional().describe("Style configuration for theming the app."),displayMode:da.optional().describe("How the UI is currently displayed."),availableDisplayModes:d.array(da).optional().describe("Display modes the host supports."),containerDimensions:d.union([d.object({height:d.number().describe("Fixed container height in pixels.")}),d.object({maxHeight:d.union([d.number(),d.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(d.union([d.object({width:d.number().describe("Fixed container width in pixels.")}),d.object({maxWidth:d.union([d.number(),d.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
|
|
103
|
-
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:d.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:d.string().optional().describe("User's timezone in IANA format."),userAgent:d.string().optional().describe("Host application identifier."),platform:d.union([d.literal("web"),d.literal("desktop"),d.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:d.object({touch:d.boolean().optional().describe("Whether the device supports touch input."),hover:d.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:d.object({top:d.number().describe("Top safe area inset in pixels."),right:d.number().describe("Right safe area inset in pixels."),bottom:d.number().describe("Bottom safe area inset in pixels."),left:d.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Fv=d.object({method:d.literal("ui/notifications/host-context-changed"),params:x_.describe("Partial context update containing only changed fields.")}),Xx=d.object({method:d.literal("ui/update-model-context"),params:d.object({content:d.array(Fe).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:d.record(d.string(),d.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),eP=d.object({method:d.literal("ui/initialize"),params:d.object({appInfo:Ve.describe("App identification (name and version)."),appCapabilities:Zv.describe("Features and capabilities this app provides."),protocolVersion:d.string().describe("Protocol version this app supports.")})}),Jv=d.object({protocolVersion:d.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ve.describe("Host application identification and version."),hostCapabilities:Uv.describe("Features and capabilities provided by the host."),hostContext:x_.describe("Rich context about the host environment.")}).passthrough(),Bv={target:"draft-2020-12"};async function R_(e,t){let o=e["~standard"];if(o.jsonSchema)return o.jsonSchema[t](Bv);throw Error(`Schema (vendor: ${o.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function w_(e,t,o=""){let r=await e["~standard"].validate(t);if(r.issues){let n=r.issues.map(s=>{let i=s.path?.map(c=>typeof c=="object"?c.key:c).join(".");return i?`${i}: ${s.message}`:s.message}).join("; ");throw Error(o+n)}return r.value}function Wv(e,t){let o={...e,...t};return t.experimental!==void 0&&(o.experimental={...e.experimental,...t.experimental}),t.tools!==void 0&&(o.tools={...e.tools,...t.tools}),o}var Jl=class e extends mf{_appInfo;_appCapabilities;options;_hostCapabilities;_hostInfo;_hostContext;_registeredTools={};_initializedSent=!1;_registeredEvents=new Set;_events=new ff;_methods=new gf;setRequestHandler=(...t)=>{let[o,...r]=z_("request",t)??t;this._methods.claim(o,"setRequestHandler"),super.setRequestHandler.call(this,o,...r)};setNotificationHandler=(...t)=>{let[o,...r]=z_("notification",t)??t;this._methods.claim(o,"setNotificationHandler"),super.setNotificationHandler.call(this,o,...r)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...o)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...o)};_assertInitialized(t){if(this._initializedSent)return;let o=`[ext-apps] App.${t}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if(this.options?.strict)throw Error(o);console.warn(`${o}. This will throw in a future release.`)}eventSchemas={toolinput:Ov,toolinputpartial:Nv,toolresult:Hv,toolcancelled:Mv,hostcontextchanged:Fv};static ONE_SHOT_EVENTS=new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]);_everHadListener=new Set;_assertHandlerTiming(t){if(!e.ONE_SHOT_EVENTS.has(t)||this._everHadListener.has(t)||(this._everHadListener.add(t),!this._initializedSent))return;let o=`[ext-apps] "${String(t)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if(this.options?.strict)throw Error(o);console.warn(o)}setEventHandler(t,o){o&&this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.setHandler(t,o)}addEventListener(t,o){this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.addEventListener(t,o)}removeEventListener(t,o){this._events.removeEventListener(t,o)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let o=this.eventSchemas[t];if(!o)throw Error(`Unknown event: ${String(t)}`);this.setNotificationHandler(o.shape.method.value,{params:o.shape.params},r=>{let n=r;t==="hostcontextchanged"&&(this._hostContext={...this._hostContext,...n}),this._events.dispatch(t,n)}),this._registeredEvents.add(t)}getEventHandler(t){return this._events.getHandler(t)}warnIfRequestHandlerReplaced(t,o,r){o&&r&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,o={},r={autoResize:!0}){super(r),this._appInfo=t,this._appCapabilities=o,this.options=r,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),!r.allowUnsafeEval&&d.config({jitless:!0}),this.replaceRequestHandler("ping",n=>(console.log("Received ping:",n.params),{})),this.setEventHandler("hostcontextchanged",void 0)}buildContext(t){return t}registerAppCapabilities(t){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._appCapabilities=Wv(this._appCapabilities,t)}registerTool(t,o,r){if(this._registeredTools[t])throw Error(`Tool ${t} is already registered`);let n=this,s=()=>{n._initializedSent&&n._appCapabilities.tools?.listChanged&&n.sendToolListChanged()},i=o.inputSchema!==void 0,c={title:o.title,description:o.description,inputSchema:o.inputSchema,outputSchema:o.outputSchema,annotations:o.annotations,_meta:o._meta,enabled:!0,enable(){this.enabled=!0,s()},disable(){this.enabled=!1,s()},update(l){Object.assign(this,l),s()},remove(){n._registeredTools[t]===c&&(delete n._registeredTools[t],s())},handler:async(l,u)=>{if(!c.enabled)throw Error(`Tool ${t} is disabled`);let m;if(i){let S=c.inputSchema,_=S?await w_(S,l??{},`Invalid input for tool ${t}: `):l??{};m=await r(_,u)}else m=await r(u);return c.outputSchema&&!m.isError&&(m.structuredContent=await w_(c.outputSchema,m.structuredContent,`Invalid output for tool ${t}: `)),m}};return this._registeredTools[t]=c,!this._appCapabilities.tools&&!this.transport&&this.registerAppCapabilities({tools:{listChanged:!0}}),this.ensureToolHandlersInitialized(),s(),c}_toolHandlersInitialized=!1;ensureToolHandlersInitialized(){this._toolHandlersInitialized||(this._toolHandlersInitialized=!0,this.oncalltool=async(t,o)=>{let r=this._registeredTools[t.name];if(!r)throw Error(`Tool ${t.name} not found`);return r.handler(t.arguments,o)},this.onlisttools=async(t,o)=>({tools:await Promise.all(Object.entries(this._registeredTools).filter(([r,n])=>n.enabled).map(async([r,n])=>{let s={name:r,title:n.title,description:n.description,inputSchema:n.inputSchema?await R_(n.inputSchema,"input"):{type:"object",properties:{}}};return n.outputSchema&&(s.outputSchema=await R_(n.outputSchema,"output")),n.annotations&&(s.annotations=n.annotations),n._meta&&(s._meta=n._meta),s}))}))}async sendToolListChanged(t={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:t})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(t){this.setEventHandler("toolinput",t)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(t){this.setEventHandler("toolinputpartial",t)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(t){this.setEventHandler("toolresult",t)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(t){this.setEventHandler("toolcancelled",t)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(t){this.setEventHandler("hostcontextchanged",t)}_onteardown;get onteardown(){return this._onteardown}set onteardown(t){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,t),this._onteardown=t,this.replaceRequestHandler("ui/resource-teardown",{params:jv.shape.params,result:Lv},(o,r)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(o,r)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",(o,r)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,r)})}_onlisttools;get onlisttools(){return this._onlisttools}set onlisttools(t){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,t),this._onlisttools=t,this.replaceRequestHandler("tools/list",(o,r)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(o.params,r)})}assertCapabilityForMethod(t){switch(t){case"sampling/createMessage":if(!this._hostCapabilities?.sampling)throw Error(`Host does not support sampling (required for ${t})`);break}}assertRequestHandlerCapability(t){switch(t){case"tools/call":case"tools/list":if(!this._appCapabilities.tools)throw Error(`Client does not support tool capability (required for ${t})`);return;default:return}}assertNotificationCapability(t){}async callServerTool(t,o){if(this._assertInitialized("callServerTool"),typeof t=="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${t}"). Did you mean: callServerTool({ name: "${t}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:t},{onprogress:()=>{},resetTimeoutOnProgress:!0,...o})}async readServerResource(t,o){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:t},o)}async listServerResources(t,o){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:t},o)}async createSamplingMessage(t,o){return this._assertInitialized("createSamplingMessage"),await this.request({method:"sampling/createMessage",params:t},o)}sendMessage(t,o){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:t},Iv,o)}sendLog(t){return this.notification({method:"notifications/message",params:t})}updateModelContext(t,o){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:t},Ct,o)}openLink(t,o){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:t},$v,o)}sendOpenLink=this.openLink;downloadFile(t,o){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:t},Cv,o)}requestTeardown(t={}){return this.notification({method:"ui/notifications/request-teardown",params:t})}requestDisplayMode(t,o){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:t},Dv,o)}sendSizeChanged(t){return this.notification({method:"ui/notifications/size-changed",params:t})}setupSizeChangedNotifications(){let t=!1,o=0,r=0,n=()=>{t||(t=!0,requestAnimationFrame(()=>{t=!1;let i=document.documentElement,c=i.style.height;i.style.height="max-content";let l=Math.ceil(i.getBoundingClientRect().height);i.style.height=c;let u=Math.ceil(window.innerWidth);(u!==o||l!==r)&&(o=u,r=l,this.sendSizeChanged({width:u,height:l}))}))};n();let s=new ResizeObserver(n);return s.observe(document.documentElement),s.observe(document.body),()=>s.disconnect()}async connect(t=new Sf(window.parent,window.parent),o){if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1;try{await super.connect(t);let r=await this.request({method:"ui/initialize",params:{appCapabilities:this._appCapabilities,appInfo:this._appInfo,protocolVersion:xv}},Jv,o);if(r===void 0)throw Error(`Server sent invalid initialize result: ${r}`);this._hostCapabilities=r.hostCapabilities,this._hostInfo=r.hostInfo,this._hostContext=r.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,this.options?.autoResize&&this.setupSizeChangedNotifications()}catch(r){throw this.close(),r}}};var Bl=class{constructor(t){this.read=t,this.records=new Map,this.bytes=new Map,this.urls=new Map,this.styles=new Map,this.styleReady=new Map,this.revisions=new Set,this.importing=Promise.resolve(),this.generation=0,this.closed=!1,globalThis.__dsPreviewImport=o=>this.importModule(o)}register(t){if(t.schemaVersion!==1||!Array.isArray(t.scenarios)||!t.assets||typeof t.revision!="string")throw new Error("Unsupported preview manifest. Rebuild previews with the installed ADS version.");if(this.revisions.add(t.revision),this.revisions.size>20)throw Object.assign(new Error("Reopen the preview to release modules retained across 20 build revisions."),{code:"ADS_PREVIEW_REOPEN"});for(let[o,r]of Object.entries(t.assets)){if(!/^ads:\/\/assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(o)||!/^sha256:[a-f0-9]{64}$/.test(r.digest)||!["imports","dynamicImports","styles","references"].every(n=>Array.isArray(r[n])&&r[n].every(s=>Object.hasOwn(t.assets,s))))throw new Error("Invalid preview dependency graph.");if(this.records.has(o)&&this.records.get(o).digest!==r.digest)throw new Error("An immutable preview resource changed its content.");this.records.set(o,r)}}async resource(t){if(!this.bytes.has(t)){let o=(async()=>{let r=this.records.get(t);if(!r)throw new Error(`Unknown preview dependency: ${t}`);let n=await this.read(t);if(this.closed)throw new Error("The preview App was closed.");let s=n.contents.find(u=>u.uri===t);if(!s)throw new Error(`Missing preview dependency: ${t}`);let i=typeof s.text=="string"?new TextEncoder().encode(s.text):Uint8Array.from(atob(s.blob),u=>u.charCodeAt(0)),c=await crypto.subtle.digest("SHA-256",i),l="sha256:"+[...new Uint8Array(c)].map(u=>u.toString(16).padStart(2,"0")).join("");if(i.length!==r.size||l!==r.digest)throw new Error("Preview resource integrity check failed. Rebuild and reopen the preview.");return i})();this.bytes.set(t,o),o.catch(()=>this.bytes.delete(t))}return this.bytes.get(t)}closure(t){let o=[],r=new Set,n=s=>{if(r.has(s))return;r.add(s);let i=this.records.get(s);if(!i)throw new Error(`Missing preview dependency metadata: ${s}`);for(let c of[...i.imports,...i.styles,...i.references])n(c);o.push(s)};return n(t),o}async materialize(t){let o=this.closure(t);await Promise.all(o.map(n=>this.resource(n)));let r={};for(let n of o){if(this.closed)throw new Error("The preview App was closed.");if(this.urls.has(n))continue;let s=this.records.get(n),i=await this.resource(n);if(s.kind==="module"||s.mimeType==="text/css"){let l=new TextDecoder().decode(i);for(let u of s.references){let m=this.urls.get(u);if(!m)throw new Error("Unsupported cyclic asset reference.");l=l.replaceAll(u,m)}i=l}let c=URL.createObjectURL(new Blob([i],{type:s.mimeType}));this.urls.set(n,c),s.kind==="module"&&(r[n]=c)}if(Object.keys(r).length){let n=document.createElement("script");n.type="importmap",n.textContent=JSON.stringify({imports:r}),document.head.append(n)}return o}async importModule(t){let o=this.importing.then(()=>this.materialize(t));this.importing=o.catch(()=>{});let r=await o;for(let n of r){if(this.records.get(n).mimeType!=="text/css")continue;if(this.styles.has(n)){await this.styleReady.get(n);continue}let i=document.createElement("link");i.rel="stylesheet",i.href=this.urls.get(n),this.styles.set(n,i);let c=new Promise((l,u)=>{i.onload=l,i.onerror=()=>u(new Error("The host CSP blocked a preview stylesheet."))});this.styleReady.set(n,c),document.head.append(i);try{await c}catch(l){throw i.remove(),this.styles.delete(n),this.styleReady.delete(n),l}}return import(t)}async mount(t,o,r){let n=++this.generation;this.register(t);let s=t.scenarios.find(l=>l.id===o);if(!s)throw new Error("The selected scenario is no longer registered.");let i=await this.importModule(s.module);if(n!==this.generation||(await this.unmount?.(),n!==this.generation||this.closed))return!1;r.replaceChildren();let c=new Set(this.closure(s.module));for(let[l,u]of this.styles)c.has(l)||(u.remove(),this.styles.delete(l),this.styleReady.delete(l));for(let l of this.closure(s.module))this.styles.has(l)&&document.head.append(this.styles.get(l));if(typeof i.mount!="function")throw new Error("The compiled scenario has no mount adapter.");if(this.unmount=i.mount(r),typeof this.unmount!="function")throw new Error("The scenario mount adapter must return an unmount callback.");return!0}async dispose(){if(!this.closed){this.closed=!0,++this.generation,await this.unmount?.();for(let t of this.styles.values())t.remove();for(let t of this.urls.values())URL.revokeObjectURL(t);this.styles.clear(),this.styleReady.clear(),this.urls.clear(),this.bytes.clear(),this.records.clear(),delete globalThis.__dsPreviewImport}}};function P_(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&typeof t=="object"&&typeof t.token=="string"&&/^--[\w-]+$/.test(t.token)&&typeof t.property=="string"&&/^-?[a-z][a-z0-9-]*$/.test(t.property)&&typeof t.probe=="string"&&t.probe.trim().length>0)&&new Set(e.map(t=>`${t.token}:${t.property}`)).size===e.length}function T_(e,t){let o=t.getComputedStyle(e),r=e.getBoundingClientRect();if(o.display==="none"||o.visibility!=="visible"||!e.getClientRects().length||!(r.width||r.height))return!1;for(let n=e;n;n=n.parentElement)if(Number(t.getComputedStyle(n).opacity)===0)return!1;return!0}function k_(e,t){if(!P_(t))return{ok:!1,diagnostics:["Token preview needs explicit { token, property, probe } samples from the project fixture. Rebuild with the current compiler."]};let o=e.ownerDocument,r=o.defaultView,n=o.documentElement,s=[],i=[],c=[...e.querySelectorAll("[data-ads-token], [data-ads-property]")],l=new Set(t.map(u=>`${u.token}:${u.property}`));for(let u of c)l.has(`${u.dataset.adsToken}:${u.dataset.adsProperty}`)||s.push(`Undeclared rendered token sample ${u.dataset.adsToken}:${u.dataset.adsProperty}.`);for(let u of t){if(!r.CSS.supports(u.property,u.probe)){s.push(`${u.token}: the fixture probe is invalid for ${u.property}.`);continue}let m=r.getComputedStyle(n).getPropertyValue(u.token).trim();if(!m||!r.CSS.supports(u.property,m)){s.push(`${u.token}: its current value is invalid for the fixture's ${u.property} binding.`);continue}let S=c.filter(y=>y.dataset.adsToken===u.token&&y.dataset.adsProperty===u.property);if(!S.length){s.push(`${u.token} needs a rendered ${u.property} sample, not only a name/value label.`);continue}let _=!1;for(let y of S){let P=[];for(let q=y.parentElement;q&&q!==e;q=q.parentElement)q.tagName==="DETAILS"&&!q.open&&(q.open=!0,P.push(q));let E=n.getAttribute("style"),C=y.getAttribute("style");try{y.style.setProperty("transition","none","important");let q=r.getComputedStyle(y).getPropertyValue(u.property),J=T_(y,r);n.style.setProperty(u.token,u.probe,"important");let L=r.getComputedStyle(y).getPropertyValue(u.property);_=!!(L&&L!==q&&(J||T_(y,r)))}finally{E===null?n.removeAttribute("style"):n.setAttribute("style",E),C===null?y.removeAttribute("style"):y.setAttribute("style",C);for(let q of P)q.open=!1}if(_)break}_?i.push(u):s.push(`${u.token} has no visible ${u.property} sample that responds to its value. Hardcoded copies do not satisfy coverage.`)}return{ok:!s.length,verified:i,diagnostics:s}}var We=new Jl({name:"ADS resource preview",version:"1.0.0"},{},{autoResize:!0}),E_=document.getElementById("root"),bf=document.getElementById("ads-preview-status"),vf=async e=>{if(!We.getHostCapabilities()?.serverResources)throw new Error("This host does not forward MCP resource reads. Open the same preview in the ADS showcase.");try{return await We.readServerResource({uri:e},{timeout:15e3})}catch(t){throw t.code===-32601||t.message==="Method not found"?new Error("This host does not forward MCP resource reads. Open the same preview in the ADS showcase."):t}},Gl=new Bl(vf),Vr,ha,$_,Rf,zf=!1,vo=0,pa={},Be="";function Mt(e,t=!1){bf.textContent=e,bf.hidden=!e,bf.setAttribute("role",t?"alert":"status")}async function Wl(e,t={}){try{We.getHostCapabilities()?.logging&&await We.sendLog({level:e==="error"?"error":"info",logger:"ads-preview",data:{state:e,id:Vr,...t}})}catch{}}function C_(e){pa={...pa,...e,styles:{...pa.styles,...e.styles}},["light","dark"].includes(pa.theme)&&(document.documentElement.dataset.theme=pa.theme)}async function I_(){return JSON.parse((await vf("ads://preview/manifest")).contents.find(e=>typeof e.text=="string").text)}async function wf(e,t){if(e===Vr&&t&&t===ha){Mt(Be,!!Be),await Wl(Be?"error":"ready",Be?{message:Be}:{revision:ha});return}let o=++vo;++Gl.generation,Vr=e,document.documentElement.dataset.adsScenario=e,document.documentElement.dataset.adsPreviewState="loading",Be="",Mt(""),await Wl("loading");try{let r=await I_();if(t&&r.revision!==t)throw new Error("Preview changed while opening. Select it again to use the current build.");if(o!==vo||!await Gl.mount(r,e,E_)||o!==vo||(ha=r.revision,$_=r.session,await new Promise(i=>requestAnimationFrame(()=>requestAnimationFrame(i))),await document.fonts.ready,o!==vo))return;let s=r.scenarios.find(i=>i.id===e);if(s?.kind==="tokens"){let i=k_(E_,s.samples);if(!i.ok)throw Be=`Token preview verification failed:
|
|
103
|
+
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:d.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:d.string().optional().describe("User's timezone in IANA format."),userAgent:d.string().optional().describe("Host application identifier."),platform:d.union([d.literal("web"),d.literal("desktop"),d.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:d.object({touch:d.boolean().optional().describe("Whether the device supports touch input."),hover:d.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:d.object({top:d.number().describe("Top safe area inset in pixels."),right:d.number().describe("Right safe area inset in pixels."),bottom:d.number().describe("Bottom safe area inset in pixels."),left:d.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Fv=d.object({method:d.literal("ui/notifications/host-context-changed"),params:x_.describe("Partial context update containing only changed fields.")}),Xx=d.object({method:d.literal("ui/update-model-context"),params:d.object({content:d.array(Fe).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:d.record(d.string(),d.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),eP=d.object({method:d.literal("ui/initialize"),params:d.object({appInfo:Ve.describe("App identification (name and version)."),appCapabilities:Zv.describe("Features and capabilities this app provides."),protocolVersion:d.string().describe("Protocol version this app supports.")})}),Jv=d.object({protocolVersion:d.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ve.describe("Host application identification and version."),hostCapabilities:Uv.describe("Features and capabilities provided by the host."),hostContext:x_.describe("Rich context about the host environment.")}).passthrough(),Bv={target:"draft-2020-12"};async function R_(e,t){let o=e["~standard"];if(o.jsonSchema)return o.jsonSchema[t](Bv);throw Error(`Schema (vendor: ${o.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function w_(e,t,o=""){let r=await e["~standard"].validate(t);if(r.issues){let n=r.issues.map(s=>{let i=s.path?.map(c=>typeof c=="object"?c.key:c).join(".");return i?`${i}: ${s.message}`:s.message}).join("; ");throw Error(o+n)}return r.value}function Wv(e,t){let o={...e,...t};return t.experimental!==void 0&&(o.experimental={...e.experimental,...t.experimental}),t.tools!==void 0&&(o.tools={...e.tools,...t.tools}),o}var Jl=class e extends mf{_appInfo;_appCapabilities;options;_hostCapabilities;_hostInfo;_hostContext;_registeredTools={};_initializedSent=!1;_registeredEvents=new Set;_events=new ff;_methods=new gf;setRequestHandler=(...t)=>{let[o,...r]=z_("request",t)??t;this._methods.claim(o,"setRequestHandler"),super.setRequestHandler.call(this,o,...r)};setNotificationHandler=(...t)=>{let[o,...r]=z_("notification",t)??t;this._methods.claim(o,"setNotificationHandler"),super.setNotificationHandler.call(this,o,...r)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...o)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...o)};_assertInitialized(t){if(this._initializedSent)return;let o=`[ext-apps] App.${t}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if(this.options?.strict)throw Error(o);console.warn(`${o}. This will throw in a future release.`)}eventSchemas={toolinput:Ov,toolinputpartial:Nv,toolresult:Hv,toolcancelled:Mv,hostcontextchanged:Fv};static ONE_SHOT_EVENTS=new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]);_everHadListener=new Set;_assertHandlerTiming(t){if(!e.ONE_SHOT_EVENTS.has(t)||this._everHadListener.has(t)||(this._everHadListener.add(t),!this._initializedSent))return;let o=`[ext-apps] "${String(t)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if(this.options?.strict)throw Error(o);console.warn(o)}setEventHandler(t,o){o&&this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.setHandler(t,o)}addEventListener(t,o){this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.addEventListener(t,o)}removeEventListener(t,o){this._events.removeEventListener(t,o)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let o=this.eventSchemas[t];if(!o)throw Error(`Unknown event: ${String(t)}`);this.setNotificationHandler(o.shape.method.value,{params:o.shape.params},r=>{let n=r;t==="hostcontextchanged"&&(this._hostContext={...this._hostContext,...n}),this._events.dispatch(t,n)}),this._registeredEvents.add(t)}getEventHandler(t){return this._events.getHandler(t)}warnIfRequestHandlerReplaced(t,o,r){o&&r&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,o={},r={autoResize:!0}){super(r),this._appInfo=t,this._appCapabilities=o,this.options=r,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),!r.allowUnsafeEval&&d.config({jitless:!0}),this.replaceRequestHandler("ping",n=>(console.log("Received ping:",n.params),{})),this.setEventHandler("hostcontextchanged",void 0)}buildContext(t){return t}registerAppCapabilities(t){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._appCapabilities=Wv(this._appCapabilities,t)}registerTool(t,o,r){if(this._registeredTools[t])throw Error(`Tool ${t} is already registered`);let n=this,s=()=>{n._initializedSent&&n._appCapabilities.tools?.listChanged&&n.sendToolListChanged()},i=o.inputSchema!==void 0,c={title:o.title,description:o.description,inputSchema:o.inputSchema,outputSchema:o.outputSchema,annotations:o.annotations,_meta:o._meta,enabled:!0,enable(){this.enabled=!0,s()},disable(){this.enabled=!1,s()},update(l){Object.assign(this,l),s()},remove(){n._registeredTools[t]===c&&(delete n._registeredTools[t],s())},handler:async(l,u)=>{if(!c.enabled)throw Error(`Tool ${t} is disabled`);let m;if(i){let S=c.inputSchema,_=S?await w_(S,l??{},`Invalid input for tool ${t}: `):l??{};m=await r(_,u)}else m=await r(u);return c.outputSchema&&!m.isError&&(m.structuredContent=await w_(c.outputSchema,m.structuredContent,`Invalid output for tool ${t}: `)),m}};return this._registeredTools[t]=c,!this._appCapabilities.tools&&!this.transport&&this.registerAppCapabilities({tools:{listChanged:!0}}),this.ensureToolHandlersInitialized(),s(),c}_toolHandlersInitialized=!1;ensureToolHandlersInitialized(){this._toolHandlersInitialized||(this._toolHandlersInitialized=!0,this.oncalltool=async(t,o)=>{let r=this._registeredTools[t.name];if(!r)throw Error(`Tool ${t.name} not found`);return r.handler(t.arguments,o)},this.onlisttools=async(t,o)=>({tools:await Promise.all(Object.entries(this._registeredTools).filter(([r,n])=>n.enabled).map(async([r,n])=>{let s={name:r,title:n.title,description:n.description,inputSchema:n.inputSchema?await R_(n.inputSchema,"input"):{type:"object",properties:{}}};return n.outputSchema&&(s.outputSchema=await R_(n.outputSchema,"output")),n.annotations&&(s.annotations=n.annotations),n._meta&&(s._meta=n._meta),s}))}))}async sendToolListChanged(t={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:t})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(t){this.setEventHandler("toolinput",t)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(t){this.setEventHandler("toolinputpartial",t)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(t){this.setEventHandler("toolresult",t)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(t){this.setEventHandler("toolcancelled",t)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(t){this.setEventHandler("hostcontextchanged",t)}_onteardown;get onteardown(){return this._onteardown}set onteardown(t){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,t),this._onteardown=t,this.replaceRequestHandler("ui/resource-teardown",{params:jv.shape.params,result:Lv},(o,r)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(o,r)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",(o,r)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,r)})}_onlisttools;get onlisttools(){return this._onlisttools}set onlisttools(t){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,t),this._onlisttools=t,this.replaceRequestHandler("tools/list",(o,r)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(o.params,r)})}assertCapabilityForMethod(t){switch(t){case"sampling/createMessage":if(!this._hostCapabilities?.sampling)throw Error(`Host does not support sampling (required for ${t})`);break}}assertRequestHandlerCapability(t){switch(t){case"tools/call":case"tools/list":if(!this._appCapabilities.tools)throw Error(`Client does not support tool capability (required for ${t})`);return;default:return}}assertNotificationCapability(t){}async callServerTool(t,o){if(this._assertInitialized("callServerTool"),typeof t=="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${t}"). Did you mean: callServerTool({ name: "${t}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:t},{onprogress:()=>{},resetTimeoutOnProgress:!0,...o})}async readServerResource(t,o){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:t},o)}async listServerResources(t,o){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:t},o)}async createSamplingMessage(t,o){return this._assertInitialized("createSamplingMessage"),await this.request({method:"sampling/createMessage",params:t},o)}sendMessage(t,o){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:t},Iv,o)}sendLog(t){return this.notification({method:"notifications/message",params:t})}updateModelContext(t,o){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:t},Ct,o)}openLink(t,o){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:t},$v,o)}sendOpenLink=this.openLink;downloadFile(t,o){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:t},Cv,o)}requestTeardown(t={}){return this.notification({method:"ui/notifications/request-teardown",params:t})}requestDisplayMode(t,o){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:t},Dv,o)}sendSizeChanged(t){return this.notification({method:"ui/notifications/size-changed",params:t})}setupSizeChangedNotifications(){let t=!1,o=0,r=0,n=()=>{t||(t=!0,requestAnimationFrame(()=>{t=!1;let i=document.documentElement,c=i.style.height;i.style.height="max-content";let l=Math.ceil(i.getBoundingClientRect().height);i.style.height=c;let u=Math.ceil(window.innerWidth);(u!==o||l!==r)&&(o=u,r=l,this.sendSizeChanged({width:u,height:l}))}))};n();let s=new ResizeObserver(n);return s.observe(document.documentElement),s.observe(document.body),()=>s.disconnect()}async connect(t=new Sf(window.parent,window.parent),o){if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1;try{await super.connect(t);let r=await this.request({method:"ui/initialize",params:{appCapabilities:this._appCapabilities,appInfo:this._appInfo,protocolVersion:xv}},Jv,o);if(r===void 0)throw Error(`Server sent invalid initialize result: ${r}`);this._hostCapabilities=r.hostCapabilities,this._hostInfo=r.hostInfo,this._hostContext=r.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,this.options?.autoResize&&this.setupSizeChangedNotifications()}catch(r){throw this.close(),r}}};var Bl=class{constructor(t){this.read=t,this.records=new Map,this.bytes=new Map,this.urls=new Map,this.styles=new Map,this.styleReady=new Map,this.revisions=new Set,this.importing=Promise.resolve(),this.generation=0,this.closed=!1,globalThis.__dsPreviewImport=o=>this.importModule(o)}register(t){if(t.schemaVersion!==1||!Array.isArray(t.scenarios)||!t.assets||typeof t.revision!="string")throw new Error("Unsupported preview manifest. Rebuild previews with the installed ADS version.");if(this.revisions.add(t.revision),this.revisions.size>20)throw Object.assign(new Error("Reopen the preview to release modules retained across 20 build revisions."),{code:"ADS_PREVIEW_REOPEN"});for(let[o,r]of Object.entries(t.assets)){if(!/^ads:\/\/assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(o)||!/^sha256:[a-f0-9]{64}$/.test(r.digest)||!["imports","dynamicImports","styles","references"].every(n=>Array.isArray(r[n])&&r[n].every(s=>Object.hasOwn(t.assets,s))))throw new Error("Invalid preview dependency graph.");if(this.records.has(o)&&this.records.get(o).digest!==r.digest)throw new Error("An immutable preview resource changed its content.");this.records.set(o,r)}}async resource(t){if(!this.bytes.has(t)){let o=(async()=>{let r=this.records.get(t);if(!r)throw new Error(`Unknown preview dependency: ${t}`);let n;try{n=await this.read(t)}catch(u){throw new Error(`Preview resource read failed for ${t}: ${u.message}`,{cause:u})}if(this.closed)throw new Error("The preview App was closed.");let s=n.contents.find(u=>u.uri===t);if(!s)throw new Error(`Missing preview dependency: ${t}`);let i=typeof s.text=="string"?new TextEncoder().encode(s.text):Uint8Array.from(atob(s.blob),u=>u.charCodeAt(0)),c=await crypto.subtle.digest("SHA-256",i),l="sha256:"+[...new Uint8Array(c)].map(u=>u.toString(16).padStart(2,"0")).join("");if(i.length!==r.size||l!==r.digest)throw new Error("Preview resource integrity check failed. Rebuild and reopen the preview.");return i})();this.bytes.set(t,o),o.catch(()=>this.bytes.delete(t))}return this.bytes.get(t)}closure(t){let o=[],r=new Set,n=s=>{if(r.has(s))return;r.add(s);let i=this.records.get(s);if(!i)throw new Error(`Missing preview dependency metadata: ${s}`);for(let c of[...i.imports,...i.styles,...i.references])n(c);o.push(s)};return n(t),o}async materialize(t){let o=this.closure(t);await Promise.all(o.map(n=>this.resource(n)));let r={};for(let n of o){if(this.closed)throw new Error("The preview App was closed.");if(this.urls.has(n))continue;let s=this.records.get(n),i=await this.resource(n);if(s.kind==="module"||s.mimeType==="text/css"){let l=new TextDecoder().decode(i);for(let u of s.references){let m=this.urls.get(u);if(!m)throw new Error("Unsupported cyclic asset reference.");l=l.replaceAll(u,m)}i=l}let c=URL.createObjectURL(new Blob([i],{type:s.mimeType}));this.urls.set(n,c),s.kind==="module"&&(r[n]=c)}if(Object.keys(r).length){let n=document.createElement("script");n.type="importmap",n.textContent=JSON.stringify({imports:r}),document.head.append(n)}return o}async importModule(t){let o=this.importing.then(()=>this.materialize(t));this.importing=o.catch(()=>{});let r=await o;for(let n of r){if(this.records.get(n).mimeType!=="text/css")continue;if(this.styles.has(n)){await this.styleReady.get(n);continue}let i=document.createElement("link");i.rel="stylesheet",i.href=this.urls.get(n),this.styles.set(n,i);let c=new Promise((l,u)=>{i.onload=l,i.onerror=()=>u(new Error("The host CSP blocked a preview stylesheet."))});this.styleReady.set(n,c),document.head.append(i);try{await c}catch(l){throw i.remove(),this.styles.delete(n),this.styleReady.delete(n),l}}try{return await import(t)}catch(n){throw new Error(`Preview module import failed for ${t}: ${n.message}. Inspect the App resource bridge, browser network/CSP diagnostics and current build.`,{cause:n})}}async mount(t,o,r){let n=++this.generation;this.register(t);let s=t.scenarios.find(l=>l.id===o);if(!s)throw new Error("The selected scenario is no longer registered.");let i=await this.importModule(s.module);if(n!==this.generation||(await this.unmount?.(),n!==this.generation||this.closed))return!1;r.replaceChildren();let c=new Set(this.closure(s.module));for(let[l,u]of this.styles)c.has(l)||(u.remove(),this.styles.delete(l),this.styleReady.delete(l));for(let l of this.closure(s.module))this.styles.has(l)&&document.head.append(this.styles.get(l));if(typeof i.mount!="function")throw new Error("The compiled scenario has no mount adapter.");if(this.unmount=i.mount(r),typeof this.unmount!="function")throw new Error("The scenario mount adapter must return an unmount callback.");return!0}async dispose(){if(!this.closed){this.closed=!0,++this.generation,await this.unmount?.();for(let t of this.styles.values())t.remove();for(let t of this.urls.values())URL.revokeObjectURL(t);this.styles.clear(),this.styleReady.clear(),this.urls.clear(),this.bytes.clear(),this.records.clear(),delete globalThis.__dsPreviewImport}}};function P_(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&typeof t=="object"&&typeof t.token=="string"&&/^--[\w-]+$/.test(t.token)&&typeof t.property=="string"&&/^-?[a-z][a-z0-9-]*$/.test(t.property)&&typeof t.probe=="string"&&t.probe.trim().length>0)&&new Set(e.map(t=>`${t.token}:${t.property}`)).size===e.length}function T_(e,t){let o=t.getComputedStyle(e),r=e.getBoundingClientRect();if(o.display==="none"||o.visibility!=="visible"||!e.getClientRects().length||!(r.width||r.height))return!1;for(let n=e;n;n=n.parentElement)if(Number(t.getComputedStyle(n).opacity)===0)return!1;return!0}function k_(e,t){if(!P_(t))return{ok:!1,diagnostics:["Token preview needs explicit { token, property, probe } samples from the project fixture. Rebuild with the current compiler."]};let o=e.ownerDocument,r=o.defaultView,n=o.documentElement,s=[],i=[],c=[...e.querySelectorAll("[data-ads-token], [data-ads-property]")],l=new Set(t.map(u=>`${u.token}:${u.property}`));for(let u of c)l.has(`${u.dataset.adsToken}:${u.dataset.adsProperty}`)||s.push(`Undeclared rendered token sample ${u.dataset.adsToken}:${u.dataset.adsProperty}.`);for(let u of t){if(!r.CSS.supports(u.property,u.probe)){s.push(`${u.token}: the fixture probe is invalid for ${u.property}.`);continue}let m=r.getComputedStyle(n).getPropertyValue(u.token).trim();if(!m||!r.CSS.supports(u.property,m)){s.push(`${u.token}: its current value is invalid for the fixture's ${u.property} binding.`);continue}let S=c.filter(y=>y.dataset.adsToken===u.token&&y.dataset.adsProperty===u.property);if(!S.length){s.push(`${u.token} needs a rendered ${u.property} sample, not only a name/value label.`);continue}let _=!1;for(let y of S){let P=[];for(let q=y.parentElement;q&&q!==e;q=q.parentElement)q.tagName==="DETAILS"&&!q.open&&(q.open=!0,P.push(q));let E=n.getAttribute("style"),C=y.getAttribute("style");try{y.style.setProperty("transition","none","important");let q=r.getComputedStyle(y).getPropertyValue(u.property),J=T_(y,r);n.style.setProperty(u.token,u.probe,"important");let L=r.getComputedStyle(y).getPropertyValue(u.property);_=!!(L&&L!==q&&(J||T_(y,r)))}finally{E===null?n.removeAttribute("style"):n.setAttribute("style",E),C===null?y.removeAttribute("style"):y.setAttribute("style",C);for(let q of P)q.open=!1}if(_)break}_?i.push(u):s.push(`${u.token} has no visible ${u.property} sample that responds to its value. Hardcoded copies do not satisfy coverage.`)}return{ok:!s.length,verified:i,diagnostics:s}}var We=new Jl({name:"ADS resource preview",version:"1.0.0"},{},{autoResize:!0}),E_=document.getElementById("root"),bf=document.getElementById("ads-preview-status"),vf=async e=>{if(!We.getHostCapabilities()?.serverResources)throw new Error("This host does not forward MCP resource reads. Open the same preview in the ADS showcase.");try{return await We.readServerResource({uri:e},{timeout:15e3})}catch(t){throw t.code===-32601||t.message==="Method not found"?new Error("This host does not forward MCP resource reads. Open the same preview in the ADS showcase."):t}},Gl=new Bl(vf),Vr,ha,$_,Rf,zf=!1,vo=0,pa={},Be="";function Mt(e,t=!1){bf.textContent=e,bf.hidden=!e,bf.setAttribute("role",t?"alert":"status")}async function Wl(e,t={}){try{We.getHostCapabilities()?.logging&&await We.sendLog({level:e==="error"?"error":"info",logger:"ads-preview",data:{state:e,id:Vr,...t}})}catch{}}function C_(e){pa={...pa,...e,styles:{...pa.styles,...e.styles}},["light","dark"].includes(pa.theme)&&(document.documentElement.dataset.theme=pa.theme)}async function I_(){return JSON.parse((await vf("ads://preview/manifest")).contents.find(e=>typeof e.text=="string").text)}async function wf(e,t){if(e===Vr&&t&&t===ha){Mt(Be,!!Be),await Wl(Be?"error":"ready",Be?{message:Be}:{revision:ha});return}let o=++vo;++Gl.generation,Vr=e,document.documentElement.dataset.adsScenario=e,document.documentElement.dataset.adsPreviewState="loading",Be="",Mt(""),await Wl("loading");try{let r=await I_();if(t&&r.revision!==t)throw new Error("Preview changed while opening. Select it again to use the current build.");if(o!==vo||!await Gl.mount(r,e,E_)||o!==vo||(ha=r.revision,$_=r.session,await new Promise(i=>requestAnimationFrame(()=>requestAnimationFrame(i))),await document.fonts.ready,o!==vo))return;let s=r.scenarios.find(i=>i.id===e);if(s?.kind==="tokens"){let i=k_(E_,s.samples);if(!i.ok)throw Be=`Token preview verification failed:
|
|
104
104
|
${i.diagnostics.slice(0,20).join(`
|
|
105
105
|
`)}`,new Error(Be)}o===vo&&(document.documentElement.dataset.adsPreviewState="ready",Mt(""),await Wl("ready",{revision:ha}))}catch(r){o===vo&&(document.documentElement.dataset.adsPreviewState="error",Mt(r.message,!0),await Wl("error",{message:r.message,...r.code?{code:r.code}:{}}))}}async function Gv(){if(!(!Vr||zf||document.visibilityState==="hidden")){zf=!0;try{let t=JSON.parse((await vf("ads://project/build")).contents.find(o=>typeof o.text=="string").text).previews.find(o=>o.id===Vr);if(!t||!t.current){let o=t?.diagnostics?.[0]?.message;Mt(o?`Build failed. Showing the previous preview.
|
|
106
106
|
${o}`:"Rebuilding. This is the previous preview.",!!o)}else{let o=await I_();o.revision!==ha||o.session!==$_?await wf(Vr):Mt(Be,!!Be)}}catch(e){Mt(`Preview freshness unavailable: ${e.message}`,!0)}finally{zf=!1}}}We.ontoolresult=e=>{let t=e.structuredContent;t?.scenario?.id&&wf(t.scenario.id,t.manifestRevision)};We.onhostcontextchanged=C_;We.onteardown=async()=>(++vo,clearInterval(Rf),await Gl.dispose(),{});try{if(await We.connect(),C_(We.getHostContext()??{}),!We.getHostCapabilities()?.serverResources)throw new Error("This host does not forward MCP resource reads. Open the same preview in the ADS showcase.");Rf=setInterval(()=>{Gv()},2e3);let e=document.getElementById("ads-preview-selection");e&&wf(JSON.parse(e.textContent).id)}catch(e){Mt(e.message,!0)}window.addEventListener("pagehide",()=>{clearInterval(Rf),Gl.dispose()},{once:!0});
|
|
@@ -29,7 +29,9 @@ export class PreviewResourceLoader {
|
|
|
29
29
|
const task = (async () => {
|
|
30
30
|
const record = this.records.get(uri);
|
|
31
31
|
if (!record) throw new Error(`Unknown preview dependency: ${uri}`);
|
|
32
|
-
|
|
32
|
+
let response;
|
|
33
|
+
try { response = await this.read(uri); }
|
|
34
|
+
catch (error) { throw new Error(`Preview resource read failed for ${uri}: ${error.message}`, { cause: error }); }
|
|
33
35
|
if (this.closed) throw new Error("The preview App was closed.");
|
|
34
36
|
const content = response.contents.find(item => item.uri === uri);
|
|
35
37
|
if (!content) throw new Error(`Missing preview dependency: ${uri}`);
|
|
@@ -105,7 +107,8 @@ export class PreviewResourceLoader {
|
|
|
105
107
|
document.head.append(style);
|
|
106
108
|
try { await ready; } catch (error) { style.remove(); this.styles.delete(id); this.styleReady.delete(id); throw error; }
|
|
107
109
|
}
|
|
108
|
-
return import(/* @vite-ignore */ uri);
|
|
110
|
+
try { return await import(/* @vite-ignore */ uri); }
|
|
111
|
+
catch (error) { throw new Error(`Preview module import failed for ${uri}: ${error.message}. Inspect the App resource bridge, browser network/CSP diagnostics and current build.`, { cause: error }); }
|
|
109
112
|
}
|
|
110
113
|
async mount(manifest, id, container) {
|
|
111
114
|
const generation = ++this.generation;
|
package/dist/mcp/server.mjs
CHANGED
|
@@ -85,7 +85,8 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
85
85
|
return [
|
|
86
86
|
{ uri: GUIDE_URI, name: "Start here: design-system workflow", description: "Read first for UI/ADS tasks, especially when only resource access is exposed. Explains the exact workspace, tool sequence, skill selection, previews, project commands and fallback.", mimeType: "text/markdown" },
|
|
87
87
|
{ uri: "ads://guides/behaviors", name: "Controller and behavioral verification guide", mimeType: "text/markdown" },
|
|
88
|
-
{ uri: "ads://guides/atomic-styles", name: "Atomic
|
|
88
|
+
{ uri: "ads://guides/atomic-styles", name: "Atomic styles: composition conflicts and custom-property extraction", mimeType: "text/markdown" },
|
|
89
|
+
{ uri: "ads://guides/scenarios", name: "Scenario coverage and public class-helper contracts", mimeType: "text/markdown" },
|
|
89
90
|
{ uri: `${PROJECT_URI}/status`, name: "ADS project status", mimeType: "application/json" },
|
|
90
91
|
{ uri: BUILD_URI, name: "Preview build status and diagnostics", mimeType: "application/json" },
|
|
91
92
|
{ uri: `${PROJECT_URI}/tokens`, name: "Project token values and themes", mimeType: "application/json" },
|
|
@@ -123,6 +124,7 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
123
124
|
if (uri === GUIDE_URI) return { contents: [{ uri, mimeType: "text/markdown", text: projectGuide(project(), [...previews(), ...scenarioApps()]) }] };
|
|
124
125
|
if (uri === "ads://guides/behaviors") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../../docs/BEHAVIORS.md", import.meta.url), "utf8") }] };
|
|
125
126
|
if (uri === "ads://guides/atomic-styles") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../skills/ads-designer/references/atomic-styles.md", import.meta.url), "utf8") }] };
|
|
127
|
+
if (uri === "ads://guides/scenarios") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../skills/ads-designer/references/scenarios.md", import.meta.url), "utf8") }] };
|
|
126
128
|
if (skills.files.has(uri)) return { contents: [skills.read(uri)] };
|
|
127
129
|
let value;
|
|
128
130
|
if (uri === `${PROJECT_URI}/status`) value = status();
|
|
@@ -28,7 +28,8 @@ export function previewEntriesPlugin(entries, mountEntry) {
|
|
|
28
28
|
load(id) {
|
|
29
29
|
const entry = ids.get(id.slice(1));
|
|
30
30
|
if (!entry) return;
|
|
31
|
-
|
|
31
|
+
const styles = (entry.stylesheets ?? []).map(file => `import ${JSON.stringify(path.resolve(entry.root, file))};`).join("");
|
|
32
|
+
return `import { mountScenario } from ${JSON.stringify(mountEntry)};${styles}import Scenario from ${JSON.stringify(path.resolve(entry.root, entry.file))};export const mount = container => mountScenario(Scenario,container);`;
|
|
32
33
|
},
|
|
33
34
|
};
|
|
34
35
|
}
|