@pmate/chat-sdk 0.1.1

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 (105) hide show
  1. package/README.md +5 -0
  2. package/dist/assets/ChatInput.stories-6Z6tmO-P.js +922 -0
  3. package/dist/assets/Color-AVL7NMMY-CdXTB89n.js +1 -0
  4. package/dist/assets/DocsRenderer-PQXLIZUC-CtoiPcLN.js +1243 -0
  5. package/dist/assets/client-BG97-bzh.js +9 -0
  6. package/dist/assets/iframe-CWturJ6d.css +1 -0
  7. package/dist/assets/iframe-fcxekwVB.js +1071 -0
  8. package/dist/assets/index-u9_9PRd-.js +1 -0
  9. package/dist/assets/preload-helper-PPVm8Dsz.js +1 -0
  10. package/dist/assets/react-18-DKDc3Hbl.js +1 -0
  11. package/dist/favicon-wrapper.svg +46 -0
  12. package/dist/favicon.svg +1 -0
  13. package/dist/iframe.html +687 -0
  14. package/dist/index.html +148 -0
  15. package/dist/index.json +1 -0
  16. package/dist/nunito-sans-bold-italic.woff2 +0 -0
  17. package/dist/nunito-sans-bold.woff2 +0 -0
  18. package/dist/nunito-sans-italic.woff2 +0 -0
  19. package/dist/nunito-sans-regular.woff2 +0 -0
  20. package/dist/project.json +1 -0
  21. package/dist/sb-addons/docs-1/manager-bundle.js +151 -0
  22. package/dist/sb-addons/docs-1/manager-bundle.js.LEGAL.txt +0 -0
  23. package/dist/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js +971 -0
  24. package/dist/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js.LEGAL.txt +0 -0
  25. package/dist/sb-common-assets/favicon-wrapper.svg +46 -0
  26. package/dist/sb-common-assets/favicon.svg +1 -0
  27. package/dist/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  28. package/dist/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  29. package/dist/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  30. package/dist/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  31. package/dist/sb-manager/globals-module-info.js +799 -0
  32. package/dist/sb-manager/globals-runtime.js +69791 -0
  33. package/dist/sb-manager/globals.js +34 -0
  34. package/dist/sb-manager/runtime.js +13198 -0
  35. package/dist/vite-inject-mocker-entry.js +2 -0
  36. package/lib/atoms/attachmentAtoms.d.ts +4 -0
  37. package/lib/atoms/attachmentAtoms.js +2 -0
  38. package/lib/atoms/chatInputAtoms.d.ts +16 -0
  39. package/lib/atoms/chatInputAtoms.js +6 -0
  40. package/lib/atoms/voiceAtoms.d.ts +6 -0
  41. package/lib/atoms/voiceAtoms.js +3 -0
  42. package/lib/components/AttachmentPreview.d.ts +6 -0
  43. package/lib/components/AttachmentPreview.js +13 -0
  44. package/lib/components/ChatInputDesktop.d.ts +2 -0
  45. package/lib/components/ChatInputDesktop.js +181 -0
  46. package/lib/components/ChatInputMobile.d.ts +2 -0
  47. package/lib/components/ChatInputMobile.js +338 -0
  48. package/lib/components/EmojiPicker.d.ts +10 -0
  49. package/lib/components/EmojiPicker.js +62 -0
  50. package/lib/components/EmojiTrigger.d.ts +2 -0
  51. package/lib/components/EmojiTrigger.js +18 -0
  52. package/lib/components/HoldToTalkButton.d.ts +13 -0
  53. package/lib/components/HoldToTalkButton.js +38 -0
  54. package/lib/components/ImageTrigger.d.ts +2 -0
  55. package/lib/components/ImageTrigger.js +18 -0
  56. package/lib/components/InputSurface.d.ts +7 -0
  57. package/lib/components/InputSurface.js +26 -0
  58. package/lib/components/SendAction.d.ts +5 -0
  59. package/lib/components/SendAction.js +18 -0
  60. package/lib/components/StatusHint.d.ts +6 -0
  61. package/lib/components/StatusHint.js +16 -0
  62. package/lib/components/TextComposer.d.ts +5 -0
  63. package/lib/components/TextComposer.js +16 -0
  64. package/lib/components/VoiceModeSwitch.d.ts +7 -0
  65. package/lib/components/VoiceModeSwitch.js +14 -0
  66. package/lib/components/WaveformFeedback.d.ts +6 -0
  67. package/lib/components/WaveformFeedback.js +44 -0
  68. package/lib/components/icons.d.ts +11 -0
  69. package/lib/components/icons.js +29 -0
  70. package/lib/hooks/useAttachmentState.d.ts +6 -0
  71. package/lib/hooks/useAttachmentState.js +21 -0
  72. package/lib/hooks/useChatInputState.d.ts +14 -0
  73. package/lib/hooks/useChatInputState.js +25 -0
  74. package/lib/hooks/useTranscriptComposer.d.ts +1 -0
  75. package/lib/hooks/useTranscriptComposer.js +11 -0
  76. package/lib/hooks/useVoiceInputController.d.ts +14 -0
  77. package/lib/hooks/useVoiceInputController.js +133 -0
  78. package/lib/index.d.ts +23 -0
  79. package/lib/index.js +23 -0
  80. package/lib/recorder-processor.js +20 -0
  81. package/lib/services/agentSdkASRClient.d.ts +28 -0
  82. package/lib/services/agentSdkASRClient.js +25 -0
  83. package/lib/services/authTokenResolver.d.ts +7 -0
  84. package/lib/services/authTokenResolver.js +7 -0
  85. package/lib/stories/ChatInput.stories.d.ts +15 -0
  86. package/lib/stories/ChatInput.stories.js +38 -0
  87. package/lib/tests/agentSdkASRClient.test.d.ts +1 -0
  88. package/lib/tests/agentSdkASRClient.test.js +39 -0
  89. package/lib/tests/defaultTokens.test.d.ts +1 -0
  90. package/lib/tests/defaultTokens.test.js +16 -0
  91. package/lib/tests/transcriptComposer.test.d.ts +1 -0
  92. package/lib/tests/transcriptComposer.test.js +10 -0
  93. package/lib/tokens/defaultTokens.d.ts +3 -0
  94. package/lib/tokens/defaultTokens.js +91 -0
  95. package/lib/types/attachment.d.ts +8 -0
  96. package/lib/types/attachment.js +1 -0
  97. package/lib/types/chatInput.d.ts +139 -0
  98. package/lib/types/chatInput.js +1 -0
  99. package/lib/types/voice.d.ts +17 -0
  100. package/lib/types/voice.js +1 -0
  101. package/lib/voice/MicStateManager.d.ts +43 -0
  102. package/lib/voice/MicStateManager.js +155 -0
  103. package/lib/voice/Recorder.d.ts +44 -0
  104. package/lib/voice/Recorder.js +224 -0
  105. package/package.json +59 -0
@@ -0,0 +1,224 @@
1
+ import recorderProcessorUrl from "./recorder-processor.js?url";
2
+ export var RecorderEvents;
3
+ (function (RecorderEvents) {
4
+ RecorderEvents["Start"] = "start";
5
+ RecorderEvents["Stop"] = "stop";
6
+ RecorderEvents["Data"] = "data";
7
+ RecorderEvents["Level"] = "level";
8
+ RecorderEvents["Error"] = "error";
9
+ })(RecorderEvents || (RecorderEvents = {}));
10
+ export class Recorder {
11
+ audioContext;
12
+ mediaStream;
13
+ mediaStreamSource;
14
+ analyserNode;
15
+ recorderProcessorNode;
16
+ silentGainNode;
17
+ meterFrameId;
18
+ isRecording = false;
19
+ setupPromise = null;
20
+ listeners = {
21
+ [RecorderEvents.Start]: new Set(),
22
+ [RecorderEvents.Stop]: new Set(),
23
+ [RecorderEvents.Data]: new Set(),
24
+ [RecorderEvents.Level]: new Set(),
25
+ [RecorderEvents.Error]: new Set(),
26
+ };
27
+ initialized = false;
28
+ on(event, handler) {
29
+ this.listeners[event].add(handler);
30
+ return () => {
31
+ this.listeners[event].delete(handler);
32
+ };
33
+ }
34
+ emit(event, ...args) {
35
+ for (const handler of this.listeners[event]) {
36
+ ;
37
+ handler(...args);
38
+ }
39
+ }
40
+ clearListeners() {
41
+ for (const event of Object.values(RecorderEvents)) {
42
+ this.listeners[event].clear();
43
+ }
44
+ }
45
+ async init(stream) {
46
+ await this.ensureAudioGraph();
47
+ this.attachStream(stream);
48
+ this.initialized = true;
49
+ }
50
+ async ensureAudioGraph() {
51
+ if (!this.setupPromise) {
52
+ this.setupPromise = this.setupAudioGraph();
53
+ }
54
+ await this.setupPromise;
55
+ }
56
+ async setupAudioGraph() {
57
+ try {
58
+ this.audioContext = new AudioContext({ sampleRate: 16000 });
59
+ await this.audioContext.audioWorklet.addModule(recorderProcessorUrl);
60
+ this.recorderProcessorNode = new AudioWorkletNode(this.audioContext, "pmate-mic-recorder-processor");
61
+ this.analyserNode = this.audioContext.createAnalyser();
62
+ this.analyserNode.fftSize = 2048;
63
+ this.analyserNode.smoothingTimeConstant = 0.5;
64
+ this.silentGainNode = this.audioContext.createGain();
65
+ this.silentGainNode.gain.value = 0;
66
+ this.recorderProcessorNode.connect(this.silentGainNode);
67
+ this.silentGainNode.connect(this.audioContext.destination);
68
+ this.recorderProcessorNode.port.onmessage = (event) => {
69
+ if (!this.isRecording) {
70
+ return;
71
+ }
72
+ const { type, data } = event.data;
73
+ if (type === "pcm" && data) {
74
+ this.emit(RecorderEvents.Data, new Blob([data]));
75
+ }
76
+ };
77
+ }
78
+ catch (error) {
79
+ const normalized = toError(error);
80
+ this.emit(RecorderEvents.Error, normalized);
81
+ this.dispose();
82
+ throw normalized;
83
+ }
84
+ }
85
+ attachStream(stream) {
86
+ this.detachStream();
87
+ this.mediaStream = stream;
88
+ this.mediaStream.getAudioTracks().forEach((track) => {
89
+ track.enabled = false;
90
+ });
91
+ if (!this.audioContext || !this.recorderProcessorNode) {
92
+ throw new Error("Recorder audio graph is not ready");
93
+ }
94
+ this.mediaStreamSource = this.audioContext.createMediaStreamSource(this.mediaStream);
95
+ this.mediaStreamSource.connect(this.analyserNode);
96
+ this.mediaStreamSource.connect(this.recorderProcessorNode);
97
+ }
98
+ detachStream(options) {
99
+ if (this.mediaStreamSource) {
100
+ try {
101
+ this.mediaStreamSource.disconnect();
102
+ }
103
+ catch {
104
+ // ignore disconnect errors during teardown
105
+ }
106
+ this.mediaStreamSource = undefined;
107
+ }
108
+ if (this.mediaStream) {
109
+ if (options?.stopTracks) {
110
+ this.mediaStream.getTracks().forEach((track) => track.stop());
111
+ }
112
+ else {
113
+ this.mediaStream.getAudioTracks().forEach((track) => {
114
+ track.enabled = false;
115
+ });
116
+ }
117
+ }
118
+ this.mediaStream = undefined;
119
+ }
120
+ async startRecording() {
121
+ if (this.isRecording) {
122
+ return;
123
+ }
124
+ if (!this.audioContext || !this.mediaStream || !this.recorderProcessorNode) {
125
+ const error = new Error("Recorder not initialized");
126
+ this.emit(RecorderEvents.Error, error);
127
+ throw error;
128
+ }
129
+ this.isRecording = true;
130
+ try {
131
+ this.mediaStream.getAudioTracks().forEach((track) => {
132
+ track.enabled = true;
133
+ });
134
+ await this.audioContext.resume();
135
+ this.startMetering();
136
+ this.emit(RecorderEvents.Start);
137
+ }
138
+ catch (error) {
139
+ const normalized = toError(error);
140
+ this.emit(RecorderEvents.Error, normalized);
141
+ this.isRecording = false;
142
+ throw normalized;
143
+ }
144
+ }
145
+ stopRecording() {
146
+ if (!this.isRecording) {
147
+ return;
148
+ }
149
+ this.isRecording = false;
150
+ this.stopMetering();
151
+ this.emit(RecorderEvents.Stop);
152
+ }
153
+ dispose() {
154
+ this.isRecording = false;
155
+ this.stopMetering();
156
+ this.detachStream({ stopTracks: true });
157
+ this.analyserNode?.disconnect();
158
+ this.recorderProcessorNode?.disconnect();
159
+ this.silentGainNode?.disconnect();
160
+ void this.audioContext?.close();
161
+ this.mediaStream = undefined;
162
+ this.recorderProcessorNode = undefined;
163
+ this.audioContext = undefined;
164
+ this.mediaStreamSource = undefined;
165
+ this.analyserNode = undefined;
166
+ this.silentGainNode = undefined;
167
+ this.setupPromise = null;
168
+ this.initialized = false;
169
+ }
170
+ get isRecordingStatus() {
171
+ return this.isRecording;
172
+ }
173
+ startMetering() {
174
+ this.stopMetering();
175
+ const analyser = this.analyserNode;
176
+ if (!analyser) {
177
+ return;
178
+ }
179
+ const buffer = new Uint8Array(analyser.fftSize);
180
+ const draw = () => {
181
+ if (!this.isRecording || !this.analyserNode) {
182
+ this.stopMetering();
183
+ return;
184
+ }
185
+ this.analyserNode.getByteTimeDomainData(buffer);
186
+ this.emit(RecorderEvents.Level, toWaveformLevels(buffer, 18));
187
+ this.meterFrameId = requestAnimationFrame(draw);
188
+ };
189
+ this.meterFrameId = requestAnimationFrame(draw);
190
+ }
191
+ stopMetering() {
192
+ if (this.meterFrameId) {
193
+ cancelAnimationFrame(this.meterFrameId);
194
+ this.meterFrameId = undefined;
195
+ }
196
+ }
197
+ }
198
+ function toError(error) {
199
+ return error instanceof Error ? error : new Error(String(error));
200
+ }
201
+ function toWaveformLevels(buffer, bars) {
202
+ const samplesPerBar = Math.max(1, Math.floor(buffer.length / bars));
203
+ const levels = [];
204
+ for (let barIndex = 0; barIndex < bars; barIndex += 1) {
205
+ let sumSquares = 0;
206
+ let maxAmplitude = 0;
207
+ let counted = 0;
208
+ for (let sampleIndex = 0; sampleIndex < samplesPerBar; sampleIndex += 1) {
209
+ const index = barIndex * samplesPerBar + sampleIndex;
210
+ if (index >= buffer.length) {
211
+ break;
212
+ }
213
+ const normalized = (buffer[index] - 128) / 128;
214
+ const amplitude = Math.abs(normalized);
215
+ sumSquares += normalized * normalized;
216
+ maxAmplitude = Math.max(maxAmplitude, amplitude);
217
+ counted += 1;
218
+ }
219
+ const rms = counted > 0 ? Math.sqrt(sumSquares / counted) : 0;
220
+ const boosted = Math.min(1, Math.max(rms, maxAmplitude) * 9);
221
+ levels.push(0.08 + boosted * 0.92);
222
+ }
223
+ return levels;
224
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@pmate/chat-sdk",
3
+ "version": "0.1.1",
4
+ "description": "Cross-platform PMate chat input components, voice input primitives, and ASR helpers",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/parrot-mate/pmate-mono.git"
11
+ },
12
+ "homepage": "https://github.com/parrot-mate/pmate-mono/tree/main/packages/chat-sdk",
13
+ "files": [
14
+ "lib",
15
+ "dist"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./lib/index.d.ts",
20
+ "import": "./lib/index.js",
21
+ "default": "./lib/index.js"
22
+ }
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.build.json && cp src/voice/recorder-processor.js lib/recorder-processor.js",
29
+ "prepack": "pnpm run build",
30
+ "npm:publish": "pnpm run build && npm publish --access public --registry https://registry.npmjs.org/",
31
+ "test": "vitest run",
32
+ "storybook": "storybook dev -p 6009",
33
+ "build-storybook": "storybook build --output-dir dist"
34
+ },
35
+ "dependencies": {
36
+ "@emoji-mart/data": "^1.2.1",
37
+ "@emoji-mart/react": "^1.1.1",
38
+ "@pmate/agent-sdk": "workspace:*",
39
+ "@pmate/utils": "workspace:*",
40
+ "jotai": "catalog:"
41
+ },
42
+ "peerDependencies": {
43
+ "react": "*",
44
+ "react-dom": "*"
45
+ },
46
+ "devDependencies": {
47
+ "@storybook/addon-docs": "^9.0.17",
48
+ "@storybook/react-vite": "9.0.17",
49
+ "@tailwindcss/vite": "catalog:",
50
+ "@types/react": "catalog:",
51
+ "@types/react-dom": "catalog:",
52
+ "react": "catalog:",
53
+ "react-dom": "catalog:",
54
+ "storybook": "^9.0.17",
55
+ "tailwindcss": "catalog:",
56
+ "typescript": "5.9.2",
57
+ "vitest": "^4.0.17"
58
+ }
59
+ }