@emseepea/create-react-ui-server 0.0.34 → 0.0.36
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.
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@emseepea/feedback": "0.2.14",
|
|
19
19
|
"playwright": "1.62.1",
|
|
20
20
|
"axe-core": "4.13.0",
|
|
21
|
-
"@emseepea/testing": "0.
|
|
21
|
+
"@emseepea/testing": "0.13.0",
|
|
22
22
|
"@types/node": "24.13.3",
|
|
23
23
|
"@types/react": "19.2.18",
|
|
24
24
|
"@types/react-dom": "19.2.5",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"private": true,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@emseepea/react": "0.
|
|
34
|
+
"@emseepea/react": "0.3.0",
|
|
35
35
|
"@emseepea/server": "0.12.1",
|
|
36
36
|
"@emseepea/tailwind": "0.1.0",
|
|
37
37
|
"react": "19.2.8",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ResultCard, useMcpApp, useMcpTheme } from "@emseepea/react";
|
|
1
|
+
import { ResultCard, useMcpAction, useMcpApp, useMcpTheme, type McpActionLifecycle } from "@emseepea/react";
|
|
2
2
|
import { defineResultView, type ResultView as ResultViewModel } from "@emseepea/server/ui";
|
|
3
|
-
import { useEffect
|
|
3
|
+
import { useEffect } from "react";
|
|
4
4
|
import { hydrateRoot } from "react-dom/client";
|
|
5
5
|
|
|
6
6
|
interface PlantingPlanResult {
|
|
@@ -17,35 +17,30 @@ interface PlantingPlanResult {
|
|
|
17
17
|
export function ResultApp() {
|
|
18
18
|
const app = useMcpApp({ name: "Pea planting plan result", version: "1.0.0", parseResult: parsePlantingPlan });
|
|
19
19
|
const theme = useMcpTheme(app.hostContext.theme);
|
|
20
|
-
const
|
|
20
|
+
const action = useMcpAction(app);
|
|
21
21
|
|
|
22
22
|
useEffect(() => {
|
|
23
23
|
document.documentElement.dataset.emseepeaTheme = theme;
|
|
24
24
|
}, [theme]);
|
|
25
25
|
|
|
26
26
|
const view = resultView(app, action);
|
|
27
|
-
const act = async () => {
|
|
28
|
-
if (action !== "idle") return;
|
|
29
|
-
setAction("sending");
|
|
30
|
-
try {
|
|
31
|
-
await app.sendMessage("Show me growing tips for these pea varieties.");
|
|
32
|
-
setAction("sent");
|
|
33
|
-
} catch {
|
|
34
|
-
setAction("error");
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
27
|
|
|
38
28
|
return (
|
|
39
29
|
<>
|
|
40
30
|
<h1>Pea planting plan result</h1>
|
|
41
|
-
<ResultCard
|
|
31
|
+
<ResultCard
|
|
32
|
+
view={view}
|
|
33
|
+
headingLevel={2}
|
|
34
|
+
idPrefix="pea-result"
|
|
35
|
+
onAction={({ id }) => void action.send(id, "Show me growing tips for these pea varieties.")}
|
|
36
|
+
/>
|
|
42
37
|
</>
|
|
43
38
|
);
|
|
44
39
|
}
|
|
45
40
|
|
|
46
41
|
function resultView(
|
|
47
42
|
app: ReturnType<typeof useMcpApp<PlantingPlanResult>>,
|
|
48
|
-
action:
|
|
43
|
+
action: McpActionLifecycle,
|
|
49
44
|
): ResultViewModel {
|
|
50
45
|
const base = {
|
|
51
46
|
id: "pea-planting-plan-result",
|
|
@@ -75,13 +70,13 @@ function resultView(
|
|
|
75
70
|
id: "growing-tips",
|
|
76
71
|
label: "Ask for growing tips",
|
|
77
72
|
accessibleName: "Ask for growing tips for these varieties",
|
|
78
|
-
disabled: action !== "idle",
|
|
73
|
+
disabled: action.status !== "idle",
|
|
79
74
|
}];
|
|
80
|
-
const actionState = action === "sending"
|
|
81
|
-
? { kind: "sending" as const, status:
|
|
82
|
-
: action === "sent"
|
|
83
|
-
? { kind: "sent" as const, status:
|
|
84
|
-
: action === "error"
|
|
75
|
+
const actionState = action.status === "sending"
|
|
76
|
+
? { kind: "sending" as const, status: `Asking ChatGPT: ${action.activePrompt}`, focusTarget: "status" as const }
|
|
77
|
+
: action.status === "sent"
|
|
78
|
+
? { kind: "sent" as const, status: `Asked ChatGPT: ${action.activePrompt}`, focusTarget: "status" as const }
|
|
79
|
+
: action.status === "error"
|
|
85
80
|
? { kind: "error" as const, status: "Growing tips could not be requested. Ask in the chat instead.", focusTarget: "status" as const }
|
|
86
81
|
: {
|
|
87
82
|
kind: app.resultRevision > 1 ? "updated" as const : count === 0 ? "empty" as const : "ready" as const,
|
|
@@ -82,6 +82,7 @@ test("the MCP Apps card completes initialization before rendering a result", asy
|
|
|
82
82
|
await page.setContent("<iframe title=\"Pea planting plan result\"></iframe>");
|
|
83
83
|
await page.evaluate(() => {
|
|
84
84
|
window.receivedAppMessages = [];
|
|
85
|
+
window.pendingAppMessages = [];
|
|
85
86
|
window.addEventListener("message", (event) => {
|
|
86
87
|
window.receivedAppMessages.push(event.data);
|
|
87
88
|
if (event.data?.method === "ui/initialize") {
|
|
@@ -105,7 +106,7 @@ test("the MCP Apps card completes initialization before rendering a result", asy
|
|
|
105
106
|
}, "*");
|
|
106
107
|
}
|
|
107
108
|
if (event.data?.method === "ui/message") {
|
|
108
|
-
window.
|
|
109
|
+
window.pendingAppMessages.push({ source: event.source, id: event.data.id });
|
|
109
110
|
}
|
|
110
111
|
});
|
|
111
112
|
});
|
|
@@ -160,14 +161,60 @@ test("the MCP Apps card completes initialization before rendering a result", asy
|
|
|
160
161
|
assert.equal(await frame.locator("html").getAttribute("data-emseepea-theme"), "dark");
|
|
161
162
|
assert.equal(await frame.evaluate((node) => node === document.querySelector("[data-emseepea-part='status']"), statusNode), true);
|
|
162
163
|
|
|
163
|
-
await frame.locator("button").
|
|
164
|
+
await frame.locator("button").evaluate((button) => {
|
|
165
|
+
button.click();
|
|
166
|
+
button.click();
|
|
167
|
+
});
|
|
164
168
|
await status.filter({ hasText: "Asking ChatGPT: Show me growing tips for these pea varieties." }).waitFor();
|
|
165
169
|
assert.equal(await frame.locator("button").isDisabled(), true);
|
|
166
170
|
await page.waitForFunction(() => window.receivedAppMessages.some((message) => message?.method === "ui/message"));
|
|
167
|
-
const sent = await page.evaluate(() => window.receivedAppMessages.
|
|
168
|
-
assert.equal(sent.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
const sent = await page.evaluate(() => window.receivedAppMessages.filter((message) => message?.method === "ui/message"));
|
|
172
|
+
assert.equal(sent.length, 1);
|
|
173
|
+
assert.equal(sent[0].params.content[0].text, "Show me growing tips for these pea varieties.");
|
|
174
|
+
|
|
175
|
+
await page.evaluate(() => document.querySelector("iframe").contentWindow.postMessage({
|
|
176
|
+
jsonrpc: "2.0", method: "ui/notifications/tool-result", params: { structuredContent: {
|
|
177
|
+
title: "Autumn peas",
|
|
178
|
+
matchingCount: 1,
|
|
179
|
+
varieties: [
|
|
180
|
+
{ name: "Early Onward", growthHabit: "climbing", peaType: "shelling" },
|
|
181
|
+
],
|
|
182
|
+
notice: "No report was sent or stored.",
|
|
183
|
+
} },
|
|
184
|
+
}, "*"));
|
|
185
|
+
await status.filter({ hasText: "Preview updated: 1 sample pea variety matches." }).waitFor();
|
|
186
|
+
assert.equal(await frame.locator("button").isDisabled(), false);
|
|
187
|
+
await page.evaluate(() => window.pendingAppMessages[0].source.postMessage({
|
|
188
|
+
jsonrpc: "2.0", id: window.pendingAppMessages[0].id, result: {},
|
|
189
|
+
}, "*"));
|
|
190
|
+
await page.waitForTimeout(50);
|
|
191
|
+
assert.equal(await status.textContent(), "Preview updated: 1 sample pea variety matches.");
|
|
192
|
+
|
|
193
|
+
await frame.locator("button").press("Enter");
|
|
194
|
+
await page.waitForFunction(() => window.pendingAppMessages.length === 2);
|
|
195
|
+
await page.evaluate(() => window.pendingAppMessages[1].source.postMessage({
|
|
196
|
+
jsonrpc: "2.0", id: window.pendingAppMessages[1].id, error: { code: -32603, message: "Rejected" },
|
|
197
|
+
}, "*"));
|
|
198
|
+
await status.filter({ hasText: "Growing tips could not be requested. Ask in the chat instead." }).waitFor();
|
|
199
|
+
assert.equal(await frame.locator("[data-emseepea-part='status']").evaluate((element) => element === document.activeElement), true);
|
|
200
|
+
|
|
201
|
+
await page.evaluate(() => document.querySelector("iframe").contentWindow.postMessage({
|
|
202
|
+
jsonrpc: "2.0", method: "ui/notifications/tool-result", params: { structuredContent: {
|
|
203
|
+
title: "Winter peas",
|
|
204
|
+
matchingCount: 1,
|
|
205
|
+
varieties: [
|
|
206
|
+
{ name: "Meteor", growthHabit: "bush", peaType: "shelling" },
|
|
207
|
+
],
|
|
208
|
+
notice: "No report was sent or stored.",
|
|
209
|
+
} },
|
|
210
|
+
}, "*"));
|
|
211
|
+
await status.filter({ hasText: "Preview updated: 1 sample pea variety matches." }).waitFor();
|
|
212
|
+
await frame.locator("button").press("Enter");
|
|
213
|
+
await page.waitForFunction(() => window.pendingAppMessages.length === 3);
|
|
214
|
+
const finalSent = await page.evaluate(() => window.receivedAppMessages.filter((message) => message?.method === "ui/message").at(-1));
|
|
215
|
+
assert.equal(finalSent.params.content[0].text, "Show me growing tips for these pea varieties.");
|
|
216
|
+
await page.evaluate(() => window.pendingAppMessages[2].source.postMessage({
|
|
217
|
+
jsonrpc: "2.0", id: window.pendingAppMessages[2].id, result: {},
|
|
171
218
|
}, "*"));
|
|
172
219
|
await status.filter({ hasText: "Asked ChatGPT: Show me growing tips for these pea varieties." }).waitFor();
|
|
173
220
|
assert.equal(await frame.locator("[data-emseepea-part='status']").evaluate((element) => element === document.activeElement), true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/create-react-ui-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.36",
|
|
4
4
|
"description": "Create an Em See Pea server with an accessible React form.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@emseepea/feedback": "0.2.14",
|
|
22
|
-
"@emseepea/react": "0.
|
|
22
|
+
"@emseepea/react": "0.3.0",
|
|
23
23
|
"@emseepea/server": "0.12.1",
|
|
24
24
|
"@emseepea/tailwind": "0.1.0",
|
|
25
25
|
"react": "19.2.8",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@emseepea/example-ui-shared": "0.0.0",
|
|
28
28
|
"playwright": "1.62.1",
|
|
29
29
|
"axe-core": "4.13.0",
|
|
30
|
-
"@emseepea/testing": "0.
|
|
30
|
+
"@emseepea/testing": "0.13.0",
|
|
31
31
|
"@types/node": "24.13.3",
|
|
32
32
|
"@types/react": "19.2.18",
|
|
33
33
|
"@types/react-dom": "19.2.5",
|