dictate-button 1.4.0 → 1.5.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 +10 -6
- package/dist/dictate-button.js +29 -27
- package/dist/inject-exclusive.js +2 -1
- package/dist/inject-inclusive.js +2 -1
- package/dist/libs/injectDictateButton.js +113 -54
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Dictate Button (Web Component)
|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
A customizable web component that adds speech-to-text dictation capabilities to any text input
|
|
4
|
+
A customizable web component that adds speech-to-text dictation capabilities to any text input, textarea field, or contenteditable element on your website.
|
|
5
5
|
|
|
6
6
|
Developed for [dictate-button.io](https://dictate-button.io).
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ Developed for [dictate-button.io](https://dictate-button.io).
|
|
|
9
9
|
|
|
10
10
|
- Easy integration with any website
|
|
11
11
|
- Compatible with any framework (or no framework)
|
|
12
|
-
- Automatic injection into
|
|
12
|
+
- Automatic injection into text fields with the `data-dictate-button-on` attribute (exclusive mode) or without the `data-dictate-button-off` attribute (inclusive mode)
|
|
13
13
|
- Simple speech-to-text functionality with clean UI
|
|
14
14
|
- Customizable size and API endpoint
|
|
15
15
|
- Dark and light theme support
|
|
@@ -23,6 +23,7 @@ Developed for [dictate-button.io](https://dictate-button.io).
|
|
|
23
23
|
- input[type="text"]
|
|
24
24
|
- input[type="search"]
|
|
25
25
|
- input (without a type; defaults to text)
|
|
26
|
+
- [contenteditable] elements
|
|
26
27
|
|
|
27
28
|
## Usage
|
|
28
29
|
|
|
@@ -39,6 +40,7 @@ Both auto-inject modes:
|
|
|
39
40
|
- Automatically run on DOMContentLoaded (or immediately if the DOM is already loaded).
|
|
40
41
|
- Watch for DOM changes to apply the dictate button to newly added elements.
|
|
41
42
|
- Set the button’s language from `document.documentElement.lang` (if present). Long codes like `en-GB` are normalized to `en`.
|
|
43
|
+
- Position the button to the top right-hand corner of the text field, respecting its padding with 4px fallback if the padding is not set (0).
|
|
42
44
|
|
|
43
45
|
### From CDN
|
|
44
46
|
|
|
@@ -51,13 +53,14 @@ In your HTML `<head>` tag, add the following script tags:
|
|
|
51
53
|
<script type="module" crossorigin src="https://cdn.dictate-button.io/inject-exclusive.js"></script>
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
Add the `data-dictate-button-on` attribute to any `textarea`, `input[type="text"]`, `input[type="search"]`,
|
|
56
|
+
Add the `data-dictate-button-on` attribute to any `textarea`, `input[type="text"]`, `input[type="search"]`, `input` without a `type` attribute, or element with the `contenteditable` attribute:
|
|
55
57
|
|
|
56
58
|
```html
|
|
57
59
|
<textarea data-dictate-button-on></textarea>
|
|
58
60
|
<input type="text" data-dictate-button-on />
|
|
59
61
|
<input type="search" data-dictate-button-on />
|
|
60
62
|
<input data-dictate-button-on />
|
|
63
|
+
<div contenteditable data-dictate-button-on />
|
|
61
64
|
```
|
|
62
65
|
|
|
63
66
|
#### Option 2: Using the inclusive auto-inject script
|
|
@@ -69,7 +72,7 @@ In your HTML `<head>` tag, add the following script tags:
|
|
|
69
72
|
<script type="module" crossorigin src="https://cdn.dictate-button.io/inject-inclusive.js"></script>
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
All `textarea`, `input[type="text"]`, `input[type="search"]`,
|
|
75
|
+
All `textarea`, `input[type="text"]`, `input[type="search"]`, `input` elements without a `type` attribute, and elements with the `contenteditable` attribute that lack `data-dictate-button-off` will be automatically enhanced by default.
|
|
73
76
|
|
|
74
77
|
To disable that for a specific field, add the `data-dictate-button-off` attribute to it this way:
|
|
75
78
|
|
|
@@ -78,6 +81,7 @@ To disable that for a specific field, add the `data-dictate-button-off` attribut
|
|
|
78
81
|
<input type="text" data-dictate-button-off />
|
|
79
82
|
<input type="search" data-dictate-button-off />
|
|
80
83
|
<input data-dictate-button-off />
|
|
84
|
+
<div contenteditable data-dictate-button-off />
|
|
81
85
|
```
|
|
82
86
|
|
|
83
87
|
#### Option 3: Manual integration
|
|
@@ -136,9 +140,9 @@ injectDictateButtonOnLoad(
|
|
|
136
140
|
'input.custom-selector', // CSS selector for target elements
|
|
137
141
|
{
|
|
138
142
|
buttonSize: 30, // Button size in pixels (optional; default: 30)
|
|
139
|
-
watchDomChanges: true, // Watch for DOM changes (optional; default: false)
|
|
140
143
|
verbose: false, // Log events to console (optional; default: false)
|
|
141
|
-
customApiEndpoint: 'https://api.example.com/transcribe' // Optional custom API endpoint
|
|
144
|
+
customApiEndpoint: 'https://api.example.com/transcribe', // Optional custom API endpoint
|
|
145
|
+
watchDomChanges: true // Watch for DOM changes (optional; default: false)
|
|
142
146
|
}
|
|
143
147
|
)
|
|
144
148
|
```
|
package/dist/dictate-button.js
CHANGED
|
@@ -8,20 +8,20 @@ const S = 1, R = 2, lt = {
|
|
|
8
8
|
context: null,
|
|
9
9
|
owner: null
|
|
10
10
|
};
|
|
11
|
-
var
|
|
11
|
+
var y = null;
|
|
12
12
|
let V = null, Ct = null, p = null, g = null, m = null, L = 0;
|
|
13
13
|
function mt(t, e) {
|
|
14
|
-
const n = p, o =
|
|
14
|
+
const n = p, o = y, r = t.length === 0, i = e === void 0 ? o : e, l = r ? lt : {
|
|
15
15
|
owned: null,
|
|
16
16
|
cleanups: null,
|
|
17
17
|
context: i ? i.context : null,
|
|
18
18
|
owner: i
|
|
19
19
|
}, s = r ? t : () => t(() => W(() => $(l)));
|
|
20
|
-
|
|
20
|
+
y = l, p = null;
|
|
21
21
|
try {
|
|
22
22
|
return T(s, !0);
|
|
23
23
|
} finally {
|
|
24
|
-
p = n,
|
|
24
|
+
p = n, y = o;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
function at(t, e) {
|
|
@@ -85,14 +85,14 @@ function K(t) {
|
|
|
85
85
|
}
|
|
86
86
|
function xt(t, e, n) {
|
|
87
87
|
let o;
|
|
88
|
-
const r =
|
|
89
|
-
p =
|
|
88
|
+
const r = y, i = p;
|
|
89
|
+
p = y = t;
|
|
90
90
|
try {
|
|
91
91
|
o = t.fn(e);
|
|
92
92
|
} catch (l) {
|
|
93
93
|
return t.pure && (t.state = S, t.owned && t.owned.forEach($), t.owned = null), t.updatedAt = n + 1, gt(l);
|
|
94
94
|
} finally {
|
|
95
|
-
p = i,
|
|
95
|
+
p = i, y = r;
|
|
96
96
|
}
|
|
97
97
|
(!t.updatedAt || t.updatedAt <= n) && (t.updatedAt != null && "observers" in t ? ut(t, o) : t.value = o, t.updatedAt = n);
|
|
98
98
|
}
|
|
@@ -106,11 +106,11 @@ function dt(t, e, n, o = S, r) {
|
|
|
106
106
|
sourceSlots: null,
|
|
107
107
|
cleanups: null,
|
|
108
108
|
value: e,
|
|
109
|
-
owner:
|
|
110
|
-
context:
|
|
109
|
+
owner: y,
|
|
110
|
+
context: y ? y.context : null,
|
|
111
111
|
pure: n
|
|
112
112
|
};
|
|
113
|
-
return
|
|
113
|
+
return y === null || y !== lt && (y.owned ? y.owned.push(i) : y.owned = [i]), i;
|
|
114
114
|
}
|
|
115
115
|
function ft(t) {
|
|
116
116
|
if (t.state === 0) return;
|
|
@@ -191,7 +191,7 @@ function Et(t) {
|
|
|
191
191
|
cause: t
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
-
function gt(t, e =
|
|
194
|
+
function gt(t, e = y) {
|
|
195
195
|
throw Et(t);
|
|
196
196
|
}
|
|
197
197
|
function N(t, e) {
|
|
@@ -387,13 +387,13 @@ function x(t, e, n, o) {
|
|
|
387
387
|
function Pt(t) {
|
|
388
388
|
return Object.keys(t).reduce((n, o) => {
|
|
389
389
|
const r = t[o];
|
|
390
|
-
return n[o] = Object.assign({}, r),
|
|
390
|
+
return n[o] = Object.assign({}, r), yt(r.value) && !Rt(r.value) && !Array.isArray(r.value) && (n[o].value = Object.assign({}, r.value)), Array.isArray(r.value) && (n[o].value = r.value.slice(0)), n;
|
|
391
391
|
}, {});
|
|
392
392
|
}
|
|
393
393
|
function Mt(t) {
|
|
394
394
|
return t ? Object.keys(t).reduce((n, o) => {
|
|
395
395
|
const r = t[o];
|
|
396
|
-
return n[o] =
|
|
396
|
+
return n[o] = yt(r) && "value" in r ? r : {
|
|
397
397
|
value: r
|
|
398
398
|
}, n[o].attribute || (n[o].attribute = Bt(o)), n[o].parse = "parse" in n[o] ? n[o].parse : typeof n[o].value != "string", n;
|
|
399
399
|
}, {}) : {};
|
|
@@ -436,7 +436,7 @@ function rt(t, e, n, o) {
|
|
|
436
436
|
function Bt(t) {
|
|
437
437
|
return t.replace(/\.?([A-Z]+)/g, (e, n) => "-" + n.toLowerCase()).replace("_", "-").replace(/^-/, "");
|
|
438
438
|
}
|
|
439
|
-
function
|
|
439
|
+
function yt(t) {
|
|
440
440
|
return t != null && (typeof t == "object" || typeof t == "function");
|
|
441
441
|
}
|
|
442
442
|
function Rt(t) {
|
|
@@ -454,6 +454,8 @@ function Dt(t, e) {
|
|
|
454
454
|
}
|
|
455
455
|
constructor() {
|
|
456
456
|
super(), this.__initialized = !1, this.__released = !1, this.__releaseCallbacks = [], this.__propertyChangedCallbacks = [], this.__updating = {}, this.props = {};
|
|
457
|
+
for (let r of n)
|
|
458
|
+
this[r] = void 0;
|
|
457
459
|
}
|
|
458
460
|
connectedCallback() {
|
|
459
461
|
if (this.__initialized) return;
|
|
@@ -605,7 +607,7 @@ const Vt = `
|
|
|
605
607
|
}
|
|
606
608
|
`;
|
|
607
609
|
var qt = /* @__PURE__ */ O('<div part=container class=dictate-button__container><style></style><div aria-live=polite class=dictate-button__status-announcer style="position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0"></div><button part=button class=dictate-button__button>'), Ht = /* @__PURE__ */ O('<svg part=icon class="dictate-button__icon dictate-button__icon--idle"fill=none viewBox="0 0 24 24"stroke-width=1.5 stroke=currentColor role=img aria-hidden=true><path stroke-linecap=round stroke-linejoin=round 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">'), Gt = /* @__PURE__ */ O('<svg part=icon class="dictate-button__icon dictate-button__icon--recording"viewBox="0 0 24 24"fill=currentColor role=img aria-hidden=true><circle cx=12 cy=12 r=10>'), Wt = /* @__PURE__ */ O('<svg part=icon class="dictate-button__icon dictate-button__icon--processing"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round role=img aria-hidden=true><path d="M12 2v4"></path><path d="m16.2 7.8 2.9-2.9"></path><path d="M18 12h4"></path><path d="m16.2 16.2 2.9 2.9"></path><path d="M12 18v4"></path><path d="m4.9 19.1 2.9-2.9"></path><path d="M2 12h4"></path><path d="m4.9 4.9 2.9 2.9">'), Xt = /* @__PURE__ */ O('<svg part=icon class="dictate-button__icon dictate-button__icon--error"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=4 stroke-linecap=round stroke-linejoin=round role=img aria-hidden=true><line x1=12 x2=12 y1=4 y2=14></line><line x1=12 x2=12.01 y1=20 y2=20>');
|
|
608
|
-
console.debug("dictate-button version:", "1.
|
|
610
|
+
console.debug("dictate-button version:", "1.5.0");
|
|
609
611
|
const Jt = "https://api.dictate-button.io/transcribe", v = "dictate-button.io", H = -70, ot = -10, it = 0, Zt = 4, Qt = 0.25, Yt = 0.05;
|
|
610
612
|
zt("dictate-button", {
|
|
611
613
|
size: 30,
|
|
@@ -618,36 +620,36 @@ zt("dictate-button", {
|
|
|
618
620
|
const [n, o] = at("idle");
|
|
619
621
|
let r = null, i = [], l = null, s = null, a = null, u = !1, d = 0;
|
|
620
622
|
const w = (f) => f <= H ? 0 : f >= ot ? 1 : (f - H) / (ot - H), E = (f) => {
|
|
621
|
-
let
|
|
623
|
+
let _ = 0;
|
|
622
624
|
for (let b = 0; b < f.length; b++) {
|
|
623
625
|
const c = (f[b] - 128) / 128;
|
|
624
|
-
|
|
626
|
+
_ += c * c;
|
|
625
627
|
}
|
|
626
|
-
return Math.sqrt(
|
|
628
|
+
return Math.sqrt(_ / f.length);
|
|
627
629
|
}, P = (f) => 20 * Math.log10(Math.max(f, 1e-8)), M = (f) => {
|
|
628
|
-
const
|
|
629
|
-
if (!
|
|
630
|
+
const _ = e.shadowRoot.querySelector(".dictate-button__button");
|
|
631
|
+
if (!_)
|
|
630
632
|
return;
|
|
631
633
|
const b = it + f * (Zt - it), c = 0 + f * 0.4;
|
|
632
|
-
|
|
634
|
+
_.style.boxShadow = `0 0 0 ${b}px light-dark(rgba(0, 0, 0, ${c}), rgba(255, 255, 255, ${c}))`;
|
|
633
635
|
}, X = () => {
|
|
634
636
|
if (!u || !s || !a) return;
|
|
635
637
|
s.getByteTimeDomainData(a);
|
|
636
|
-
const f = E(a),
|
|
638
|
+
const f = E(a), _ = P(f), b = w(_), c = b > d ? Qt : Yt;
|
|
637
639
|
d = c * b + (1 - c) * d, M(d), requestAnimationFrame(X);
|
|
638
640
|
}, U = () => {
|
|
639
641
|
r && r.state !== "inactive" && r.stop(), i = [], u = !1, l && l.state !== "closed" && l.close(), l = null, s = null, a = null, d = 0, M(0);
|
|
640
642
|
};
|
|
641
643
|
e.addEventListener("disconnected", U);
|
|
642
|
-
const
|
|
644
|
+
const _t = async () => {
|
|
643
645
|
if (U(), n() === "idle")
|
|
644
646
|
try {
|
|
645
647
|
const f = await navigator.mediaDevices.getUserMedia({
|
|
646
648
|
audio: !0
|
|
647
649
|
});
|
|
648
650
|
l = new (window.AudioContext || window.webkitAudioContext)();
|
|
649
|
-
const
|
|
650
|
-
s = l.createAnalyser(), s.fftSize = 2048,
|
|
651
|
+
const _ = l.createMediaStreamSource(f);
|
|
652
|
+
s = l.createAnalyser(), s.fftSize = 2048, _.connect(s), a = new Uint8Array(s.fftSize), r = new MediaRecorder(f, {
|
|
651
653
|
mimeType: "audio/webm"
|
|
652
654
|
}), i = [], r.ondataavailable = (b) => {
|
|
653
655
|
i.push(b.data);
|
|
@@ -680,8 +682,8 @@ zt("dictate-button", {
|
|
|
680
682
|
o("error"), setTimeout(() => o("idle"), 2e3);
|
|
681
683
|
};
|
|
682
684
|
return (() => {
|
|
683
|
-
var f = qt(),
|
|
684
|
-
return C(
|
|
685
|
+
var f = qt(), _ = f.firstChild, b = _.nextSibling, c = b.nextSibling;
|
|
686
|
+
return C(_, Vt), C(b, () => st(n())), c.$$click = _t, C(c, (() => {
|
|
685
687
|
var h = j(() => n() === "idle");
|
|
686
688
|
return () => h() && N(ee, {});
|
|
687
689
|
})(), null), C(c, (() => {
|
package/dist/inject-exclusive.js
CHANGED
|
@@ -3,7 +3,8 @@ const a = 30, n = !0, e = !1, o = [
|
|
|
3
3
|
"textarea[data-dictate-button-on]:not([data-dictate-button-enabled])",
|
|
4
4
|
'input[type="text"][data-dictate-button-on]:not([data-dictate-button-enabled])',
|
|
5
5
|
'input[type="search"][data-dictate-button-on]:not([data-dictate-button-enabled])',
|
|
6
|
-
"input[data-dictate-button-on]:not([type]):not([data-dictate-button-enabled])"
|
|
6
|
+
"input[data-dictate-button-on]:not([type]):not([data-dictate-button-enabled])",
|
|
7
|
+
"*[contenteditable][data-dictate-button-on]:not([data-dictate-button-enabled])"
|
|
7
8
|
].join(",");
|
|
8
9
|
t(o, {
|
|
9
10
|
buttonSize: a,
|
package/dist/inject-inclusive.js
CHANGED
|
@@ -3,7 +3,8 @@ const a = 30, n = !0, o = !1, e = [
|
|
|
3
3
|
"textarea:not([data-dictate-button-off]):not([data-dictate-button-enabled])",
|
|
4
4
|
'input[type="text"]:not([data-dictate-button-off]):not([data-dictate-button-enabled])',
|
|
5
5
|
'input[type="search"]:not([data-dictate-button-off]):not([data-dictate-button-enabled])',
|
|
6
|
-
"input:not([type]):not([data-dictate-button-off]):not([data-dictate-button-enabled])"
|
|
6
|
+
"input:not([type]):not([data-dictate-button-off]):not([data-dictate-button-enabled])",
|
|
7
|
+
"*[contenteditable]:not([data-dictate-button-off]):not([data-dictate-button-enabled])"
|
|
7
8
|
].join(",");
|
|
8
9
|
t(e, {
|
|
9
10
|
buttonSize: a,
|
|
@@ -1,39 +1,40 @@
|
|
|
1
|
-
function
|
|
2
|
-
const { buttonSize:
|
|
3
|
-
for (const
|
|
4
|
-
if (
|
|
5
|
-
const
|
|
6
|
-
if (!
|
|
7
|
-
|
|
1
|
+
function v(t, c = {}) {
|
|
2
|
+
const { buttonSize: n = 30, verbose: i = !1, customApiEndpoint: a } = c, l = document.querySelectorAll(t);
|
|
3
|
+
for (const e of l) {
|
|
4
|
+
if (e.hasAttribute("data-dictate-button-enabled")) continue;
|
|
5
|
+
const p = e.parentNode;
|
|
6
|
+
if (!e.isConnected || !p) {
|
|
7
|
+
i && console.debug("injectDictateButton: skipping detached field", e);
|
|
8
8
|
continue;
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
e.setAttribute("data-dictate-button-enabled", "");
|
|
11
|
+
const r = document.createElement("div");
|
|
12
|
+
r.style.position = "relative";
|
|
13
|
+
const g = getComputedStyle(e), s = g.display === "block";
|
|
14
|
+
r.style.display = s ? "block" : "inline-block", r.style.width = s ? "100%" : "auto", r.style.color = "inherit", r.classList.add("dictate-button-wrapper"), p.insertBefore(r, e), r.appendChild(e), r.style.margin = g.margin, e.style.margin = "0", e.style.boxSizing = "border-box";
|
|
15
|
+
const u = m(g);
|
|
16
|
+
e.style.paddingRight = `${n + u * 2}px`;
|
|
17
|
+
const o = document.createElement("dictate-button");
|
|
18
|
+
o.size = n, o.style.position = "absolute", o.style.right = "0", o.style.top = E(
|
|
19
|
+
r,
|
|
20
|
+
g,
|
|
21
|
+
e.tagName,
|
|
22
|
+
n
|
|
23
|
+
) + "px", o.style.marginRight = o.style.marginLeft = `${u}px`, o.style.marginTop = "0", o.style.marginBottom = "0", a && (o.apiEndpoint = a), o.language = h(), o.addEventListener("recording:started", (d) => {
|
|
24
|
+
i && console.debug("recording:started", d);
|
|
25
|
+
}), o.addEventListener("recording:stopped", (d) => {
|
|
26
|
+
i && console.debug("recording:stopped", d);
|
|
27
|
+
}), o.addEventListener("recording:failed", (d) => {
|
|
28
|
+
i && console.debug("recording:failed", d), f(e);
|
|
29
|
+
}), o.addEventListener("transcribing:started", (d) => {
|
|
30
|
+
i && console.debug("transcribing:started", d);
|
|
31
|
+
}), o.addEventListener("transcribing:finished", (d) => {
|
|
32
|
+
i && console.debug("transcribing:finished", d);
|
|
33
|
+
const b = d.detail;
|
|
34
|
+
T(e, b);
|
|
35
|
+
}), o.addEventListener("transcribing:failed", (d) => {
|
|
36
|
+
i && console.debug("transcribing:failed", d), f(e);
|
|
37
|
+
}), r.appendChild(o);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
function h() {
|
|
@@ -45,37 +46,95 @@ function h() {
|
|
|
45
46
|
return t.split(/[-_]/)[0].toLowerCase();
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
function
|
|
49
|
-
if (
|
|
50
|
-
const
|
|
51
|
-
return
|
|
49
|
+
function E(t, c, n, i) {
|
|
50
|
+
if (n.toLowerCase() === "textarea") {
|
|
51
|
+
const l = parseFloat(c.paddingTop || "0");
|
|
52
|
+
return Math.max(4, l);
|
|
52
53
|
}
|
|
53
|
-
const
|
|
54
|
-
return
|
|
54
|
+
const a = Math.round(t.clientHeight / 2 - i / 2);
|
|
55
|
+
return Math.max(4, a);
|
|
55
56
|
}
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
function m(t) {
|
|
58
|
+
const c = parseFloat(t.paddingRight || "0");
|
|
59
|
+
return Math.max(c, 4);
|
|
60
|
+
}
|
|
61
|
+
function T(t, c) {
|
|
62
|
+
const n = typeof c == "string" ? c.trim() : String(c ?? "").trim();
|
|
63
|
+
n.length !== 0 && (y(t) ? N(t, n) : C(t, n), t.dispatchEvent(new Event("input", { bubbles: !0, composed: !0 })), f(t));
|
|
64
|
+
}
|
|
65
|
+
function f(t) {
|
|
66
|
+
try {
|
|
67
|
+
t.focus({ preventScroll: !0 });
|
|
68
|
+
} catch {
|
|
69
|
+
t.focus();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function y(t) {
|
|
73
|
+
return t.isContentEditable;
|
|
74
|
+
}
|
|
75
|
+
function C(t, c) {
|
|
76
|
+
const n = t.selectionStart ?? 0, i = t.selectionEnd ?? 0, a = n > 0 ? t.value.charAt(n - 1) : "", l = a && !/\s/.test(a), e = i < t.value.length ? t.value.charAt(i) : "", p = e && !/\s/.test(e), r = (l ? " " : "") + c + (p ? " " : ""), g = n + r.length, s = typeof t.scrollTop == "number" ? t.scrollTop : null;
|
|
61
77
|
if (typeof t.setRangeText == "function")
|
|
62
|
-
t.setRangeText(
|
|
78
|
+
t.setRangeText(r, n, i, "end");
|
|
63
79
|
else {
|
|
64
|
-
t.value = t.value.substring(0,
|
|
80
|
+
t.value = t.value.substring(0, n) + r + t.value.substring(i);
|
|
65
81
|
try {
|
|
66
82
|
t.selectionStart = g, t.selectionEnd = g;
|
|
67
83
|
} catch {
|
|
68
84
|
}
|
|
69
85
|
}
|
|
70
|
-
|
|
86
|
+
s !== null && (t.scrollTop = s);
|
|
71
87
|
}
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
function N(t, c) {
|
|
89
|
+
const n = window.getSelection();
|
|
90
|
+
if (!(n && n.rangeCount > 0 && t.contains(n.getRangeAt(0).commonAncestorContainer))) {
|
|
91
|
+
f(t);
|
|
92
|
+
const l = document.createRange();
|
|
93
|
+
l.selectNodeContents(t), l.collapse(!1), n?.removeAllRanges(), n?.addRange(l);
|
|
94
|
+
}
|
|
95
|
+
const a = n?.getRangeAt(0);
|
|
96
|
+
if (a) {
|
|
97
|
+
const l = a.cloneRange(), e = a.cloneRange();
|
|
98
|
+
let p = !1;
|
|
99
|
+
l.collapse(!0);
|
|
100
|
+
try {
|
|
101
|
+
l.setStart(a.startContainer, 0);
|
|
102
|
+
const s = l.toString(), u = s.length > 0 ? s.charAt(s.length - 1) : "";
|
|
103
|
+
p = u !== "" && !/\s/.test(u);
|
|
104
|
+
} catch (s) {
|
|
105
|
+
console.debug(
|
|
106
|
+
"insertIntoContentEditable: Error checking text before cursor:",
|
|
107
|
+
s
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
let r = !1;
|
|
111
|
+
e.collapse(!1);
|
|
112
|
+
try {
|
|
113
|
+
if (e.endContainer.nodeType === Node.TEXT_NODE) {
|
|
114
|
+
const o = e.endContainer;
|
|
115
|
+
e.setEnd(o, o.length);
|
|
116
|
+
} else if (e.endContainer.nodeType === Node.ELEMENT_NODE) {
|
|
117
|
+
const o = e.endContainer;
|
|
118
|
+
o.childNodes.length > e.endOffset && e.setEnd(o, e.endOffset + 1);
|
|
119
|
+
}
|
|
120
|
+
const s = e.toString(), u = s.length > 0 ? s.charAt(0) : "";
|
|
121
|
+
r = u !== "" && !/\s/.test(u);
|
|
122
|
+
} catch (s) {
|
|
123
|
+
console.debug(
|
|
124
|
+
"insertIntoContentEditable: Error checking text after cursor:",
|
|
125
|
+
s
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const g = (p ? " " : "") + c + (r ? " " : "");
|
|
129
|
+
try {
|
|
130
|
+
a.deleteContents();
|
|
131
|
+
const s = document.createTextNode(g);
|
|
132
|
+
a.insertNode(s), a.setStartAfter(s), a.setEndAfter(s), n?.removeAllRanges(), n?.addRange(a);
|
|
133
|
+
} catch (s) {
|
|
134
|
+
console.debug("insertIntoContentEditable: Error inserting text:", s), f(t), t.textContent = (t.textContent || "") + g;
|
|
135
|
+
}
|
|
77
136
|
}
|
|
78
137
|
}
|
|
79
138
|
export {
|
|
80
|
-
|
|
139
|
+
v as injectDictateButton
|
|
81
140
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dictate-button",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Dictate Button (Web Component)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"custom-element",
|
|
@@ -65,18 +65,18 @@
|
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"prettier": "^3.6.2",
|
|
67
67
|
"typescript": "^5.9.2",
|
|
68
|
-
"vite": "^7.1.
|
|
68
|
+
"vite": "^7.1.7",
|
|
69
69
|
"vite-plugin-dts": "^4.5.4",
|
|
70
70
|
"vite-plugin-solid": "^2.11.8",
|
|
71
71
|
"vite-plugin-static-copy": "^3.1.2"
|
|
72
72
|
},
|
|
73
|
-
"homepage": "https://github.com/
|
|
73
|
+
"homepage": "https://github.com/dictate-button/dictate-button",
|
|
74
74
|
"repository": {
|
|
75
75
|
"type": "git",
|
|
76
|
-
"url": "https://github.com/
|
|
76
|
+
"url": "https://github.com/dictate-button/dictate-button.git"
|
|
77
77
|
},
|
|
78
78
|
"bugs": {
|
|
79
|
-
"url": "https://github.com/
|
|
79
|
+
"url": "https://github.com/dictate-button/dictate-button/issues"
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|
|
82
82
|
"build": "vite build",
|