diff --git a/examples/index.html b/examples/index.html
new file mode 100644
index 0000000..1c13529
--- /dev/null
+++ b/examples/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Interactive Video React Wrapper Example
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/index.tsx b/examples/index.tsx
new file mode 100644
index 0000000..c0a3afb
--- /dev/null
+++ b/examples/index.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import { InteractiveVideo } from '../dist/index';
+
+const App = () => {
+ return (
+
+
Interactive Video React Wrapper Example
+
+ {
+ console.log('Analytics Event:', event, payload);
+ }}
+ />
+
+
+ );
+};
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+);
\ No newline at end of file
diff --git a/package.json b/package.json
index f4b0d71..357b9c9 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"name": "@interactive-video-labs/react",
"version": "0.0.1",
"description": "Thin React wrapper for the @interactive-video-labs/core engine. Enables cue-based interactive video playback in React apps.",
+ "type": "module",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
@@ -16,11 +17,13 @@
"dist"
],
"scripts": {
- "build": "tsup src/index.ts --format esm,cjs --dts",
- "dev": "tsup src/index.ts --watch --format esm,cjs",
+ "build": "tsup src/index.tsx --format esm,cjs --dts",
+ "dev": "tsup src/index.tsx --watch --format esm,cjs",
"test": "vitest run",
"clean": "rm -rf dist",
- "prepare": "pnpm build"
+ "prepare": "pnpm build",
+ "serve-examples": "pnpm build-examples && http-server examples -c-1",
+ "build-examples": "esbuild examples/index.tsx --bundle --outfile=examples/dist/index.js --jsx=automatic"
},
"keywords": [
"interactive-video",
@@ -44,14 +47,20 @@
},
"packageManager": "pnpm@10.13.1",
"peerDependencies": {
+ "@interactive-video-labs/core": "^0.1.2",
"react": ">=17.0.0 || >=18.0.0",
- "react-dom": ">=17.0.0 || >=18.0.0",
- "@interactive-video-labs/core": "^0.1.2"
+ "react-dom": ">=17.0.0 || >=18.0.0"
},
"dependencies": {
"jsdom": "^26.1.0",
"tsup": "^8.5.0",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
+ },
+ "devDependencies": {
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.0.0",
+ "@types/react": "^19.1.9",
+ "http-server": "^14.1.1"
}
}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 92b32a5..404b7e5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,12 +29,40 @@ importers:
vitest:
specifier: ^3.2.4
version: 3.2.4(jsdom@26.1.0)
+ devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^6.6.4
+ version: 6.6.4
+ '@testing-library/react':
+ specifier: ^16.0.0
+ version: 16.3.0(@testing-library/dom@10.4.1)(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@types/react':
+ specifier: ^19.1.9
+ version: 19.1.9
+ http-server:
+ specifier: ^14.1.1
+ version: 14.1.1
packages:
+ '@adobe/css-tools@4.4.3':
+ resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==}
+
'@asamuzakjp/css-color@3.2.0':
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/runtime@7.28.2':
+ resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
+ engines: {node: '>=6.9.0'}
+
'@csstools/color-helpers@5.0.2':
resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
engines: {node: '>=18'}
@@ -343,6 +371,32 @@ packages:
cpu: [x64]
os: [win32]
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.6.4':
+ resolution: {integrity: sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.0':
+ resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
'@types/chai@5.2.2':
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
@@ -352,6 +406,9 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+ '@types/react@19.1.9':
+ resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==}
+
'@vitest/expect@3.2.4':
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
@@ -402,6 +459,10 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
@@ -409,13 +470,23 @@ packages:
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
+ async@3.2.6:
+ resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ basic-auth@2.0.1:
+ resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==}
+ engines: {node: '>= 0.8'}
+
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
@@ -429,10 +500,22 @@ packages:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
chai@5.2.1:
resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==}
engines: {node: '>=18'}
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
check-error@2.1.1:
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
engines: {node: '>= 16'}
@@ -459,14 +542,24 @@ packages:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
engines: {node: ^14.18.0 || >=16.10.0}
+ corser@2.0.1:
+ resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==}
+ engines: {node: '>= 0.4.0'}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
cssstyle@4.6.0:
resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
engines: {node: '>=18'}
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
@@ -487,6 +580,20 @@ packages:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -500,9 +607,21 @@ packages:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
esbuild@0.25.8:
resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
engines: {node: '>=18'}
@@ -511,6 +630,9 @@ packages:
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+ eventemitter3@4.0.7:
+ resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+
expect-type@1.2.2:
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
engines: {node: '>=12.0.0'}
@@ -526,6 +648,15 @@ packages:
fix-dts-default-cjs-exports@1.0.1:
resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==}
+ follow-redirects@1.15.11:
+ resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
@@ -535,10 +666,45 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
hasBin: true
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
+ html-encoding-sniffer@3.0.0:
+ resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
+ engines: {node: '>=12'}
+
html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
@@ -547,6 +713,15 @@ packages:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
+ http-proxy@1.18.1:
+ resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
+ engines: {node: '>=8.0.0'}
+
+ http-server@14.1.1:
+ resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==}
+ engines: {node: '>=12'}
+ hasBin: true
+
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
@@ -555,6 +730,10 @@ packages:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
@@ -572,6 +751,9 @@ packages:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
@@ -598,19 +780,42 @@ packages:
lodash.sortby@4.7.0:
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
loupe@3.2.0:
resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ mime@1.6.0:
+ resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -636,6 +841,14 @@ packages:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ opener@1.5.2:
+ resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
+ hasBin: true
+
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
@@ -671,6 +884,10 @@ packages:
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+ portfinder@1.0.37:
+ resolution: {integrity: sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==}
+ engines: {node: '>= 10.12'}
+
postcss-load-config@6.0.1:
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
engines: {node: '>= 18'}
@@ -693,15 +910,26 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
+ qs@6.14.0:
+ resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ engines: {node: '>=0.6'}
+
react-dom@19.1.1:
resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
peerDependencies:
react: ^19.1.1
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
react@19.1.1:
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
engines: {node: '>=0.10.0'}
@@ -710,6 +938,13 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
+ requires-port@1.0.0:
+ resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
resolve-from@5.0.0:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
@@ -722,6 +957,9 @@ packages:
rrweb-cssom@0.8.0:
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
@@ -732,6 +970,9 @@ packages:
scheduler@0.26.0:
resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+ secure-compare@3.0.1:
+ resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -740,6 +981,22 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
@@ -777,6 +1034,10 @@ packages:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
strip-literal@3.0.0:
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
@@ -785,6 +1046,10 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
@@ -869,6 +1134,13 @@ packages:
ufo@1.6.1:
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+ union@0.5.0:
+ resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==}
+ engines: {node: '>= 0.8.0'}
+
+ url-join@4.0.1:
+ resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
+
vite-node@3.2.4:
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -953,6 +1225,10 @@ packages:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
+ whatwg-encoding@2.0.0:
+ resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
+ engines: {node: '>=12'}
+
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
@@ -1007,6 +1283,8 @@ packages:
snapshots:
+ '@adobe/css-tools@4.4.3': {}
+
'@asamuzakjp/css-color@3.2.0':
dependencies:
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
@@ -1015,6 +1293,16 @@ snapshots:
'@csstools/css-tokenizer': 3.0.4
lru-cache: 10.4.3
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/runtime@7.28.2': {}
+
'@csstools/color-helpers@5.0.2': {}
'@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
@@ -1201,6 +1489,38 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.46.2':
optional: true
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/runtime': 7.28.2
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.6.4':
+ dependencies:
+ '@adobe/css-tools': 4.4.3
+ aria-query: 5.3.0
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ lodash: 4.17.21
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
+ dependencies:
+ '@babel/runtime': 7.28.2
+ '@testing-library/dom': 10.4.1
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ optionalDependencies:
+ '@types/react': 19.1.9
+
+ '@types/aria-query@5.0.4': {}
+
'@types/chai@5.2.2':
dependencies:
'@types/deep-eql': 4.0.2
@@ -1209,6 +1529,10 @@ snapshots:
'@types/estree@1.0.8': {}
+ '@types/react@19.1.9':
+ dependencies:
+ csstype: 3.1.3
+
'@vitest/expect@3.2.4':
dependencies:
'@types/chai': 5.2.2
@@ -1263,14 +1587,26 @@ snapshots:
dependencies:
color-convert: 2.0.1
+ ansi-styles@5.2.0: {}
+
ansi-styles@6.2.1: {}
any-promise@1.3.0: {}
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
assertion-error@2.0.1: {}
+ async@3.2.6: {}
+
balanced-match@1.0.2: {}
+ basic-auth@2.0.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
@@ -1282,6 +1618,16 @@ snapshots:
cac@6.7.14: {}
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
chai@5.2.1:
dependencies:
assertion-error: 2.0.1
@@ -1290,6 +1636,11 @@ snapshots:
loupe: 3.2.0
pathval: 2.0.1
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
check-error@2.1.1: {}
chokidar@4.0.3:
@@ -1308,17 +1659,23 @@ snapshots:
consola@3.4.2: {}
+ corser@2.0.1: {}
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
+ css.escape@1.5.1: {}
+
cssstyle@4.6.0:
dependencies:
'@asamuzakjp/css-color': 3.2.0
rrweb-cssom: 0.8.0
+ csstype@3.1.3: {}
+
data-urls@5.0.0:
dependencies:
whatwg-mimetype: 4.0.0
@@ -1332,6 +1689,18 @@ snapshots:
deep-eql@5.0.2: {}
+ dequal@2.0.3: {}
+
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
eastasianwidth@0.2.0: {}
emoji-regex@8.0.0: {}
@@ -1340,8 +1709,16 @@ snapshots:
entities@6.0.1: {}
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
es-module-lexer@1.7.0: {}
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
esbuild@0.25.8:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.8
@@ -1375,6 +1752,8 @@ snapshots:
dependencies:
'@types/estree': 1.0.8
+ eventemitter3@4.0.7: {}
+
expect-type@1.2.2: {}
fdir@6.4.6(picomatch@4.0.3):
@@ -1387,6 +1766,8 @@ snapshots:
mlly: 1.7.4
rollup: 4.46.2
+ follow-redirects@1.15.11: {}
+
foreground-child@3.3.1:
dependencies:
cross-spawn: 7.0.6
@@ -1395,6 +1776,26 @@ snapshots:
fsevents@2.3.3:
optional: true
+ function-bind@1.1.2: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
glob@10.4.5:
dependencies:
foreground-child: 3.3.1
@@ -1404,6 +1805,22 @@ snapshots:
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
+ gopd@1.2.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-symbols@1.1.0: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ he@1.2.0: {}
+
+ html-encoding-sniffer@3.0.0:
+ dependencies:
+ whatwg-encoding: 2.0.0
+
html-encoding-sniffer@4.0.0:
dependencies:
whatwg-encoding: 3.1.1
@@ -1415,6 +1832,33 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ http-proxy@1.18.1:
+ dependencies:
+ eventemitter3: 4.0.7
+ follow-redirects: 1.15.11
+ requires-port: 1.0.0
+ transitivePeerDependencies:
+ - debug
+
+ http-server@14.1.1:
+ dependencies:
+ basic-auth: 2.0.1
+ chalk: 4.1.2
+ corser: 2.0.1
+ he: 1.2.0
+ html-encoding-sniffer: 3.0.0
+ http-proxy: 1.18.1
+ mime: 1.6.0
+ minimist: 1.2.8
+ opener: 1.5.2
+ portfinder: 1.0.37
+ secure-compare: 3.0.1
+ union: 0.5.0
+ url-join: 4.0.1
+ transitivePeerDependencies:
+ - debug
+ - supports-color
+
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
@@ -1426,6 +1870,8 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
+ indent-string@4.0.0: {}
+
is-fullwidth-code-point@3.0.0: {}
is-potential-custom-element-name@1.0.1: {}
@@ -1440,6 +1886,8 @@ snapshots:
joycon@3.1.1: {}
+ js-tokens@4.0.0: {}
+
js-tokens@9.0.1: {}
jsdom@26.1.0:
@@ -1477,18 +1925,30 @@ snapshots:
lodash.sortby@4.7.0: {}
+ lodash@4.17.21: {}
+
loupe@3.2.0: {}
lru-cache@10.4.3: {}
+ lz-string@1.5.0: {}
+
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.4
+ math-intrinsics@1.1.0: {}
+
+ mime@1.6.0: {}
+
+ min-indent@1.0.1: {}
+
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.2
+ minimist@1.2.8: {}
+
minipass@7.1.2: {}
mlly@1.7.4:
@@ -1512,6 +1972,10 @@ snapshots:
object-assign@4.1.1: {}
+ object-inspect@1.13.4: {}
+
+ opener@1.5.2: {}
+
package-json-from-dist@1.0.1: {}
parse5@7.3.0:
@@ -1541,6 +2005,13 @@ snapshots:
mlly: 1.7.4
pathe: 2.0.3
+ portfinder@1.0.37:
+ dependencies:
+ async: 3.2.6
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
postcss-load-config@6.0.1(postcss@8.5.6):
dependencies:
lilconfig: 3.1.3
@@ -1553,17 +2024,36 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
punycode@2.3.1: {}
+ qs@6.14.0:
+ dependencies:
+ side-channel: 1.1.0
+
react-dom@19.1.1(react@19.1.1):
dependencies:
react: 19.1.1
scheduler: 0.26.0
+ react-is@17.0.2: {}
+
react@19.1.1: {}
readdirp@4.1.2: {}
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
+ requires-port@1.0.0: {}
+
resolve-from@5.0.0: {}
rollup@4.46.2:
@@ -1594,6 +2084,8 @@ snapshots:
rrweb-cssom@0.8.0: {}
+ safe-buffer@5.1.2: {}
+
safer-buffer@2.1.2: {}
saxes@6.0.0:
@@ -1602,12 +2094,42 @@ snapshots:
scheduler@0.26.0: {}
+ secure-compare@3.0.1: {}
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@3.0.0: {}
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
siginfo@2.0.0: {}
signal-exit@4.1.0: {}
@@ -1642,6 +2164,10 @@ snapshots:
dependencies:
ansi-regex: 6.1.0
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
strip-literal@3.0.0:
dependencies:
js-tokens: 9.0.1
@@ -1656,6 +2182,10 @@ snapshots:
pirates: 4.0.7
ts-interface-checker: 0.1.13
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
symbol-tree@3.2.4: {}
thenify-all@1.6.0:
@@ -1735,6 +2265,12 @@ snapshots:
ufo@1.6.1: {}
+ union@0.5.0:
+ dependencies:
+ qs: 6.14.0
+
+ url-join@4.0.1: {}
+
vite-node@3.2.4:
dependencies:
cac: 6.7.14
@@ -1816,6 +2352,10 @@ snapshots:
webidl-conversions@7.0.0: {}
+ whatwg-encoding@2.0.0:
+ dependencies:
+ iconv-lite: 0.6.3
+
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3
diff --git a/src/index.ts b/src/index.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/index.tsx b/src/index.tsx
new file mode 100644
index 0000000..cf1e48e
--- /dev/null
+++ b/src/index.tsx
@@ -0,0 +1,89 @@
+import React, { useEffect, useRef } from 'react';
+import { IVLabsPlayer, PlayerConfig, CuePoint, Translations, AnalyticsEvent, AnalyticsPayload } from '@interactive-video-labs/core';
+
+export interface InteractiveVideoProps extends Omit {
+ videoUrl: string; // Make videoUrl mandatory
+ onAnalyticsEvent?: (event: AnalyticsEvent, payload?: AnalyticsPayload) => void;
+ cues?: CuePoint[]; // Explicitly use CuePoint
+ translations?: Translations; // Explicitly use Translations
+}
+
+const generateUniqueId = () => `ivlabs-player-${Math.random().toString(36).substr(2, 9)}`;
+
+/**
+ * `InteractiveVideo` is a React component that wraps the `@interactive-video-labs/core` IVLabsPlayer.
+ * It provides a convenient way to embed and manage interactive video experiences within a React application.
+ *
+ * The component handles the lifecycle of the IVLabsPlayer, including initialization, updates based on prop changes,
+ * and destruction when the component unmounts. It also provides a mechanism for handling analytics events.
+ *
+ * @param {InteractiveVideoProps} props - The props for the InteractiveVideo component.
+ * @param {string} props.videoUrl - The URL of the video to be played. This is a mandatory prop.
+ * @param {(event: AnalyticsEvent, payload?: AnalyticsPayload) => void} [props.onAnalyticsEvent] - Optional callback for analytics events.
+ * @param {CuePoint[]} [props.cues] - Optional array of cue points to load into the player.
+ * @param {Translations} [props.translations] - Optional translations object for player localization.
+ * @param {Omit} [props.restOptions] - Any other valid PlayerConfig options
+ * that are not `videoUrl`, `cues`, or `translations`.
+ *
+ * @example
+ * ```tsx
+ * console.log(event, payload)}
+ * cues={[{ time: 10, id: 'my-cue', type: 'pause' }]}
+ * />
+ * ```
+ */
+export const InteractiveVideo: React.FC = ({ videoUrl, onAnalyticsEvent, cues, translations, ...restOptions }) => {
+ const containerRef = useRef(null);
+ const playerRef = useRef(null);
+ const uniqueIdRef = useRef(generateUniqueId());
+
+ useEffect(() => {
+ if (containerRef.current && !playerRef.current) {
+ const playerConfig: PlayerConfig = {
+ videoUrl,
+ ...restOptions,
+ };
+
+ try {
+ playerRef.current = new IVLabsPlayer(`#${uniqueIdRef.current}`, playerConfig);
+
+ if (onAnalyticsEvent) {
+ playerRef.current.on('PLAYER_LOADED', (payload) => onAnalyticsEvent('PLAYER_LOADED', payload));
+ playerRef.current.on('VIDEO_STARTED', (payload) => onAnalyticsEvent('VIDEO_STARTED', payload));
+ playerRef.current.on('VIDEO_PAUSED', (payload) => onAnalyticsEvent('VIDEO_PAUSED', payload));
+ playerRef.current.on('VIDEO_ENDED', (payload) => onAnalyticsEvent('VIDEO_ENDED', payload));
+ playerRef.current.on('CUE_TRIGGERED', (payload) => onAnalyticsEvent('CUE_TRIGGERED', payload));
+ playerRef.current.on('INTERACTION_COMPLETED', (payload) => onAnalyticsEvent('INTERACTION_COMPLETED', payload));
+ playerRef.current.on('ERROR', (payload) => onAnalyticsEvent('ERROR', payload));
+ }
+
+ if (cues) {
+ playerRef.current.loadCues(cues);
+ }
+
+ if (translations) {
+ const locale = restOptions.locale || 'en';
+ playerRef.current.loadTranslations(locale, translations);
+ }
+ } catch (error) {
+ console.error('Error initializing IVLabsPlayer:', error);
+ }
+ }
+
+ return () => {
+ if (playerRef.current) {
+ playerRef.current.destroy();
+ playerRef.current = null;
+ }
+ };
+ }, [videoUrl, onAnalyticsEvent, cues, translations, restOptions]);
+
+ return (
+
+ {/* The IVLabsPlayer will inject the video element here */}
+
+ );
+};
\ No newline at end of file
diff --git a/test/InteractiveVideo.test.tsx b/test/InteractiveVideo.test.tsx
new file mode 100644
index 0000000..f038fa2
--- /dev/null
+++ b/test/InteractiveVideo.test.tsx
@@ -0,0 +1,76 @@
+import { render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { InteractiveVideo } from '../src/index';
+import { IVLabsPlayer } from '@interactive-video-labs/core';
+
+// Mock the @interactive-video-labs/core module
+const mockLoadCues = vi.fn();
+const mockLoadTranslations = vi.fn();
+const mockOn = vi.fn();
+const mockDestroy = vi.fn();
+
+vi.mock('@interactive-video-labs/core', () => ({
+ IVLabsPlayer: vi.fn(() => ({
+ loadCues: mockLoadCues,
+ loadTranslations: mockLoadTranslations,
+ on: mockOn,
+ destroy: mockDestroy,
+ })),
+}));
+
+describe('InteractiveVideo', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('renders a div element', () => {
+ render();
+ const divElement = screen.getByTestId('interactive-video-container');
+ expect(divElement).toBeInTheDocument();
+ });
+
+ it('initializes IVLabsPlayer with correct parameters and calls loadCues/loadTranslations', () => {
+ const videoSrc = 'test-video.mp4';
+ const options = { autoplay: true };
+ const cues = [{ id: 'cue1', time: 5 }];
+ const translations = { en: { hello: 'Hello' } };
+ const onAnalyticsEvent = vi.fn();
+
+ render();
+
+ expect(vi.mocked(IVLabsPlayer)).toHaveBeenCalled();
+
+ const calls = vi.mocked(IVLabsPlayer).mock.calls;
+ const playerCall = calls.find(call => {
+ const config = call[1];
+ return config.videoUrl === videoSrc && config.autoplay === options.autoplay;
+ });
+
+ expect(playerCall).toBeDefined();
+ if (playerCall) {
+ expect(playerCall[0]).toMatch(/^#ivlabs-player-/);
+ expect(playerCall[1]).toEqual({ videoUrl: videoSrc, ...options });
+ }
+
+ expect(mockLoadCues).toHaveBeenCalledTimes(1);
+ expect(mockLoadCues).toHaveBeenCalledWith(cues);
+
+ expect(mockLoadTranslations).toHaveBeenCalledTimes(1);
+ expect(mockLoadTranslations).toHaveBeenCalledWith('en', translations);
+
+ expect(mockOn).toHaveBeenCalledWith('PLAYER_LOADED', expect.any(Function));
+ expect(mockOn).toHaveBeenCalledWith('VIDEO_STARTED', expect.any(Function));
+ expect(mockOn).toHaveBeenCalledWith('VIDEO_PAUSED', expect.any(Function));
+ expect(mockOn).toHaveBeenCalledWith('VIDEO_ENDED', expect.any(Function));
+ expect(mockOn).toHaveBeenCalledWith('CUE_TRIGGERED', expect.any(Function));
+ expect(mockOn).toHaveBeenCalledWith('INTERACTION_COMPLETED', expect.any(Function));
+ expect(mockOn).toHaveBeenCalledWith('ERROR', expect.any(Function));
+ });
+
+ it('calls destroy on unmount', () => {
+ const { unmount } = render();
+ unmount();
+ expect(mockDestroy).toHaveBeenCalledTimes(1);
+ });
+});
\ No newline at end of file
diff --git a/test/setupTests.ts b/test/setupTests.ts
new file mode 100644
index 0000000..19600da
--- /dev/null
+++ b/test/setupTests.ts
@@ -0,0 +1,2 @@
+import { expect } from 'vitest';
+import '@testing-library/jest-dom/vitest';
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 2d6475e..c2908d6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -12,8 +12,9 @@
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"outDir": "dist",
- "rootDir": "src"
+ "rootDir": "src",
+ "jsx": "react-jsx"
},
- "include": ["src"],
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "dist","test"]
-}
+}
\ No newline at end of file
diff --git a/vitest.config.ts b/vitest.config.ts
index 582eda0..2b9a63b 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -3,5 +3,7 @@ import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
+ setupFiles: ['./test/setupTests.ts'],
+ globals: true,
},
});
\ No newline at end of file