@vvfx/sdk 0.2.7 → 0.2.9

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 (108) hide show
  1. package/dist/config.js +231 -0
  2. package/dist/exporter/config.js +18 -0
  3. package/dist/exporter/const.js +71 -0
  4. package/dist/exporter/export-media.js +878 -0
  5. package/dist/exporter/exporter.js +121 -0
  6. package/dist/exporter/index.js +1 -0
  7. package/dist/exporter/types.js +1 -0
  8. package/dist/exporter/utils.js +190 -0
  9. package/dist/exporter/wav-audio.js +50 -0
  10. package/dist/gesture-handler/gizmo/adsorption-gizmo.js +227 -0
  11. package/dist/gesture-handler/gizmo/control-gizmo.js +149 -0
  12. package/dist/gesture-handler/gizmo/gizmo.js +17 -0
  13. package/dist/gesture-handler/gizmo/icon-gizmo.js +189 -0
  14. package/dist/gesture-handler/gizmo/index.js +6 -0
  15. package/dist/gesture-handler/gizmo/item-create-gizmo.js +256 -0
  16. package/dist/gesture-handler/gizmo/loading-gizmo.js +320 -0
  17. package/dist/gesture-handler/gizmo/mask-gizmo.js +648 -0
  18. package/dist/gesture-handler/gizmo/picture-cut-gizmo.js +550 -0
  19. package/dist/gesture-handler/gizmo/picture-expand-gizmo.js +491 -0
  20. package/dist/gesture-handler/gizmo/preference-gizmo.js +148 -0
  21. package/dist/gesture-handler/gizmo/selector-gizmo.d.ts +1 -1
  22. package/dist/gesture-handler/gizmo/selector-gizmo.js +468 -0
  23. package/dist/gesture-handler/gizmo/sprite-text-edit-gizmo.js +247 -0
  24. package/dist/gesture-handler/gizmo/text-gizmo.js +1033 -0
  25. package/dist/gesture-handler/gizmo/transform-gizmo.js +1199 -0
  26. package/dist/gesture-handler/gizmo/type.js +18 -0
  27. package/dist/gesture-handler/index.js +3 -0
  28. package/dist/gesture-handler/src/gesture-handler.js +770 -0
  29. package/dist/gesture-handler/src/icons.js +24 -0
  30. package/dist/gesture-handler/utils.js +40 -0
  31. package/dist/html-overlay/anchor-navigation-runtime.js +27 -0
  32. package/dist/html-overlay/auto-height-runtime.js +132 -0
  33. package/dist/html-overlay/document-runtime.js +71 -0
  34. package/dist/html-overlay/document-transform.js +96 -0
  35. package/dist/html-overlay/dom-order.js +8 -0
  36. package/dist/html-overlay/host-message-runtime.js +24 -0
  37. package/dist/html-overlay/html-capture-runtime.js +282 -0
  38. package/dist/html-overlay/html-viewport-style.js +10 -0
  39. package/dist/html-overlay/index.js +1 -0
  40. package/dist/html-overlay/manager.js +1663 -0
  41. package/dist/html-overlay/mime-utils.js +21 -0
  42. package/dist/html-overlay/overlay-transform.js +57 -0
  43. package/dist/html-overlay/path-utils.js +35 -0
  44. package/dist/html-overlay/shell-runtime.js +12 -0
  45. package/dist/index.cjs +1 -7
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.js +13 -75317
  48. package/dist/index.mjs +75311 -0
  49. package/dist/index.mjs.map +1 -0
  50. package/dist/layer-order.js +8 -0
  51. package/dist/math/box2.js +396 -0
  52. package/dist/math/circle.js +131 -0
  53. package/dist/math/euler.js +35 -0
  54. package/dist/math/index.js +13 -0
  55. package/dist/math/line2.js +153 -0
  56. package/dist/math/line3.js +36 -0
  57. package/dist/math/matrix4.js +207 -0
  58. package/dist/math/plane.js +52 -0
  59. package/dist/math/quaternion.js +3 -0
  60. package/dist/math/ray-caster.js +25 -0
  61. package/dist/math/ray.js +47 -0
  62. package/dist/math/type.js +1 -0
  63. package/dist/math/utils.js +77 -0
  64. package/dist/math/vector2.js +70 -0
  65. package/dist/math/vector3.js +60 -0
  66. package/dist/screen-shot/index.js +1 -0
  67. package/dist/screen-shot/screen-shot.js +113 -0
  68. package/dist/sdk-item/base-item.js +160 -0
  69. package/dist/sdk-item/card-item.js +131 -0
  70. package/dist/sdk-item/effects-item.js +144 -0
  71. package/dist/sdk-item/frame-item.js +217 -0
  72. package/dist/sdk-item/generator-item.js +188 -0
  73. package/dist/sdk-item/group-item.js +116 -0
  74. package/dist/sdk-item/index.js +39 -0
  75. package/dist/sdk-item/sprite-item.js +147 -0
  76. package/dist/sdk-item/text-item.js +223 -0
  77. package/dist/sdk-item/types.d.ts +1 -11
  78. package/dist/sdk-item/types.js +15 -0
  79. package/dist/sdk-item/video-item.js +174 -0
  80. package/dist/sdk.js +1903 -0
  81. package/dist/service/UndoRedo.js +79 -0
  82. package/dist/shared/index.d.ts +1 -0
  83. package/dist/shared/index.js +2 -0
  84. package/dist/shared/player.d.ts +1 -1
  85. package/dist/shared/player.js +9 -0
  86. package/dist/shared/spec.d.ts +1 -0
  87. package/dist/shared/spec.js +1 -0
  88. package/dist/size-adapte/index.js +1 -0
  89. package/dist/size-adapte/size-adapt.js +395 -0
  90. package/dist/types.d.ts +5 -1
  91. package/dist/types.js +17 -0
  92. package/dist/utils/background-manager.js +189 -0
  93. package/dist/utils/common-utils.js +183 -0
  94. package/dist/utils/index.js +8 -0
  95. package/dist/utils/interaction-utils.js +205 -0
  96. package/dist/utils/json-data-utils.js +1529 -0
  97. package/dist/utils/layout-utils.js +1217 -0
  98. package/dist/utils/page-data-utils.js +5397 -0
  99. package/dist/utils/player-data-utils.js +48 -0
  100. package/dist/utils/types.js +8 -0
  101. package/dist/wireframe/common/box.js +18 -0
  102. package/dist/wireframe/common/dashed-line.js +21 -0
  103. package/dist/wireframe/common/line.js +17 -0
  104. package/dist/wireframe/common/pixi-ext.js +1 -0
  105. package/dist/wireframe/common/pixi.js +31 -0
  106. package/dist/wireframe/index.js +4 -0
  107. package/package.json +10 -3
  108. package/dist/index.js.map +0 -1
@@ -0,0 +1,24 @@
1
+ const assetsPrefix = 'https://mdn.alipayobjects.com/rms/uri/file/as/0.0.6';
2
+ export const cursorMap = {
3
+ normal: { type: 'preset', content: 'default' },
4
+ rotation: { type: 'svg', content: 'default', url: `${assetsPrefix}/icons/cursor-rotate-32.svg` },
5
+ circle: { type: 'svg', content: 'default', url: `${assetsPrefix}/icons/cursor-rotate-32.svg` },
6
+ scale: { type: 'svg', content: 'default', url: `${assetsPrefix}/icons/cursor-scale-32.svg` },
7
+ hand: { type: 'preset', content: 'grab' },
8
+ pointer: { type: 'preset', content: 'pointer' },
9
+ 'text-create': { type: 'preset', content: 'text' },
10
+ 'active-hand': { type: 'preset', content: 'grabbing' },
11
+ 'frame-create': { type: 'preset', content: 'crosshair' },
12
+ 'box-select': { type: 'preset', content: 'crosshair' },
13
+ 'text-rotation': { type: 'svg', content: 'default', url: 'https://mdn.alipayobjects.com/huamei_ixsp8m/afts/img/A*R722QoloC44AAAAAKkAAAAgAev-aAQ/original' },
14
+ };
15
+ export async function loadCursorIcons() {
16
+ const cursorIconLoaders = Object.entries(cursorMap)
17
+ .filter((entry) => Boolean(entry[1].url))
18
+ .map(([key, value]) => fetch(value.url)
19
+ .then(res => res.text())
20
+ .then(text => cursorMap[key] = { ...value, content: text }));
21
+ await Promise.all(cursorIconLoaders).catch((e) => {
22
+ console.error(e);
23
+ });
24
+ }
@@ -0,0 +1,40 @@
1
+ import { Text } from '../wireframe';
2
+ // 截断逻辑
3
+ export function processText(textContent, style, width, maxLines) {
4
+ const lines = [];
5
+ let currentLine = '';
6
+ const chars = textContent.split('');
7
+ const tempText = new Text('', style);
8
+ for (let i = 0; i < chars.length; i++) {
9
+ const char = chars[i];
10
+ const testLine = currentLine + char;
11
+ tempText.text = testLine;
12
+ // 宽度检查
13
+ if (tempText.width > width) {
14
+ lines.push(currentLine);
15
+ // 行数检查
16
+ if (lines.length >= maxLines) {
17
+ // 最后一行添加...
18
+ const lastLine = lines[maxLines - 1];
19
+ if (lastLine.length > 2) {
20
+ lines[maxLines - 1] = lastLine.slice(0, -2) + '...';
21
+ }
22
+ else {
23
+ lines[maxLines - 1] = '...';
24
+ }
25
+ break;
26
+ }
27
+ currentLine = char;
28
+ }
29
+ else {
30
+ currentLine = testLine;
31
+ }
32
+ // 处理最后一行
33
+ if (i === chars.length - 1 && currentLine) {
34
+ lines.push(currentLine);
35
+ }
36
+ }
37
+ // 限制行数
38
+ const displayLines = lines.slice(0, maxLines);
39
+ return displayLines.join('\n');
40
+ }
@@ -0,0 +1,27 @@
1
+ export function withCardHTMLAnchorNavigationGuard(html, allowAnchorNavigation) {
2
+ if (allowAnchorNavigation === true) {
3
+ return html;
4
+ }
5
+ const script = `
6
+ <script data-vvfx-card-html-anchor-navigation-guard>
7
+ (() => {
8
+ document.addEventListener('click', event => {
9
+ const target = event.target;
10
+ if (!(target instanceof Element)) {
11
+ return;
12
+ }
13
+
14
+ const anchor = target.closest('a[href]');
15
+ if (!anchor) {
16
+ return;
17
+ }
18
+
19
+ event.preventDefault();
20
+ }, true);
21
+ })();
22
+ </script>`;
23
+ if (/<\/body>/i.test(html)) {
24
+ return html.replace(/<\/body>/i, `${script}</body>`);
25
+ }
26
+ return `${html}${script}`;
27
+ }
@@ -0,0 +1,132 @@
1
+ export function createAutoHeightMessageListener(iframe, options) {
2
+ if (!options?.autoHeightId || !options.onAutoHeight) {
3
+ return () => {
4
+ // pass
5
+ };
6
+ }
7
+ const handleMessage = (event) => {
8
+ if (event.source !== iframe.contentWindow) {
9
+ return;
10
+ }
11
+ if (isCardHTMLAutoHeightMessage(event.data, options.autoHeightId)) {
12
+ options.onAutoHeight?.(event.data.height);
13
+ return;
14
+ }
15
+ const phase = getCardHTMLStreamRuntimePhase(event.data);
16
+ if (phase) {
17
+ options.onStreamRuntimePhase?.(phase);
18
+ }
19
+ };
20
+ window.addEventListener('message', handleMessage);
21
+ return () => {
22
+ window.removeEventListener('message', handleMessage);
23
+ };
24
+ }
25
+ export function getCardHTMLStreamRuntimePhase(data) {
26
+ if (typeof data !== 'object' || data === null) {
27
+ return undefined;
28
+ }
29
+ const message = data;
30
+ if (message.type !== 'huamei:html-stream-runtime-status' || typeof message.payload !== 'object' || message.payload === null) {
31
+ return undefined;
32
+ }
33
+ const phase = message.payload.phase;
34
+ return ['parsing', 'draining', 'complete', 'finalizing', 'disposed', 'error'].includes(String(phase))
35
+ ? phase
36
+ : undefined;
37
+ }
38
+ export function isCardHTMLAutoHeightMessage(data, id) {
39
+ if (typeof data !== 'object' || data === null) {
40
+ return false;
41
+ }
42
+ const message = data;
43
+ return message.source === 'vvfx-card-html-auto-height' &&
44
+ message.id === id &&
45
+ typeof message.height === 'number';
46
+ }
47
+ export function requestCardHTMLAutoHeight(iframe, id) {
48
+ iframe.contentWindow?.postMessage({
49
+ source: 'vvfx-card-html-auto-height-request',
50
+ id,
51
+ }, '*');
52
+ }
53
+ export function withCardHTMLAutoHeightBridge(html, id) {
54
+ if (!id) {
55
+ return html;
56
+ }
57
+ const script = `
58
+ <script data-vvfx-card-html-auto-height>
59
+ (() => {
60
+ const id = ${JSON.stringify(id)};
61
+ let frame = 0;
62
+ const getNodeHeight = (node) => {
63
+ if (node.nodeType === Node.ELEMENT_NODE) {
64
+ if (window.getComputedStyle(node).position === 'fixed') {
65
+ return 0;
66
+ }
67
+ const rect = node.getBoundingClientRect();
68
+ return rect.bottom + window.scrollY;
69
+ }
70
+ if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
71
+ const range = document.createRange();
72
+ range.selectNodeContents(node);
73
+ const rects = Array.from(range.getClientRects());
74
+ range.detach();
75
+ return rects.reduce((height, rect) => Math.max(height, rect.bottom + window.scrollY), 0);
76
+ }
77
+
78
+ return 0;
79
+ };
80
+ const getContentHeight = (element) => {
81
+ if (!element) return 0;
82
+ return Array.from(element.childNodes).reduce((height, child) => {
83
+ return Math.max(height, getNodeHeight(child));
84
+ }, 0);
85
+ };
86
+ const getHeight = () => {
87
+ const body = document.body;
88
+ const root = document.documentElement;
89
+ const contentHeight = getContentHeight(body);
90
+ const scrollHeight = Math.max(root.scrollHeight, body ? body.scrollHeight : 0);
91
+ return Math.ceil(contentHeight > 0 ? contentHeight : scrollHeight);
92
+ };
93
+ const postHeight = () => {
94
+ frame = 0;
95
+ window.parent.postMessage({
96
+ source: 'vvfx-card-html-auto-height',
97
+ id,
98
+ height: getHeight(),
99
+ }, '*');
100
+ };
101
+ const schedule = () => {
102
+ if (frame) return;
103
+ frame = requestAnimationFrame(postHeight);
104
+ };
105
+ window.addEventListener('message', (event) => {
106
+ const message = event.data;
107
+ if (!message || message.source !== 'vvfx-card-html-auto-height-request' || message.id !== id) {
108
+ return;
109
+ }
110
+
111
+ schedule();
112
+ });
113
+ window.addEventListener('load', schedule);
114
+ window.addEventListener('resize', schedule);
115
+ new ResizeObserver(schedule).observe(document.documentElement);
116
+ if (document.body) {
117
+ new ResizeObserver(schedule).observe(document.body);
118
+ new MutationObserver(schedule).observe(document.body, {
119
+ attributes: true,
120
+ childList: true,
121
+ subtree: true,
122
+ characterData: true,
123
+ });
124
+ }
125
+ schedule();
126
+ })();
127
+ </script>`;
128
+ if (/<\/body>/i.test(html)) {
129
+ return html.replace(/<\/body>/i, `${script}</body>`);
130
+ }
131
+ return `${html}${script}`;
132
+ }
@@ -0,0 +1,71 @@
1
+ import { createAutoHeightMessageListener, withCardHTMLAutoHeightBridge } from './auto-height-runtime';
2
+ import { createDocumentFileUrlFactory, normalizeDocumentFiles, transformHTMLDocument } from './document-transform';
3
+ import { createHostMessageListener } from './host-message-runtime';
4
+ import { normalizePath } from './path-utils';
5
+ import { withCardHTMLViewportStyle } from './html-viewport-style';
6
+ import { withCardHTMLAnchorNavigationGuard } from './anchor-navigation-runtime';
7
+ export function renderDocumentContent(overlay, content, options) {
8
+ const iframe = document.createElement('iframe');
9
+ const files = normalizeDocumentFiles(content.files);
10
+ const entry = normalizePath(content.entry);
11
+ const createFileUrl = createDocumentFileUrlFactory(files);
12
+ const messageCleanup = createHostMessageListener(iframe, content);
13
+ const autoHeightMessageCleanup = createAutoHeightMessageListener(iframe, options);
14
+ iframe.style.width = '100%';
15
+ iframe.style.height = '100%';
16
+ iframe.style.border = '0';
17
+ iframe.style.display = 'block';
18
+ if (content.sandbox === true || content.sandbox === undefined) {
19
+ iframe.setAttribute('sandbox', 'allow-scripts');
20
+ }
21
+ else if (typeof content.sandbox === 'string') {
22
+ iframe.setAttribute('sandbox', content.sandbox);
23
+ }
24
+ const entryHtml = files.get(entry) ?? '';
25
+ iframe.srcdoc = withCardHTMLAutoHeightBridge(withCardHTMLAnchorNavigationGuard(withCardHTMLViewportStyle(transformHTMLDocument(entry, entryHtml, createFileUrl)), content.allowAnchorNavigation), options?.autoHeightId);
26
+ overlay.appendChild(iframe);
27
+ return () => {
28
+ messageCleanup();
29
+ autoHeightMessageCleanup();
30
+ iframe.remove();
31
+ };
32
+ }
33
+ export function renderDOMContentInIframe(overlay, render, cleanup) {
34
+ const iframe = document.createElement('iframe');
35
+ let mount;
36
+ let disposed = false;
37
+ iframe.style.width = '100%';
38
+ iframe.style.height = '100%';
39
+ iframe.style.border = '0';
40
+ iframe.style.display = 'block';
41
+ const handleLoad = () => {
42
+ if (disposed) {
43
+ return;
44
+ }
45
+ if (mount) {
46
+ cleanup?.(mount);
47
+ mount = undefined;
48
+ }
49
+ const doc = iframe.contentDocument;
50
+ if (!doc?.body) {
51
+ return;
52
+ }
53
+ mount = doc.createElement('div');
54
+ mount.style.width = '100%';
55
+ mount.style.height = '100%';
56
+ doc.body.appendChild(mount);
57
+ render(mount);
58
+ };
59
+ iframe.addEventListener('load', handleLoad);
60
+ overlay.appendChild(iframe);
61
+ iframe.srcdoc = withCardHTMLViewportStyle('');
62
+ return () => {
63
+ disposed = true;
64
+ iframe.removeEventListener('load', handleLoad);
65
+ if (mount) {
66
+ cleanup?.(mount);
67
+ mount = undefined;
68
+ }
69
+ iframe.remove();
70
+ };
71
+ }
@@ -0,0 +1,96 @@
1
+ import { createDataUrl, getMimeType } from './mime-utils';
2
+ import { normalizePath, resolvePath, shouldRewriteResource, splitResourcePath } from './path-utils';
3
+ export function normalizeDocumentFiles(files) {
4
+ return new Map(Object.entries(files).map(([path, source]) => [normalizePath(path), source]));
5
+ }
6
+ export function createDocumentFileUrlFactory(files) {
7
+ const urlCache = new Map();
8
+ const resolving = new Set();
9
+ const createFileUrl = (rawPath, fromPath) => {
10
+ const { path, suffix } = splitResourcePath(rawPath);
11
+ const resolvedPath = resolvePath(path, fromPath);
12
+ if (!files.has(resolvedPath)) {
13
+ return rawPath;
14
+ }
15
+ let url = urlCache.get(resolvedPath);
16
+ if (!url) {
17
+ const source = files.get(resolvedPath) ?? '';
18
+ const transformedSource = resolving.has(resolvedPath)
19
+ ? source
20
+ : transformDocumentFile(resolvedPath, source, createFileUrl, resolving);
21
+ url = createDataUrl(transformedSource, getMimeType(resolvedPath));
22
+ urlCache.set(resolvedPath, url);
23
+ }
24
+ return `${url}${suffix}`;
25
+ };
26
+ return createFileUrl;
27
+ }
28
+ export function transformDocumentFile(path, source, createFileUrl, resolving = new Set()) {
29
+ resolving.add(path);
30
+ try {
31
+ if (path.endsWith('.css')) {
32
+ return transformCSSDocument(path, source, createFileUrl);
33
+ }
34
+ if (path.endsWith('.js') || path.endsWith('.mjs')) {
35
+ return transformJSDocument(path, source, createFileUrl);
36
+ }
37
+ if (path.endsWith('.html') || path.endsWith('.htm')) {
38
+ return transformHTMLDocument(path, source, createFileUrl);
39
+ }
40
+ return source;
41
+ }
42
+ finally {
43
+ resolving.delete(path);
44
+ }
45
+ }
46
+ export function transformHTMLDocument(path, source, createFileUrl) {
47
+ return source
48
+ .replace(/\b(src|href)=("|')([^"']+)\2/g, (match, attr, quote, url) => {
49
+ if (!shouldRewriteResource(url)) {
50
+ return match;
51
+ }
52
+ return `${attr}=${quote}${createFileUrl(url, path)}${quote}`;
53
+ })
54
+ .replace(/<style\b([^>]*)>([\s\S]*?)<\/style>/gi, (_match, attrs, css) => {
55
+ return `<style${attrs}>${transformCSSDocument(path, css, createFileUrl)}</style>`;
56
+ })
57
+ .replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/gi, (_match, attrs, js) => {
58
+ return `<script${attrs}>${transformJSDocument(path, js, createFileUrl)}</script>`;
59
+ });
60
+ }
61
+ export function transformCSSDocument(path, source, createFileUrl) {
62
+ return source
63
+ .replace(/url\(\s*(['"]?)([^'")]+)\1\s*\)/g, (match, quote, url) => {
64
+ if (!shouldRewriteResource(url)) {
65
+ return match;
66
+ }
67
+ return `url(${quote}${createFileUrl(url, path)}${quote})`;
68
+ })
69
+ .replace(/@import\s+(?:url\()?(['"])([^'"]+)\1\)?/g, (match, quote, url) => {
70
+ if (!shouldRewriteResource(url)) {
71
+ return match;
72
+ }
73
+ return `@import ${quote}${createFileUrl(url, path)}${quote}`;
74
+ });
75
+ }
76
+ export function transformJSDocument(path, source, createFileUrl) {
77
+ return source
78
+ .replace(/\bfrom\s+(['"])([^'"]+)\1/g, (match, quote, url) => {
79
+ if (!shouldRewriteResource(url)) {
80
+ return match;
81
+ }
82
+ return `from ${quote}${createFileUrl(url, path)}${quote}`;
83
+ })
84
+ .replace(/\bimport\s+(['"])([^'"]+)\1/g, (match, quote, url) => {
85
+ if (!shouldRewriteResource(url)) {
86
+ return match;
87
+ }
88
+ return `import ${quote}${createFileUrl(url, path)}${quote}`;
89
+ })
90
+ .replace(/\bimport\(\s*(['"])([^'"]+)\1\s*\)/g, (match, quote, url) => {
91
+ if (!shouldRewriteResource(url)) {
92
+ return match;
93
+ }
94
+ return `import(${quote}${createFileUrl(url, path)}${quote})`;
95
+ });
96
+ }
@@ -0,0 +1,8 @@
1
+ export function syncElementStackOrder(_container, elements) {
2
+ elements.forEach((element, index) => {
3
+ const zIndex = `${index + 1}`;
4
+ if (element.style.zIndex !== zIndex) {
5
+ element.style.zIndex = zIndex;
6
+ }
7
+ });
8
+ }
@@ -0,0 +1,24 @@
1
+ export function createHostMessageListener(iframe, content) {
2
+ if (!content.allowHostMessaging || !content.onMessage) {
3
+ return () => {
4
+ // pass
5
+ };
6
+ }
7
+ const handleMessage = (event) => {
8
+ if (event.source !== iframe.contentWindow || !isCardHTMLDocumentMessage(event.data)) {
9
+ return;
10
+ }
11
+ content.onMessage?.(event.data);
12
+ };
13
+ window.addEventListener('message', handleMessage);
14
+ return () => {
15
+ window.removeEventListener('message', handleMessage);
16
+ };
17
+ }
18
+ export function isCardHTMLDocumentMessage(data) {
19
+ if (typeof data !== 'object' || data === null) {
20
+ return false;
21
+ }
22
+ const message = data;
23
+ return message.source === 'vvfx-card-html' && typeof message.type === 'string';
24
+ }