@opendatalabs/vana-sdk 4.4.0 → 4.5.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.
Files changed (106) hide show
  1. package/README.md +176 -0
  2. package/dist/connect/account-origin.cjs +72 -0
  3. package/dist/connect/account-origin.cjs.map +1 -0
  4. package/dist/connect/account-origin.d.ts +92 -0
  5. package/dist/connect/account-origin.js +47 -0
  6. package/dist/connect/account-origin.js.map +1 -0
  7. package/dist/connect/account-origin.test.d.ts +1 -0
  8. package/dist/connect/chrome.cjs +284 -0
  9. package/dist/connect/chrome.cjs.map +1 -0
  10. package/dist/connect/chrome.d.ts +42 -0
  11. package/dist/connect/chrome.js +256 -0
  12. package/dist/connect/chrome.js.map +1 -0
  13. package/dist/connect/http-transports.cjs +100 -0
  14. package/dist/connect/http-transports.cjs.map +1 -0
  15. package/dist/connect/http-transports.d.ts +77 -0
  16. package/dist/connect/http-transports.js +75 -0
  17. package/dist/connect/http-transports.js.map +1 -0
  18. package/dist/connect/http-transports.test.d.ts +1 -0
  19. package/dist/connect/launcher.cjs +232 -0
  20. package/dist/connect/launcher.cjs.map +1 -0
  21. package/dist/connect/launcher.d.ts +129 -0
  22. package/dist/connect/launcher.js +220 -0
  23. package/dist/connect/launcher.js.map +1 -0
  24. package/dist/connect/launcher.test.d.ts +1 -0
  25. package/dist/connect/protocol.cjs +259 -0
  26. package/dist/connect/protocol.cjs.map +1 -0
  27. package/dist/connect/protocol.d.ts +331 -0
  28. package/dist/connect/protocol.js +215 -0
  29. package/dist/connect/protocol.js.map +1 -0
  30. package/dist/connect/protocol.test.d.ts +1 -0
  31. package/dist/connect.cjs +55 -0
  32. package/dist/connect.cjs.map +1 -0
  33. package/dist/connect.d.ts +35 -0
  34. package/dist/connect.js +34 -0
  35. package/dist/connect.js.map +1 -0
  36. package/dist/direct/connect-flow-sheet.test.d.ts +1 -0
  37. package/dist/direct/connect-flow.cjs +211 -1
  38. package/dist/direct/connect-flow.cjs.map +1 -1
  39. package/dist/direct/connect-flow.d.ts +75 -0
  40. package/dist/direct/connect-flow.js +213 -1
  41. package/dist/direct/connect-flow.js.map +1 -1
  42. package/dist/direct/connect-handler.cjs +493 -0
  43. package/dist/direct/connect-handler.cjs.map +1 -0
  44. package/dist/direct/connect-handler.d.ts +246 -0
  45. package/dist/direct/connect-handler.js +476 -0
  46. package/dist/direct/connect-handler.js.map +1 -0
  47. package/dist/direct/connect-handler.test.d.ts +1 -0
  48. package/dist/direct/controller.cjs +86 -17
  49. package/dist/direct/controller.cjs.map +1 -1
  50. package/dist/direct/controller.d.ts +101 -6
  51. package/dist/direct/controller.js +86 -16
  52. package/dist/direct/controller.js.map +1 -1
  53. package/dist/direct/endpoints.cjs +5 -2
  54. package/dist/direct/endpoints.cjs.map +1 -1
  55. package/dist/direct/endpoints.js +5 -2
  56. package/dist/direct/endpoints.js.map +1 -1
  57. package/dist/direct/request-authorization.cjs +65 -0
  58. package/dist/direct/request-authorization.cjs.map +1 -0
  59. package/dist/direct/request-authorization.d.ts +65 -0
  60. package/dist/direct/request-authorization.js +39 -0
  61. package/dist/direct/request-authorization.js.map +1 -0
  62. package/dist/direct/request-authorization.test.d.ts +1 -0
  63. package/dist/direct/types.cjs.map +1 -1
  64. package/dist/direct/types.d.ts +6 -0
  65. package/dist/direct/types.js.map +1 -1
  66. package/dist/direct/use-direct-vana-connect.cjs +18 -1
  67. package/dist/direct/use-direct-vana-connect.cjs.map +1 -1
  68. package/dist/direct/use-direct-vana-connect.js +18 -1
  69. package/dist/direct/use-direct-vana-connect.js.map +1 -1
  70. package/dist/direct/use-vana-connect.cjs +84 -0
  71. package/dist/direct/use-vana-connect.cjs.map +1 -0
  72. package/dist/direct/use-vana-connect.d.ts +59 -0
  73. package/dist/direct/use-vana-connect.js +64 -0
  74. package/dist/direct/use-vana-connect.js.map +1 -0
  75. package/dist/direct/use-vana-connect.test.d.ts +1 -0
  76. package/dist/index.browser.d.ts +1 -0
  77. package/dist/index.browser.js +104 -0
  78. package/dist/index.browser.js.map +4 -4
  79. package/dist/index.node.cjs +112 -0
  80. package/dist/index.node.cjs.map +3 -3
  81. package/dist/index.node.d.ts +1 -0
  82. package/dist/index.node.js +104 -0
  83. package/dist/index.node.js.map +4 -4
  84. package/dist/protocol/fixtures/pdpp-scope-keys.json +133 -0
  85. package/dist/protocol/pdpp-scopes.cjs +134 -0
  86. package/dist/protocol/pdpp-scopes.cjs.map +1 -0
  87. package/dist/protocol/pdpp-scopes.d.ts +123 -0
  88. package/dist/protocol/pdpp-scopes.js +103 -0
  89. package/dist/protocol/pdpp-scopes.js.map +1 -0
  90. package/dist/protocol/pdpp-scopes.test.d.ts +1 -0
  91. package/dist/protocol/scopes.cjs +17 -0
  92. package/dist/protocol/scopes.cjs.map +1 -1
  93. package/dist/protocol/scopes.d.ts +1 -0
  94. package/dist/protocol/scopes.js +18 -0
  95. package/dist/protocol/scopes.js.map +1 -1
  96. package/dist/react.cjs +10 -2
  97. package/dist/react.cjs.map +1 -1
  98. package/dist/react.d.ts +4 -1
  99. package/dist/react.js +11 -1
  100. package/dist/react.js.map +1 -1
  101. package/dist/server.cjs +61 -2
  102. package/dist/server.cjs.map +1 -1
  103. package/dist/server.d.ts +16 -4
  104. package/dist/server.js +67 -3
  105. package/dist/server.js.map +1 -1
  106. package/package.json +11 -1
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var chrome_exports = {};
20
+ __export(chrome_exports, {
21
+ DRAG_CLOSE_PX: () => DRAG_CLOSE_PX,
22
+ MAX_SHEET_RATIO: () => MAX_SHEET_RATIO,
23
+ RETURN_TAP_GUARD_MS: () => RETURN_TAP_GUARD_MS,
24
+ createSheetChrome: () => createSheetChrome,
25
+ sheetTopPx: () => sheetTopPx
26
+ });
27
+ module.exports = __toCommonJS(chrome_exports);
28
+ const SHEET_RADIUS_PX = 28;
29
+ const SHEET_MAX_WIDTH_PX = 480;
30
+ const HANDLE_STRIP_PX = 20;
31
+ const INITIAL_CONTENT_PX = 320;
32
+ const TRANSITION_MS = 280;
33
+ const SHEET_BACKGROUND = "#f4f4f4";
34
+ const DRAG_CLOSE_PX = 120;
35
+ const MAX_SHEET_RATIO = 0.92;
36
+ const STYLES = `
37
+ :host { all: initial; }
38
+ .scrim {
39
+ position: fixed; inset: 0; z-index: 2147483646;
40
+ background: rgb(0 0 0 / 0.4);
41
+ opacity: 0; transition: opacity ${TRANSITION_MS}ms ease;
42
+ }
43
+ .sheet {
44
+ position: fixed; left: 0; right: 0; z-index: 2147483647;
45
+ margin: 0 auto; max-width: ${SHEET_MAX_WIDTH_PX}px;
46
+ top: 100vh;
47
+ transition: top ${TRANSITION_MS}ms cubic-bezier(0.32, 0.72, 0, 1);
48
+ }
49
+ .sheet.dragging { transition: none; }
50
+ .handle {
51
+ height: ${HANDLE_STRIP_PX}px;
52
+ display: flex; justify-content: center; align-items: center;
53
+ background: ${SHEET_BACKGROUND};
54
+ border-radius: ${SHEET_RADIUS_PX}px ${SHEET_RADIUS_PX}px 0 0;
55
+ touch-action: none; cursor: grab;
56
+ }
57
+ .handle::before {
58
+ content: ""; width: 36px; height: 5px; border-radius: 3px;
59
+ background: rgb(0 0 0 / 0.18);
60
+ }
61
+ iframe {
62
+ display: block; width: 100%; border: 0;
63
+ background: ${SHEET_BACKGROUND};
64
+ }
65
+ .open .scrim { opacity: 1; }
66
+ .sheet.fullscreen {
67
+ top: 0; height: 100vh; max-width: none;
68
+ display: flex; flex-direction: column; box-sizing: border-box;
69
+ padding: env(safe-area-inset-top) env(safe-area-inset-right)
70
+ env(safe-area-inset-bottom) env(safe-area-inset-left);
71
+ background: ${SHEET_BACKGROUND};
72
+ transition: none;
73
+ }
74
+ .sheet.fullscreen iframe { flex: 1; min-height: 0; }
75
+ @media (prefers-reduced-motion: reduce) {
76
+ .scrim, .sheet { transition: none; }
77
+ }
78
+ `;
79
+ function viewportPx() {
80
+ return window.visualViewport?.height ?? window.innerHeight;
81
+ }
82
+ function viewportTopPx() {
83
+ return window.visualViewport?.offsetTop ?? 0;
84
+ }
85
+ function capPx() {
86
+ return Math.round(viewportPx() * MAX_SHEET_RATIO);
87
+ }
88
+ function placeSheet(sheet, content) {
89
+ sheet.style.top = `${Math.round(viewportTopPx() + sheetTopPx(viewportPx(), content))}px`;
90
+ }
91
+ function sheetTopPx(viewport, content) {
92
+ const cap = Math.round(viewport * MAX_SHEET_RATIO);
93
+ const visible = Math.min(content, cap - HANDLE_STRIP_PX);
94
+ return Math.round(viewport - HANDLE_STRIP_PX - visible);
95
+ }
96
+ const RETURN_TAP_GUARD_MS = 500;
97
+ function installReturnGuard(sheet, iframe, state) {
98
+ const fitIframe = () => {
99
+ iframe.style.height = `${capPx() - HANDLE_STRIP_PX}px`;
100
+ if (state.shown) {
101
+ placeSheet(sheet, state.lastHeight ?? INITIAL_CONTENT_PX);
102
+ }
103
+ };
104
+ const onReturn = () => {
105
+ if (document.visibilityState !== "visible") {
106
+ return;
107
+ }
108
+ state.returnedAt = performance.now();
109
+ fitIframe();
110
+ };
111
+ fitIframe();
112
+ window.addEventListener("resize", fitIframe);
113
+ window.visualViewport?.addEventListener("resize", fitIframe);
114
+ window.visualViewport?.addEventListener("scroll", fitIframe);
115
+ document.addEventListener("visibilitychange", onReturn);
116
+ window.addEventListener("focus", onReturn);
117
+ return () => {
118
+ window.removeEventListener("resize", fitIframe);
119
+ window.visualViewport?.removeEventListener("resize", fitIframe);
120
+ window.visualViewport?.removeEventListener("scroll", fitIframe);
121
+ document.removeEventListener("visibilitychange", onReturn);
122
+ window.removeEventListener("focus", onReturn);
123
+ sheet.style.transform = "";
124
+ };
125
+ }
126
+ function installDragToClose(sheet, handle, onClose) {
127
+ let startY = null;
128
+ handle.addEventListener("pointerdown", (event) => {
129
+ startY = event.clientY;
130
+ handle.setPointerCapture?.(event.pointerId);
131
+ sheet.classList.add("dragging");
132
+ });
133
+ handle.addEventListener("pointermove", (event) => {
134
+ if (startY === null) {
135
+ return;
136
+ }
137
+ const dy = Math.max(0, event.clientY - startY);
138
+ sheet.style.transform = `translateY(${dy}px)`;
139
+ });
140
+ const end = (event) => {
141
+ if (startY === null) {
142
+ return;
143
+ }
144
+ const dy = event.clientY - startY;
145
+ startY = null;
146
+ sheet.classList.remove("dragging");
147
+ sheet.style.transform = "";
148
+ if (dy > DRAG_CLOSE_PX) {
149
+ onClose("drag");
150
+ }
151
+ };
152
+ handle.addEventListener("pointerup", end);
153
+ handle.addEventListener("pointercancel", end);
154
+ }
155
+ function placeBottomSheet({
156
+ frame,
157
+ iframe,
158
+ onClose,
159
+ sheet
160
+ }) {
161
+ const returnState = {
162
+ lastHeight: null,
163
+ returnedAt: 0,
164
+ shown: false
165
+ };
166
+ const scrim = document.createElement("div");
167
+ scrim.className = "scrim";
168
+ scrim.setAttribute("data-slot", "scrim");
169
+ scrim.addEventListener("click", () => {
170
+ if (performance.now() - returnState.returnedAt < RETURN_TAP_GUARD_MS) {
171
+ return;
172
+ }
173
+ onClose("scrim");
174
+ });
175
+ const handle = document.createElement("div");
176
+ handle.className = "handle";
177
+ handle.setAttribute("aria-hidden", "true");
178
+ handle.setAttribute("data-slot", "handle");
179
+ sheet.prepend(handle);
180
+ frame.prepend(scrim);
181
+ const removeReturnGuard = installReturnGuard(sheet, iframe, returnState);
182
+ installDragToClose(sheet, handle, onClose);
183
+ return {
184
+ remove: removeReturnGuard,
185
+ setContentHeight(px) {
186
+ if (document.visibilityState !== "visible") {
187
+ return;
188
+ }
189
+ returnState.lastHeight = px;
190
+ if (returnState.shown) {
191
+ placeSheet(sheet, px);
192
+ }
193
+ },
194
+ show() {
195
+ requestAnimationFrame(() => {
196
+ returnState.shown = true;
197
+ frame.classList.add("open");
198
+ placeSheet(sheet, returnState.lastHeight ?? INITIAL_CONTENT_PX);
199
+ });
200
+ }
201
+ };
202
+ }
203
+ function placeFullscreen({ frame, sheet }) {
204
+ sheet.classList.add("fullscreen");
205
+ const fitToViewport = () => {
206
+ sheet.style.top = `${Math.round(viewportTopPx())}px`;
207
+ sheet.style.height = `${Math.round(viewportPx())}px`;
208
+ };
209
+ fitToViewport();
210
+ window.addEventListener("resize", fitToViewport);
211
+ window.visualViewport?.addEventListener("resize", fitToViewport);
212
+ window.visualViewport?.addEventListener("scroll", fitToViewport);
213
+ return {
214
+ remove() {
215
+ window.removeEventListener("resize", fitToViewport);
216
+ window.visualViewport?.removeEventListener("resize", fitToViewport);
217
+ window.visualViewport?.removeEventListener("scroll", fitToViewport);
218
+ },
219
+ setContentHeight() {
220
+ return;
221
+ },
222
+ show() {
223
+ frame.classList.add("open");
224
+ }
225
+ };
226
+ }
227
+ const PLACEMENTS = {
228
+ fullscreen: placeFullscreen,
229
+ sheet: placeBottomSheet
230
+ };
231
+ function createSheetChrome(input) {
232
+ const host = document.createElement("div");
233
+ host.setAttribute("data-vana-connect", "");
234
+ const root = host.attachShadow({ mode: "open" });
235
+ const style = document.createElement("style");
236
+ style.textContent = STYLES;
237
+ const frame = document.createElement("div");
238
+ const sheet = document.createElement("div");
239
+ sheet.className = "sheet";
240
+ sheet.setAttribute("role", "dialog");
241
+ sheet.setAttribute("aria-modal", "true");
242
+ sheet.setAttribute("aria-label", input.ariaLabel);
243
+ const iframe = document.createElement("iframe");
244
+ iframe.setAttribute(
245
+ "sandbox",
246
+ "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
247
+ );
248
+ iframe.setAttribute(
249
+ "allow",
250
+ "otp-credentials; publickey-credentials-get; clipboard-write"
251
+ );
252
+ iframe.setAttribute("title", "Vana");
253
+ iframe.referrerPolicy = "strict-origin-when-cross-origin";
254
+ iframe.src = input.src;
255
+ sheet.append(iframe);
256
+ frame.append(sheet);
257
+ root.append(style, frame);
258
+ input.container.append(host);
259
+ const placement = PLACEMENTS[input.display]({
260
+ frame,
261
+ iframe,
262
+ onClose: input.onClose,
263
+ sheet
264
+ });
265
+ return {
266
+ host,
267
+ iframe,
268
+ remove() {
269
+ placement.remove();
270
+ host.remove();
271
+ },
272
+ setContentHeight: placement.setContentHeight,
273
+ show: placement.show
274
+ };
275
+ }
276
+ // Annotate the CommonJS export names for ESM import in node:
277
+ 0 && (module.exports = {
278
+ DRAG_CLOSE_PX,
279
+ MAX_SHEET_RATIO,
280
+ RETURN_TAP_GUARD_MS,
281
+ createSheetChrome,
282
+ sheetTopPx
283
+ });
284
+ //# sourceMappingURL=chrome.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/connect/chrome.ts"],"sourcesContent":["/**\n * Sheet chrome for the Vana connect sheet, in a Shadow DOM root: a bottom\n * sheet with scrim and drag handle, or a window-filling frame.\n *\n * @remarks\n * Ported unchanged from `@vana-unity/connect-embed`. See the layout notes\n * below.\n *\n * @category Connect\n * @module connect/chrome\n */\n\n// Sheet chrome. Lives in a Shadow DOM root so partner CSS cannot restyle it, and\n// the partner cannot reach the iframe's document (cross-origin) through it.\n// One placement per display:\n//\n// host (partner DOM)\n// └─ #shadow-root\n// ├─ bottom sheet\n// │ ├─ .scrim click closes\n// │ └─ .sheet top = viewport − (handle + content)\n// │ ├─ .handle drag down to close (above the frame, never over it)\n// │ └─ iframe fixed height = cap; the unused part sits below\n// │ the viewport\n// └─ fullscreen\n// └─ .sheet.fullscreen the visible viewport, inside its safe areas\n// └─ iframe fills it; the frame scrolls its own content\n//\n// Nothing overlaps or clips the iframe. IntersectionObserver v2 inside the\n// sheet reports it \"not visible\" when anything covers or clips it, and the\n// consent step waits for real visibility. The part of the frame below the\n// viewport is simply off screen, which does not count as occlusion.\n\nimport type { ConnectDisplay } from \"./protocol\";\n\nconst SHEET_RADIUS_PX = 28;\nconst SHEET_MAX_WIDTH_PX = 480;\nconst HANDLE_STRIP_PX = 20;\nconst INITIAL_CONTENT_PX = 320;\nconst TRANSITION_MS = 280;\nconst SHEET_BACKGROUND = \"#f4f4f4\";\n\n/** Drag the handle this far down to dismiss. */\nexport const DRAG_CLOSE_PX = 120;\n\n/** The sheet never covers more of the viewport than this. */\nexport const MAX_SHEET_RATIO = 0.92;\n\nconst STYLES = `\n:host { all: initial; }\n.scrim {\n position: fixed; inset: 0; z-index: 2147483646;\n background: rgb(0 0 0 / 0.4);\n opacity: 0; transition: opacity ${TRANSITION_MS}ms ease;\n}\n.sheet {\n position: fixed; left: 0; right: 0; z-index: 2147483647;\n margin: 0 auto; max-width: ${SHEET_MAX_WIDTH_PX}px;\n top: 100vh;\n transition: top ${TRANSITION_MS}ms cubic-bezier(0.32, 0.72, 0, 1);\n}\n.sheet.dragging { transition: none; }\n.handle {\n height: ${HANDLE_STRIP_PX}px;\n display: flex; justify-content: center; align-items: center;\n background: ${SHEET_BACKGROUND};\n border-radius: ${SHEET_RADIUS_PX}px ${SHEET_RADIUS_PX}px 0 0;\n touch-action: none; cursor: grab;\n}\n.handle::before {\n content: \"\"; width: 36px; height: 5px; border-radius: 3px;\n background: rgb(0 0 0 / 0.18);\n}\niframe {\n display: block; width: 100%; border: 0;\n background: ${SHEET_BACKGROUND};\n}\n.open .scrim { opacity: 1; }\n.sheet.fullscreen {\n top: 0; height: 100vh; max-width: none;\n display: flex; flex-direction: column; box-sizing: border-box;\n padding: env(safe-area-inset-top) env(safe-area-inset-right)\n env(safe-area-inset-bottom) env(safe-area-inset-left);\n background: ${SHEET_BACKGROUND};\n transition: none;\n}\n.sheet.fullscreen iframe { flex: 1; min-height: 0; }\n@media (prefers-reduced-motion: reduce) {\n .scrim, .sheet { transition: none; }\n}\n`;\n\nexport type CloseReason = \"drag\" | \"scrim\";\n\nexport interface SheetChrome {\n host: HTMLElement;\n iframe: HTMLIFrameElement;\n remove(): void;\n setContentHeight(px: number): void;\n show(): void;\n}\n\nfunction viewportPx(): number {\n return window.visualViewport?.height ?? window.innerHeight;\n}\n\n/**\n * How far the visible viewport has panned inside the layout viewport `fixed`\n * measures from. It is non-zero when an on-screen keyboard pans the page.\n */\nfunction viewportTopPx(): number {\n return window.visualViewport?.offsetTop ?? 0;\n}\n\nfunction capPx(): number {\n return Math.round(viewportPx() * MAX_SHEET_RATIO);\n}\n\n/** Places the sheet so exactly `content` px of the frame show above the fold. */\nfunction placeSheet(sheet: HTMLElement, content: number): void {\n sheet.style.top = `${Math.round(viewportTopPx() + sheetTopPx(viewportPx(), content))}px`;\n}\n\n/**\n * Where the sheet's top edge goes so `content` px of the frame show, capped\n * at {@link MAX_SHEET_RATIO} of the viewport.\n *\n * @param viewport - Viewport height in px.\n * @param content - Content height the sheet reported, in px.\n * @returns The sheet's `top`, in whole px.\n */\nexport function sheetTopPx(viewport: number, content: number): number {\n const cap = Math.round(viewport * MAX_SHEET_RATIO);\n const visible = Math.min(content, cap - HANDLE_STRIP_PX);\n // Whole pixels: a fractional offset blurs the framed text.\n return Math.round(viewport - HANDLE_STRIP_PX - visible);\n}\n\n/** Scrim taps this soon after the page returns to view are ignored. */\nexport const RETURN_TAP_GUARD_MS = 500;\n\n/**\n * Return-to-view handling, isolated so it can change on its own.\n *\n * Root cause of the spike's \"blank sheet\" (scratchpad blank-render notes): not\n * a paint failure. Returning from the Google popup tab, a stray tap landed on\n * the scrim and silently closed the sheet; a headless-Chrome harness artefact\n * looked similar. So: taps on the scrim right after the page regains\n * visibility or focus are ignored, heights reported while hidden are dropped,\n * and the last good height is re-applied on return. The iframe keeps a fixed\n * viewport (the cap) and only the sheet's position animates, so measuring\n * content inside the frame never resizes the frame itself.\n */\nfunction installReturnGuard(\n sheet: HTMLElement,\n iframe: HTMLIFrameElement,\n state: { lastHeight: number | null; returnedAt: number; shown: boolean },\n): () => void {\n const fitIframe = () => {\n iframe.style.height = `${capPx() - HANDLE_STRIP_PX}px`;\n if (state.shown) {\n placeSheet(sheet, state.lastHeight ?? INITIAL_CONTENT_PX);\n }\n };\n const onReturn = () => {\n if (document.visibilityState !== \"visible\") {\n return;\n }\n state.returnedAt = performance.now();\n fitIframe();\n };\n fitIframe();\n window.addEventListener(\"resize\", fitIframe);\n window.visualViewport?.addEventListener(\"resize\", fitIframe);\n window.visualViewport?.addEventListener(\"scroll\", fitIframe);\n document.addEventListener(\"visibilitychange\", onReturn);\n window.addEventListener(\"focus\", onReturn);\n return () => {\n window.removeEventListener(\"resize\", fitIframe);\n window.visualViewport?.removeEventListener(\"resize\", fitIframe);\n window.visualViewport?.removeEventListener(\"scroll\", fitIframe);\n document.removeEventListener(\"visibilitychange\", onReturn);\n window.removeEventListener(\"focus\", onReturn);\n sheet.style.transform = \"\";\n };\n}\n\nfunction installDragToClose(\n sheet: HTMLElement,\n handle: HTMLElement,\n onClose: (reason: CloseReason) => void,\n): void {\n let startY: number | null = null;\n handle.addEventListener(\"pointerdown\", (event) => {\n startY = event.clientY;\n handle.setPointerCapture?.(event.pointerId);\n sheet.classList.add(\"dragging\");\n });\n handle.addEventListener(\"pointermove\", (event) => {\n if (startY === null) {\n return;\n }\n const dy = Math.max(0, event.clientY - startY);\n sheet.style.transform = `translateY(${dy}px)`;\n });\n const end = (event: PointerEvent) => {\n if (startY === null) {\n return;\n }\n const dy = event.clientY - startY;\n startY = null;\n sheet.classList.remove(\"dragging\");\n sheet.style.transform = \"\";\n if (dy > DRAG_CLOSE_PX) {\n onClose(\"drag\");\n }\n };\n handle.addEventListener(\"pointerup\", end);\n handle.addEventListener(\"pointercancel\", end);\n}\n\n/** What differs between displays; the frame, focus and messages do not. */\ninterface Placement {\n remove(): void;\n setContentHeight(px: number): void;\n show(): void;\n}\n\ninterface PlacementParts {\n frame: HTMLElement;\n iframe: HTMLIFrameElement;\n onClose: (reason: CloseReason) => void;\n sheet: HTMLElement;\n}\n\nfunction placeBottomSheet({\n frame,\n iframe,\n onClose,\n sheet,\n}: PlacementParts): Placement {\n const returnState = {\n lastHeight: null as number | null,\n returnedAt: 0,\n shown: false,\n };\n const scrim = document.createElement(\"div\");\n scrim.className = \"scrim\";\n scrim.setAttribute(\"data-slot\", \"scrim\");\n scrim.addEventListener(\"click\", () => {\n if (performance.now() - returnState.returnedAt < RETURN_TAP_GUARD_MS) {\n return;\n }\n onClose(\"scrim\");\n });\n\n const handle = document.createElement(\"div\");\n handle.className = \"handle\";\n handle.setAttribute(\"aria-hidden\", \"true\");\n handle.setAttribute(\"data-slot\", \"handle\");\n\n sheet.prepend(handle);\n frame.prepend(scrim);\n\n const removeReturnGuard = installReturnGuard(sheet, iframe, returnState);\n installDragToClose(sheet, handle, onClose);\n\n return {\n remove: removeReturnGuard,\n setContentHeight(px) {\n // A hidden page can report a throttled frame's height; keep the last good one.\n if (document.visibilityState !== \"visible\") {\n return;\n }\n returnState.lastHeight = px;\n if (returnState.shown) {\n placeSheet(sheet, px);\n }\n },\n show() {\n // Next frame, so the closed state paints first and the sheet slides in.\n requestAnimationFrame(() => {\n returnState.shown = true;\n frame.classList.add(\"open\");\n placeSheet(sheet, returnState.lastHeight ?? INITIAL_CONTENT_PX);\n });\n },\n };\n}\n\n/**\n * Fills the visible viewport, so an on-screen keyboard shortens the frame and\n * its content scrolls above it. The window holds nothing else, so\n * there is no scrim to tap and no handle to drag: the frame's own close\n * control and Escape close it. Content height does not size anything.\n */\nfunction placeFullscreen({ frame, sheet }: PlacementParts): Placement {\n sheet.classList.add(\"fullscreen\");\n const fitToViewport = () => {\n sheet.style.top = `${Math.round(viewportTopPx())}px`;\n sheet.style.height = `${Math.round(viewportPx())}px`;\n };\n fitToViewport();\n window.addEventListener(\"resize\", fitToViewport);\n window.visualViewport?.addEventListener(\"resize\", fitToViewport);\n window.visualViewport?.addEventListener(\"scroll\", fitToViewport);\n return {\n remove() {\n window.removeEventListener(\"resize\", fitToViewport);\n window.visualViewport?.removeEventListener(\"resize\", fitToViewport);\n window.visualViewport?.removeEventListener(\"scroll\", fitToViewport);\n },\n setContentHeight() {\n return;\n },\n show() {\n frame.classList.add(\"open\");\n },\n };\n}\n\nconst PLACEMENTS: Record<ConnectDisplay, (parts: PlacementParts) => Placement> =\n {\n fullscreen: placeFullscreen,\n sheet: placeBottomSheet,\n };\n\nexport function createSheetChrome(input: {\n ariaLabel: string;\n container: HTMLElement;\n display: ConnectDisplay;\n onClose: (reason: CloseReason) => void;\n src: string;\n}): SheetChrome {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-vana-connect\", \"\");\n const root = host.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = STYLES;\n\n const frame = document.createElement(\"div\");\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"sheet\";\n sheet.setAttribute(\"role\", \"dialog\");\n sheet.setAttribute(\"aria-modal\", \"true\");\n sheet.setAttribute(\"aria-label\", input.ariaLabel);\n\n const iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\n \"sandbox\",\n \"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\",\n );\n iframe.setAttribute(\n \"allow\",\n \"otp-credentials; publickey-credentials-get; clipboard-write\",\n );\n iframe.setAttribute(\"title\", \"Vana\");\n iframe.referrerPolicy = \"strict-origin-when-cross-origin\";\n iframe.src = input.src;\n\n sheet.append(iframe);\n frame.append(sheet);\n root.append(style, frame);\n input.container.append(host);\n\n const placement = PLACEMENTS[input.display]({\n frame,\n iframe,\n onClose: input.onClose,\n sheet,\n });\n\n return {\n host,\n iframe,\n remove() {\n placement.remove();\n host.remove();\n },\n setContentHeight: placement.setContentHeight,\n show: placement.show,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Sheet chrome for the Vana connect sheet, in a Shadow DOM root: a bottom
3
+ * sheet with scrim and drag handle, or a window-filling frame.
4
+ *
5
+ * @remarks
6
+ * Ported unchanged from `@vana-unity/connect-embed`. See the layout notes
7
+ * below.
8
+ *
9
+ * @category Connect
10
+ * @module connect/chrome
11
+ */
12
+ import type { ConnectDisplay } from "./protocol.js";
13
+ /** Drag the handle this far down to dismiss. */
14
+ export declare const DRAG_CLOSE_PX = 120;
15
+ /** The sheet never covers more of the viewport than this. */
16
+ export declare const MAX_SHEET_RATIO = 0.92;
17
+ export type CloseReason = "drag" | "scrim";
18
+ export interface SheetChrome {
19
+ host: HTMLElement;
20
+ iframe: HTMLIFrameElement;
21
+ remove(): void;
22
+ setContentHeight(px: number): void;
23
+ show(): void;
24
+ }
25
+ /**
26
+ * Where the sheet's top edge goes so `content` px of the frame show, capped
27
+ * at {@link MAX_SHEET_RATIO} of the viewport.
28
+ *
29
+ * @param viewport - Viewport height in px.
30
+ * @param content - Content height the sheet reported, in px.
31
+ * @returns The sheet's `top`, in whole px.
32
+ */
33
+ export declare function sheetTopPx(viewport: number, content: number): number;
34
+ /** Scrim taps this soon after the page returns to view are ignored. */
35
+ export declare const RETURN_TAP_GUARD_MS = 500;
36
+ export declare function createSheetChrome(input: {
37
+ ariaLabel: string;
38
+ container: HTMLElement;
39
+ display: ConnectDisplay;
40
+ onClose: (reason: CloseReason) => void;
41
+ src: string;
42
+ }): SheetChrome;
@@ -0,0 +1,256 @@
1
+ const SHEET_RADIUS_PX = 28;
2
+ const SHEET_MAX_WIDTH_PX = 480;
3
+ const HANDLE_STRIP_PX = 20;
4
+ const INITIAL_CONTENT_PX = 320;
5
+ const TRANSITION_MS = 280;
6
+ const SHEET_BACKGROUND = "#f4f4f4";
7
+ const DRAG_CLOSE_PX = 120;
8
+ const MAX_SHEET_RATIO = 0.92;
9
+ const STYLES = `
10
+ :host { all: initial; }
11
+ .scrim {
12
+ position: fixed; inset: 0; z-index: 2147483646;
13
+ background: rgb(0 0 0 / 0.4);
14
+ opacity: 0; transition: opacity ${TRANSITION_MS}ms ease;
15
+ }
16
+ .sheet {
17
+ position: fixed; left: 0; right: 0; z-index: 2147483647;
18
+ margin: 0 auto; max-width: ${SHEET_MAX_WIDTH_PX}px;
19
+ top: 100vh;
20
+ transition: top ${TRANSITION_MS}ms cubic-bezier(0.32, 0.72, 0, 1);
21
+ }
22
+ .sheet.dragging { transition: none; }
23
+ .handle {
24
+ height: ${HANDLE_STRIP_PX}px;
25
+ display: flex; justify-content: center; align-items: center;
26
+ background: ${SHEET_BACKGROUND};
27
+ border-radius: ${SHEET_RADIUS_PX}px ${SHEET_RADIUS_PX}px 0 0;
28
+ touch-action: none; cursor: grab;
29
+ }
30
+ .handle::before {
31
+ content: ""; width: 36px; height: 5px; border-radius: 3px;
32
+ background: rgb(0 0 0 / 0.18);
33
+ }
34
+ iframe {
35
+ display: block; width: 100%; border: 0;
36
+ background: ${SHEET_BACKGROUND};
37
+ }
38
+ .open .scrim { opacity: 1; }
39
+ .sheet.fullscreen {
40
+ top: 0; height: 100vh; max-width: none;
41
+ display: flex; flex-direction: column; box-sizing: border-box;
42
+ padding: env(safe-area-inset-top) env(safe-area-inset-right)
43
+ env(safe-area-inset-bottom) env(safe-area-inset-left);
44
+ background: ${SHEET_BACKGROUND};
45
+ transition: none;
46
+ }
47
+ .sheet.fullscreen iframe { flex: 1; min-height: 0; }
48
+ @media (prefers-reduced-motion: reduce) {
49
+ .scrim, .sheet { transition: none; }
50
+ }
51
+ `;
52
+ function viewportPx() {
53
+ return window.visualViewport?.height ?? window.innerHeight;
54
+ }
55
+ function viewportTopPx() {
56
+ return window.visualViewport?.offsetTop ?? 0;
57
+ }
58
+ function capPx() {
59
+ return Math.round(viewportPx() * MAX_SHEET_RATIO);
60
+ }
61
+ function placeSheet(sheet, content) {
62
+ sheet.style.top = `${Math.round(viewportTopPx() + sheetTopPx(viewportPx(), content))}px`;
63
+ }
64
+ function sheetTopPx(viewport, content) {
65
+ const cap = Math.round(viewport * MAX_SHEET_RATIO);
66
+ const visible = Math.min(content, cap - HANDLE_STRIP_PX);
67
+ return Math.round(viewport - HANDLE_STRIP_PX - visible);
68
+ }
69
+ const RETURN_TAP_GUARD_MS = 500;
70
+ function installReturnGuard(sheet, iframe, state) {
71
+ const fitIframe = () => {
72
+ iframe.style.height = `${capPx() - HANDLE_STRIP_PX}px`;
73
+ if (state.shown) {
74
+ placeSheet(sheet, state.lastHeight ?? INITIAL_CONTENT_PX);
75
+ }
76
+ };
77
+ const onReturn = () => {
78
+ if (document.visibilityState !== "visible") {
79
+ return;
80
+ }
81
+ state.returnedAt = performance.now();
82
+ fitIframe();
83
+ };
84
+ fitIframe();
85
+ window.addEventListener("resize", fitIframe);
86
+ window.visualViewport?.addEventListener("resize", fitIframe);
87
+ window.visualViewport?.addEventListener("scroll", fitIframe);
88
+ document.addEventListener("visibilitychange", onReturn);
89
+ window.addEventListener("focus", onReturn);
90
+ return () => {
91
+ window.removeEventListener("resize", fitIframe);
92
+ window.visualViewport?.removeEventListener("resize", fitIframe);
93
+ window.visualViewport?.removeEventListener("scroll", fitIframe);
94
+ document.removeEventListener("visibilitychange", onReturn);
95
+ window.removeEventListener("focus", onReturn);
96
+ sheet.style.transform = "";
97
+ };
98
+ }
99
+ function installDragToClose(sheet, handle, onClose) {
100
+ let startY = null;
101
+ handle.addEventListener("pointerdown", (event) => {
102
+ startY = event.clientY;
103
+ handle.setPointerCapture?.(event.pointerId);
104
+ sheet.classList.add("dragging");
105
+ });
106
+ handle.addEventListener("pointermove", (event) => {
107
+ if (startY === null) {
108
+ return;
109
+ }
110
+ const dy = Math.max(0, event.clientY - startY);
111
+ sheet.style.transform = `translateY(${dy}px)`;
112
+ });
113
+ const end = (event) => {
114
+ if (startY === null) {
115
+ return;
116
+ }
117
+ const dy = event.clientY - startY;
118
+ startY = null;
119
+ sheet.classList.remove("dragging");
120
+ sheet.style.transform = "";
121
+ if (dy > DRAG_CLOSE_PX) {
122
+ onClose("drag");
123
+ }
124
+ };
125
+ handle.addEventListener("pointerup", end);
126
+ handle.addEventListener("pointercancel", end);
127
+ }
128
+ function placeBottomSheet({
129
+ frame,
130
+ iframe,
131
+ onClose,
132
+ sheet
133
+ }) {
134
+ const returnState = {
135
+ lastHeight: null,
136
+ returnedAt: 0,
137
+ shown: false
138
+ };
139
+ const scrim = document.createElement("div");
140
+ scrim.className = "scrim";
141
+ scrim.setAttribute("data-slot", "scrim");
142
+ scrim.addEventListener("click", () => {
143
+ if (performance.now() - returnState.returnedAt < RETURN_TAP_GUARD_MS) {
144
+ return;
145
+ }
146
+ onClose("scrim");
147
+ });
148
+ const handle = document.createElement("div");
149
+ handle.className = "handle";
150
+ handle.setAttribute("aria-hidden", "true");
151
+ handle.setAttribute("data-slot", "handle");
152
+ sheet.prepend(handle);
153
+ frame.prepend(scrim);
154
+ const removeReturnGuard = installReturnGuard(sheet, iframe, returnState);
155
+ installDragToClose(sheet, handle, onClose);
156
+ return {
157
+ remove: removeReturnGuard,
158
+ setContentHeight(px) {
159
+ if (document.visibilityState !== "visible") {
160
+ return;
161
+ }
162
+ returnState.lastHeight = px;
163
+ if (returnState.shown) {
164
+ placeSheet(sheet, px);
165
+ }
166
+ },
167
+ show() {
168
+ requestAnimationFrame(() => {
169
+ returnState.shown = true;
170
+ frame.classList.add("open");
171
+ placeSheet(sheet, returnState.lastHeight ?? INITIAL_CONTENT_PX);
172
+ });
173
+ }
174
+ };
175
+ }
176
+ function placeFullscreen({ frame, sheet }) {
177
+ sheet.classList.add("fullscreen");
178
+ const fitToViewport = () => {
179
+ sheet.style.top = `${Math.round(viewportTopPx())}px`;
180
+ sheet.style.height = `${Math.round(viewportPx())}px`;
181
+ };
182
+ fitToViewport();
183
+ window.addEventListener("resize", fitToViewport);
184
+ window.visualViewport?.addEventListener("resize", fitToViewport);
185
+ window.visualViewport?.addEventListener("scroll", fitToViewport);
186
+ return {
187
+ remove() {
188
+ window.removeEventListener("resize", fitToViewport);
189
+ window.visualViewport?.removeEventListener("resize", fitToViewport);
190
+ window.visualViewport?.removeEventListener("scroll", fitToViewport);
191
+ },
192
+ setContentHeight() {
193
+ return;
194
+ },
195
+ show() {
196
+ frame.classList.add("open");
197
+ }
198
+ };
199
+ }
200
+ const PLACEMENTS = {
201
+ fullscreen: placeFullscreen,
202
+ sheet: placeBottomSheet
203
+ };
204
+ function createSheetChrome(input) {
205
+ const host = document.createElement("div");
206
+ host.setAttribute("data-vana-connect", "");
207
+ const root = host.attachShadow({ mode: "open" });
208
+ const style = document.createElement("style");
209
+ style.textContent = STYLES;
210
+ const frame = document.createElement("div");
211
+ const sheet = document.createElement("div");
212
+ sheet.className = "sheet";
213
+ sheet.setAttribute("role", "dialog");
214
+ sheet.setAttribute("aria-modal", "true");
215
+ sheet.setAttribute("aria-label", input.ariaLabel);
216
+ const iframe = document.createElement("iframe");
217
+ iframe.setAttribute(
218
+ "sandbox",
219
+ "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
220
+ );
221
+ iframe.setAttribute(
222
+ "allow",
223
+ "otp-credentials; publickey-credentials-get; clipboard-write"
224
+ );
225
+ iframe.setAttribute("title", "Vana");
226
+ iframe.referrerPolicy = "strict-origin-when-cross-origin";
227
+ iframe.src = input.src;
228
+ sheet.append(iframe);
229
+ frame.append(sheet);
230
+ root.append(style, frame);
231
+ input.container.append(host);
232
+ const placement = PLACEMENTS[input.display]({
233
+ frame,
234
+ iframe,
235
+ onClose: input.onClose,
236
+ sheet
237
+ });
238
+ return {
239
+ host,
240
+ iframe,
241
+ remove() {
242
+ placement.remove();
243
+ host.remove();
244
+ },
245
+ setContentHeight: placement.setContentHeight,
246
+ show: placement.show
247
+ };
248
+ }
249
+ export {
250
+ DRAG_CLOSE_PX,
251
+ MAX_SHEET_RATIO,
252
+ RETURN_TAP_GUARD_MS,
253
+ createSheetChrome,
254
+ sheetTopPx
255
+ };
256
+ //# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/connect/chrome.ts"],"sourcesContent":["/**\n * Sheet chrome for the Vana connect sheet, in a Shadow DOM root: a bottom\n * sheet with scrim and drag handle, or a window-filling frame.\n *\n * @remarks\n * Ported unchanged from `@vana-unity/connect-embed`. See the layout notes\n * below.\n *\n * @category Connect\n * @module connect/chrome\n */\n\n// Sheet chrome. Lives in a Shadow DOM root so partner CSS cannot restyle it, and\n// the partner cannot reach the iframe's document (cross-origin) through it.\n// One placement per display:\n//\n// host (partner DOM)\n// └─ #shadow-root\n// ├─ bottom sheet\n// │ ├─ .scrim click closes\n// │ └─ .sheet top = viewport − (handle + content)\n// │ ├─ .handle drag down to close (above the frame, never over it)\n// │ └─ iframe fixed height = cap; the unused part sits below\n// │ the viewport\n// └─ fullscreen\n// └─ .sheet.fullscreen the visible viewport, inside its safe areas\n// └─ iframe fills it; the frame scrolls its own content\n//\n// Nothing overlaps or clips the iframe. IntersectionObserver v2 inside the\n// sheet reports it \"not visible\" when anything covers or clips it, and the\n// consent step waits for real visibility. The part of the frame below the\n// viewport is simply off screen, which does not count as occlusion.\n\nimport type { ConnectDisplay } from \"./protocol\";\n\nconst SHEET_RADIUS_PX = 28;\nconst SHEET_MAX_WIDTH_PX = 480;\nconst HANDLE_STRIP_PX = 20;\nconst INITIAL_CONTENT_PX = 320;\nconst TRANSITION_MS = 280;\nconst SHEET_BACKGROUND = \"#f4f4f4\";\n\n/** Drag the handle this far down to dismiss. */\nexport const DRAG_CLOSE_PX = 120;\n\n/** The sheet never covers more of the viewport than this. */\nexport const MAX_SHEET_RATIO = 0.92;\n\nconst STYLES = `\n:host { all: initial; }\n.scrim {\n position: fixed; inset: 0; z-index: 2147483646;\n background: rgb(0 0 0 / 0.4);\n opacity: 0; transition: opacity ${TRANSITION_MS}ms ease;\n}\n.sheet {\n position: fixed; left: 0; right: 0; z-index: 2147483647;\n margin: 0 auto; max-width: ${SHEET_MAX_WIDTH_PX}px;\n top: 100vh;\n transition: top ${TRANSITION_MS}ms cubic-bezier(0.32, 0.72, 0, 1);\n}\n.sheet.dragging { transition: none; }\n.handle {\n height: ${HANDLE_STRIP_PX}px;\n display: flex; justify-content: center; align-items: center;\n background: ${SHEET_BACKGROUND};\n border-radius: ${SHEET_RADIUS_PX}px ${SHEET_RADIUS_PX}px 0 0;\n touch-action: none; cursor: grab;\n}\n.handle::before {\n content: \"\"; width: 36px; height: 5px; border-radius: 3px;\n background: rgb(0 0 0 / 0.18);\n}\niframe {\n display: block; width: 100%; border: 0;\n background: ${SHEET_BACKGROUND};\n}\n.open .scrim { opacity: 1; }\n.sheet.fullscreen {\n top: 0; height: 100vh; max-width: none;\n display: flex; flex-direction: column; box-sizing: border-box;\n padding: env(safe-area-inset-top) env(safe-area-inset-right)\n env(safe-area-inset-bottom) env(safe-area-inset-left);\n background: ${SHEET_BACKGROUND};\n transition: none;\n}\n.sheet.fullscreen iframe { flex: 1; min-height: 0; }\n@media (prefers-reduced-motion: reduce) {\n .scrim, .sheet { transition: none; }\n}\n`;\n\nexport type CloseReason = \"drag\" | \"scrim\";\n\nexport interface SheetChrome {\n host: HTMLElement;\n iframe: HTMLIFrameElement;\n remove(): void;\n setContentHeight(px: number): void;\n show(): void;\n}\n\nfunction viewportPx(): number {\n return window.visualViewport?.height ?? window.innerHeight;\n}\n\n/**\n * How far the visible viewport has panned inside the layout viewport `fixed`\n * measures from. It is non-zero when an on-screen keyboard pans the page.\n */\nfunction viewportTopPx(): number {\n return window.visualViewport?.offsetTop ?? 0;\n}\n\nfunction capPx(): number {\n return Math.round(viewportPx() * MAX_SHEET_RATIO);\n}\n\n/** Places the sheet so exactly `content` px of the frame show above the fold. */\nfunction placeSheet(sheet: HTMLElement, content: number): void {\n sheet.style.top = `${Math.round(viewportTopPx() + sheetTopPx(viewportPx(), content))}px`;\n}\n\n/**\n * Where the sheet's top edge goes so `content` px of the frame show, capped\n * at {@link MAX_SHEET_RATIO} of the viewport.\n *\n * @param viewport - Viewport height in px.\n * @param content - Content height the sheet reported, in px.\n * @returns The sheet's `top`, in whole px.\n */\nexport function sheetTopPx(viewport: number, content: number): number {\n const cap = Math.round(viewport * MAX_SHEET_RATIO);\n const visible = Math.min(content, cap - HANDLE_STRIP_PX);\n // Whole pixels: a fractional offset blurs the framed text.\n return Math.round(viewport - HANDLE_STRIP_PX - visible);\n}\n\n/** Scrim taps this soon after the page returns to view are ignored. */\nexport const RETURN_TAP_GUARD_MS = 500;\n\n/**\n * Return-to-view handling, isolated so it can change on its own.\n *\n * Root cause of the spike's \"blank sheet\" (scratchpad blank-render notes): not\n * a paint failure. Returning from the Google popup tab, a stray tap landed on\n * the scrim and silently closed the sheet; a headless-Chrome harness artefact\n * looked similar. So: taps on the scrim right after the page regains\n * visibility or focus are ignored, heights reported while hidden are dropped,\n * and the last good height is re-applied on return. The iframe keeps a fixed\n * viewport (the cap) and only the sheet's position animates, so measuring\n * content inside the frame never resizes the frame itself.\n */\nfunction installReturnGuard(\n sheet: HTMLElement,\n iframe: HTMLIFrameElement,\n state: { lastHeight: number | null; returnedAt: number; shown: boolean },\n): () => void {\n const fitIframe = () => {\n iframe.style.height = `${capPx() - HANDLE_STRIP_PX}px`;\n if (state.shown) {\n placeSheet(sheet, state.lastHeight ?? INITIAL_CONTENT_PX);\n }\n };\n const onReturn = () => {\n if (document.visibilityState !== \"visible\") {\n return;\n }\n state.returnedAt = performance.now();\n fitIframe();\n };\n fitIframe();\n window.addEventListener(\"resize\", fitIframe);\n window.visualViewport?.addEventListener(\"resize\", fitIframe);\n window.visualViewport?.addEventListener(\"scroll\", fitIframe);\n document.addEventListener(\"visibilitychange\", onReturn);\n window.addEventListener(\"focus\", onReturn);\n return () => {\n window.removeEventListener(\"resize\", fitIframe);\n window.visualViewport?.removeEventListener(\"resize\", fitIframe);\n window.visualViewport?.removeEventListener(\"scroll\", fitIframe);\n document.removeEventListener(\"visibilitychange\", onReturn);\n window.removeEventListener(\"focus\", onReturn);\n sheet.style.transform = \"\";\n };\n}\n\nfunction installDragToClose(\n sheet: HTMLElement,\n handle: HTMLElement,\n onClose: (reason: CloseReason) => void,\n): void {\n let startY: number | null = null;\n handle.addEventListener(\"pointerdown\", (event) => {\n startY = event.clientY;\n handle.setPointerCapture?.(event.pointerId);\n sheet.classList.add(\"dragging\");\n });\n handle.addEventListener(\"pointermove\", (event) => {\n if (startY === null) {\n return;\n }\n const dy = Math.max(0, event.clientY - startY);\n sheet.style.transform = `translateY(${dy}px)`;\n });\n const end = (event: PointerEvent) => {\n if (startY === null) {\n return;\n }\n const dy = event.clientY - startY;\n startY = null;\n sheet.classList.remove(\"dragging\");\n sheet.style.transform = \"\";\n if (dy > DRAG_CLOSE_PX) {\n onClose(\"drag\");\n }\n };\n handle.addEventListener(\"pointerup\", end);\n handle.addEventListener(\"pointercancel\", end);\n}\n\n/** What differs between displays; the frame, focus and messages do not. */\ninterface Placement {\n remove(): void;\n setContentHeight(px: number): void;\n show(): void;\n}\n\ninterface PlacementParts {\n frame: HTMLElement;\n iframe: HTMLIFrameElement;\n onClose: (reason: CloseReason) => void;\n sheet: HTMLElement;\n}\n\nfunction placeBottomSheet({\n frame,\n iframe,\n onClose,\n sheet,\n}: PlacementParts): Placement {\n const returnState = {\n lastHeight: null as number | null,\n returnedAt: 0,\n shown: false,\n };\n const scrim = document.createElement(\"div\");\n scrim.className = \"scrim\";\n scrim.setAttribute(\"data-slot\", \"scrim\");\n scrim.addEventListener(\"click\", () => {\n if (performance.now() - returnState.returnedAt < RETURN_TAP_GUARD_MS) {\n return;\n }\n onClose(\"scrim\");\n });\n\n const handle = document.createElement(\"div\");\n handle.className = \"handle\";\n handle.setAttribute(\"aria-hidden\", \"true\");\n handle.setAttribute(\"data-slot\", \"handle\");\n\n sheet.prepend(handle);\n frame.prepend(scrim);\n\n const removeReturnGuard = installReturnGuard(sheet, iframe, returnState);\n installDragToClose(sheet, handle, onClose);\n\n return {\n remove: removeReturnGuard,\n setContentHeight(px) {\n // A hidden page can report a throttled frame's height; keep the last good one.\n if (document.visibilityState !== \"visible\") {\n return;\n }\n returnState.lastHeight = px;\n if (returnState.shown) {\n placeSheet(sheet, px);\n }\n },\n show() {\n // Next frame, so the closed state paints first and the sheet slides in.\n requestAnimationFrame(() => {\n returnState.shown = true;\n frame.classList.add(\"open\");\n placeSheet(sheet, returnState.lastHeight ?? INITIAL_CONTENT_PX);\n });\n },\n };\n}\n\n/**\n * Fills the visible viewport, so an on-screen keyboard shortens the frame and\n * its content scrolls above it. The window holds nothing else, so\n * there is no scrim to tap and no handle to drag: the frame's own close\n * control and Escape close it. Content height does not size anything.\n */\nfunction placeFullscreen({ frame, sheet }: PlacementParts): Placement {\n sheet.classList.add(\"fullscreen\");\n const fitToViewport = () => {\n sheet.style.top = `${Math.round(viewportTopPx())}px`;\n sheet.style.height = `${Math.round(viewportPx())}px`;\n };\n fitToViewport();\n window.addEventListener(\"resize\", fitToViewport);\n window.visualViewport?.addEventListener(\"resize\", fitToViewport);\n window.visualViewport?.addEventListener(\"scroll\", fitToViewport);\n return {\n remove() {\n window.removeEventListener(\"resize\", fitToViewport);\n window.visualViewport?.removeEventListener(\"resize\", fitToViewport);\n window.visualViewport?.removeEventListener(\"scroll\", fitToViewport);\n },\n setContentHeight() {\n return;\n },\n show() {\n frame.classList.add(\"open\");\n },\n };\n}\n\nconst PLACEMENTS: Record<ConnectDisplay, (parts: PlacementParts) => Placement> =\n {\n fullscreen: placeFullscreen,\n sheet: placeBottomSheet,\n };\n\nexport function createSheetChrome(input: {\n ariaLabel: string;\n container: HTMLElement;\n display: ConnectDisplay;\n onClose: (reason: CloseReason) => void;\n src: string;\n}): SheetChrome {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-vana-connect\", \"\");\n const root = host.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = STYLES;\n\n const frame = document.createElement(\"div\");\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"sheet\";\n sheet.setAttribute(\"role\", \"dialog\");\n sheet.setAttribute(\"aria-modal\", \"true\");\n sheet.setAttribute(\"aria-label\", input.ariaLabel);\n\n const iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\n \"sandbox\",\n \"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\",\n );\n iframe.setAttribute(\n \"allow\",\n \"otp-credentials; publickey-credentials-get; clipboard-write\",\n );\n iframe.setAttribute(\"title\", \"Vana\");\n iframe.referrerPolicy = \"strict-origin-when-cross-origin\";\n iframe.src = input.src;\n\n sheet.append(iframe);\n frame.append(sheet);\n root.append(style, frame);\n input.container.append(host);\n\n const placement = PLACEMENTS[input.display]({\n frame,\n iframe,\n onClose: input.onClose,\n sheet,\n });\n\n return {\n host,\n iframe,\n remove() {\n placement.remove();\n host.remove();\n },\n setContentHeight: placement.setContentHeight,\n show: placement.show,\n };\n}\n"],"mappings":"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","names":[]}