@corti/dictation-web 0.0.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 (121) hide show
  1. package/.editorconfig +29 -0
  2. package/.eslintrc.json +16 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.storybook/main.js +8 -0
  5. package/README.md +120 -0
  6. package/demo/index.html +98 -0
  7. package/dist/src/CortiDictation.d.ts +19 -0
  8. package/dist/src/CortiDictation.js +137 -0
  9. package/dist/src/CortiDictation.js.map +1 -0
  10. package/dist/src/DictationService.d.ts +13 -0
  11. package/dist/src/DictationService.js +70 -0
  12. package/dist/src/DictationService.js.map +1 -0
  13. package/dist/src/RecorderManager.d.ts +20 -0
  14. package/dist/src/RecorderManager.js +85 -0
  15. package/dist/src/RecorderManager.js.map +1 -0
  16. package/dist/src/audioRecorderManager.d.ts +17 -0
  17. package/dist/src/audioRecorderManager.js +78 -0
  18. package/dist/src/audioRecorderManager.js.map +1 -0
  19. package/dist/src/audioService.d.ts +6 -0
  20. package/dist/src/audioService.js +21 -0
  21. package/dist/src/audioService.js.map +1 -0
  22. package/dist/src/componentStyles.d.ts +1 -0
  23. package/dist/src/componentStyles.js +51 -0
  24. package/dist/src/componentStyles.js.map +1 -0
  25. package/dist/src/components/audio-visualiser.d.ts +12 -0
  26. package/dist/src/components/audio-visualiser.js +60 -0
  27. package/dist/src/components/audio-visualiser.js.map +1 -0
  28. package/dist/src/components/settings-menu.d.ts +15 -0
  29. package/dist/src/components/settings-menu.js +148 -0
  30. package/dist/src/components/settings-menu.js.map +1 -0
  31. package/dist/src/components/visualiser.d.ts +7 -0
  32. package/dist/src/components/visualiser.js +62 -0
  33. package/dist/src/components/visualiser.js.map +1 -0
  34. package/dist/src/constants.d.ts +3 -0
  35. package/dist/src/constants.js +9 -0
  36. package/dist/src/constants.js.map +1 -0
  37. package/dist/src/corti-dictation.d.ts +1 -0
  38. package/dist/src/corti-dictation.js +3 -0
  39. package/dist/src/corti-dictation.js.map +1 -0
  40. package/dist/src/dictationService.d.ts +13 -0
  41. package/dist/src/dictationService.js +70 -0
  42. package/dist/src/dictationService.js.map +1 -0
  43. package/dist/src/icons/icons.d.ts +17 -0
  44. package/dist/src/icons/icons.js +153 -0
  45. package/dist/src/icons/icons.js.map +1 -0
  46. package/dist/src/icons/index.d.ts +0 -0
  47. package/dist/src/icons/index.js +2 -0
  48. package/dist/src/icons/index.js.map +1 -0
  49. package/dist/src/icons/micOn.d.ts +7 -0
  50. package/dist/src/icons/micOn.js +25 -0
  51. package/dist/src/icons/micOn.js.map +1 -0
  52. package/dist/src/index.d.ts +20 -0
  53. package/dist/src/index.js +147 -0
  54. package/dist/src/index.js.map +1 -0
  55. package/dist/src/mediaRecorderService.d.ts +6 -0
  56. package/dist/src/mediaRecorderService.js +31 -0
  57. package/dist/src/mediaRecorderService.js.map +1 -0
  58. package/dist/src/mic-selector.d.ts +18 -0
  59. package/dist/src/mic-selector.js +131 -0
  60. package/dist/src/mic-selector.js.map +1 -0
  61. package/dist/src/settings-menu.d.ts +18 -0
  62. package/dist/src/settings-menu.js +131 -0
  63. package/dist/src/settings-menu.js.map +1 -0
  64. package/dist/src/settings-popover.d.ts +18 -0
  65. package/dist/src/settings-popover.js +131 -0
  66. package/dist/src/settings-popover.js.map +1 -0
  67. package/dist/src/settings.d.ts +18 -0
  68. package/dist/src/settings.js +131 -0
  69. package/dist/src/settings.js.map +1 -0
  70. package/dist/src/styles/ComponentStyles.d.ts +2 -0
  71. package/dist/src/styles/ComponentStyles.js +52 -0
  72. package/dist/src/styles/ComponentStyles.js.map +1 -0
  73. package/dist/src/styles/buttons.d.ts +2 -0
  74. package/dist/src/styles/buttons.js +58 -0
  75. package/dist/src/styles/buttons.js.map +1 -0
  76. package/dist/src/styles/callout.d.ts +2 -0
  77. package/dist/src/styles/callout.js +26 -0
  78. package/dist/src/styles/callout.js.map +1 -0
  79. package/dist/src/styles/select.d.ts +2 -0
  80. package/dist/src/styles/select.js +36 -0
  81. package/dist/src/styles/select.js.map +1 -0
  82. package/dist/src/styles/theme.d.ts +2 -0
  83. package/dist/src/styles/theme.js +74 -0
  84. package/dist/src/styles/theme.js.map +1 -0
  85. package/dist/src/types.d.ts +20 -0
  86. package/dist/src/types.js +2 -0
  87. package/dist/src/types.js.map +1 -0
  88. package/dist/src/utils.d.ts +31 -0
  89. package/dist/src/utils.js +77 -0
  90. package/dist/src/utils.js.map +1 -0
  91. package/dist/stories/index.stories.d.ts +33 -0
  92. package/dist/stories/index.stories.js +37 -0
  93. package/dist/stories/index.stories.js.map +1 -0
  94. package/dist/test/corti-dictation.test.d.ts +1 -0
  95. package/dist/test/corti-dictation.test.js +100 -0
  96. package/dist/test/corti-dictation.test.js.map +1 -0
  97. package/dist/tsconfig.tsbuildinfo +1 -0
  98. package/docs/DEV_README.md +80 -0
  99. package/package.json +92 -0
  100. package/src/DictationService.ts +99 -0
  101. package/src/RecorderManager.ts +114 -0
  102. package/src/audioService.ts +25 -0
  103. package/src/components/audio-visualiser.ts +56 -0
  104. package/src/components/settings-menu.ts +152 -0
  105. package/src/constants.ts +10 -0
  106. package/src/corti-dictation.ts +3 -0
  107. package/src/icons/icons.ts +141 -0
  108. package/src/icons/index.ts +0 -0
  109. package/src/index.ts +154 -0
  110. package/src/styles/ComponentStyles.ts +53 -0
  111. package/src/styles/buttons.ts +59 -0
  112. package/src/styles/callout.ts +27 -0
  113. package/src/styles/select.ts +37 -0
  114. package/src/styles/theme.ts +75 -0
  115. package/src/types.ts +28 -0
  116. package/src/utils.ts +83 -0
  117. package/stories/index.stories.ts +60 -0
  118. package/test/corti-dictation.test.ts +124 -0
  119. package/tsconfig.json +22 -0
  120. package/web-dev-server.config.js +27 -0
  121. package/web-test-runner.config.js +41 -0
@@ -0,0 +1,52 @@
1
+ import { css } from 'lit';
2
+ const ComponentStyles = css `
3
+ .wrapper {
4
+ background-color: var(--card-background);
5
+ border: 1px solid var(--card-border-color);
6
+ border-radius: var(--card-border-radius);
7
+ box-shadow: var(--card-box-shadow);
8
+ padding: var(--card-padding);
9
+ display: flex;
10
+ width: min-content;
11
+ gap: 4px;
12
+ height: 46px;
13
+ box-sizing: border-box;
14
+ overflow: hidden;
15
+ }
16
+ h2 {
17
+ margin: 0 0 10px;
18
+ font-size: 1rem;
19
+ font-weight: 500;
20
+ }
21
+ label {
22
+ font-size: 0.9rem;
23
+ margin-right: 8px;
24
+ }
25
+ select {
26
+ padding: 4px 6px;
27
+ font-size: 0.9rem;
28
+ border: 1px solid var(--card-border-color);
29
+ border-radius: 4px;
30
+ background-color: var(--card-background);
31
+ color: inherit;
32
+ }
33
+
34
+ .visualiser {
35
+ width: 16px;
36
+ height: 100%;
37
+ background: var(--visualiser-background);
38
+ margin-top: 8px;
39
+ border-radius: 5px;
40
+ overflow: hidden;
41
+ display: flex;
42
+ align-items: flex-end;
43
+ }
44
+ .level {
45
+ height: 100%;
46
+ width: 100%;
47
+ background: var(--visualiser-level-color);
48
+ transition: width 0.1s ease-in-out;
49
+ }
50
+ `;
51
+ export default ComponentStyles;
52
+ //# sourceMappingURL=ComponentStyles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentStyles.js","sourceRoot":"","sources":["../../../src/styles/ComponentStyles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgD1B,CAAC;AAEF,eAAe,eAAe,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst ComponentStyles = css`\n .wrapper {\n background-color: var(--card-background);\n border: 1px solid var(--card-border-color);\n border-radius: var(--card-border-radius);\n box-shadow: var(--card-box-shadow);\n padding: var(--card-padding);\n display: flex;\n width: min-content;\n gap: 4px;\n height: 46px;\n box-sizing: border-box;\n overflow: hidden;\n }\n h2 {\n margin: 0 0 10px;\n font-size: 1rem;\n font-weight: 500;\n }\n label {\n font-size: 0.9rem;\n margin-right: 8px;\n }\n select {\n padding: 4px 6px;\n font-size: 0.9rem;\n border: 1px solid var(--card-border-color);\n border-radius: 4px;\n background-color: var(--card-background);\n color: inherit;\n }\n\n .visualiser {\n width: 16px;\n height: 100%;\n background: var(--visualiser-background);\n margin-top: 8px;\n border-radius: 5px;\n overflow: hidden;\n display: flex;\n align-items: flex-end;\n }\n .level {\n height: 100%;\n width: 100%;\n background: var(--visualiser-level-color);\n transition: width 0.1s ease-in-out;\n }\n`;\n\nexport default ComponentStyles;\n"]}
@@ -0,0 +1,2 @@
1
+ declare const ButtonStyles: import("lit").CSSResult;
2
+ export default ButtonStyles;
@@ -0,0 +1,58 @@
1
+ import { css } from 'lit';
2
+ const ButtonStyles = css `
3
+ /* Default (plain) button styling */
4
+ button,
5
+ .button {
6
+ background: var(--action-plain-background);
7
+ /* border: 1px solid var(--action-plain-border-color); */
8
+ border: none;
9
+ color: var(--component-text-color);
10
+ cursor: pointer;
11
+ padding: 8px;
12
+ border-radius: var(--card-inner-border-radius);
13
+ display: inline-flex;
14
+ gap: 4px;
15
+ align-items: center;
16
+ justify-content: center;
17
+ transition: background 0.3s ease;
18
+ font-family: var(--component-font-family);
19
+ }
20
+
21
+ button:hover,
22
+ .button:hover {
23
+ background: var(--action-plain-background-hover);
24
+ }
25
+
26
+ button:focus-visible {
27
+ outline: 2px solid var(--action-accent-background);
28
+ outline-offset: 2px;
29
+ }
30
+
31
+ /* Accent variant */
32
+ button.accent,
33
+ .button.accent {
34
+ background: var(--action-accent-background);
35
+ color: var(--action-accent-text-color);
36
+ border: none;
37
+ }
38
+
39
+ button.accent:hover,
40
+ .button.accent:hover {
41
+ background: var(--action-accent-background-hover);
42
+ }
43
+
44
+ /* Accent variant */
45
+ button.red,
46
+ .button.red {
47
+ background: var(--action-red-background);
48
+ color: var(--action-red-text-color);
49
+ border: none;
50
+ }
51
+
52
+ button.red:hover,
53
+ .button.red:hover {
54
+ background: var(--action-red-background-hover);
55
+ }
56
+ `;
57
+ export default ButtonStyles;
58
+ //# sourceMappingURL=buttons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buttons.js","sourceRoot":"","sources":["../../../src/styles/buttons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDvB,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst ButtonStyles = css`\n /* Default (plain) button styling */\n button,\n .button {\n background: var(--action-plain-background);\n /* border: 1px solid var(--action-plain-border-color); */\n border: none;\n color: var(--component-text-color);\n cursor: pointer;\n padding: 8px;\n border-radius: var(--card-inner-border-radius);\n display: inline-flex;\n gap: 4px;\n align-items: center;\n justify-content: center;\n transition: background 0.3s ease;\n font-family: var(--component-font-family);\n }\n\n button:hover,\n .button:hover {\n background: var(--action-plain-background-hover);\n }\n\n button:focus-visible {\n outline: 2px solid var(--action-accent-background);\n outline-offset: 2px;\n }\n\n /* Accent variant */\n button.accent,\n .button.accent {\n background: var(--action-accent-background);\n color: var(--action-accent-text-color);\n border: none;\n }\n\n button.accent:hover,\n .button.accent:hover {\n background: var(--action-accent-background-hover);\n }\n\n /* Accent variant */\n button.red,\n .button.red {\n background: var(--action-red-background);\n color: var(--action-red-text-color);\n border: none;\n }\n\n button.red:hover,\n .button.red:hover {\n background: var(--action-red-background-hover);\n }\n`;\n\nexport default ButtonStyles;\n"]}
@@ -0,0 +1,2 @@
1
+ declare const CalloutStyles: import("lit").CSSResult;
2
+ export default CalloutStyles;
@@ -0,0 +1,26 @@
1
+ import { css } from 'lit';
2
+ const CalloutStyles = css `
3
+ .callout {
4
+ background: var(--callout-accent-background);
5
+ border: 1px solid var(--callout-accent-border);
6
+ color: var(--callout-accent-text);
7
+ padding: 8px;
8
+ border-radius: var(--card-inner-border-radius);
9
+ display: flex;
10
+ font-size: 0.9rem;
11
+ gap: 8px;
12
+ align-items: flex-start;
13
+ &.red {
14
+ background: var(--callout-red-background);
15
+ border: 1px solid var(--callout-red-border);
16
+ color: var(--callout-red-text);
17
+ }
18
+ &.orange {
19
+ background: var(--callout-orange-background);
20
+ border: 1px solid var(--callout-orange-border);
21
+ color: var(--callout-orange-text);
22
+ }
23
+ }
24
+ `;
25
+ export default CalloutStyles;
26
+ //# sourceMappingURL=callout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callout.js","sourceRoot":"","sources":["../../../src/styles/callout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,aAAa,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;CAsBxB,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst CalloutStyles = css`\n .callout {\n background: var(--callout-accent-background);\n border: 1px solid var(--callout-accent-border);\n color: var(--callout-accent-text);\n padding: 8px;\n border-radius: var(--card-inner-border-radius);\n display: flex;\n font-size: 0.9rem;\n gap: 8px;\n align-items: flex-start;\n &.red {\n background: var(--callout-red-background);\n border: 1px solid var(--callout-red-border);\n color: var(--callout-red-text);\n }\n &.orange {\n background: var(--callout-orange-background);\n border: 1px solid var(--callout-orange-border);\n color: var(--callout-orange-text);\n }\n }\n`;\n\nexport default CalloutStyles;\n"]}
@@ -0,0 +1,2 @@
1
+ declare const SelectStyles: import("lit").CSSResult;
2
+ export default SelectStyles;
@@ -0,0 +1,36 @@
1
+ import { css } from 'lit';
2
+ const SelectStyles = css `
3
+ label {
4
+ display: block;
5
+ font-size: 0.8rem;
6
+ padding-bottom: 0.5rem;
7
+ font-weight: 500;
8
+ color: var(--component-text-color);
9
+ pointer-events: none;
10
+ }
11
+ select {
12
+ background: var(--card-background);
13
+ color: var(--component-text-color);
14
+ border: 1px solid var(--card-border-color);
15
+ padding: var(--card-padding);
16
+ border-radius: var(--card-inner-border-radius);
17
+ outline: none;
18
+ width: 100%;
19
+ }
20
+
21
+ select:disabled {
22
+ opacity: 0.5;
23
+ cursor: not-allowed;
24
+ }
25
+
26
+ select:hover {
27
+ background: var(--action-plain-background-hover);
28
+ }
29
+
30
+ select:focus-visible {
31
+ outline: 2px solid var(--action-accent-background);
32
+ /* outline-offset: 2px; */
33
+ }
34
+ `;
35
+ export default SelectStyles;
36
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../../src/styles/select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCvB,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst SelectStyles = css`\n label {\n display: block;\n font-size: 0.8rem;\n padding-bottom: 0.5rem;\n font-weight: 500;\n color: var(--component-text-color);\n pointer-events: none;\n }\n select {\n background: var(--card-background);\n color: var(--component-text-color);\n border: 1px solid var(--card-border-color);\n padding: var(--card-padding);\n border-radius: var(--card-inner-border-radius);\n outline: none;\n width: 100%;\n }\n\n select:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n select:hover {\n background: var(--action-plain-background-hover);\n }\n\n select:focus-visible {\n outline: 2px solid var(--action-accent-background);\n /* outline-offset: 2px; */\n }\n`;\n\nexport default SelectStyles;\n"]}
@@ -0,0 +1,2 @@
1
+ declare const ThemeStyles: import("lit").CSSResult;
2
+ export default ThemeStyles;
@@ -0,0 +1,74 @@
1
+ import { css } from 'lit';
2
+ const ThemeStyles = css `
3
+ :host {
4
+ /* Component Defaults */
5
+ --component-font-family: 'Segoe UI', Roboto, sans-serif;
6
+ --component-text-color: #333;
7
+
8
+ /* Card Defaults */
9
+ --card-background: #fff;
10
+ --card-border-color: #ddd;
11
+ --card-padding: 4px;
12
+ --card-border-radius: 8px;
13
+ --card-inner-border-radius: 6px;
14
+ --card-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
15
+
16
+ /* Actions Defaults */
17
+ --action-plain-border-color: #ccc;
18
+ --action-plain-background-hover: #ddd;
19
+
20
+ --action-accent-background: #007bff;
21
+ --action-accent-background-hover: #0056b3;
22
+ --action-accent-text-color: #fff;
23
+
24
+ --action-red-background: #dc3545;
25
+ --action-red-background-hover: #bd2130;
26
+ --action-red-text-color: #fff;
27
+
28
+ /* Callout Defaults */
29
+ --callout-accent-background: #007bff33;
30
+ --callout-accent-border: #007bff99;
31
+ --callout-accent-text: #007bff;
32
+
33
+ --callout-red-background: #dc354533;
34
+ --callout-red-border: #dc354599;
35
+ --callout-red-text: #dc3545;
36
+
37
+ --callout-orange-background: #fd7e1433;
38
+ --callout-orange-border: #fd7e1499;
39
+ --callout-orange-text: #fd7e14;
40
+
41
+ /* Visualiser Defaults */
42
+ --visualiser-background: #e0e0e0;
43
+ --visualiser-level-color: #28a745;
44
+ }
45
+
46
+ @media (prefers-color-scheme: dark) {
47
+ :host {
48
+ /* Component Dark */
49
+ --component-text-color: #eee;
50
+
51
+ /* Card Dark */
52
+ --card-background: #333;
53
+ --card-border-color: #555;
54
+
55
+ /* Actions Dark */
56
+ --action-plain-border-color: #555;
57
+ --action-plain-background: #333;
58
+ --action-plain-background-hover: #444;
59
+
60
+ --action-accent-background: #0056b3;
61
+ --action-accent-background-hover: #003d80;
62
+
63
+ /* Visualiser Dark */
64
+ --visualiser-background: #fff;
65
+ }
66
+ }
67
+ :host {
68
+ box-sizing: border-box;
69
+ font-family: var(--component-font-family);
70
+ color: var(--component-text-color);
71
+ }
72
+ `;
73
+ export default ThemeStyles;
74
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../../src/styles/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,WAAW,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEtB,CAAC;AAEF,eAAe,WAAW,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst ThemeStyles = css`\n :host {\n /* Component Defaults */\n --component-font-family: 'Segoe UI', Roboto, sans-serif;\n --component-text-color: #333;\n\n /* Card Defaults */\n --card-background: #fff;\n --card-border-color: #ddd;\n --card-padding: 4px;\n --card-border-radius: 8px;\n --card-inner-border-radius: 6px;\n --card-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n\n /* Actions Defaults */\n --action-plain-border-color: #ccc;\n --action-plain-background-hover: #ddd;\n\n --action-accent-background: #007bff;\n --action-accent-background-hover: #0056b3;\n --action-accent-text-color: #fff;\n\n --action-red-background: #dc3545;\n --action-red-background-hover: #bd2130;\n --action-red-text-color: #fff;\n\n /* Callout Defaults */\n --callout-accent-background: #007bff33;\n --callout-accent-border: #007bff99;\n --callout-accent-text: #007bff;\n\n --callout-red-background: #dc354533;\n --callout-red-border: #dc354599;\n --callout-red-text: #dc3545;\n\n --callout-orange-background: #fd7e1433;\n --callout-orange-border: #fd7e1499;\n --callout-orange-text: #fd7e14;\n\n /* Visualiser Defaults */\n --visualiser-background: #e0e0e0;\n --visualiser-level-color: #28a745;\n }\n\n @media (prefers-color-scheme: dark) {\n :host {\n /* Component Dark */\n --component-text-color: #eee;\n\n /* Card Dark */\n --card-background: #333;\n --card-border-color: #555;\n\n /* Actions Dark */\n --action-plain-border-color: #555;\n --action-plain-background: #333;\n --action-plain-background-hover: #444;\n\n --action-accent-background: #0056b3;\n --action-accent-background-hover: #003d80;\n\n /* Visualiser Dark */\n --visualiser-background: #fff;\n }\n }\n :host {\n box-sizing: border-box;\n font-family: var(--component-font-family);\n color: var(--component-text-color);\n }\n`;\n\nexport default ThemeStyles;\n"]}
@@ -0,0 +1,20 @@
1
+ export type RecordingState = 'initializing' | 'recording' | 'stopping' | 'stopped';
2
+ export interface Command {
3
+ command: string;
4
+ action: string;
5
+ keywords: string[];
6
+ }
7
+ export interface DictationConfig {
8
+ primaryLanguage: string;
9
+ interimResults: boolean;
10
+ spokenPunctuation: boolean;
11
+ automaticPunctuation: boolean;
12
+ model: string;
13
+ commands?: Command[];
14
+ }
15
+ export type PartialDictationConfig = Partial<DictationConfig>;
16
+ export interface ServerConfig {
17
+ environment?: string;
18
+ tenant?: string;
19
+ token?: string;
20
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type RecordingState =\n | 'initializing'\n | 'recording'\n | 'stopping'\n | 'stopped';\n\nexport interface Command {\n command: string;\n action: string;\n keywords: string[];\n}\n\nexport interface DictationConfig {\n primaryLanguage: string;\n interimResults: boolean;\n spokenPunctuation: boolean;\n automaticPunctuation: boolean;\n model: string;\n commands?: Command[];\n}\n\nexport type PartialDictationConfig = Partial<DictationConfig>;\n\nexport interface ServerConfig {\n environment?: string;\n tenant?: string;\n token?: string;\n}\n"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Returns the localized name of a language given its BCP-47 code.
3
+ *
4
+ * @param languageCode - The BCP-47 language code (e.g. "en")
5
+ * @returns The localized language name (e.g. "English") or the original code if unavailable.
6
+ */
7
+ export declare function getLanguageName(languageCode: string): string;
8
+ /**
9
+ * Requests access to the microphone.
10
+ *
11
+ * This function checks if the microphone permission is in "prompt" state, then requests
12
+ * access and stops any active tracks immediately. It also logs if permission is already granted.
13
+ *
14
+ * @returns A promise that resolves when the permission request is complete.
15
+ */
16
+ export declare function requestMicAccess(): Promise<void>;
17
+ /**
18
+ * Retrieves available audio input devices.
19
+ *
20
+ * This function uses the mediaDevices API to enumerate devices and filters out those
21
+ * which are audio inputs. In some browsers, you may need to request user media before
22
+ * device labels are populated.
23
+ *
24
+ * @returns A promise that resolves with an object containing:
25
+ * - `devices`: an array of MediaDeviceInfo objects for audio inputs.
26
+ * - `defaultDeviceId`: the deviceId of the first audio input, if available.
27
+ */
28
+ export declare function getAudioDevices(): Promise<{
29
+ devices: MediaDeviceInfo[];
30
+ defaultDeviceId?: string;
31
+ }>;
@@ -0,0 +1,77 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * Returns the localized name of a language given its BCP-47 code.
4
+ *
5
+ * @param languageCode - The BCP-47 language code (e.g. "en")
6
+ * @returns The localized language name (e.g. "English") or the original code if unavailable.
7
+ */
8
+ export function getLanguageName(languageCode) {
9
+ const userLocale = navigator.language || 'en';
10
+ const displayNames = new Intl.DisplayNames([userLocale], {
11
+ type: 'language',
12
+ });
13
+ const languageName = displayNames.of(languageCode);
14
+ return languageName || languageCode;
15
+ }
16
+ /**
17
+ * Requests access to the microphone.
18
+ *
19
+ * This function checks if the microphone permission is in "prompt" state, then requests
20
+ * access and stops any active tracks immediately. It also logs if permission is already granted.
21
+ *
22
+ * @returns A promise that resolves when the permission request is complete.
23
+ */
24
+ export async function requestMicAccess() {
25
+ try {
26
+ // Fallback if Permissions API is not available
27
+ if (!navigator.permissions) {
28
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
29
+ stream.getTracks().forEach(track => track.stop());
30
+ return;
31
+ }
32
+ const permissionStatus = await navigator.permissions.query({
33
+ // eslint-disable-next-line no-undef
34
+ name: 'microphone',
35
+ });
36
+ if (permissionStatus.state === 'prompt') {
37
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
38
+ stream.getTracks().forEach(track => track.stop());
39
+ }
40
+ else if (permissionStatus.state === 'denied') {
41
+ console.warn('Microphone permission is denied.');
42
+ }
43
+ }
44
+ catch (error) {
45
+ console.error('Error checking/requesting microphone permission:', error);
46
+ }
47
+ }
48
+ /**
49
+ * Retrieves available audio input devices.
50
+ *
51
+ * This function uses the mediaDevices API to enumerate devices and filters out those
52
+ * which are audio inputs. In some browsers, you may need to request user media before
53
+ * device labels are populated.
54
+ *
55
+ * @returns A promise that resolves with an object containing:
56
+ * - `devices`: an array of MediaDeviceInfo objects for audio inputs.
57
+ * - `defaultDeviceId`: the deviceId of the first audio input, if available.
58
+ */
59
+ export async function getAudioDevices() {
60
+ if (!navigator.mediaDevices?.enumerateDevices) {
61
+ console.error('Media devices API not supported.');
62
+ return { devices: [] };
63
+ }
64
+ await requestMicAccess();
65
+ try {
66
+ // Optionally: await navigator.mediaDevices.getUserMedia({ audio: true });
67
+ const devices = await navigator.mediaDevices.enumerateDevices();
68
+ const audioDevices = devices.filter(device => device.kind === 'audioinput');
69
+ const defaultDeviceId = audioDevices.length > 0 ? audioDevices[0].deviceId : undefined;
70
+ return { devices: audioDevices, defaultDeviceId };
71
+ }
72
+ catch (error) {
73
+ console.error('Error enumerating devices:', error);
74
+ return { devices: [] };
75
+ }
76
+ }
77
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,UAAU,CAAC,EAAE;QACvD,IAAI,EAAE,UAAU;KACjB,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,YAAY,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;IACnD,OAAO,YAAY,IAAI,YAAY,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,+CAA+C;QAC/C,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1E,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,MAAM,gBAAgB,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC;YACzD,oCAAoC;YACpC,IAAI,EAAE,YAA8B;SACrC,CAAC,CAAC;QAEH,IAAI,gBAAgB,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1E,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,gBAAgB,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IAInC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,gBAAgB,EAAE,CAAC;QAC9C,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,gBAAgB,EAAE,CAAC;IAEzB,IAAI,CAAC;QACH,0EAA0E;QAC1E,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,gBAAgB,EAAE,CAAC;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;QAC5E,MAAM,eAAe,GACnB,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACjE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QACnD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC;AACH,CAAC","sourcesContent":["/* eslint-disable no-console */\n/**\n * Returns the localized name of a language given its BCP-47 code.\n *\n * @param languageCode - The BCP-47 language code (e.g. \"en\")\n * @returns The localized language name (e.g. \"English\") or the original code if unavailable.\n */\nexport function getLanguageName(languageCode: string): string {\n const userLocale = navigator.language || 'en';\n const displayNames = new Intl.DisplayNames([userLocale], {\n type: 'language',\n });\n const languageName = displayNames.of(languageCode);\n return languageName || languageCode;\n}\n\n/**\n * Requests access to the microphone.\n *\n * This function checks if the microphone permission is in \"prompt\" state, then requests\n * access and stops any active tracks immediately. It also logs if permission is already granted.\n *\n * @returns A promise that resolves when the permission request is complete.\n */\nexport async function requestMicAccess(): Promise<void> {\n try {\n // Fallback if Permissions API is not available\n if (!navigator.permissions) {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n return;\n }\n\n const permissionStatus = await navigator.permissions.query({\n // eslint-disable-next-line no-undef\n name: 'microphone' as PermissionName,\n });\n\n if (permissionStatus.state === 'prompt') {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n } else if (permissionStatus.state === 'denied') {\n console.warn('Microphone permission is denied.');\n }\n } catch (error) {\n console.error('Error checking/requesting microphone permission:', error);\n }\n}\n\n/**\n * Retrieves available audio input devices.\n *\n * This function uses the mediaDevices API to enumerate devices and filters out those\n * which are audio inputs. In some browsers, you may need to request user media before\n * device labels are populated.\n *\n * @returns A promise that resolves with an object containing:\n * - `devices`: an array of MediaDeviceInfo objects for audio inputs.\n * - `defaultDeviceId`: the deviceId of the first audio input, if available.\n */\nexport async function getAudioDevices(): Promise<{\n devices: MediaDeviceInfo[];\n defaultDeviceId?: string;\n}> {\n if (!navigator.mediaDevices?.enumerateDevices) {\n console.error('Media devices API not supported.');\n return { devices: [] };\n }\n\n await requestMicAccess();\n\n try {\n // Optionally: await navigator.mediaDevices.getUserMedia({ audio: true });\n const devices = await navigator.mediaDevices.enumerateDevices();\n const audioDevices = devices.filter(device => device.kind === 'audioinput');\n const defaultDeviceId =\n audioDevices.length > 0 ? audioDevices[0].deviceId : undefined;\n return { devices: audioDevices, defaultDeviceId };\n } catch (error) {\n console.error('Error enumerating devices:', error);\n return { devices: [] };\n }\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { TemplateResult } from 'lit';
2
+ import '../src/corti-dictation.js';
3
+ declare const _default: {
4
+ title: string;
5
+ component: string;
6
+ argTypes: {
7
+ header: {
8
+ control: string;
9
+ };
10
+ counter: {
11
+ control: string;
12
+ };
13
+ textColor: {
14
+ control: string;
15
+ };
16
+ };
17
+ };
18
+ export default _default;
19
+ interface Story<T> {
20
+ (args: T): TemplateResult;
21
+ args?: Partial<T>;
22
+ argTypes?: Record<string, unknown>;
23
+ }
24
+ interface ArgTypes {
25
+ header?: string;
26
+ counter?: number;
27
+ textColor?: string;
28
+ slot?: TemplateResult;
29
+ }
30
+ export declare const Regular: Story<ArgTypes>;
31
+ export declare const CustomHeader: Story<ArgTypes>;
32
+ export declare const CustomCounter: Story<ArgTypes>;
33
+ export declare const SlottedContent: Story<ArgTypes>;
@@ -0,0 +1,37 @@
1
+ import { html } from 'lit';
2
+ import '../src/corti-dictation.js';
3
+ export default {
4
+ title: 'CortiDictation',
5
+ component: 'corti-dictation',
6
+ argTypes: {
7
+ header: { control: 'text' },
8
+ counter: { control: 'number' },
9
+ textColor: { control: 'color' },
10
+ },
11
+ };
12
+ const Template = ({ header = 'Hello world', counter = 5, textColor, slot, }) => html `
13
+ <corti-dictation
14
+ style="--corti-dictation-text-color: ${textColor || 'black'}"
15
+ .header=${header}
16
+ .counter=${counter}
17
+ >
18
+ ${slot}
19
+ </corti-dictation>
20
+ `;
21
+ export const Regular = Template.bind({});
22
+ export const CustomHeader = Template.bind({});
23
+ CustomHeader.args = {
24
+ header: 'My header',
25
+ };
26
+ export const CustomCounter = Template.bind({});
27
+ CustomCounter.args = {
28
+ counter: 123456,
29
+ };
30
+ export const SlottedContent = Template.bind({});
31
+ SlottedContent.args = {
32
+ slot: html `<p>Slotted content</p>`,
33
+ };
34
+ SlottedContent.argTypes = {
35
+ slot: { table: { disable: true } },
36
+ };
37
+ //# sourceMappingURL=index.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.stories.js","sourceRoot":"","sources":["../../stories/index.stories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAC;AAC3C,OAAO,2BAA2B,CAAC;AAEnC,eAAe;IACb,KAAK,EAAE,gBAAgB;IACvB,SAAS,EAAE,iBAAiB;IAC5B,QAAQ,EAAE;QACR,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;QAC3B,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC9B,SAAS,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;KAChC;CACF,CAAC;AAeF,MAAM,QAAQ,GAAoB,CAAC,EACjC,MAAM,GAAG,aAAa,EACtB,OAAO,GAAG,CAAC,EACX,SAAS,EACT,IAAI,GACK,EAAE,EAAE,CAAC,IAAI,CAAA;;2CAEuB,SAAS,IAAI,OAAO;cACjD,MAAM;eACL,OAAO;;MAEhB,IAAI;;CAET,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEzC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC9C,YAAY,CAAC,IAAI,GAAG;IAClB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC/C,aAAa,CAAC,IAAI,GAAG;IACnB,OAAO,EAAE,MAAM;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChD,cAAc,CAAC,IAAI,GAAG;IACpB,IAAI,EAAE,IAAI,CAAA,wBAAwB;CACnC,CAAC;AACF,cAAc,CAAC,QAAQ,GAAG;IACxB,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;CACnC,CAAC","sourcesContent":["import { html, TemplateResult } from 'lit';\nimport '../src/corti-dictation.js';\n\nexport default {\n title: 'CortiDictation',\n component: 'corti-dictation',\n argTypes: {\n header: { control: 'text' },\n counter: { control: 'number' },\n textColor: { control: 'color' },\n },\n};\n\ninterface Story<T> {\n (args: T): TemplateResult;\n args?: Partial<T>;\n argTypes?: Record<string, unknown>;\n}\n\ninterface ArgTypes {\n header?: string;\n counter?: number;\n textColor?: string;\n slot?: TemplateResult;\n}\n\nconst Template: Story<ArgTypes> = ({\n header = 'Hello world',\n counter = 5,\n textColor,\n slot,\n}: ArgTypes) => html`\n <corti-dictation\n style=\"--corti-dictation-text-color: ${textColor || 'black'}\"\n .header=${header}\n .counter=${counter}\n >\n ${slot}\n </corti-dictation>\n`;\n\nexport const Regular = Template.bind({});\n\nexport const CustomHeader = Template.bind({});\nCustomHeader.args = {\n header: 'My header',\n};\n\nexport const CustomCounter = Template.bind({});\nCustomCounter.args = {\n counter: 123456,\n};\n\nexport const SlottedContent = Template.bind({});\nSlottedContent.args = {\n slot: html`<p>Slotted content</p>`,\n};\nSlottedContent.argTypes = {\n slot: { table: { disable: true } },\n};\n"]}
@@ -0,0 +1 @@
1
+ import '../src/corti-dictation.js';
@@ -0,0 +1,100 @@
1
+ import { html } from 'lit';
2
+ import { fixture, expect, oneEvent } from '@open-wc/testing';
3
+ import sinon from 'sinon';
4
+ import '../src/corti-dictation.js';
5
+ // Stub class for RecorderManager
6
+ class StubRecorderManager extends EventTarget {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.devices = [];
10
+ this.selectedDevice = '';
11
+ this.startRecording = sinon.spy();
12
+ this.stopRecording = sinon.spy();
13
+ }
14
+ async initialize() {
15
+ // Simulate async initialization.
16
+ }
17
+ }
18
+ describe('CortiDictation', () => {
19
+ let stubRecorder;
20
+ beforeEach(() => {
21
+ stubRecorder = new StubRecorderManager();
22
+ });
23
+ it('renders a callout warning if serverConfig is not configured', async () => {
24
+ const el = await fixture(html `<corti-dictation></corti-dictation>`);
25
+ // Override the recorderManager to avoid real initialization.
26
+ el.recorderManager = stubRecorder;
27
+ el.serverConfig = {}; // Missing required keys.
28
+ await el.updateComplete;
29
+ const callout = el.shadowRoot?.querySelector('.callout');
30
+ expect(callout).to.exist;
31
+ expect(el.shadowRoot?.textContent).to.include('Please configure the server settings in the parent component');
32
+ });
33
+ it('renders the recording icon when recordingState is "recording"', async () => {
34
+ const configured = { token: 'abc', environment: 'prod', tenant: '123' };
35
+ const el = await fixture(html `<corti-dictation></corti-dictation>`);
36
+ el.recorderManager = stubRecorder;
37
+ el.serverConfig = configured;
38
+ el.recordingState = 'recording';
39
+ await el.updateComplete;
40
+ const recordingIcon = el.shadowRoot?.querySelector('icon-recording');
41
+ expect(recordingIcon).to.exist;
42
+ });
43
+ it('calls startRecording when button is clicked and state is "stopped"', async () => {
44
+ const configured = { token: 'abc', environment: 'prod', tenant: '123' };
45
+ const el = await fixture(html `<corti-dictation></corti-dictation>`);
46
+ el.recorderManager = stubRecorder;
47
+ el.serverConfig = configured;
48
+ el.recordingState = 'stopped';
49
+ await el.updateComplete;
50
+ const button = el.shadowRoot.querySelector('button');
51
+ button.click();
52
+ expect(stubRecorder.startRecording.calledOnce).to.be.true;
53
+ // Check that startRecording was called with a config object
54
+ const args = stubRecorder.startRecording.getCall(0).args[0];
55
+ expect(args).to.have.property('dictationConfig');
56
+ expect(args).to.have.property('serverConfig');
57
+ });
58
+ it('calls stopRecording when button is clicked and state is "recording"', async () => {
59
+ const configured = { token: 'abc', environment: 'prod', tenant: '123' };
60
+ const el = await fixture(html `<corti-dictation></corti-dictation>`);
61
+ el.recorderManager = stubRecorder;
62
+ el.serverConfig = configured;
63
+ el.recordingState = 'recording';
64
+ await el.updateComplete;
65
+ const button = el.shadowRoot.querySelector('button');
66
+ button.click();
67
+ expect(stubRecorder.stopRecording.calledOnce).to.be.true;
68
+ });
69
+ it('updates the audio level when an "audio-level-changed" event is dispatched', async () => {
70
+ const configured = { token: 'abc', environment: 'prod', tenant: '123' };
71
+ const el = await fixture(html `<corti-dictation></corti-dictation>`);
72
+ el.recorderManager = stubRecorder;
73
+ el.serverConfig = configured;
74
+ el.recordingState = 'recording';
75
+ await el.updateComplete;
76
+ const testLevel = 42;
77
+ const event = new CustomEvent('audio-level-changed', { detail: { audioLevel: testLevel } });
78
+ stubRecorder.dispatchEvent(event);
79
+ await el.updateComplete;
80
+ const audioVisualiser = el.shadowRoot?.querySelector('audio-visualiser');
81
+ expect(audioVisualiser).to.exist;
82
+ expect(audioVisualiser.level).to.equal(testLevel);
83
+ });
84
+ it('re-dispatches recorderManager events', async () => {
85
+ const configured = { token: 'abc', environment: 'prod', tenant: '123' };
86
+ const el = await fixture(html `<corti-dictation></corti-dictation>`);
87
+ el.recorderManager = stubRecorder;
88
+ el.serverConfig = configured;
89
+ el.recordingState = 'stopped';
90
+ await el.updateComplete;
91
+ // Listen for a re-dispatched event from the component.
92
+ setTimeout(() => {
93
+ const event = new CustomEvent('transcript', { detail: { transcript: 'hello' } });
94
+ stubRecorder.dispatchEvent(event);
95
+ });
96
+ const dispatchedEvent = (await oneEvent(el, 'transcript'));
97
+ expect(dispatchedEvent.detail.transcript).to.equal('hello');
98
+ });
99
+ });
100
+ //# sourceMappingURL=corti-dictation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corti-dictation.test.js","sourceRoot":"","sources":["../../test/corti-dictation.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit';\nimport { fixture, expect, oneEvent } from '@open-wc/testing';\nimport sinon from 'sinon';\nimport { CortiDictation } from '../src/index.js';\nimport '../src/corti-dictation.js';\n\n// Stub class for RecorderManager\nclass StubRecorderManager extends EventTarget {\n devices: MediaDeviceInfo[] = [];\n\n selectedDevice = '';\n\n startRecording = sinon.spy();\n\n stopRecording = sinon.spy();\n\n async initialize() {\n // Simulate async initialization.\n }\n}\n\ndescribe('CortiDictation', () => {\n let stubRecorder: StubRecorderManager;\n\n beforeEach(() => {\n stubRecorder = new StubRecorderManager();\n });\n\n it('renders a callout warning if serverConfig is not configured', async () => {\n const el = await fixture<CortiDictation>(\n html`<corti-dictation></corti-dictation>`\n );\n // Override the recorderManager to avoid real initialization.\n (el as any).recorderManager = stubRecorder;\n el.serverConfig = {}; // Missing required keys.\n await el.updateComplete;\n const callout = el.shadowRoot?.querySelector('.callout');\n expect(callout).to.exist;\n expect(el.shadowRoot?.textContent).to.include(\n 'Please configure the server settings in the parent component'\n );\n });\n\n it('renders the recording icon when recordingState is \"recording\"', async () => {\n const configured = { token: 'abc', environment: 'prod', tenant: '123' };\n const el = await fixture<CortiDictation>(\n html`<corti-dictation></corti-dictation>`\n );\n (el as any).recorderManager = stubRecorder;\n el.serverConfig = configured;\n el.recordingState = 'recording';\n await el.updateComplete;\n const recordingIcon = el.shadowRoot?.querySelector('icon-recording');\n expect(recordingIcon).to.exist;\n });\n\n it('calls startRecording when button is clicked and state is \"stopped\"', async () => {\n const configured = { token: 'abc', environment: 'prod', tenant: '123' };\n const el = await fixture<CortiDictation>(\n html`<corti-dictation></corti-dictation>`\n );\n (el as any).recorderManager = stubRecorder;\n el.serverConfig = configured;\n el.recordingState = 'stopped';\n await el.updateComplete;\n const button = el.shadowRoot!.querySelector('button')!;\n button.click();\n expect(stubRecorder.startRecording.calledOnce).to.be.true;\n // Check that startRecording was called with a config object\n const args = stubRecorder.startRecording.getCall(0).args[0];\n expect(args).to.have.property('dictationConfig');\n expect(args).to.have.property('serverConfig');\n });\n\n it('calls stopRecording when button is clicked and state is \"recording\"', async () => {\n const configured = { token: 'abc', environment: 'prod', tenant: '123' };\n const el = await fixture<CortiDictation>(\n html`<corti-dictation></corti-dictation>`\n );\n (el as any).recorderManager = stubRecorder;\n el.serverConfig = configured;\n el.recordingState = 'recording';\n await el.updateComplete;\n const button = el.shadowRoot!.querySelector('button')!;\n button.click();\n expect(stubRecorder.stopRecording.calledOnce).to.be.true;\n });\n\n it('updates the audio level when an \"audio-level-changed\" event is dispatched', async () => {\n const configured = { token: 'abc', environment: 'prod', tenant: '123' };\n const el = await fixture<CortiDictation>(\n html`<corti-dictation></corti-dictation>`\n );\n (el as any).recorderManager = stubRecorder;\n el.serverConfig = configured;\n el.recordingState = 'recording';\n await el.updateComplete;\n const testLevel = 42;\n const event = new CustomEvent('audio-level-changed', { detail: { audioLevel: testLevel } });\n stubRecorder.dispatchEvent(event);\n await el.updateComplete;\n const audioVisualiser = el.shadowRoot?.querySelector('audio-visualiser');\n expect(audioVisualiser).to.exist;\n expect((audioVisualiser as any).level).to.equal(testLevel);\n });\n\n it('re-dispatches recorderManager events', async () => {\n const configured = { token: 'abc', environment: 'prod', tenant: '123' };\n const el = await fixture<CortiDictation>(\n html`<corti-dictation></corti-dictation>`\n );\n (el as any).recorderManager = stubRecorder;\n el.serverConfig = configured;\n el.recordingState = 'stopped';\n await el.updateComplete;\n // Listen for a re-dispatched event from the component.\n setTimeout(() => {\n const event = new CustomEvent('transcript', { detail: { transcript: 'hello' } });\n stubRecorder.dispatchEvent(event);\n });\n const dispatchedEvent = (await oneEvent(el, 'transcript')) as CustomEvent;\n expect(dispatchedEvent.detail.transcript).to.equal('hello');\n });\n});\n"]}