@usero/sdk 1.1.12 → 1.1.13
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.
|
@@ -93,6 +93,7 @@ interface RecorderStore {
|
|
|
93
93
|
tasksPanelOpen: boolean;
|
|
94
94
|
outsidePointerHandler: ((event: PointerEvent) => void) | null;
|
|
95
95
|
keydownHandler: ((event: KeyboardEvent) => void) | null;
|
|
96
|
+
keyboardWatcherCleanup: (() => void) | null;
|
|
96
97
|
hasMicPermission: boolean;
|
|
97
98
|
micAcquiring: boolean;
|
|
98
99
|
micFailReason: 'blocked' | 'not-found' | 'unsupported' | null;
|
|
@@ -153,6 +154,7 @@ declare function persistActiveSession(store: RecorderStore, status: 'active' | '
|
|
|
153
154
|
declare function getTestSlug(queryParam: string): string | null;
|
|
154
155
|
declare function adoptSession(apiUrl: string, sessionId: string): Promise<AdoptResult>;
|
|
155
156
|
|
|
157
|
+
declare function computeKeyboardInset(innerHeight: number, viewportHeight: number, viewportOffsetTop: number): number;
|
|
156
158
|
declare function micChipState(store: RecorderStore): 'recording' | 'muted' | 'none' | 'connecting' | 'silent' | 'inactive';
|
|
157
159
|
|
|
158
160
|
declare function userTest(options?: UserTestOptions): UseroPlugin;
|
|
@@ -163,6 +165,7 @@ declare const __test__: {
|
|
|
163
165
|
classifyChunkResponse: typeof classifyChunkResponse;
|
|
164
166
|
handleSessionClosed: typeof handleSessionClosed;
|
|
165
167
|
micChipState: typeof micChipState;
|
|
168
|
+
computeKeyboardInset: typeof computeKeyboardInset;
|
|
166
169
|
isStreamSilent: typeof isStreamSilent;
|
|
167
170
|
rmsDbFromSamples: typeof rmsDbFromSamples;
|
|
168
171
|
SILENCE_RMS_DB_THRESHOLD: number;
|
|
@@ -93,6 +93,7 @@ interface RecorderStore {
|
|
|
93
93
|
tasksPanelOpen: boolean;
|
|
94
94
|
outsidePointerHandler: ((event: PointerEvent) => void) | null;
|
|
95
95
|
keydownHandler: ((event: KeyboardEvent) => void) | null;
|
|
96
|
+
keyboardWatcherCleanup: (() => void) | null;
|
|
96
97
|
hasMicPermission: boolean;
|
|
97
98
|
micAcquiring: boolean;
|
|
98
99
|
micFailReason: 'blocked' | 'not-found' | 'unsupported' | null;
|
|
@@ -153,6 +154,7 @@ declare function persistActiveSession(store: RecorderStore, status: 'active' | '
|
|
|
153
154
|
declare function getTestSlug(queryParam: string): string | null;
|
|
154
155
|
declare function adoptSession(apiUrl: string, sessionId: string): Promise<AdoptResult>;
|
|
155
156
|
|
|
157
|
+
declare function computeKeyboardInset(innerHeight: number, viewportHeight: number, viewportOffsetTop: number): number;
|
|
156
158
|
declare function micChipState(store: RecorderStore): 'recording' | 'muted' | 'none' | 'connecting' | 'silent' | 'inactive';
|
|
157
159
|
|
|
158
160
|
declare function userTest(options?: UserTestOptions): UseroPlugin;
|
|
@@ -163,6 +165,7 @@ declare const __test__: {
|
|
|
163
165
|
classifyChunkResponse: typeof classifyChunkResponse;
|
|
164
166
|
handleSessionClosed: typeof handleSessionClosed;
|
|
165
167
|
micChipState: typeof micChipState;
|
|
168
|
+
computeKeyboardInset: typeof computeKeyboardInset;
|
|
166
169
|
isStreamSilent: typeof isStreamSilent;
|
|
167
170
|
rmsDbFromSamples: typeof rmsDbFromSamples;
|
|
168
171
|
SILENCE_RMS_DB_THRESHOLD: number;
|
|
@@ -291,19 +291,77 @@ async function postNoteWithRetry(apiUrl, sessionId, atMs, text, logger) {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
// src/plugins/user-test/ui.ts
|
|
294
|
+
var KEYBOARD_OPEN_MIN_INSET_PX = 80;
|
|
295
|
+
function computeKeyboardInset(innerHeight, viewportHeight, viewportOffsetTop) {
|
|
296
|
+
return Math.max(0, Math.round(innerHeight - (viewportHeight + viewportOffsetTop)));
|
|
297
|
+
}
|
|
298
|
+
function installKeyboardInsetWatcher(anchor) {
|
|
299
|
+
const viewport = window.visualViewport;
|
|
300
|
+
if (!viewport) return null;
|
|
301
|
+
let rafId = 0;
|
|
302
|
+
let lastInset = -1;
|
|
303
|
+
let resizeSeen = false;
|
|
304
|
+
const apply = () => {
|
|
305
|
+
rafId = 0;
|
|
306
|
+
const fromResize = resizeSeen;
|
|
307
|
+
resizeSeen = false;
|
|
308
|
+
const inset = computeKeyboardInset(window.innerHeight, viewport.height, viewport.offsetTop);
|
|
309
|
+
if (inset === lastInset) return;
|
|
310
|
+
lastInset = inset;
|
|
311
|
+
anchor.setAttribute("data-vv-scrolling", fromResize ? "false" : "true");
|
|
312
|
+
anchor.style.setProperty("--keyboard-inset", `${inset}px`);
|
|
313
|
+
anchor.style.setProperty("--vv-height", `${Math.round(viewport.height)}px`);
|
|
314
|
+
anchor.setAttribute("data-keyboard-open", inset >= KEYBOARD_OPEN_MIN_INSET_PX ? "true" : "false");
|
|
315
|
+
};
|
|
316
|
+
const schedule = () => {
|
|
317
|
+
if (rafId === 0) rafId = window.requestAnimationFrame(apply);
|
|
318
|
+
};
|
|
319
|
+
const onResize = () => {
|
|
320
|
+
resizeSeen = true;
|
|
321
|
+
schedule();
|
|
322
|
+
};
|
|
323
|
+
const onScroll = () => {
|
|
324
|
+
schedule();
|
|
325
|
+
};
|
|
326
|
+
viewport.addEventListener("resize", onResize);
|
|
327
|
+
viewport.addEventListener("scroll", onScroll);
|
|
328
|
+
resizeSeen = true;
|
|
329
|
+
apply();
|
|
330
|
+
return () => {
|
|
331
|
+
viewport.removeEventListener("resize", onResize);
|
|
332
|
+
viewport.removeEventListener("scroll", onScroll);
|
|
333
|
+
if (rafId !== 0) window.cancelAnimationFrame(rafId);
|
|
334
|
+
};
|
|
335
|
+
}
|
|
294
336
|
function buildIndicator(host, store, callbacks) {
|
|
295
337
|
const root = host.attachShadow({ mode: "open" });
|
|
296
338
|
const style = document.createElement("style");
|
|
297
339
|
style.textContent = `
|
|
298
340
|
:host { all: initial; }
|
|
299
341
|
.anchor {
|
|
342
|
+
/* --keyboard-inset is the height of the mobile soft keyboard, written by
|
|
343
|
+
the visualViewport watcher (0 when closed / unsupported). position:fixed
|
|
344
|
+
anchors to the LAYOUT viewport, which the keyboard does not shrink, so
|
|
345
|
+
without this the open keyboard covers the bar and task panel entirely. */
|
|
346
|
+
--keyboard-inset: 0px;
|
|
300
347
|
position: fixed;
|
|
301
|
-
bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
|
|
348
|
+
bottom: calc(env(safe-area-inset-bottom, 0px) + 16px + var(--keyboard-inset));
|
|
302
349
|
left: 50%; transform: translateX(-50%);
|
|
303
350
|
display: flex; flex-direction: column; align-items: center; gap: 8px;
|
|
304
351
|
z-index: 2147483646; max-width: calc(100vw - 32px);
|
|
305
352
|
font: 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
306
353
|
color: #fff;
|
|
354
|
+
/* Eased only for keyboard show/hide; scroll ticks suppress it below. */
|
|
355
|
+
transition: bottom 0.18s ease-out;
|
|
356
|
+
}
|
|
357
|
+
/* While the visual viewport is being panned (URL bar collapse, pinch,
|
|
358
|
+
keyboard-driven scroll) the inset must track 1:1, animating every tick
|
|
359
|
+
reads as lag. The watcher flips this attribute per update source. */
|
|
360
|
+
.anchor[data-vv-scrolling="true"] { transition: none; }
|
|
361
|
+
/* Keyboard open: the keyboard covers the home-indicator zone, so the
|
|
362
|
+
safe-area + 16px margin is dead space. Tuck in to an 8px gap instead. */
|
|
363
|
+
.anchor[data-keyboard-open="true"] {
|
|
364
|
+
bottom: calc(var(--keyboard-inset) + 8px);
|
|
307
365
|
}
|
|
308
366
|
.bar {
|
|
309
367
|
display: inline-flex; align-items: center; gap: 6px;
|
|
@@ -326,6 +384,16 @@ function buildIndicator(host, store, callbacks) {
|
|
|
326
384
|
width: max-content; overflow-y: auto;
|
|
327
385
|
}
|
|
328
386
|
.panel[hidden] { display: none; }
|
|
387
|
+
/* Compact state while the keyboard is up: 60vh is measured against the
|
|
388
|
+
LAYOUT viewport and can exceed the visible strip above the keyboard,
|
|
389
|
+
clipping the instructions. Cap against the VISUAL viewport height
|
|
390
|
+
(--vv-height, written by the watcher) minus the bar's footprint, with a
|
|
391
|
+
96px floor so at least a couple of lines stay readable and scrollable.
|
|
392
|
+
Slightly tighter padding to make the most of the scarce space. */
|
|
393
|
+
.anchor[data-keyboard-open="true"] .panel {
|
|
394
|
+
max-height: min(480px, max(96px, calc(var(--vv-height, 100vh) - 96px)));
|
|
395
|
+
padding: 10px 12px 10px 8px;
|
|
396
|
+
}
|
|
329
397
|
.panel ol { margin: 0; padding-left: 26px; }
|
|
330
398
|
.panel li { margin: 0 0 8px; }
|
|
331
399
|
.panel li:last-child { margin: 0; }
|
|
@@ -761,10 +829,12 @@ function buildIndicator(host, store, callbacks) {
|
|
|
761
829
|
.dot { animation: none; }
|
|
762
830
|
.toast, .note-popover, .resume-toast { animation: none; }
|
|
763
831
|
.resume-toast[data-leaving="true"] { opacity: 0; }
|
|
832
|
+
.anchor { transition: none; }
|
|
764
833
|
}
|
|
765
834
|
`;
|
|
766
835
|
const anchor = document.createElement("div");
|
|
767
836
|
anchor.className = "anchor";
|
|
837
|
+
store.keyboardWatcherCleanup = installKeyboardInsetWatcher(anchor);
|
|
768
838
|
const panel = document.createElement("div");
|
|
769
839
|
panel.className = "panel";
|
|
770
840
|
panel.hidden = true;
|
|
@@ -1921,6 +1991,7 @@ function userTest(options = {}) {
|
|
|
1921
1991
|
tasksPanelOpen: readTasksPanelOpen(),
|
|
1922
1992
|
outsidePointerHandler: null,
|
|
1923
1993
|
keydownHandler: null,
|
|
1994
|
+
keyboardWatcherCleanup: null,
|
|
1924
1995
|
hasMicPermission: false,
|
|
1925
1996
|
micAcquiring: true,
|
|
1926
1997
|
micFailReason: null,
|
|
@@ -2127,6 +2198,10 @@ function userTest(options = {}) {
|
|
|
2127
2198
|
document.removeEventListener("keydown", store.keydownHandler);
|
|
2128
2199
|
store.keydownHandler = null;
|
|
2129
2200
|
}
|
|
2201
|
+
if (store.keyboardWatcherCleanup) {
|
|
2202
|
+
store.keyboardWatcherCleanup();
|
|
2203
|
+
store.keyboardWatcherCleanup = null;
|
|
2204
|
+
}
|
|
2130
2205
|
for (const id of store.muteToastTimers) {
|
|
2131
2206
|
try {
|
|
2132
2207
|
window.clearTimeout(id);
|
|
@@ -2156,6 +2231,7 @@ var __test__ = {
|
|
|
2156
2231
|
classifyChunkResponse,
|
|
2157
2232
|
handleSessionClosed,
|
|
2158
2233
|
micChipState,
|
|
2234
|
+
computeKeyboardInset,
|
|
2159
2235
|
isStreamSilent,
|
|
2160
2236
|
rmsDbFromSamples,
|
|
2161
2237
|
SILENCE_RMS_DB_THRESHOLD,
|