@spatialwalk/avatarkit 1.0.0-beta.1 → 1.0.0-beta.100

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 (112) hide show
  1. package/CHANGELOG.md +938 -0
  2. package/README.md +821 -208
  3. package/dist/StreamingAudioPlayer-CY6WeP2p.js +643 -0
  4. package/dist/avatar_core_wasm-6656456a.wasm +0 -0
  5. package/dist/avatar_core_wasm-Dci9E9jF.js +2696 -0
  6. package/dist/core/Avatar.d.ts +4 -14
  7. package/dist/core/AvatarController.d.ts +108 -93
  8. package/dist/core/AvatarManager.d.ts +32 -12
  9. package/dist/core/AvatarSDK.d.ts +58 -0
  10. package/dist/core/AvatarView.d.ts +132 -123
  11. package/dist/index-DADGbRoo.js +18392 -0
  12. package/dist/index.d.ts +2 -5
  13. package/dist/index.js +17 -17
  14. package/dist/next.d.ts +2 -0
  15. package/dist/performance/FrameRateMonitor.d.ts +85 -0
  16. package/dist/types/character-settings.d.ts +7 -1
  17. package/dist/types/character.d.ts +42 -16
  18. package/dist/types/index.d.ts +170 -32
  19. package/dist/vite.d.ts +19 -0
  20. package/next.d.ts +3 -0
  21. package/next.js +187 -0
  22. package/package.json +42 -15
  23. package/vite.d.ts +20 -0
  24. package/vite.js +126 -0
  25. package/dist/StreamingAudioPlayer-C2TfYsO8.js +0 -293
  26. package/dist/StreamingAudioPlayer-C2TfYsO8.js.map +0 -1
  27. package/dist/animation/AnimationWebSocketClient.d.ts +0 -50
  28. package/dist/animation/AnimationWebSocketClient.d.ts.map +0 -1
  29. package/dist/animation/utils/eventEmitter.d.ts +0 -13
  30. package/dist/animation/utils/eventEmitter.d.ts.map +0 -1
  31. package/dist/animation/utils/flameConverter.d.ts +0 -26
  32. package/dist/animation/utils/flameConverter.d.ts.map +0 -1
  33. package/dist/audio/AnimationPlayer.d.ts +0 -53
  34. package/dist/audio/AnimationPlayer.d.ts.map +0 -1
  35. package/dist/audio/StreamingAudioPlayer.d.ts +0 -113
  36. package/dist/audio/StreamingAudioPlayer.d.ts.map +0 -1
  37. package/dist/avatar_core_wasm-DmkU6dYn.js +0 -1666
  38. package/dist/avatar_core_wasm-DmkU6dYn.js.map +0 -1
  39. package/dist/avatar_core_wasm.wasm +0 -0
  40. package/dist/config/app-config.d.ts +0 -48
  41. package/dist/config/app-config.d.ts.map +0 -1
  42. package/dist/config/constants.d.ts +0 -13
  43. package/dist/config/constants.d.ts.map +0 -1
  44. package/dist/config/region-config.d.ts +0 -17
  45. package/dist/config/region-config.d.ts.map +0 -1
  46. package/dist/config/sdk-config-loader.d.ts +0 -12
  47. package/dist/config/sdk-config-loader.d.ts.map +0 -1
  48. package/dist/core/Avatar.d.ts.map +0 -1
  49. package/dist/core/AvatarController.d.ts.map +0 -1
  50. package/dist/core/AvatarDownloader.d.ts +0 -100
  51. package/dist/core/AvatarDownloader.d.ts.map +0 -1
  52. package/dist/core/AvatarKit.d.ts +0 -60
  53. package/dist/core/AvatarKit.d.ts.map +0 -1
  54. package/dist/core/AvatarManager.d.ts.map +0 -1
  55. package/dist/core/AvatarView.d.ts.map +0 -1
  56. package/dist/generated/driveningress/v1/driveningress.d.ts +0 -80
  57. package/dist/generated/driveningress/v1/driveningress.d.ts.map +0 -1
  58. package/dist/generated/driveningress/v2/driveningress.d.ts +0 -81
  59. package/dist/generated/driveningress/v2/driveningress.d.ts.map +0 -1
  60. package/dist/generated/google/protobuf/any.d.ts +0 -145
  61. package/dist/generated/google/protobuf/any.d.ts.map +0 -1
  62. package/dist/generated/google/protobuf/struct.d.ts +0 -108
  63. package/dist/generated/google/protobuf/struct.d.ts.map +0 -1
  64. package/dist/generated/google/protobuf/timestamp.d.ts +0 -129
  65. package/dist/generated/google/protobuf/timestamp.d.ts.map +0 -1
  66. package/dist/generated/jsonapi/v1/base.d.ts +0 -140
  67. package/dist/generated/jsonapi/v1/base.d.ts.map +0 -1
  68. package/dist/generated/platform/v1/asset_groups.d.ts +0 -225
  69. package/dist/generated/platform/v1/asset_groups.d.ts.map +0 -1
  70. package/dist/generated/platform/v1/assets.d.ts +0 -149
  71. package/dist/generated/platform/v1/assets.d.ts.map +0 -1
  72. package/dist/generated/platform/v1/character.d.ts +0 -395
  73. package/dist/generated/platform/v1/character.d.ts.map +0 -1
  74. package/dist/generated/platform/v1/redeem.d.ts +0 -22
  75. package/dist/generated/platform/v1/redeem.d.ts.map +0 -1
  76. package/dist/index-DwhR9l52.js +0 -9712
  77. package/dist/index-DwhR9l52.js.map +0 -1
  78. package/dist/index.d.ts.map +0 -1
  79. package/dist/index.js.map +0 -1
  80. package/dist/renderer/RenderSystem.d.ts +0 -77
  81. package/dist/renderer/RenderSystem.d.ts.map +0 -1
  82. package/dist/renderer/covariance.d.ts +0 -13
  83. package/dist/renderer/covariance.d.ts.map +0 -1
  84. package/dist/renderer/renderer.d.ts +0 -8
  85. package/dist/renderer/renderer.d.ts.map +0 -1
  86. package/dist/renderer/sortSplats.d.ts +0 -12
  87. package/dist/renderer/sortSplats.d.ts.map +0 -1
  88. package/dist/renderer/webgl/reorderData.d.ts +0 -14
  89. package/dist/renderer/webgl/reorderData.d.ts.map +0 -1
  90. package/dist/renderer/webgl/webglRenderer.d.ts +0 -66
  91. package/dist/renderer/webgl/webglRenderer.d.ts.map +0 -1
  92. package/dist/renderer/webgpu/webgpuRenderer.d.ts +0 -54
  93. package/dist/renderer/webgpu/webgpuRenderer.d.ts.map +0 -1
  94. package/dist/types/character-settings.d.ts.map +0 -1
  95. package/dist/types/character.d.ts.map +0 -1
  96. package/dist/types/index.d.ts.map +0 -1
  97. package/dist/utils/animation-interpolation.d.ts +0 -17
  98. package/dist/utils/animation-interpolation.d.ts.map +0 -1
  99. package/dist/utils/error-utils.d.ts +0 -27
  100. package/dist/utils/error-utils.d.ts.map +0 -1
  101. package/dist/utils/logger.d.ts +0 -35
  102. package/dist/utils/logger.d.ts.map +0 -1
  103. package/dist/utils/posthog-tracker.d.ts +0 -82
  104. package/dist/utils/posthog-tracker.d.ts.map +0 -1
  105. package/dist/utils/reqId.d.ts +0 -20
  106. package/dist/utils/reqId.d.ts.map +0 -1
  107. package/dist/utils/toast.d.ts +0 -74
  108. package/dist/utils/toast.d.ts.map +0 -1
  109. package/dist/wasm/avatarCoreAdapter.d.ts +0 -188
  110. package/dist/wasm/avatarCoreAdapter.d.ts.map +0 -1
  111. package/dist/wasm/avatarCoreMemory.d.ts +0 -141
  112. package/dist/wasm/avatarCoreMemory.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@spatialwalk/avatarkit",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.1",
4
+ "version": "1.0.0-beta.100",
5
5
  "packageManager": "pnpm@10.18.2",
6
- "description": "SPAvatar SDK - 3D Gaussian Splatting Avatar Rendering SDK",
7
- "author": "SPAvatar Team",
6
+ "description": "AvatarKit SDK - 3D Gaussian Splatting Avatar Rendering SDK",
7
+ "author": "AvatarKit Team",
8
8
  "license": "MIT",
9
9
  "keywords": [
10
10
  "avatar",
@@ -19,6 +19,14 @@
19
19
  "types": "./dist/index.d.ts",
20
20
  "import": "./dist/index.js"
21
21
  },
22
+ "./vite": {
23
+ "types": "./vite.d.ts",
24
+ "import": "./vite.js"
25
+ },
26
+ "./next": {
27
+ "types": "./next.d.ts",
28
+ "import": "./next.js"
29
+ },
22
30
  "./*": {
23
31
  "types": "./dist/*.d.ts",
24
32
  "import": "./dist/*.js"
@@ -29,32 +37,51 @@
29
37
  "types": "./dist/index.d.ts",
30
38
  "files": [
31
39
  "README.md",
32
- "dist"
40
+ "CHANGELOG.md",
41
+ "dist",
42
+ "vite.js",
43
+ "vite.d.ts",
44
+ "next.js",
45
+ "next.d.ts"
33
46
  ],
34
- "repository": {
35
- "type": "git",
36
- "url": "https://github.com/spatialwalk/avatarkit-web-sdk.git"
37
- },
38
- "publishConfig": {
39
- "access": "public"
40
- },
41
47
  "scripts": {
42
- "build": "SDK_BUILD=true vite build --mode library",
48
+ "build": "SDK_BUILD=true vite build --mode library && npm run build:vite-plugin && npm run build:next-plugin",
49
+ "build:vite-plugin": "tsc vite.ts --outDir . --module esnext --target es2020 --moduleResolution bundler --esModuleInterop --skipLibCheck --declaration --declarationMap",
50
+ "build:next-plugin": "tsc next.ts --outDir . --module esnext --target es2020 --moduleResolution bundler --esModuleInterop --skipLibCheck --declaration --declarationMap",
43
51
  "dev": "vite build --mode library --watch",
52
+ "demo": "vite --config demo/vite.config.mjs",
53
+ "demo:benchmark": "vite --config benchmark-demo/vite.config.mjs",
54
+ "test:integration": "vite --config tests/integration-runner/vite.config.mjs",
44
55
  "clean": "rm -rf dist",
45
- "typecheck": "tsc --noEmit"
56
+ "typecheck": "tsc --noEmit",
57
+ "test": "cd tests && pnpm test",
58
+ "test:watch": "cd tests && pnpm run test:watch",
59
+ "test:e2e": "cd tests && pnpm run test:e2e",
60
+ "test:perf": "cd tests && pnpm run test:perf"
46
61
  },
47
62
  "peerDependencies": {
48
- "@webgpu/types": "*"
63
+ "@webgpu/types": "*",
64
+ "next": ">=13.0.0",
65
+ "vite": "^5.0.0"
66
+ },
67
+ "peerDependenciesMeta": {
68
+ "vite": {
69
+ "optional": true
70
+ },
71
+ "next": {
72
+ "optional": true
73
+ }
49
74
  },
50
75
  "dependencies": {
51
76
  "@bufbuild/protobuf": "^2.10.0",
52
77
  "@guiiai/logg": "^1.2.4",
53
78
  "nanoid": "^5.1.6",
54
- "posthog-js": "^1.275.3"
79
+ "posthog-js": "^1.310.1"
55
80
  },
56
81
  "devDependencies": {
82
+ "@types/node": "^20.11.30",
57
83
  "@webgpu/types": "^0.1.65",
84
+ "tsx": "^4.20.6",
58
85
  "typescript": "^5.0.0",
59
86
  "vite": "^5.0.0",
60
87
  "vite-plugin-dts": "^4.5.4"
package/vite.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { Plugin } from 'vite';
2
+ /**
3
+ * Vite plugin for @spatialwalk/avatarkit
4
+ * Automatically handles WASM file configuration for development and production builds
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { defineConfig } from 'vite'
9
+ * import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
10
+ *
11
+ * export default defineConfig({
12
+ * plugins: [
13
+ * avatarkitVitePlugin()
14
+ * ]
15
+ * })
16
+ * ```
17
+ */
18
+ export declare function avatarkitVitePlugin(): Plugin;
19
+ export default avatarkitVitePlugin;
20
+ //# sourceMappingURL=vite.d.ts.map
package/vite.js ADDED
@@ -0,0 +1,126 @@
1
+ import { copyFileSync, existsSync, writeFileSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Vite plugin for @spatialwalk/avatarkit
5
+ * Automatically handles WASM file configuration for development and production builds
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { defineConfig } from 'vite'
10
+ * import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
11
+ *
12
+ * export default defineConfig({
13
+ * plugins: [
14
+ * avatarkitVitePlugin()
15
+ * ]
16
+ * })
17
+ * ```
18
+ */
19
+ export function avatarkitVitePlugin() {
20
+ let rootDir;
21
+ return {
22
+ name: 'avatarkit-wasm',
23
+ // 保存项目根目录
24
+ configResolved(config) {
25
+ rootDir = config.root;
26
+ },
27
+ // 开发服务器 MIME 类型配置
28
+ configureServer(server) {
29
+ server.middlewares.use((req, res, next) => {
30
+ if (req.url?.endsWith('.wasm')) {
31
+ res.setHeader('Content-Type', 'application/wasm');
32
+ }
33
+ next();
34
+ });
35
+ },
36
+ // 构建时自动复制 WASM 文件和生成 headers
37
+ closeBundle() {
38
+ if (!rootDir)
39
+ return;
40
+ const wasmSourceDir = join(rootDir, 'node_modules/@spatialwalk/avatarkit/dist');
41
+ // 先查找并读取 JS glue 文件,提取它引用的 WASM 文件名
42
+ let wasmFileName = null;
43
+ if (existsSync(wasmSourceDir)) {
44
+ const files = readdirSync(wasmSourceDir);
45
+ const wasmJsFile = files.find((f) => f.startsWith('avatar_core_wasm') && f.endsWith('.js'));
46
+ if (wasmJsFile) {
47
+ const wasmJsSource = join(wasmSourceDir, wasmJsFile);
48
+ try {
49
+ const jsContent = readFileSync(wasmJsSource, 'utf-8');
50
+ // 从 JS 文件中提取 WASM 文件名
51
+ // 匹配 avatar_core_wasm-{hash}.wasm 或 avatar_core_wasm.wasm
52
+ // 使用更精确的正则,匹配带引号或不带引号的情况
53
+ const wasmMatch = jsContent.match(/["'`]?avatar_core_wasm[-\w]*\.wasm["'`]?/g);
54
+ if (wasmMatch && wasmMatch.length > 0) {
55
+ // 取第一个匹配,去掉引号
56
+ wasmFileName = wasmMatch[0].replace(/["'`]/g, '');
57
+ }
58
+ }
59
+ catch (error) {
60
+ console.warn('⚠️ [avatarkit] Could not read JS glue file:', error);
61
+ }
62
+ }
63
+ }
64
+ // 如果从 JS 文件中找到了 WASM 文件名,使用它;否则回退到默认名称
65
+ const targetWasmName = wasmFileName || 'avatar_core_wasm.wasm';
66
+ const wasmSource = join(wasmSourceDir, targetWasmName);
67
+ const wasmDest = join(rootDir, `dist/assets/${targetWasmName}`);
68
+ const wasmJsDest = join(rootDir, 'dist/assets/avatar_core_wasm.js');
69
+ const headersDest = join(rootDir, 'dist/_headers');
70
+ // 复制 WASM 文件
71
+ if (existsSync(wasmSource)) {
72
+ copyFileSync(wasmSource, wasmDest);
73
+ console.log(`✅ [avatarkit] Copied WASM file to dist/assets/${targetWasmName}`);
74
+ }
75
+ else {
76
+ console.warn(`⚠️ [avatarkit] WASM file not found: ${wasmSource}`);
77
+ if (wasmFileName) {
78
+ console.warn(` Expected file: ${targetWasmName} (extracted from JS glue file)`);
79
+ }
80
+ }
81
+ // 复制 WASM JS glue 文件
82
+ if (existsSync(wasmSourceDir)) {
83
+ const files = readdirSync(wasmSourceDir);
84
+ const wasmJsFile = files.find((f) => f.startsWith('avatar_core_wasm') && f.endsWith('.js'));
85
+ if (wasmJsFile) {
86
+ const wasmJsSource = join(wasmSourceDir, wasmJsFile);
87
+ copyFileSync(wasmJsSource, wasmJsDest);
88
+ console.log(`✅ [avatarkit] Copied WASM JS file to dist/assets/avatar_core_wasm.js (from ${wasmJsFile})`);
89
+ }
90
+ else {
91
+ console.log('ℹ️ [avatarkit] WASM JS file not found (may be handled by Vite):', wasmSourceDir);
92
+ }
93
+ }
94
+ // 生成 _headers 文件(用于 Cloudflare Pages 等平台)
95
+ const headersContent = '/*.wasm\n Content-Type: application/wasm\n';
96
+ writeFileSync(headersDest, headersContent);
97
+ console.log('✅ [avatarkit] Created _headers file for Cloudflare Pages');
98
+ },
99
+ // 自动配置 Vite 选项
100
+ config() {
101
+ return {
102
+ optimizeDeps: {
103
+ exclude: ['@spatialwalk/avatarkit']
104
+ },
105
+ assetsInclude: ['**/*.wasm'],
106
+ build: {
107
+ assetsInlineLimit: 0, // 确保 WASM 文件不被内联
108
+ rollupOptions: {
109
+ output: {
110
+ assetFileNames: (assetInfo) => {
111
+ // WASM 文件使用固定名称
112
+ if (assetInfo.name?.endsWith('.wasm')) {
113
+ return 'assets/[name][extname]';
114
+ }
115
+ // 其他资源使用 hash
116
+ return 'assets/[name]-[hash][extname]';
117
+ }
118
+ }
119
+ }
120
+ }
121
+ };
122
+ }
123
+ };
124
+ }
125
+ // 默认导出
126
+ export default avatarkitVitePlugin;
@@ -1,293 +0,0 @@
1
- var c = Object.defineProperty;
2
- var g = (h, t, e) => t in h ? c(h, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[t] = e;
3
- var s = (h, t, e) => g(h, typeof t != "symbol" ? t + "" : t, e);
4
- import { l as r } from "./index-DwhR9l52.js";
5
- class k {
6
- constructor(t) {
7
- // AudioContext is managed internally
8
- s(this, "audioContext", null);
9
- s(this, "sampleRate");
10
- s(this, "channelCount");
11
- s(this, "debug");
12
- // Session-level state
13
- s(this, "sessionId");
14
- s(this, "sessionStartTime", 0);
15
- // AudioContext time when session started
16
- s(this, "pausedTimeOffset", 0);
17
- // Accumulated paused time
18
- s(this, "pausedAt", 0);
19
- // Time when paused
20
- s(this, "scheduledTime", 0);
21
- // Next chunk schedule time in AudioContext time
22
- // Playback state
23
- s(this, "isPlaying", !1);
24
- s(this, "isPaused", !1);
25
- // Audio buffer queue
26
- s(this, "audioChunks", []);
27
- s(this, "scheduledChunks", 0);
28
- // Number of chunks already scheduled
29
- s(this, "activeSources", []);
30
- // Event callbacks
31
- s(this, "onEndedCallback");
32
- this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.sampleRate = (t == null ? void 0 : t.sampleRate) ?? 24e3, this.channelCount = (t == null ? void 0 : t.channelCount) ?? 1, this.debug = (t == null ? void 0 : t.debug) ?? !1;
33
- }
34
- /**
35
- * Initialize audio context (create and ensure it's ready)
36
- */
37
- async initialize() {
38
- this.audioContext || (this.audioContext = new AudioContext({
39
- sampleRate: this.sampleRate
40
- }), this.audioContext.state === "suspended" && await this.audioContext.resume(), this.log("AudioContext initialized", {
41
- sessionId: this.sessionId,
42
- sampleRate: this.audioContext.sampleRate,
43
- state: this.audioContext.state
44
- }));
45
- }
46
- /**
47
- * Add audio chunk (16-bit PCM)
48
- */
49
- addChunk(t, e = !1) {
50
- if (!this.audioContext) {
51
- r.error("AudioContext not initialized");
52
- return;
53
- }
54
- this.audioChunks.push({ data: t, isLast: e }), this.log(`Added chunk ${this.audioChunks.length}`, {
55
- size: t.length,
56
- totalChunks: this.audioChunks.length,
57
- isLast: e,
58
- isPlaying: this.isPlaying,
59
- scheduledChunks: this.scheduledChunks
60
- }), !this.isPlaying && this.audioChunks.length > 0 ? (this.log("[StreamingAudioPlayer] Auto-starting playback from addChunk"), this.startPlayback()) : this.isPlaying ? (this.log("[StreamingAudioPlayer] Already playing, scheduling next chunk"), this.scheduleNextChunk()) : this.log("[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks");
61
- }
62
- /**
63
- * Start new session (stop current and start fresh)
64
- */
65
- async startNewSession(t) {
66
- this.stop(), this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.audioChunks = [], this.scheduledChunks = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.log("Starting new session", {
67
- chunks: t.length
68
- });
69
- for (const e of t)
70
- this.addChunk(e.data, e.isLast);
71
- }
72
- /**
73
- * Start playback
74
- */
75
- startPlayback() {
76
- if (!this.audioContext) {
77
- this.log("[StreamingAudioPlayer] Cannot start playback: AudioContext not initialized");
78
- return;
79
- }
80
- if (this.isPlaying) {
81
- this.log("[StreamingAudioPlayer] Cannot start playback: Already playing");
82
- return;
83
- }
84
- this.isPlaying = !0, this.sessionStartTime = this.audioContext.currentTime, this.scheduledTime = this.sessionStartTime, this.log("[StreamingAudioPlayer] Starting playback", {
85
- sessionStartTime: this.sessionStartTime,
86
- bufferedChunks: this.audioChunks.length,
87
- scheduledChunks: this.scheduledChunks,
88
- activeSources: this.activeSources.length
89
- }), this.scheduleAllChunks();
90
- }
91
- /**
92
- * Schedule all pending chunks
93
- */
94
- scheduleAllChunks() {
95
- for (; this.scheduledChunks < this.audioChunks.length; )
96
- this.scheduleNextChunk();
97
- }
98
- /**
99
- * Schedule next audio chunk
100
- */
101
- scheduleNextChunk() {
102
- if (!this.audioContext) {
103
- this.log("[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized");
104
- return;
105
- }
106
- if (!this.isPlaying) {
107
- this.log("[StreamingAudioPlayer] Cannot schedule chunk: Not playing");
108
- return;
109
- }
110
- const t = this.scheduledChunks;
111
- if (t >= this.audioChunks.length) {
112
- this.log(`[StreamingAudioPlayer] No more chunks to schedule (chunkIndex: ${t}, totalChunks: ${this.audioChunks.length})`);
113
- return;
114
- }
115
- const e = this.audioChunks[t];
116
- if (e.data.length === 0 && !e.isLast) {
117
- this.scheduledChunks++;
118
- return;
119
- }
120
- const l = e.data, d = e.isLast, n = this.pcmToAudioBuffer(l);
121
- if (!n) {
122
- r.error("Failed to create AudioBuffer from PCM data");
123
- return;
124
- }
125
- try {
126
- const i = this.audioContext.createBufferSource();
127
- i.buffer = n, i.connect(this.audioContext.destination), i.start(this.scheduledTime), this.activeSources.push(i), i.onended = () => {
128
- const u = this.activeSources.indexOf(i);
129
- u >= 0 && this.activeSources.splice(u, 1), d && this.activeSources.length === 0 && (this.log("Last audio chunk ended, marking playback as ended"), this.markEnded());
130
- }, this.scheduledTime += n.duration, this.scheduledChunks++, this.log(`[StreamingAudioPlayer] Scheduled chunk ${t + 1}/${this.audioChunks.length}`, {
131
- startTime: this.scheduledTime - n.duration,
132
- duration: n.duration,
133
- nextScheduleTime: this.scheduledTime,
134
- isLast: d,
135
- activeSources: this.activeSources.length
136
- });
137
- } catch (i) {
138
- r.errorWithError("Failed to schedule audio chunk:", i);
139
- }
140
- }
141
- /**
142
- * Convert PCM data to AudioBuffer
143
- * Input: 16-bit PCM (int16), Output: AudioBuffer (float32 [-1, 1])
144
- */
145
- pcmToAudioBuffer(t) {
146
- if (!this.audioContext)
147
- return null;
148
- if (t.length === 0) {
149
- const u = Math.floor(this.sampleRate * 0.01), a = this.audioContext.createBuffer(
150
- this.channelCount,
151
- u,
152
- this.sampleRate
153
- );
154
- for (let o = 0; o < this.channelCount; o++)
155
- a.getChannelData(o).fill(0);
156
- return a;
157
- }
158
- const e = new Uint8Array(t), l = new Int16Array(e.buffer, 0, e.length / 2), d = l.length / this.channelCount, n = this.audioContext.createBuffer(
159
- this.channelCount,
160
- d,
161
- this.sampleRate
162
- );
163
- for (let i = 0; i < this.channelCount; i++) {
164
- const u = n.getChannelData(i);
165
- for (let a = 0; a < d; a++) {
166
- const o = a * this.channelCount + i;
167
- u[a] = l[o] / 32768;
168
- }
169
- }
170
- return n;
171
- }
172
- /**
173
- * Get current playback time (seconds)
174
- */
175
- getCurrentTime() {
176
- if (!this.audioContext || !this.isPlaying)
177
- return 0;
178
- if (this.isPaused)
179
- return this.pausedAt;
180
- const e = this.audioContext.currentTime - this.sessionStartTime - this.pausedTimeOffset;
181
- return Math.max(0, e);
182
- }
183
- /**
184
- * Pause playback
185
- */
186
- pause() {
187
- !this.isPlaying || this.isPaused || (this.isPaused = !0, this.pausedAt = this.getCurrentTime(), this.log("Playback paused", {
188
- pausedAt: this.pausedAt
189
- }));
190
- }
191
- /**
192
- * Resume playback
193
- */
194
- async resume() {
195
- if (!this.isPaused || !this.audioContext)
196
- return;
197
- const t = this.audioContext.currentTime - (this.sessionStartTime + this.pausedAt);
198
- this.pausedTimeOffset += t, this.isPaused = !1, this.log("Playback resumed", {
199
- pauseDuration: t,
200
- totalPausedOffset: this.pausedTimeOffset
201
- });
202
- }
203
- /**
204
- * Stop playback
205
- */
206
- stop() {
207
- if (this.audioContext) {
208
- this.log("[StreamingAudioPlayer] Stopping playback", {
209
- isPlaying: this.isPlaying,
210
- audioChunks: this.audioChunks.length,
211
- scheduledChunks: this.scheduledChunks,
212
- activeSources: this.activeSources.length
213
- }), this.isPlaying = !1, this.isPaused = !1, this.sessionStartTime = 0, this.scheduledTime = 0;
214
- for (const t of this.activeSources) {
215
- t.onended = null;
216
- try {
217
- t.stop(0);
218
- } catch {
219
- }
220
- try {
221
- t.disconnect();
222
- } catch {
223
- }
224
- }
225
- this.activeSources = [], this.audioChunks = [], this.scheduledChunks = 0, this.log("[StreamingAudioPlayer] Playback stopped, state reset");
226
- }
227
- }
228
- /**
229
- * Mark playback as ended
230
- */
231
- markEnded() {
232
- var t;
233
- this.log("Playback ended"), this.isPlaying = !1, (t = this.onEndedCallback) == null || t.call(this);
234
- }
235
- /**
236
- * Set ended callback
237
- */
238
- onEnded(t) {
239
- this.onEndedCallback = t;
240
- }
241
- /**
242
- * Check if playing
243
- */
244
- isPlayingNow() {
245
- return this.isPlaying && !this.isPaused;
246
- }
247
- /**
248
- * Get total duration of buffered audio
249
- */
250
- getBufferedDuration() {
251
- if (!this.audioContext)
252
- return 0;
253
- let t = 0;
254
- for (const e of this.audioChunks)
255
- t += e.data.length / 2 / this.channelCount;
256
- return t / this.sampleRate;
257
- }
258
- /**
259
- * Get remaining duration (buffered - played) in seconds
260
- */
261
- getRemainingDuration() {
262
- const t = this.getBufferedDuration(), e = this.getCurrentTime();
263
- return Math.max(0, t - e);
264
- }
265
- /**
266
- * Dispose and cleanup
267
- */
268
- dispose() {
269
- this.stop(), this.audioContext && (this.audioContext.close(), this.audioContext = null), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.scheduledTime = 0, this.onEndedCallback = void 0, this.log("StreamingAudioPlayer disposed");
270
- }
271
- /**
272
- * Flush buffered audio
273
- * - hard: stops all playing sources and clears all chunks
274
- * - soft (default): clears UNSCHEDULED chunks only
275
- */
276
- flush(t) {
277
- if ((t == null ? void 0 : t.hard) === !0) {
278
- this.stop(), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedAt = 0, this.scheduledTime = 0, this.log("Flushed (hard)");
279
- return;
280
- }
281
- this.scheduledChunks < this.audioChunks.length && this.audioChunks.splice(this.scheduledChunks), this.log("Flushed (soft)", { remainingScheduled: this.scheduledChunks });
282
- }
283
- /**
284
- * Debug logging
285
- */
286
- log(t, e) {
287
- this.debug && r.log(`[StreamingAudioPlayer] ${t}`, e || "");
288
- }
289
- }
290
- export {
291
- k as StreamingAudioPlayer
292
- };
293
- //# sourceMappingURL=StreamingAudioPlayer-C2TfYsO8.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"StreamingAudioPlayer-C2TfYsO8.js","sources":["../audio/StreamingAudioPlayer.ts"],"sourcesContent":["/**\n * Streaming Audio Player\n * Implements real-time audio playback using Web Audio API\n * Supports dynamic PCM chunk addition without Workers\n */\n\nimport { logger } from '../utils/logger'\n\nexport interface StreamingAudioPlayerOptions {\n sampleRate?: number // PCM sample rate (default: 24000, backend returns 24kHz)\n channelCount?: number // Number of channels (default: 1)\n debug?: boolean\n}\n\nexport class StreamingAudioPlayer {\n // AudioContext is managed internally\n private audioContext: AudioContext | null = null\n private sampleRate: number\n private channelCount: number\n private debug: boolean\n\n // Session-level state\n private sessionId: string\n private sessionStartTime = 0 // AudioContext time when session started\n private pausedTimeOffset = 0 // Accumulated paused time\n private pausedAt = 0 // Time when paused\n private scheduledTime = 0 // Next chunk schedule time in AudioContext time\n\n // Playback state\n private isPlaying = false\n private isPaused = false\n\n // Audio buffer queue\n private audioChunks: Array<{ data: Uint8Array, isLast: boolean }> = []\n private scheduledChunks = 0 // Number of chunks already scheduled\n private activeSources: AudioBufferSourceNode[] = []\n\n // Event callbacks\n private onEndedCallback?: () => void\n\n constructor(options?: StreamingAudioPlayerOptions) {\n this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n this.sampleRate = options?.sampleRate ?? 24000 // Backend returns 24kHz by default\n this.channelCount = options?.channelCount ?? 1\n this.debug = options?.debug ?? false\n }\n\n /**\n * Initialize audio context (create and ensure it's ready)\n */\n async initialize(): Promise<void> {\n if (this.audioContext) {\n return\n }\n\n // Create AudioContext\n this.audioContext = new AudioContext({\n sampleRate: this.sampleRate,\n })\n\n // Resume context (required for some browsers)\n if (this.audioContext.state === 'suspended') {\n await this.audioContext.resume()\n }\n\n this.log('AudioContext initialized', {\n sessionId: this.sessionId,\n sampleRate: this.audioContext.sampleRate,\n state: this.audioContext.state,\n })\n }\n\n /**\n * Add audio chunk (16-bit PCM)\n */\n addChunk(pcmData: Uint8Array, isLast: boolean = false): void {\n if (!this.audioContext) {\n logger.error('AudioContext not initialized')\n return\n }\n\n // Store chunk with metadata\n this.audioChunks.push({ data: pcmData, isLast })\n\n // Track buffer underrun warning\n if (this.isPlaying && this.audioChunks.length === this.scheduledChunks) {\n // Buffer underrun detected - chunks consumed faster than added\n }\n\n this.log(`Added chunk ${this.audioChunks.length}`, {\n size: pcmData.length,\n totalChunks: this.audioChunks.length,\n isLast,\n isPlaying: this.isPlaying,\n scheduledChunks: this.scheduledChunks,\n })\n\n // Auto-start if we have any audio chunks\n if (!this.isPlaying && this.audioChunks.length > 0) {\n this.log('[StreamingAudioPlayer] Auto-starting playback from addChunk')\n this.startPlayback()\n }\n // Schedule next chunk if already playing\n else if (this.isPlaying) {\n this.log('[StreamingAudioPlayer] Already playing, scheduling next chunk')\n this.scheduleNextChunk()\n } else {\n this.log('[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks')\n }\n }\n\n /**\n * Start new session (stop current and start fresh)\n */\n async startNewSession(audioChunks: Array<{ data: Uint8Array, isLast: boolean }>): Promise<void> {\n // Stop current session if playing\n this.stop()\n\n // Generate new session ID to prevent data mixing\n this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n\n // Reset session state\n this.audioChunks = []\n this.scheduledChunks = 0\n this.pausedTimeOffset = 0\n this.pausedAt = 0\n // Don't set sessionStartTime or scheduledTime here - let startPlayback() set them\n this.log('Starting new session', {\n chunks: audioChunks.length,\n })\n\n // Add audio chunks with their metadata\n for (const chunk of audioChunks) {\n this.addChunk(chunk.data, chunk.isLast)\n }\n }\n\n /**\n * Start playback\n */\n private startPlayback(): void {\n if (!this.audioContext) {\n this.log('[StreamingAudioPlayer] Cannot start playback: AudioContext not initialized')\n return\n }\n if (this.isPlaying) {\n this.log('[StreamingAudioPlayer] Cannot start playback: Already playing')\n return\n }\n\n this.isPlaying = true\n this.sessionStartTime = this.audioContext.currentTime\n this.scheduledTime = this.sessionStartTime\n\n this.log('[StreamingAudioPlayer] Starting playback', {\n sessionStartTime: this.sessionStartTime,\n bufferedChunks: this.audioChunks.length,\n scheduledChunks: this.scheduledChunks,\n activeSources: this.activeSources.length,\n })\n\n // Schedule all available chunks\n this.scheduleAllChunks()\n }\n\n /**\n * Schedule all pending chunks\n */\n private scheduleAllChunks(): void {\n while (this.scheduledChunks < this.audioChunks.length) {\n this.scheduleNextChunk()\n }\n }\n\n /**\n * Schedule next audio chunk\n */\n private scheduleNextChunk(): void {\n if (!this.audioContext) {\n this.log('[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized')\n return\n }\n if (!this.isPlaying) {\n this.log('[StreamingAudioPlayer] Cannot schedule chunk: Not playing')\n return\n }\n\n const chunkIndex = this.scheduledChunks\n if (chunkIndex >= this.audioChunks.length) {\n this.log(`[StreamingAudioPlayer] No more chunks to schedule (chunkIndex: ${chunkIndex}, totalChunks: ${this.audioChunks.length})`)\n return\n }\n\n const chunk = this.audioChunks[chunkIndex]\n\n // 当音频块为空且不是最后一个块时,跳过调度\n if (chunk.data.length === 0 && !chunk.isLast) {\n this.scheduledChunks++\n return\n }\n\n const pcmData = chunk.data\n const isLast = chunk.isLast\n const audioBuffer = this.pcmToAudioBuffer(pcmData)\n\n if (!audioBuffer) {\n const errorMessage = 'Failed to create AudioBuffer from PCM data'\n logger.error(errorMessage)\n return\n }\n\n try {\n // Create and configure source node\n const source = this.audioContext.createBufferSource()\n source.buffer = audioBuffer\n source.connect(this.audioContext.destination)\n\n // Schedule playback\n source.start(this.scheduledTime)\n\n // Track active source for hard-cancel\n this.activeSources.push(source)\n source.onended = () => {\n // Remove from active list when it ends\n const idx = this.activeSources.indexOf(source)\n if (idx >= 0)\n this.activeSources.splice(idx, 1)\n\n // Check if this was the last chunk and all sources have ended\n if (isLast && this.activeSources.length === 0) {\n this.log('Last audio chunk ended, marking playback as ended')\n this.markEnded()\n }\n }\n\n // Update scheduled time for next chunk\n this.scheduledTime += audioBuffer.duration\n\n this.scheduledChunks++\n\n this.log(`[StreamingAudioPlayer] Scheduled chunk ${chunkIndex + 1}/${this.audioChunks.length}`, {\n startTime: this.scheduledTime - audioBuffer.duration,\n duration: audioBuffer.duration,\n nextScheduleTime: this.scheduledTime,\n isLast,\n activeSources: this.activeSources.length,\n })\n }\n catch (err) {\n logger.errorWithError('Failed to schedule audio chunk:', err)\n }\n }\n\n /**\n * Convert PCM data to AudioBuffer\n * Input: 16-bit PCM (int16), Output: AudioBuffer (float32 [-1, 1])\n */\n private pcmToAudioBuffer(pcmData: Uint8Array): AudioBuffer | null {\n if (!this.audioContext) {\n return null\n }\n\n // Handle empty PCM data (e.g., when isLast is true)\n if (pcmData.length === 0) {\n // For empty chunks (typically the last chunk), create minimal silence\n // Use a very short duration to avoid playback stuttering\n const silenceDuration = 0.01 // 1ms - minimal silence to maintain timing\n const numSamples = Math.floor(this.sampleRate * silenceDuration)\n\n const audioBuffer = this.audioContext.createBuffer(\n this.channelCount,\n numSamples,\n this.sampleRate,\n )\n\n // Fill with silence (all zeros)\n for (let channel = 0; channel < this.channelCount; channel++) {\n const channelData = audioBuffer.getChannelData(channel)\n channelData.fill(0) // Fill with silence\n }\n\n return audioBuffer\n }\n\n // Create aligned copy to avoid byte offset issues\n // Int16Array requires byteOffset to be a multiple of 2\n const alignedData = new Uint8Array(pcmData)\n const int16Array = new Int16Array(alignedData.buffer, 0, alignedData.length / 2)\n\n // Calculate number of samples\n const numSamples = int16Array.length / this.channelCount\n\n // Create AudioBuffer\n const audioBuffer = this.audioContext.createBuffer(\n this.channelCount,\n numSamples,\n this.sampleRate,\n )\n\n // Convert int16 to float32 [-1, 1]\n for (let channel = 0; channel < this.channelCount; channel++) {\n const channelData = audioBuffer.getChannelData(channel)\n\n for (let i = 0; i < numSamples; i++) {\n const sampleIndex = i * this.channelCount + channel\n // Normalize int16 (-32768 to 32767) to float32 (-1 to 1)\n channelData[i] = int16Array[sampleIndex] / 32768.0\n }\n }\n\n return audioBuffer\n }\n\n /**\n * Get current playback time (seconds)\n */\n getCurrentTime(): number {\n if (!this.audioContext || !this.isPlaying) {\n return 0\n }\n\n if (this.isPaused) {\n return this.pausedAt\n }\n\n // Calculate elapsed time using session start time and paused offset\n const currentAudioTime = this.audioContext.currentTime\n const elapsed = currentAudioTime - this.sessionStartTime - this.pausedTimeOffset\n\n return Math.max(0, elapsed)\n }\n\n /**\n * Pause playback\n */\n pause(): void {\n if (!this.isPlaying || this.isPaused) {\n return\n }\n\n this.isPaused = true\n this.pausedAt = this.getCurrentTime()\n\n // Don't suspend AudioContext to maintain time continuity\n // Just mark as paused and stop scheduling new chunks\n this.log('Playback paused', {\n pausedAt: this.pausedAt,\n })\n }\n\n /**\n * Resume playback\n */\n async resume(): Promise<void> {\n if (!this.isPaused || !this.audioContext) {\n return\n }\n\n // Calculate paused duration and add to offset\n const pauseDuration = this.audioContext.currentTime - (this.sessionStartTime + this.pausedAt)\n this.pausedTimeOffset += pauseDuration\n\n this.isPaused = false\n\n this.log('Playback resumed', {\n pauseDuration,\n totalPausedOffset: this.pausedTimeOffset,\n })\n }\n\n /**\n * Stop playback\n */\n stop(): void {\n if (!this.audioContext) {\n return\n }\n\n this.log('[StreamingAudioPlayer] Stopping playback', {\n isPlaying: this.isPlaying,\n audioChunks: this.audioChunks.length,\n scheduledChunks: this.scheduledChunks,\n activeSources: this.activeSources.length,\n })\n\n this.isPlaying = false\n this.isPaused = false\n this.sessionStartTime = 0 // Reset session start time\n this.scheduledTime = 0 // Reset scheduled time for next session\n\n // Hard stop all scheduled sources immediately\n for (const source of this.activeSources) {\n source.onended = null\n try {\n source.stop(0)\n }\n catch {}\n try {\n source.disconnect()\n }\n catch {}\n }\n this.activeSources = []\n\n // 清理音频块和调度状态,确保下次播放时状态干净\n this.audioChunks = []\n this.scheduledChunks = 0\n\n this.log('[StreamingAudioPlayer] Playback stopped, state reset')\n\n // Note: Individual source nodes will stop automatically\n // We just reset our state\n }\n\n /**\n * Mark playback as ended\n */\n markEnded(): void {\n this.log('Playback ended')\n this.isPlaying = false\n this.onEndedCallback?.()\n }\n\n /**\n * Set ended callback\n */\n onEnded(callback: () => void): void {\n this.onEndedCallback = callback\n }\n\n /**\n * Check if playing\n */\n isPlayingNow(): boolean {\n return this.isPlaying && !this.isPaused\n }\n\n /**\n * Get total duration of buffered audio\n */\n getBufferedDuration(): number {\n if (!this.audioContext) {\n return 0\n }\n\n let totalSamples = 0\n for (const chunk of this.audioChunks) {\n totalSamples += chunk.data.length / 2 / this.channelCount // 16-bit = 2 bytes per sample\n }\n\n return totalSamples / this.sampleRate\n }\n\n /**\n * Get remaining duration (buffered - played) in seconds\n */\n getRemainingDuration(): number {\n const total = this.getBufferedDuration()\n const played = this.getCurrentTime()\n return Math.max(0, total - played)\n }\n\n\n /**\n * Dispose and cleanup\n */\n dispose(): void {\n this.stop()\n\n // Close AudioContext\n if (this.audioContext) {\n this.audioContext.close()\n this.audioContext = null\n }\n\n // Clear session state\n this.audioChunks = []\n this.scheduledChunks = 0\n this.sessionStartTime = 0\n this.pausedTimeOffset = 0\n this.pausedAt = 0\n this.scheduledTime = 0\n this.onEndedCallback = undefined\n\n this.log('StreamingAudioPlayer disposed')\n }\n\n /**\n * Flush buffered audio\n * - hard: stops all playing sources and clears all chunks\n * - soft (default): clears UNSCHEDULED chunks only\n */\n flush(options?: { hard?: boolean }): void {\n const hard = options?.hard === true\n if (hard) {\n this.stop()\n this.audioChunks = []\n this.scheduledChunks = 0\n this.sessionStartTime = 0\n this.pausedAt = 0\n this.scheduledTime = 0\n this.log('Flushed (hard)')\n return\n }\n\n // Soft flush: drop unscheduled region\n if (this.scheduledChunks < this.audioChunks.length) {\n this.audioChunks.splice(this.scheduledChunks)\n }\n this.log('Flushed (soft)', { remainingScheduled: this.scheduledChunks })\n }\n\n /**\n * Debug logging\n */\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n logger.log(`[StreamingAudioPlayer] ${message}`, data || '')\n }\n }\n}\n"],"names":["StreamingAudioPlayer","options","__publicField","pcmData","isLast","logger","audioChunks","chunk","chunkIndex","audioBuffer","source","idx","err","numSamples","channel","alignedData","int16Array","channelData","i","sampleIndex","elapsed","pauseDuration","_a","callback","totalSamples","total","played","message","data"],"mappings":"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"}
@@ -1,50 +0,0 @@
1
- import { EventEmitter } from './utils/eventEmitter';
2
- export interface AnimationWebSocketClientOptions {
3
- wsUrl: string;
4
- reconnectAttempts?: number;
5
- debug?: boolean;
6
- jwtToken?: string;
7
- }
8
- export declare class AnimationWebSocketClient extends EventEmitter {
9
- private wsUrl;
10
- private reconnectAttempts;
11
- private debug;
12
- private jwtToken?;
13
- private ws;
14
- private currentCharacterId;
15
- private currentRetryCount;
16
- private isConnecting;
17
- private isManuallyDisconnected;
18
- private reconnectTimer;
19
- constructor(options: AnimationWebSocketClientOptions);
20
- /**
21
- * 连接WebSocket
22
- */
23
- connect(characterId: string): Promise<void>;
24
- /**
25
- * 断开连接
26
- */
27
- disconnect(): void;
28
- /**
29
- * 发送音频数据
30
- */
31
- sendAudioData(reqId: string, audioData: ArrayBuffer, end: boolean): boolean;
32
- /**
33
- * 生成请求ID
34
- * 使用统一的 ReqID 生成规则:YYYYMMDDHHmmss_nanoid
35
- */
36
- generateReqId(): string;
37
- /**
38
- * 获取连接状态
39
- */
40
- isConnected(): boolean;
41
- /**
42
- * 获取当前角色ID
43
- */
44
- getCurrentCharacterId(): string;
45
- private buildWebSocketUrl;
46
- private connectWebSocket;
47
- private handleMessage;
48
- private scheduleReconnect;
49
- }
50
- //# sourceMappingURL=AnimationWebSocketClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AnimationWebSocketClient.d.ts","sourceRoot":"","sources":["../../animation/AnimationWebSocketClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,qBAAa,wBAAyB,SAAQ,YAAY;IACxD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,CAAQ;IAEzB,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,sBAAsB,CAAiB;IAC/C,OAAO,CAAC,cAAc,CAAsB;gBAEhC,OAAO,EAAE,+BAA+B;IAQpD;;OAEG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BjD;;OAEG;IACH,UAAU,IAAI,IAAI;IAgBlB;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO;IA+B3E;;;OAGG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,qBAAqB,IAAI,MAAM;IAM/B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;IAmDxB,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,iBAAiB;CAmB1B"}
@@ -1,13 +0,0 @@
1
- /**
2
- * Simple Event Emitter
3
- */
4
- type EventHandler = (...args: any[]) => void;
5
- export declare class EventEmitter {
6
- private events;
7
- on(event: string, handler: EventHandler): void;
8
- off(event: string, handler: EventHandler): void;
9
- emit(event: string, ...args: any[]): void;
10
- removeAllListeners(event?: string): void;
11
- }
12
- export {};
13
- //# sourceMappingURL=eventEmitter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"eventEmitter.d.ts","sourceRoot":"","sources":["../../../animation/utils/eventEmitter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,KAAK,YAAY,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;AAE5C,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA4C;IAE1D,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAO9C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAW/C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAOzC,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAQzC"}