dictate-button 0.2.0 → 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.
- package/README.md +18 -12
- package/dist/dictate-button.d.ts +1 -0
- package/dist/dictate-button.js +709 -0
- package/dist/dictate-button.styles.d.ts +1 -1
- package/dist/inject-exclusive.js +42 -0
- package/dist/inject-inclusive.js +42 -0
- package/package.json +21 -17
- package/.firebaserc +0 -5
- package/.prettierrc.mjs +0 -10
- package/404.html +0 -17
- package/dist/404.html +0 -17
- package/dist/dictate-button.cjs.js +0 -36
- package/dist/dictate-button.es.js +0 -682
- package/dist/inject-exclusive.cjs.js +0 -1
- package/dist/inject-exclusive.es.js +0 -35
- package/dist/inject-inclusive.cjs.js +0 -1
- package/dist/inject-inclusive.es.js +0 -35
- package/dist/inject.cjs.js +0 -1
- package/dist/inject.d.ts +0 -1
- package/dist/inject.es.js +0 -1
- package/firebase.json +0 -42
- package/pnpm-workspace.yaml +0 -2
- package/src/dictate-button.styles.ts +0 -36
- package/src/dictate-button.tsx +0 -240
- package/src/inject-exclusive.js +0 -76
- package/src/inject-inclusive.js +0 -76
- package/src/inject.js +0 -2
- package/tsconfig.json +0 -17
- package/vite-env.d.ts +0 -3
- package/vite.config.ts +0 -37
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
function i() {
|
|
2
|
-
const a = document.querySelectorAll(
|
|
3
|
-
'textarea[data-dictate-button-on]:not([data-dictate-button-enabled]), input[type="text"][data-dictate-button-on]:not([data-dictate-button-enabled]), textarea[data-dictate-button-target]:not([data-dictate-button-enabled]), input[type="text"][data-dictate-button-target]:not([data-dictate-button-enabled])'
|
|
4
|
-
);
|
|
5
|
-
for (const o of a) {
|
|
6
|
-
const n = document.createElement("div");
|
|
7
|
-
n.style.position = "relative", n.style.display = "inline-block", n.style.width = "auto", n.style.color = "inherit", o.parentNode.insertBefore(n, o), o.setAttribute("data-dictate-button-enabled", ""), n.appendChild(o), o.style.boxSizing = "border-box";
|
|
8
|
-
const t = document.createElement("dictate-button");
|
|
9
|
-
t.size = 24, t.style.position = "absolute", t.style.right = "0", t.style.top = "0", t.addEventListener("recording:started", (e) => {
|
|
10
|
-
console.log("recording:started", e);
|
|
11
|
-
}), t.addEventListener("recording:stopped", (e) => {
|
|
12
|
-
console.log("recording:stopped", e);
|
|
13
|
-
}), t.addEventListener("recording:failed", (e) => {
|
|
14
|
-
console.log("recording:failed", e);
|
|
15
|
-
}), t.addEventListener("transcribing:started", (e) => {
|
|
16
|
-
console.log("transcribing:started", e);
|
|
17
|
-
}), t.addEventListener("transcribing:finished", (e) => {
|
|
18
|
-
console.log("transcribing:finished", e);
|
|
19
|
-
const d = e.detail;
|
|
20
|
-
r(o, d);
|
|
21
|
-
}), t.addEventListener("transcribing:failed", (e) => {
|
|
22
|
-
console.log("transcribing:failed", e);
|
|
23
|
-
}), n.appendChild(t);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function r(a, o) {
|
|
27
|
-
const n = a.selectionStart || 0, t = a.selectionEnd || 0;
|
|
28
|
-
a.value = a.value.substring(0, n) + o + a.value.substring(t);
|
|
29
|
-
}
|
|
30
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
31
|
-
i(), new MutationObserver(i).observe(document.body, {
|
|
32
|
-
childList: !0,
|
|
33
|
-
subtree: !0
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";function s(){const i=document.querySelectorAll('textarea:not([data-dictate-button-off]):not([data-dictate-button-enabled]), input[type="text"]:not([data-dictate-button-off]):not([data-dictate-button-enabled])');for(const o of i){const n=document.createElement("div");n.style.position="relative",n.style.display="inline-block",n.style.width="auto",n.style.color="inherit",o.parentNode.insertBefore(n,o),o.setAttribute("data-dictate-button-enabled",""),n.appendChild(o),o.style.boxSizing="border-box";const t=document.createElement("dictate-button");t.size=24,t.style.position="absolute",t.style.right="0",t.style.top="0",t.addEventListener("recording:started",e=>{console.log("recording:started",e)}),t.addEventListener("recording:stopped",e=>{console.log("recording:stopped",e)}),t.addEventListener("recording:failed",e=>{console.log("recording:failed",e)}),t.addEventListener("transcribing:started",e=>{console.log("transcribing:started",e)}),t.addEventListener("transcribing:finished",e=>{console.log("transcribing:finished",e);const d=e.detail;r(o,d)}),t.addEventListener("transcribing:failed",e=>{console.log("transcribing:failed",e)}),n.appendChild(t)}}function r(i,o){const n=i.selectionStart||0,t=i.selectionEnd||0;i.value=i.value.substring(0,n)+o+i.value.substring(t)}document.addEventListener("DOMContentLoaded",()=>{s(),new MutationObserver(s).observe(document.body,{childList:!0,subtree:!0})});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
function d() {
|
|
2
|
-
const i = document.querySelectorAll(
|
|
3
|
-
'textarea:not([data-dictate-button-off]):not([data-dictate-button-enabled]), input[type="text"]:not([data-dictate-button-off]):not([data-dictate-button-enabled])'
|
|
4
|
-
);
|
|
5
|
-
for (const o of i) {
|
|
6
|
-
const n = document.createElement("div");
|
|
7
|
-
n.style.position = "relative", n.style.display = "inline-block", n.style.width = "auto", n.style.color = "inherit", o.parentNode.insertBefore(n, o), o.setAttribute("data-dictate-button-enabled", ""), n.appendChild(o), o.style.boxSizing = "border-box";
|
|
8
|
-
const t = document.createElement("dictate-button");
|
|
9
|
-
t.size = 24, t.style.position = "absolute", t.style.right = "0", t.style.top = "0", t.addEventListener("recording:started", (e) => {
|
|
10
|
-
console.log("recording:started", e);
|
|
11
|
-
}), t.addEventListener("recording:stopped", (e) => {
|
|
12
|
-
console.log("recording:stopped", e);
|
|
13
|
-
}), t.addEventListener("recording:failed", (e) => {
|
|
14
|
-
console.log("recording:failed", e);
|
|
15
|
-
}), t.addEventListener("transcribing:started", (e) => {
|
|
16
|
-
console.log("transcribing:started", e);
|
|
17
|
-
}), t.addEventListener("transcribing:finished", (e) => {
|
|
18
|
-
console.log("transcribing:finished", e);
|
|
19
|
-
const s = e.detail;
|
|
20
|
-
r(o, s);
|
|
21
|
-
}), t.addEventListener("transcribing:failed", (e) => {
|
|
22
|
-
console.log("transcribing:failed", e);
|
|
23
|
-
}), n.appendChild(t);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function r(i, o) {
|
|
27
|
-
const n = i.selectionStart || 0, t = i.selectionEnd || 0;
|
|
28
|
-
i.value = i.value.substring(0, n) + o + i.value.substring(t);
|
|
29
|
-
}
|
|
30
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
31
|
-
d(), new MutationObserver(d).observe(document.body, {
|
|
32
|
-
childList: !0,
|
|
33
|
-
subtree: !0
|
|
34
|
-
});
|
|
35
|
-
});
|
package/dist/inject.cjs.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";require("./inject-exclusive.cjs.js");
|
package/dist/inject.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {}
|
package/dist/inject.es.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import "./inject-exclusive.es.js";
|
package/firebase.json
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"hosting": {
|
|
3
|
-
"predeploy": [
|
|
4
|
-
"pnpm run build"
|
|
5
|
-
],
|
|
6
|
-
"public": "dist",
|
|
7
|
-
"ignore": [
|
|
8
|
-
"firebase.json",
|
|
9
|
-
"**/.*",
|
|
10
|
-
"**/node_modules/**"
|
|
11
|
-
],
|
|
12
|
-
"headers": [
|
|
13
|
-
{
|
|
14
|
-
"source": "**/*.@(js|ts)",
|
|
15
|
-
"headers": [
|
|
16
|
-
{
|
|
17
|
-
"key": "Access-Control-Allow-Origin",
|
|
18
|
-
"value": "*"
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"source": "**/*.@(js|ts)",
|
|
24
|
-
"headers": [
|
|
25
|
-
{
|
|
26
|
-
"key": "Cache-Control",
|
|
27
|
-
"value": "no-cache"
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"source": "404.html",
|
|
33
|
-
"headers": [
|
|
34
|
-
{
|
|
35
|
-
"key": "Cache-Control",
|
|
36
|
-
"value": "max-age=300"
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
}
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
}
|
package/pnpm-workspace.yaml
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export const dictateButtonStyles = `
|
|
2
|
-
:host([theme="dark"]) {
|
|
3
|
-
color-scheme: only dark;
|
|
4
|
-
}
|
|
5
|
-
:host([theme="light"]) {
|
|
6
|
-
color-scheme: only light;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
:host .dictate-button__container {
|
|
10
|
-
margin: 5px;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
:host .dictate-button__button {
|
|
14
|
-
cursor: pointer;
|
|
15
|
-
padding: 2px;
|
|
16
|
-
display: inline-flex;
|
|
17
|
-
align-items: center;
|
|
18
|
-
justify-content: center;
|
|
19
|
-
opacity: 0.8;
|
|
20
|
-
transition: opacity 0.2s ease-in-out;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
:host .dictate-button__button .dictate-button__icon {
|
|
24
|
-
width: 100%;
|
|
25
|
-
height: 100%;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
:host .dictate-button__button .dictate-button__icon.dictate-button__icon--processing {
|
|
29
|
-
animation: dictate-button-rotate 1s linear infinite;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@keyframes dictate-button-rotate {
|
|
33
|
-
0% { transform: rotate(0deg); }
|
|
34
|
-
100% { transform: rotate(360deg); }
|
|
35
|
-
}
|
|
36
|
-
`
|
package/src/dictate-button.tsx
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import { customElement } from 'solid-element'
|
|
2
|
-
import { createSignal } from 'solid-js'
|
|
3
|
-
import { dictateButtonStyles } from './dictate-button.styles'
|
|
4
|
-
|
|
5
|
-
console.debug('dictate-button version:', __APP_VERSION__)
|
|
6
|
-
|
|
7
|
-
export interface DictateButtonProps {
|
|
8
|
-
size?: number
|
|
9
|
-
apiEndpoint?: string
|
|
10
|
-
// The props below are for types only. We don't use them inside the component.
|
|
11
|
-
theme?: 'light' | 'dark'
|
|
12
|
-
class?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
declare module 'solid-js' {
|
|
16
|
-
namespace JSX {
|
|
17
|
-
interface IntrinsicElements {
|
|
18
|
-
'dictate-button': Element & DictateButtonProps
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type DictateButtonStatus = 'idle' | 'recording' | 'processing' | 'error'
|
|
24
|
-
|
|
25
|
-
const DEFAULT_TRANSCRIBE_API_ENDPOINT =
|
|
26
|
-
'https://api.dictate-button.io/transcribe'
|
|
27
|
-
const APP_NAME = 'dictate-button.io'
|
|
28
|
-
|
|
29
|
-
customElement(
|
|
30
|
-
'dictate-button',
|
|
31
|
-
{
|
|
32
|
-
size: 24,
|
|
33
|
-
apiEndpoint: DEFAULT_TRANSCRIBE_API_ENDPOINT,
|
|
34
|
-
},
|
|
35
|
-
(props: DictateButtonProps, { element }) => {
|
|
36
|
-
const { size, apiEndpoint } = props
|
|
37
|
-
|
|
38
|
-
console.debug('api', apiEndpoint)
|
|
39
|
-
|
|
40
|
-
const [status, setStatus] = createSignal<DictateButtonStatus>('idle')
|
|
41
|
-
|
|
42
|
-
let mediaRecorder: MediaRecorder | null = null
|
|
43
|
-
let audioChunks: Blob[] = []
|
|
44
|
-
|
|
45
|
-
const cleanup = () => {
|
|
46
|
-
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
|
47
|
-
mediaRecorder.stop()
|
|
48
|
-
}
|
|
49
|
-
audioChunks = []
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
element.addEventListener('disconnected', cleanup)
|
|
53
|
-
|
|
54
|
-
const toggleRecording = async () => {
|
|
55
|
-
cleanup()
|
|
56
|
-
|
|
57
|
-
if (status() === 'idle') {
|
|
58
|
-
try {
|
|
59
|
-
const stream = await navigator.mediaDevices.getUserMedia({
|
|
60
|
-
audio: true,
|
|
61
|
-
})
|
|
62
|
-
mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' })
|
|
63
|
-
audioChunks = []
|
|
64
|
-
|
|
65
|
-
mediaRecorder.ondataavailable = (event) => {
|
|
66
|
-
audioChunks.push(event.data)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
mediaRecorder.onstop = async () => {
|
|
70
|
-
setStatus('processing')
|
|
71
|
-
|
|
72
|
-
event(element, 'transcribing:started', 'Started transcribing')
|
|
73
|
-
|
|
74
|
-
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' })
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const response = await fetch(apiEndpoint!, {
|
|
78
|
-
method: 'POST',
|
|
79
|
-
body: audioBlob,
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
if (!response.ok) throw new Error('Failed to transcribe audio')
|
|
83
|
-
|
|
84
|
-
const data = await response.json()
|
|
85
|
-
|
|
86
|
-
// If user cancelled processing, don't emit transcribing:finished event.
|
|
87
|
-
if (status() !== 'processing') return
|
|
88
|
-
|
|
89
|
-
event(element, 'transcribing:finished', data.text)
|
|
90
|
-
|
|
91
|
-
setStatus('idle')
|
|
92
|
-
} catch (error) {
|
|
93
|
-
console.error('Failed to transcribe audio:', error)
|
|
94
|
-
|
|
95
|
-
event(
|
|
96
|
-
element,
|
|
97
|
-
'transcribing:failed',
|
|
98
|
-
'Failed to transcribe audio'
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
setErrorStatus()
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
mediaRecorder.start()
|
|
106
|
-
|
|
107
|
-
event(element, 'recording:started', 'Started recording')
|
|
108
|
-
|
|
109
|
-
setStatus('recording')
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error('Failed to start recording:', error)
|
|
112
|
-
|
|
113
|
-
event(element, 'recording:failed', 'Failed to start recording')
|
|
114
|
-
|
|
115
|
-
setErrorStatus()
|
|
116
|
-
}
|
|
117
|
-
} else {
|
|
118
|
-
event(element, 'recording:stopped', 'Stopped recording')
|
|
119
|
-
|
|
120
|
-
setStatus('idle')
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const setErrorStatus = () => {
|
|
125
|
-
setStatus('error')
|
|
126
|
-
setTimeout(() => setStatus('idle'), 2000)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<div part="container" class="dictate-button__container">
|
|
131
|
-
<style>{dictateButtonStyles}</style>
|
|
132
|
-
<button
|
|
133
|
-
part="button"
|
|
134
|
-
style={`width:${size}px;height:${size}px"`}
|
|
135
|
-
class="dictate-button__button"
|
|
136
|
-
onClick={toggleRecording}
|
|
137
|
-
title={buttonTitle(status())}
|
|
138
|
-
>
|
|
139
|
-
{status() === 'idle' && <IdleIcon />}
|
|
140
|
-
{status() === 'recording' && <RecordingIcon />}
|
|
141
|
-
{status() === 'processing' && <ProcessingIcon />}
|
|
142
|
-
{status() === 'error' && <ErrorIcon />}
|
|
143
|
-
</button>
|
|
144
|
-
</div>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
const buttonTitle = (status: DictateButtonStatus) => {
|
|
150
|
-
switch (status) {
|
|
151
|
-
case 'idle':
|
|
152
|
-
return `Start dictation (${APP_NAME})`
|
|
153
|
-
case 'recording':
|
|
154
|
-
return `Stop dictation (${APP_NAME})`
|
|
155
|
-
case 'processing':
|
|
156
|
-
return `Stop processing (${APP_NAME})`
|
|
157
|
-
case 'error':
|
|
158
|
-
return `Click to reset (${APP_NAME})`
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const event = (element: any, eventName: string, detail: string) => {
|
|
163
|
-
element.dispatchEvent(
|
|
164
|
-
new CustomEvent(eventName, {
|
|
165
|
-
detail,
|
|
166
|
-
bubbles: true,
|
|
167
|
-
composed: true,
|
|
168
|
-
})
|
|
169
|
-
)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const IdleIcon = () => (
|
|
173
|
-
<svg
|
|
174
|
-
// @ts-ignore
|
|
175
|
-
part="icon"
|
|
176
|
-
class={`dictate-button__icon dictate-button__icon--idle`}
|
|
177
|
-
fill="none"
|
|
178
|
-
viewBox="0 0 24 24"
|
|
179
|
-
stroke-width="1.5"
|
|
180
|
-
stroke="currentColor"
|
|
181
|
-
>
|
|
182
|
-
<path
|
|
183
|
-
stroke-linecap="round"
|
|
184
|
-
stroke-linejoin="round"
|
|
185
|
-
d="M12 18.75a6 6 0 0 0 6-6v-1.5m-6 7.5a6 6 0 0 1-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 0 1-3-3V4.5a3 3 0 1 1 6 0v8.25a3 3 0 0 1-3 3Z"
|
|
186
|
-
/>
|
|
187
|
-
</svg>
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
const RecordingIcon = () => (
|
|
191
|
-
<svg
|
|
192
|
-
// @ts-ignore
|
|
193
|
-
part="icon"
|
|
194
|
-
class="dictate-button__icon dictate-button__icon--recording"
|
|
195
|
-
viewBox="0 0 24 24"
|
|
196
|
-
fill="currentColor"
|
|
197
|
-
>
|
|
198
|
-
<path
|
|
199
|
-
fill-rule="evenodd"
|
|
200
|
-
d="M4.5 7.5a3 3 0 0 1 3-3h9a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3h-9a3 3 0 0 1-3-3v-9Z"
|
|
201
|
-
clip-rule="evenodd"
|
|
202
|
-
/>
|
|
203
|
-
</svg>
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
const ProcessingIcon = () => (
|
|
207
|
-
<svg
|
|
208
|
-
// @ts-ignore
|
|
209
|
-
part="icon"
|
|
210
|
-
class="dictate-button__icon dictate-button__icon--processing"
|
|
211
|
-
fill="none"
|
|
212
|
-
viewBox="0 0 24 24"
|
|
213
|
-
stroke-width="1.5"
|
|
214
|
-
stroke="currentColor"
|
|
215
|
-
>
|
|
216
|
-
<path
|
|
217
|
-
stroke-linecap="round"
|
|
218
|
-
stroke-linejoin="round"
|
|
219
|
-
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"
|
|
220
|
-
/>
|
|
221
|
-
</svg>
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
const ErrorIcon = () => (
|
|
225
|
-
<svg
|
|
226
|
-
// @ts-ignore
|
|
227
|
-
part="icon"
|
|
228
|
-
class="dictate-button__icon dictate-button__icon--error"
|
|
229
|
-
fill="none"
|
|
230
|
-
viewBox="0 0 24 24"
|
|
231
|
-
stroke-width="1.5"
|
|
232
|
-
stroke="currentColor"
|
|
233
|
-
>
|
|
234
|
-
<path
|
|
235
|
-
stroke-linecap="round"
|
|
236
|
-
stroke-linejoin="round"
|
|
237
|
-
d="M6 18 18 6M6 6l12 12"
|
|
238
|
-
/>
|
|
239
|
-
</svg>
|
|
240
|
-
)
|
package/src/inject-exclusive.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
const BUTTONS_SIZE = 24 // px
|
|
2
|
-
const WATCH_DOM_CHANGES = true
|
|
3
|
-
|
|
4
|
-
function injectDictateButton() {
|
|
5
|
-
const textFields = document.querySelectorAll(
|
|
6
|
-
'textarea[data-dictate-button-on]:not([data-dictate-button-enabled]), input[type="text"][data-dictate-button-on]:not([data-dictate-button-enabled]), textarea[data-dictate-button-target]:not([data-dictate-button-enabled]), input[type="text"][data-dictate-button-target]:not([data-dictate-button-enabled])'
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
for (const textField of textFields) {
|
|
10
|
-
// Add a wrapper div with relative positioning.
|
|
11
|
-
const container = document.createElement('div')
|
|
12
|
-
container.style.position = 'relative'
|
|
13
|
-
container.style.display = 'inline-block'
|
|
14
|
-
container.style.width = 'auto'
|
|
15
|
-
container.style.color = 'inherit'
|
|
16
|
-
textField.parentNode.insertBefore(container, textField)
|
|
17
|
-
|
|
18
|
-
textField.setAttribute('data-dictate-button-enabled', '')
|
|
19
|
-
|
|
20
|
-
container.appendChild(textField)
|
|
21
|
-
|
|
22
|
-
// Ensure textarea fills container
|
|
23
|
-
textField.style.boxSizing = 'border-box'
|
|
24
|
-
// textarea.style.paddingRight = `${BUTTONS_SIZE + 2 * 2 + 6}px`
|
|
25
|
-
|
|
26
|
-
// Add the dictate-button component.
|
|
27
|
-
const dictateBtn = document.createElement('dictate-button')
|
|
28
|
-
dictateBtn.size = BUTTONS_SIZE
|
|
29
|
-
dictateBtn.style.position = 'absolute'
|
|
30
|
-
dictateBtn.style.right = '0'
|
|
31
|
-
dictateBtn.style.top = '0'
|
|
32
|
-
|
|
33
|
-
// Add event listeners for the dictate-button component.
|
|
34
|
-
dictateBtn.addEventListener('recording:started', (e) => {
|
|
35
|
-
console.log('recording:started', e)
|
|
36
|
-
})
|
|
37
|
-
dictateBtn.addEventListener('recording:stopped', (e) => {
|
|
38
|
-
console.log('recording:stopped', e)
|
|
39
|
-
})
|
|
40
|
-
dictateBtn.addEventListener('recording:failed', (e) => {
|
|
41
|
-
console.log('recording:failed', e)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
dictateBtn.addEventListener('transcribing:started', (e) => {
|
|
45
|
-
console.log('transcribing:started', e)
|
|
46
|
-
})
|
|
47
|
-
dictateBtn.addEventListener('transcribing:finished', (e) => {
|
|
48
|
-
console.log('transcribing:finished', e)
|
|
49
|
-
const customEvent = e
|
|
50
|
-
const text = customEvent.detail
|
|
51
|
-
receiveText(textField, text)
|
|
52
|
-
})
|
|
53
|
-
dictateBtn.addEventListener('transcribing:failed', (e) => {
|
|
54
|
-
console.log('transcribing:failed', e)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
container.appendChild(dictateBtn)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function receiveText(textField, text) {
|
|
62
|
-
const start = textField.selectionStart || 0
|
|
63
|
-
const end = textField.selectionEnd || 0
|
|
64
|
-
textField.value =
|
|
65
|
-
textField.value.substring(0, start) + text + textField.value.substring(end)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
69
|
-
injectDictateButton()
|
|
70
|
-
if (WATCH_DOM_CHANGES) {
|
|
71
|
-
new MutationObserver(injectDictateButton).observe(document.body, {
|
|
72
|
-
childList: true,
|
|
73
|
-
subtree: true,
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
})
|
package/src/inject-inclusive.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
const BUTTONS_SIZE = 24 // px
|
|
2
|
-
const WATCH_DOM_CHANGES = true
|
|
3
|
-
|
|
4
|
-
function injectDictateButton() {
|
|
5
|
-
const textFields = document.querySelectorAll(
|
|
6
|
-
'textarea:not([data-dictate-button-off]):not([data-dictate-button-enabled]), input[type="text"]:not([data-dictate-button-off]):not([data-dictate-button-enabled])'
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
for (const textField of textFields) {
|
|
10
|
-
// Add a wrapper div with relative positioning.
|
|
11
|
-
const container = document.createElement('div')
|
|
12
|
-
container.style.position = 'relative'
|
|
13
|
-
container.style.display = 'inline-block'
|
|
14
|
-
container.style.width = 'auto'
|
|
15
|
-
container.style.color = 'inherit'
|
|
16
|
-
textField.parentNode.insertBefore(container, textField)
|
|
17
|
-
|
|
18
|
-
textField.setAttribute('data-dictate-button-enabled', '')
|
|
19
|
-
|
|
20
|
-
container.appendChild(textField)
|
|
21
|
-
|
|
22
|
-
// Ensure textarea fills container
|
|
23
|
-
textField.style.boxSizing = 'border-box'
|
|
24
|
-
// textarea.style.paddingRight = `${BUTTONS_SIZE + 2 * 2 + 6}px`
|
|
25
|
-
|
|
26
|
-
// Add the dictate-button component.
|
|
27
|
-
const dictateBtn = document.createElement('dictate-button')
|
|
28
|
-
dictateBtn.size = BUTTONS_SIZE
|
|
29
|
-
dictateBtn.style.position = 'absolute'
|
|
30
|
-
dictateBtn.style.right = '0'
|
|
31
|
-
dictateBtn.style.top = '0'
|
|
32
|
-
|
|
33
|
-
// Add event listeners for the dictate-button component.
|
|
34
|
-
dictateBtn.addEventListener('recording:started', (e) => {
|
|
35
|
-
console.log('recording:started', e)
|
|
36
|
-
})
|
|
37
|
-
dictateBtn.addEventListener('recording:stopped', (e) => {
|
|
38
|
-
console.log('recording:stopped', e)
|
|
39
|
-
})
|
|
40
|
-
dictateBtn.addEventListener('recording:failed', (e) => {
|
|
41
|
-
console.log('recording:failed', e)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
dictateBtn.addEventListener('transcribing:started', (e) => {
|
|
45
|
-
console.log('transcribing:started', e)
|
|
46
|
-
})
|
|
47
|
-
dictateBtn.addEventListener('transcribing:finished', (e) => {
|
|
48
|
-
console.log('transcribing:finished', e)
|
|
49
|
-
const customEvent = e
|
|
50
|
-
const text = customEvent.detail
|
|
51
|
-
receiveText(textField, text)
|
|
52
|
-
})
|
|
53
|
-
dictateBtn.addEventListener('transcribing:failed', (e) => {
|
|
54
|
-
console.log('transcribing:failed', e)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
container.appendChild(dictateBtn)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function receiveText(textField, text) {
|
|
62
|
-
const start = textField.selectionStart || 0
|
|
63
|
-
const end = textField.selectionEnd || 0
|
|
64
|
-
textField.value =
|
|
65
|
-
textField.value.substring(0, start) + text + textField.value.substring(end)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
69
|
-
injectDictateButton()
|
|
70
|
-
if (WATCH_DOM_CHANGES) {
|
|
71
|
-
new MutationObserver(injectDictateButton).observe(document.body, {
|
|
72
|
-
childList: true,
|
|
73
|
-
subtree: true,
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
})
|
package/src/inject.js
DELETED
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"declaration": true,
|
|
4
|
-
"declarationDir": "dist",
|
|
5
|
-
"outDir": "dist",
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"target": "ESNext",
|
|
8
|
-
"moduleResolution": "Node",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"allowSyntheticDefaultImports": true,
|
|
13
|
-
"jsx": "preserve",
|
|
14
|
-
"jsxImportSource": "solid-js"
|
|
15
|
-
},
|
|
16
|
-
"include": ["src", "vite-env.d.ts"]
|
|
17
|
-
}
|
package/vite-env.d.ts
DELETED
package/vite.config.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "vite";
|
|
2
|
-
import dts from "vite-plugin-dts";
|
|
3
|
-
import solidPlugin from "vite-plugin-solid";
|
|
4
|
-
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
|
5
|
-
import pkg from './package.json'
|
|
6
|
-
|
|
7
|
-
export default defineConfig({
|
|
8
|
-
define: {
|
|
9
|
-
__APP_VERSION__: JSON.stringify(pkg.version),
|
|
10
|
-
},
|
|
11
|
-
plugins: [
|
|
12
|
-
solidPlugin(),
|
|
13
|
-
dts({
|
|
14
|
-
insertTypesEntry: true,
|
|
15
|
-
}),
|
|
16
|
-
viteStaticCopy({
|
|
17
|
-
targets: [
|
|
18
|
-
{
|
|
19
|
-
src: '404.html',
|
|
20
|
-
dest: '',
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
}),
|
|
24
|
-
],
|
|
25
|
-
build: {
|
|
26
|
-
lib: {
|
|
27
|
-
entry: {
|
|
28
|
-
'dictate-button': 'src/dictate-button.tsx',
|
|
29
|
-
inject: 'src/inject.js',
|
|
30
|
-
'inject-exclusive': 'src/inject-exclusive.js',
|
|
31
|
-
'inject-inclusive': 'src/inject-inclusive.js',
|
|
32
|
-
},
|
|
33
|
-
formats: ['es', 'cjs'],
|
|
34
|
-
fileName: (format, entryName) => `${entryName}.${format}.js`,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
})
|