scribe-widget 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,495 @@
1
+ :host {
2
+ all: initial;
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
4
+ }
5
+
6
+ * {
7
+ box-sizing: border-box;
8
+ margin: 0;
9
+ padding: 0;
10
+ }
11
+
12
+ .scribe-panel {
13
+ position: fixed;
14
+ bottom: 20px;
15
+ right: 20px;
16
+ background: #ffffff;
17
+ border-radius: 16px;
18
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.05);
19
+ z-index: 2147483647;
20
+ overflow: hidden;
21
+ min-width: 320px;
22
+ }
23
+
24
+ .panel-header {
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: space-between;
28
+ padding: 8px 12px;
29
+ background: #fafafa;
30
+ border-bottom: 1px solid #eee;
31
+ cursor: grab;
32
+ }
33
+
34
+ .panel-header:active {
35
+ cursor: grabbing;
36
+ }
37
+
38
+ .drag-handle {
39
+ display: flex;
40
+ gap: 2px;
41
+ flex-wrap: wrap;
42
+ width: 16px;
43
+ opacity: 0.4;
44
+ }
45
+
46
+ .drag-handle span {
47
+ width: 4px;
48
+ height: 4px;
49
+ background: #666;
50
+ border-radius: 50%;
51
+ }
52
+
53
+ .header-actions {
54
+ display: flex;
55
+ gap: 8px;
56
+ }
57
+
58
+ .header-btn {
59
+ background: none;
60
+ border: none;
61
+ cursor: pointer;
62
+ padding: 4px;
63
+ color: #666;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ width: 24px;
68
+ height: 24px;
69
+ }
70
+
71
+ .header-btn:hover {
72
+ color: #333;
73
+ }
74
+
75
+ .header-btn svg {
76
+ width: 16px;
77
+ height: 16px;
78
+ }
79
+
80
+ .panel-content {
81
+ padding: 20px 24px;
82
+ }
83
+
84
+ /* Idle State */
85
+ .idle-state {
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 24px;
89
+ }
90
+
91
+ .logo {
92
+ display: flex;
93
+ align-items: center;
94
+ gap: 8px;
95
+ }
96
+
97
+ .logo-icon {
98
+ width: 32px;
99
+ height: 32px;
100
+ }
101
+
102
+ .logo-icon svg {
103
+ width: 100%;
104
+ height: 100%;
105
+ }
106
+
107
+ .logo-text {
108
+ font-size: 20px;
109
+ font-weight: 600;
110
+ color: #2563eb;
111
+ }
112
+
113
+ .start-btn {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: 8px;
117
+ background: #2563eb;
118
+ color: white;
119
+ border: none;
120
+ padding: 12px 24px;
121
+ border-radius: 24px;
122
+ font-size: 16px;
123
+ font-weight: 500;
124
+ cursor: pointer;
125
+ transition: background 0.2s;
126
+ }
127
+
128
+ .start-btn:hover {
129
+ background: #1d4ed8;
130
+ }
131
+
132
+ .start-btn:disabled {
133
+ background: #93c5fd;
134
+ cursor: not-allowed;
135
+ }
136
+
137
+ .mic-icon {
138
+ width: 20px;
139
+ height: 20px;
140
+ }
141
+
142
+ .mic-icon svg {
143
+ width: 100%;
144
+ height: 100%;
145
+ }
146
+
147
+ /* Recording State */
148
+ .recording-state {
149
+ display: flex;
150
+ align-items: center;
151
+ gap: 16px;
152
+ }
153
+
154
+ .recording-indicator {
155
+ display: flex;
156
+ align-items: center;
157
+ gap: 12px;
158
+ color: #666;
159
+ }
160
+
161
+ .recording-icon {
162
+ width: 24px;
163
+ height: 24px;
164
+ color: #666;
165
+ }
166
+
167
+ .recording-icon svg {
168
+ width: 100%;
169
+ height: 100%;
170
+ }
171
+
172
+ .recording-icon.active {
173
+ color: #ef4444;
174
+ animation: pulse 1.5s infinite;
175
+ }
176
+
177
+ @keyframes pulse {
178
+ 0%, 100% { opacity: 1; }
179
+ 50% { opacity: 0.5; }
180
+ }
181
+
182
+ .timer {
183
+ font-size: 20px;
184
+ font-weight: 500;
185
+ font-variant-numeric: tabular-nums;
186
+ color: #374151;
187
+ }
188
+
189
+ .recording-actions {
190
+ display: flex;
191
+ gap: 8px;
192
+ margin-left: auto;
193
+ }
194
+
195
+ .resume-btn {
196
+ background: white;
197
+ color: #2563eb;
198
+ border: 2px solid #2563eb;
199
+ padding: 10px 20px;
200
+ border-radius: 8px;
201
+ font-size: 14px;
202
+ font-weight: 500;
203
+ cursor: pointer;
204
+ transition: all 0.2s;
205
+ }
206
+
207
+ .resume-btn:hover {
208
+ background: #eff6ff;
209
+ }
210
+
211
+ .pause-btn {
212
+ background: white;
213
+ color: #f59e0b;
214
+ border: 2px solid #f59e0b;
215
+ padding: 10px 20px;
216
+ border-radius: 8px;
217
+ font-size: 14px;
218
+ font-weight: 500;
219
+ cursor: pointer;
220
+ transition: all 0.2s;
221
+ }
222
+
223
+ .pause-btn:hover {
224
+ background: #fffbeb;
225
+ }
226
+
227
+ .stop-btn {
228
+ background: #ef4444;
229
+ color: white;
230
+ border: none;
231
+ padding: 10px 16px;
232
+ border-radius: 8px;
233
+ cursor: pointer;
234
+ display: flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ transition: background 0.2s;
238
+ }
239
+
240
+ .stop-btn:hover {
241
+ background: #dc2626;
242
+ }
243
+
244
+ .stop-icon {
245
+ width: 16px;
246
+ height: 16px;
247
+ background: white;
248
+ border-radius: 2px;
249
+ }
250
+
251
+ /* Processing State */
252
+ .processing-state {
253
+ display: flex;
254
+ flex-direction: column;
255
+ align-items: center;
256
+ gap: 16px;
257
+ padding: 20px;
258
+ }
259
+
260
+ .spinner {
261
+ width: 40px;
262
+ height: 40px;
263
+ border: 3px solid #e5e7eb;
264
+ border-top-color: #2563eb;
265
+ border-radius: 50%;
266
+ animation: spin 1s linear infinite;
267
+ }
268
+
269
+ @keyframes spin {
270
+ to { transform: rotate(360deg); }
271
+ }
272
+
273
+ .processing-text {
274
+ color: #6b7280;
275
+ font-size: 14px;
276
+ }
277
+
278
+ /* Results State */
279
+ .results-state {
280
+ display: flex;
281
+ flex-direction: column;
282
+ gap: 16px;
283
+ max-height: 400px;
284
+ }
285
+
286
+ .results-header {
287
+ display: flex;
288
+ align-items: center;
289
+ justify-content: space-between;
290
+ }
291
+
292
+ .results-title {
293
+ font-size: 16px;
294
+ font-weight: 600;
295
+ color: #111827;
296
+ }
297
+
298
+ .new-recording-btn {
299
+ background: #2563eb;
300
+ color: white;
301
+ border: none;
302
+ padding: 8px 16px;
303
+ border-radius: 6px;
304
+ font-size: 13px;
305
+ cursor: pointer;
306
+ transition: background 0.2s;
307
+ }
308
+
309
+ .new-recording-btn:hover {
310
+ background: #1d4ed8;
311
+ }
312
+
313
+ .results-content {
314
+ overflow-y: auto;
315
+ max-height: 300px;
316
+ }
317
+
318
+ .transcript-section {
319
+ margin-bottom: 16px;
320
+ }
321
+
322
+ .section-title {
323
+ font-size: 13px;
324
+ font-weight: 600;
325
+ color: #6b7280;
326
+ text-transform: uppercase;
327
+ letter-spacing: 0.05em;
328
+ margin-bottom: 8px;
329
+ }
330
+
331
+ .transcript-text {
332
+ font-size: 14px;
333
+ line-height: 1.6;
334
+ color: #374151;
335
+ background: #f9fafb;
336
+ padding: 12px;
337
+ border-radius: 8px;
338
+ white-space: pre-wrap;
339
+ }
340
+
341
+ /* Error State */
342
+ .error-state {
343
+ display: flex;
344
+ flex-direction: column;
345
+ align-items: center;
346
+ gap: 12px;
347
+ padding: 20px;
348
+ text-align: center;
349
+ }
350
+
351
+ .error-icon {
352
+ width: 48px;
353
+ height: 48px;
354
+ color: #ef4444;
355
+ }
356
+
357
+ .error-icon svg {
358
+ width: 100%;
359
+ height: 100%;
360
+ }
361
+
362
+ .error-message {
363
+ color: #ef4444;
364
+ font-size: 14px;
365
+ }
366
+
367
+ .retry-btn {
368
+ background: #ef4444;
369
+ color: white;
370
+ border: none;
371
+ padding: 10px 20px;
372
+ border-radius: 8px;
373
+ font-size: 14px;
374
+ cursor: pointer;
375
+ transition: background 0.2s;
376
+ }
377
+
378
+ .retry-btn:hover {
379
+ background: #dc2626;
380
+ }
381
+
382
+ /* Permission Request */
383
+ .permission-state {
384
+ display: flex;
385
+ flex-direction: column;
386
+ align-items: center;
387
+ gap: 16px;
388
+ padding: 20px;
389
+ text-align: center;
390
+ }
391
+
392
+ .permission-icon {
393
+ width: 48px;
394
+ height: 48px;
395
+ color: #2563eb;
396
+ }
397
+
398
+ .permission-icon svg {
399
+ width: 100%;
400
+ height: 100%;
401
+ }
402
+
403
+ .permission-text {
404
+ color: #374151;
405
+ font-size: 14px;
406
+ line-height: 1.5;
407
+ }
408
+
409
+ .permission-btn {
410
+ background: #2563eb;
411
+ color: white;
412
+ border: none;
413
+ padding: 12px 24px;
414
+ border-radius: 8px;
415
+ font-size: 14px;
416
+ font-weight: 500;
417
+ cursor: pointer;
418
+ transition: background 0.2s;
419
+ }
420
+
421
+ .permission-btn:hover {
422
+ background: #1d4ed8;
423
+ }
424
+
425
+ /* Config State */
426
+ .config-state {
427
+ display: flex;
428
+ flex-direction: column;
429
+ gap: 20px;
430
+ }
431
+
432
+ .config-state .logo {
433
+ justify-content: center;
434
+ }
435
+
436
+ .config-form {
437
+ display: flex;
438
+ flex-direction: column;
439
+ gap: 12px;
440
+ }
441
+
442
+ .config-form .form-group {
443
+ display: flex;
444
+ flex-direction: column;
445
+ gap: 4px;
446
+ }
447
+
448
+ .config-form label {
449
+ font-size: 13px;
450
+ font-weight: 500;
451
+ color: #374151;
452
+ }
453
+
454
+ .config-form input {
455
+ padding: 10px 12px;
456
+ border: 1px solid #d1d5db;
457
+ border-radius: 8px;
458
+ font-size: 14px;
459
+ color: #111827;
460
+ background: #fff;
461
+ transition: border-color 0.2s, box-shadow 0.2s;
462
+ }
463
+
464
+ .config-form input:focus {
465
+ outline: none;
466
+ border-color: #2563eb;
467
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
468
+ }
469
+
470
+ .config-form input::placeholder {
471
+ color: #9ca3af;
472
+ }
473
+
474
+ .config-error {
475
+ color: #ef4444;
476
+ font-size: 13px;
477
+ margin: 0;
478
+ }
479
+
480
+ .config-submit-btn {
481
+ background: #2563eb;
482
+ color: white;
483
+ border: none;
484
+ padding: 12px 24px;
485
+ border-radius: 8px;
486
+ font-size: 14px;
487
+ font-weight: 500;
488
+ cursor: pointer;
489
+ transition: background 0.2s;
490
+ margin-top: 4px;
491
+ }
492
+
493
+ .config-submit-btn:hover {
494
+ background: #1d4ed8;
495
+ }
package/src/types.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { GetSessionStatusResponse } from 'med-scribe-alliance-ts-sdk';
2
+
3
+ export type WidgetState = 'idle' | 'permission' | 'recording' | 'paused' | 'processing' | 'results' | 'error';
4
+
5
+ export interface ScribeWidgetConfig {
6
+ apiKey: string;
7
+ baseUrl: string;
8
+ templates?: string[];
9
+ languageHint?: string[];
10
+ position?: { bottom?: number; right?: number; top?: number; left?: number };
11
+ onResult?: (result: GetSessionStatusResponse) => void;
12
+ onError?: (error: Error) => void;
13
+ debug?: boolean;
14
+ }
@@ -0,0 +1,6 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare module '*.css?inline' {
4
+ const content: string;
5
+ export default content;
6
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "resolveJsonModule": true,
11
+ "isolatedModules": true,
12
+ "noEmit": true,
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true
18
+ },
19
+ "include": ["src"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import { resolve } from 'path';
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ build: {
8
+ lib: {
9
+ entry: resolve(__dirname, 'src/index.tsx'),
10
+ name: 'EkaScribe',
11
+ fileName: (format) => `scribe-widget.${format}.js`,
12
+ formats: ['umd', 'es'],
13
+ },
14
+ rollupOptions: {
15
+ // Don't externalize React - bundle it for standalone use
16
+ output: {
17
+ globals: {},
18
+ },
19
+ },
20
+ cssCodeSplit: false,
21
+ minify: 'terser',
22
+ },
23
+ server: {
24
+ port: 3000,
25
+ open: true,
26
+ },
27
+ });