-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.js
More file actions
329 lines (281 loc) · 9.91 KB
/
Copy pathcontent.js
File metadata and controls
329 lines (281 loc) · 9.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
// Storage keys
const STORAGE_KEY = 'pasteTyperSequences';
const ACTIVE_SEQUENCE_KEY = 'pasteTyperActiveSequence';
const HUMANIZE_KEY = 'pasteTyperHumanize';
const SPEED_KEY = 'pasteTyperSpeed';
// State
let sequences = {};
let activeSequenceId = null;
let currentBlockIndex = 0;
let isTyping = false;
let humanizeTyping = true;
let typingSpeed = 1.0;
// Configuration
const TYPING_SPEED_MIN = 40; // milliseconds
const TYPING_SPEED_MAX = 120; // milliseconds
const TYPING_SPEED_BURST_MIN = 20; // Fast typing bursts
const TYPING_SPEED_BURST_MAX = 50; // Fast typing bursts
const PAUSE_AFTER_WORD_MIN = 100; // Pause after word/space
const PAUSE_AFTER_WORD_MAX = 200; // Pause after word/space
const BURST_PROBABILITY = 0.6; // 60% chance of fast burst typing
// Logging helper
function log(type, message, meta = {}) {
chrome.runtime.sendMessage({
type: 'LOG',
logType: type,
message,
meta
}).catch(() => {
// Popup might not be open
console.log(`[Paste Typer] ${type.toUpperCase()}: ${message}`, meta);
});
}
// Initialize
async function init() {
log('info', 'Content script initializing');
await loadData();
setupKeyboardListener();
setupMessageListener();
log('success', 'Content script initialized', {
activeSequenceId,
numSequences: Object.keys(sequences).length
});
}
// Load sequences and active sequence
async function loadData() {
const result = await chrome.storage.local.get([STORAGE_KEY, ACTIVE_SEQUENCE_KEY, HUMANIZE_KEY, SPEED_KEY]);
sequences = result[STORAGE_KEY] || {};
activeSequenceId = result[ACTIVE_SEQUENCE_KEY];
humanizeTyping = result[HUMANIZE_KEY] !== undefined ? result[HUMANIZE_KEY] : true;
typingSpeed = result[SPEED_KEY] !== undefined ? result[SPEED_KEY] : 1.0;
log('info', 'Data loaded from storage', {
activeSequenceId,
numSequences: Object.keys(sequences).length,
currentBlockIndex,
humanizeTyping,
typingSpeed
});
}
// Setup keyboard listener for Ctrl+B or Cmd+B
function setupKeyboardListener() {
log('info', 'Keyboard listener setup');
document.addEventListener('keydown', async (e) => {
// Check for Ctrl+B (Windows/Linux) or Cmd+B (Mac)
const isTrigger = (e.ctrlKey || e.metaKey) && e.key === 'b';
if (isTrigger) {
log('info', 'Ctrl/Cmd+B pressed', {
isTyping,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
key: e.key
});
if (!isTyping) {
e.preventDefault();
e.stopPropagation();
await handlePasteTyping();
} else {
log('warning', 'Typing already in progress, ignoring keypress');
}
}
}, true);
}
// Setup message listener for updates from popup
function setupMessageListener() {
log('info', 'Message listener setup');
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
log('info', 'Message received', { messageType: message.type });
if (message.type === 'PING') {
log('info', 'PING received, responding with PONG');
sendResponse({
status: 'alive',
currentBlockIndex,
activeSequenceId,
numSequences: Object.keys(sequences).length,
humanizeTyping,
typingSpeed
});
return true;
} else if (message.type === 'RELOAD_SEQUENCES') {
loadData().then(() => {
currentBlockIndex = 0; // Reset to first block when sequences are updated
log('success', 'Sequences reloaded, index reset to 0');
});
} else if (message.type === 'RESET_SEQUENCE') {
const oldIndex = currentBlockIndex;
currentBlockIndex = 0; // Reset to first block
log('success', 'Sequence reset to first block', {
oldIndex,
newIndex: currentBlockIndex
});
sendResponse({ success: true, newIndex: currentBlockIndex });
return true;
} else if (message.type === 'UPDATE_HUMANIZE') {
humanizeTyping = message.humanize;
log('success', 'Humanize typing setting updated', { humanizeTyping });
sendResponse({ success: true, humanizeTyping });
return true;
} else if (message.type === 'UPDATE_SPEED') {
typingSpeed = message.speed;
log('success', 'Typing speed updated', { typingSpeed });
sendResponse({ success: true, typingSpeed });
return true;
}
});
}
// Handle paste typing
async function handlePasteTyping() {
const activeElement = document.activeElement;
log('info', 'handlePasteTyping called', {
tagName: activeElement?.tagName,
type: activeElement?.type,
isContentEditable: activeElement?.isContentEditable
});
// Check if we have a valid input element
if (!isValidInputElement(activeElement)) {
log('warning', 'Active element is not a valid input', {
tagName: activeElement?.tagName,
type: activeElement?.type
});
return;
}
// Reload data to get latest sequences
await loadData();
// Get active sequence
const activeSequence = sequences[activeSequenceId];
if (!activeSequence || !activeSequence.blocks || activeSequence.blocks.length === 0) {
log('error', 'No active sequence or blocks found', {
activeSequenceId,
hasSequence: !!activeSequence,
numBlocks: activeSequence?.blocks?.length || 0
});
return;
}
// Get current text block
const textBlocks = activeSequence.blocks.filter(block => block.trim() !== '');
if (textBlocks.length === 0) {
log('warning', 'No non-empty text blocks found');
return;
}
const textToType = textBlocks[currentBlockIndex % textBlocks.length];
log('info', 'Starting to type text block', {
blockIndex: currentBlockIndex,
totalBlocks: textBlocks.length,
textLength: textToType.length,
textPreview: textToType.substring(0, 30)
});
// Increment index for next paste
const oldIndex = currentBlockIndex;
currentBlockIndex = (currentBlockIndex + 1) % textBlocks.length;
log('info', 'Block index incremented', {
oldIndex,
newIndex: currentBlockIndex
});
// Type the text
await typeText(activeElement, textToType);
log('success', 'Text typing completed');
}
// Check if element is a valid input element
function isValidInputElement(element) {
if (!element) return false;
const tagName = element.tagName.toLowerCase();
const isInput = tagName === 'input' || tagName === 'textarea';
const isContentEditable = element.isContentEditable;
// For input elements, check if they accept text
if (tagName === 'input') {
const type = element.type.toLowerCase();
const textTypes = ['text', 'email', 'search', 'url', 'tel', 'password'];
return textTypes.includes(type);
}
return isInput || isContentEditable;
}
// Type text character by character with random delays
async function typeText(element, text) {
if (!text) return;
isTyping = true;
const isContentEditable = element.isContentEditable;
// Variables for humanized typing
let inBurst = humanizeTyping ? Math.random() < BURST_PROBABILITY : false;
let burstLength = Math.floor(Math.random() * 5) + 3; // Burst of 3-7 chars
let burstCount = 0;
for (let i = 0; i < text.length; i++) {
const char = text[i];
const prevChar = i > 0 ? text[i - 1] : '';
if (isContentEditable) {
// For contenteditable elements
insertTextAtCursor(char);
} else {
// For input/textarea elements
const start = element.selectionStart || 0;
const end = element.selectionEnd || 0;
const currentValue = element.value;
element.value = currentValue.substring(0, start) + char + currentValue.substring(end);
element.selectionStart = element.selectionEnd = start + 1;
// Trigger input event
element.dispatchEvent(new Event('input', { bubbles: true }));
}
// Determine delay based on typing pattern
let delay;
if (humanizeTyping) {
// Humanized typing with bursts and pauses
// Pause after punctuation or space (end of word/sentence)
if (prevChar === ' ' || prevChar === '.' || prevChar === ',' || prevChar === '!' || prevChar === '?') {
delay = Math.random() * (PAUSE_AFTER_WORD_MAX - PAUSE_AFTER_WORD_MIN) + PAUSE_AFTER_WORD_MIN;
// Start new burst after pause
inBurst = Math.random() < BURST_PROBABILITY;
burstLength = Math.floor(Math.random() * 5) + 3;
burstCount = 0;
}
// Fast burst typing
else if (inBurst && burstCount < burstLength) {
delay = Math.random() * (TYPING_SPEED_BURST_MAX - TYPING_SPEED_BURST_MIN) + TYPING_SPEED_BURST_MIN;
burstCount++;
}
// Normal typing speed
else {
delay = Math.random() * (TYPING_SPEED_MAX - TYPING_SPEED_MIN) + TYPING_SPEED_MIN;
// Maybe start a new burst
if (!inBurst && Math.random() < 0.3) {
inBurst = true;
burstLength = Math.floor(Math.random() * 5) + 3;
burstCount = 0;
} else {
inBurst = false;
}
}
} else {
// Simple consistent typing speed (original behavior)
delay = Math.random() * (TYPING_SPEED_MAX - TYPING_SPEED_MIN) + TYPING_SPEED_MIN;
}
// Apply speed multiplier (inverse: higher speed = lower delay)
delay = delay / typingSpeed;
await sleep(delay);
}
// Trigger change event after typing is complete
if (!isContentEditable) {
element.dispatchEvent(new Event('change', { bubbles: true }));
}
isTyping = false;
}
// Insert text at cursor position in contenteditable
function insertTextAtCursor(text) {
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
range.deleteContents();
const textNode = document.createTextNode(text);
range.insertNode(textNode);
// Move cursor after inserted text
range.setStartAfter(textNode);
range.setEndAfter(textNode);
selection.removeAllRanges();
selection.addRange(range);
// Trigger input event
const event = new Event('input', { bubbles: true });
range.commonAncestorContainer.dispatchEvent(event);
}
// Sleep utility
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Initialize
init();