@ggui-ai/iframe-runtime 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dismiss-intent.d.ts +28 -0
- package/dist/dismiss-intent.js +65 -0
- package/dist/iframe-runtime.js +64 -64
- package/dist/iframe-runtime.meta.json +1 -1
- package/dist/react-renderer.d.ts +9 -0
- package/dist/react-renderer.js +3 -0
- package/dist/render-item.d.ts +8 -0
- package/dist/render-item.js +5 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.js +45 -3
- package/package.json +7 -7
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dismiss INTENT the card forwards and never acts on (ggui#1109).
|
|
3
|
+
*
|
|
4
|
+
* An MCP-Apps card is an iframe: a key pressed while focus is inside its
|
|
5
|
+
* document never reaches the page around it. So a user who has just clicked
|
|
6
|
+
* the card — the exact moment they press Escape to close it — presses it into
|
|
7
|
+
* a document that has no reason to care and no way to pass it on. Before this,
|
|
8
|
+
* the runtime had NO keydown listener at all, and the surrounding host could
|
|
9
|
+
* add a perfect one and still never hear that gesture.
|
|
10
|
+
*
|
|
11
|
+
* What the card guarantees (protocol's docblock on `McpAppDismissMessage`, and
|
|
12
|
+
* this module is its implementation): it emits on the gesture, at most ONE
|
|
13
|
+
* intent per gesture — a held key auto-repeating is one gesture, so repeats are
|
|
14
|
+
* skipped — it does NOT act on it (no unmount, no visual dismissal, no state
|
|
15
|
+
* change), and it never learns whether the host honoured it. Ignoring is
|
|
16
|
+
* conformant: the host owns the surface.
|
|
17
|
+
*/
|
|
18
|
+
import { type McpAppDismissReason } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
19
|
+
/** Post one intent to the embedding host. Best-effort, like every other parent post. */
|
|
20
|
+
export declare function postDismissIntent(reason: McpAppDismissReason): void;
|
|
21
|
+
/**
|
|
22
|
+
* Listen for the dismiss gesture inside the card document and forward it.
|
|
23
|
+
* Idempotent: a second call replaces the first listener rather than stacking a
|
|
24
|
+
* second one, so a re-boot in the same document cannot double-post.
|
|
25
|
+
*/
|
|
26
|
+
export declare function installDismissIntentListener(doc?: Document): void;
|
|
27
|
+
/** Remove the listener (teardown; also the test seam). */
|
|
28
|
+
export declare function removeDismissIntentListener(doc?: Document): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dismiss INTENT the card forwards and never acts on (ggui#1109).
|
|
3
|
+
*
|
|
4
|
+
* An MCP-Apps card is an iframe: a key pressed while focus is inside its
|
|
5
|
+
* document never reaches the page around it. So a user who has just clicked
|
|
6
|
+
* the card — the exact moment they press Escape to close it — presses it into
|
|
7
|
+
* a document that has no reason to care and no way to pass it on. Before this,
|
|
8
|
+
* the runtime had NO keydown listener at all, and the surrounding host could
|
|
9
|
+
* add a perfect one and still never hear that gesture.
|
|
10
|
+
*
|
|
11
|
+
* What the card guarantees (protocol's docblock on `McpAppDismissMessage`, and
|
|
12
|
+
* this module is its implementation): it emits on the gesture, at most ONE
|
|
13
|
+
* intent per gesture — a held key auto-repeating is one gesture, so repeats are
|
|
14
|
+
* skipped — it does NOT act on it (no unmount, no visual dismissal, no state
|
|
15
|
+
* change), and it never learns whether the host honoured it. Ignoring is
|
|
16
|
+
* conformant: the host owns the surface.
|
|
17
|
+
*/
|
|
18
|
+
import { MCP_APP_DISMISS_TYPE, } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
19
|
+
/** The key that means "close this", as the platform names it. */
|
|
20
|
+
const ESCAPE_KEY = 'Escape';
|
|
21
|
+
let installed = null;
|
|
22
|
+
/** Post one intent to the embedding host. Best-effort, like every other parent post. */
|
|
23
|
+
export function postDismissIntent(reason) {
|
|
24
|
+
if (typeof window === 'undefined' || window.parent === null)
|
|
25
|
+
return;
|
|
26
|
+
const message = { type: MCP_APP_DISMISS_TYPE, reason };
|
|
27
|
+
try {
|
|
28
|
+
window.parent.postMessage(message, '*');
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Parent unreachable (detached window). Fire-and-forget — the same posture
|
|
32
|
+
// every other outbound notification takes, and dismissing is the host's
|
|
33
|
+
// decision anyway.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Listen for the dismiss gesture inside the card document and forward it.
|
|
38
|
+
* Idempotent: a second call replaces the first listener rather than stacking a
|
|
39
|
+
* second one, so a re-boot in the same document cannot double-post.
|
|
40
|
+
*/
|
|
41
|
+
export function installDismissIntentListener(doc = document) {
|
|
42
|
+
removeDismissIntentListener(doc);
|
|
43
|
+
const listener = (ev) => {
|
|
44
|
+
if (ev.key !== ESCAPE_KEY)
|
|
45
|
+
return;
|
|
46
|
+
// A held key auto-repeating is ONE gesture (protocol's guarantee): a user
|
|
47
|
+
// leaning on Escape must not flood the host with intents.
|
|
48
|
+
if (ev.repeat)
|
|
49
|
+
return;
|
|
50
|
+
// The gesture is NOT consumed: no `preventDefault`, no `stopPropagation`.
|
|
51
|
+
// Anything else inside the card that listens for Escape — a menu, a
|
|
52
|
+
// combobox — keeps hearing it, and the host hears it too. The card
|
|
53
|
+
// forwards; it does not decide.
|
|
54
|
+
postDismissIntent('escape');
|
|
55
|
+
};
|
|
56
|
+
doc.addEventListener('keydown', listener);
|
|
57
|
+
installed = listener;
|
|
58
|
+
}
|
|
59
|
+
/** Remove the listener (teardown; also the test seam). */
|
|
60
|
+
export function removeDismissIntentListener(doc = document) {
|
|
61
|
+
if (installed === null)
|
|
62
|
+
return;
|
|
63
|
+
doc.removeEventListener('keydown', installed);
|
|
64
|
+
installed = null;
|
|
65
|
+
}
|