@daugasauron/dolly-host-microphone 0.1.5 → 0.1.6
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/microphone.mjs +25 -3
- package/package.json +2 -2
- package/provider.mjs +5 -2
package/microphone.mjs
CHANGED
|
@@ -17,7 +17,7 @@ function indicate({ mount, indicators }, state) {
|
|
|
17
17
|
element.className = "page-indicator";
|
|
18
18
|
element.setAttribute("role", "status");
|
|
19
19
|
element.style.cssText = "position:absolute;left:0.5rem;bottom:2rem;z-index:8;max-width:calc(100% - 1rem);" +
|
|
20
|
-
"padding:0.15rem 0.45rem;background
|
|
20
|
+
"padding:0.15rem 0.45rem;background:var(--dolly-ui-background);border:1px solid var(--dolly-ui-alert);color:var(--dolly-ui-alert);font-size:13px;pointer-events:none";
|
|
21
21
|
}
|
|
22
22
|
const text = { waiting: "Microphone: waiting for permission", capturing: "Microphone on" }[state];
|
|
23
23
|
element.hidden = !text;
|
|
@@ -25,8 +25,30 @@ function indicate({ mount, indicators }, state) {
|
|
|
25
25
|
indicators.hold(element, Boolean(text));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// A session that enabled the module itself (nena@0, `switched`) captures only
|
|
29
|
+
// after the user says yes on the page, once a session: the browser's own
|
|
30
|
+
// prompt is per origin and remembered, so it may not ask again.
|
|
31
|
+
function asked(mount) {
|
|
32
|
+
return new Promise(resolve => {
|
|
33
|
+
const element = mount.appendChild(document.createElement("div"));
|
|
34
|
+
element.setAttribute("role", "alertdialog");
|
|
35
|
+
element.style.cssText = "position:absolute;left:50%;top:40%;transform:translate(-50%,-50%);z-index:9;padding:0.8rem 1rem;" +
|
|
36
|
+
"background:#262626;border:1px solid #e98773;color:#e8e3d7;font-size:14px;max-width:min(30rem,calc(100% - 2rem))";
|
|
37
|
+
element.textContent = "A program in this session asks to use the microphone.";
|
|
38
|
+
const choice = allowed => () => { element.remove(); resolve(allowed); };
|
|
39
|
+
for (const [label, allowed] of [["Allow", true], ["Refuse", false]]) {
|
|
40
|
+
const button = Object.assign(document.createElement("button"), { type: "button", textContent: label });
|
|
41
|
+
button.style.cssText = "margin:0.6rem 0.6rem 0 0;padding:0.2rem 0.9rem;color:inherit;background:transparent;border:1px solid currentColor";
|
|
42
|
+
button.addEventListener("click", choice(allowed));
|
|
43
|
+
element.append(button);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function browser({ mount, send, indicators, switched }) {
|
|
49
|
+
let consented = !switched;
|
|
50
|
+
const consent = async () => consented ||= await asked(mount);
|
|
51
|
+
const provider = createMicrophoneProvider(() => indicate({ mount, indicators }, provider.status().state), consent);
|
|
30
52
|
return {
|
|
31
53
|
page: { get microphone() { return provider.status(); } },
|
|
32
54
|
messages: {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.1.
|
|
2
|
+
"version": "0.1.6",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"name": "@daugasauron/dolly-host-microphone",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@daugasauron/dolly": "0.1.
|
|
7
|
+
"@daugasauron/dolly": "0.1.6"
|
|
8
8
|
},
|
|
9
9
|
"description": "Dolly's microphone@0 host module: the default microphone, behind the browser's permission prompt.",
|
|
10
10
|
"repository": {
|
package/provider.mjs
CHANGED
|
@@ -8,8 +8,9 @@ const names = { [M.DOLLY_MICROPHONE_WAITING]: "waiting", [M.DOLLY_MICROPHONE_CAP
|
|
|
8
8
|
|
|
9
9
|
// The page side of microphone@0: one capture of the browser's default input.
|
|
10
10
|
// OPEN asks the browser, whose own prompt decides; nothing here grants it.
|
|
11
|
-
// `changed` runs whenever the capture's state does
|
|
12
|
-
|
|
11
|
+
// `changed` runs whenever the capture's state does; `consent` resolves to
|
|
12
|
+
// whether the user lets the capture go ahead, before the browser is asked.
|
|
13
|
+
export function createMicrophoneProvider(changed = () => {}, consent = async () => true) {
|
|
13
14
|
let capture, generation = 0;
|
|
14
15
|
|
|
15
16
|
function set(target, state) {
|
|
@@ -36,6 +37,8 @@ export function createMicrophoneProvider(changed = () => {}) {
|
|
|
36
37
|
}
|
|
37
38
|
async function start(target) {
|
|
38
39
|
try {
|
|
40
|
+
if (!await consent()) return set(target, M.DOLLY_MICROPHONE_DENIED);
|
|
41
|
+
if (target.released) return;
|
|
39
42
|
target.stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
40
43
|
if (target.released) return stop(target);
|
|
41
44
|
target.context = new AudioContext({ sampleRate: M.DOLLY_MICROPHONE_RATE });
|