alright
This commit is contained in:
parent
c645b0f1d4
commit
39ef5c6646
79 changed files with 5660 additions and 2199 deletions
|
|
@ -74,11 +74,10 @@ export async function smoothMove(
|
|||
* "Fake" type: progressively set the textarea value, dispatching
|
||||
* React-compatible input events.
|
||||
*
|
||||
* Cadence is generated as a per-char weight ratio (so spaces and punctuation
|
||||
* read as natural pauses), then **rescaled** so that the sum of delays equals
|
||||
* `totalDurationMs` exactly. The runner depends on this: it budgets a
|
||||
* specific number of ms for the type step, and any divergence would cascade
|
||||
* into narration drift.
|
||||
* Do not do one Playwright round-trip per character here. Long prompts can
|
||||
* turn a 4s typing budget into 9s of wall-clock time on a busy recorder.
|
||||
* Instead, animate through paced chunks. It still reads as typing on video,
|
||||
* but the runner can keep narration and visuals aligned.
|
||||
*/
|
||||
export async function fakeType(
|
||||
page: Page,
|
||||
|
|
@ -86,17 +85,19 @@ export async function fakeType(
|
|||
text: string,
|
||||
totalDurationMs: number
|
||||
): Promise<void> {
|
||||
const steps = text.length;
|
||||
if (steps === 0) {
|
||||
if (text.length === 0) {
|
||||
if (totalDurationMs > 0) await sleep(totalDurationMs);
|
||||
return;
|
||||
}
|
||||
|
||||
const weights = computeTypingWeights(text);
|
||||
const weightSum = weights.reduce((a, b) => a + b, 0);
|
||||
const msPerWeight = totalDurationMs / weightSum;
|
||||
const steps = Math.min(
|
||||
text.length,
|
||||
Math.max(1, Math.min(48, Math.round(totalDurationMs / 95)))
|
||||
);
|
||||
const startedAt = Date.now();
|
||||
|
||||
for (let i = 1; i <= steps; i++) {
|
||||
const charCount = Math.max(1, Math.round((i / steps) * text.length));
|
||||
await page.evaluate(
|
||||
({ selector, value }) => {
|
||||
const ta = document.querySelector(selector) as HTMLTextAreaElement | null;
|
||||
|
|
@ -110,27 +111,16 @@ export async function fakeType(
|
|||
setValue.call(ta, value);
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
},
|
||||
{ selector, value: text.slice(0, i) }
|
||||
{ selector, value: text.slice(0, charCount) }
|
||||
);
|
||||
if (i < steps) {
|
||||
const ms = Math.max(0, Math.round(weights[i - 1] * msPerWeight));
|
||||
if (ms > 0) await sleep(ms);
|
||||
const targetElapsed = (totalDurationMs * i) / steps;
|
||||
const waitMs = startedAt + targetElapsed - Date.now();
|
||||
if (waitMs > 0) await sleep(waitMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function computeTypingWeights(text: string): number[] {
|
||||
const cadence = [0.82, 1.08, 0.94, 1.22, 0.88, 1.14, 0.98, 1.28];
|
||||
return Array.from(text, (char, index) => {
|
||||
let weight = cadence[index % cadence.length];
|
||||
if (char === ' ') weight += 0.9;
|
||||
if (/[,.!?;:]/.test(char)) weight += 1.8;
|
||||
const next = text[index + 1];
|
||||
if (next === ' ' && index % 4 === 0) weight += 0.55;
|
||||
return weight;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag the right-hand thumb of a Radix slider to a target track fraction.
|
||||
* Returns the final cursor position so callers can chain a smoothMove afterwards.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue