@trustquery/browser 0.2.5 → 0.2.8
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/dist/trustquery.js +187 -5
- package/dist/trustquery.js.map +1 -1
- package/package.json +1 -1
- package/src/InteractionHandler.js +75 -4
- package/src/TrustQuery.js +112 -1
package/package.json
CHANGED
|
@@ -139,15 +139,84 @@ export default class InteractionHandler {
|
|
|
139
139
|
const behavior = matchEl.getAttribute('data-behavior');
|
|
140
140
|
const matchData = this.getMatchData(matchEl);
|
|
141
141
|
|
|
142
|
-
console.log('[InteractionHandler]
|
|
142
|
+
console.log('[InteractionHandler] ===== CLICK EVENT START =====');
|
|
143
|
+
console.log('[InteractionHandler] Click details:', {
|
|
144
|
+
behavior: behavior,
|
|
145
|
+
matchText: matchData.text,
|
|
146
|
+
eventType: e.type,
|
|
147
|
+
target: e.target.tagName,
|
|
148
|
+
currentTarget: e.currentTarget.tagName,
|
|
149
|
+
button: e.button,
|
|
150
|
+
buttons: e.buttons,
|
|
151
|
+
clientX: e.clientX,
|
|
152
|
+
clientY: e.clientY,
|
|
153
|
+
offsetX: e.offsetX,
|
|
154
|
+
offsetY: e.offsetY
|
|
155
|
+
});
|
|
156
|
+
console.log('[InteractionHandler] Match element:', {
|
|
157
|
+
textContent: matchEl.textContent,
|
|
158
|
+
offsetWidth: matchEl.offsetWidth,
|
|
159
|
+
offsetHeight: matchEl.offsetHeight,
|
|
160
|
+
attributes: {
|
|
161
|
+
'data-line': matchEl.getAttribute('data-line'),
|
|
162
|
+
'data-col': matchEl.getAttribute('data-col'),
|
|
163
|
+
'data-behavior': behavior
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
console.log('[InteractionHandler] Active element before click:', document.activeElement.tagName, document.activeElement.id || '(no id)');
|
|
143
167
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
168
|
+
// For non-interactive elements (bubbles), manually pass click to textarea
|
|
169
|
+
if (behavior !== 'dropdown' && behavior !== 'action') {
|
|
170
|
+
console.log('[InteractionHandler] Non-interactive match - manually focusing textarea');
|
|
147
171
|
e.preventDefault();
|
|
148
172
|
e.stopPropagation();
|
|
173
|
+
|
|
174
|
+
// Focus textarea and position cursor at click location
|
|
175
|
+
if (this.options.textarea) {
|
|
176
|
+
this.options.textarea.focus();
|
|
177
|
+
console.log('[InteractionHandler] Textarea focused. Active element now:', document.activeElement.tagName, document.activeElement.id || '(no id)');
|
|
178
|
+
|
|
179
|
+
// Get the character offset by finding the match position
|
|
180
|
+
const line = parseInt(matchEl.getAttribute('data-line'));
|
|
181
|
+
const col = parseInt(matchEl.getAttribute('data-col'));
|
|
182
|
+
|
|
183
|
+
// Calculate absolute position in textarea
|
|
184
|
+
const lines = this.options.textarea.value.split('\n');
|
|
185
|
+
let offset = 0;
|
|
186
|
+
for (let i = 0; i < line; i++) {
|
|
187
|
+
offset += lines[i].length + 1; // +1 for newline
|
|
188
|
+
}
|
|
189
|
+
const clickOffsetInMatch = (e.offsetX / matchEl.offsetWidth * matchEl.textContent.length);
|
|
190
|
+
offset += col + clickOffsetInMatch;
|
|
191
|
+
|
|
192
|
+
console.log('[InteractionHandler] Cursor positioning:', {
|
|
193
|
+
line: line,
|
|
194
|
+
col: col,
|
|
195
|
+
clickOffsetInMatch: clickOffsetInMatch,
|
|
196
|
+
finalOffset: offset,
|
|
197
|
+
textareaValue: this.options.textarea.value,
|
|
198
|
+
textareaValueLength: this.options.textarea.value.length
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Set cursor position
|
|
202
|
+
this.options.textarea.setSelectionRange(offset, offset);
|
|
203
|
+
|
|
204
|
+
console.log('[InteractionHandler] Selection set:', {
|
|
205
|
+
selectionStart: this.options.textarea.selectionStart,
|
|
206
|
+
selectionEnd: this.options.textarea.selectionEnd,
|
|
207
|
+
selectedText: this.options.textarea.value.substring(this.options.textarea.selectionStart, this.options.textarea.selectionEnd)
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log('[InteractionHandler] ===== CLICK EVENT END (non-interactive) =====');
|
|
212
|
+
return; // Don't process further for bubbles
|
|
149
213
|
}
|
|
150
214
|
|
|
215
|
+
console.log('[InteractionHandler] Interactive match - handling dropdown/action');
|
|
216
|
+
// Prevent default for interactive elements (dropdown/action)
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
|
|
151
220
|
// Handle different behaviors
|
|
152
221
|
if (behavior === 'dropdown') {
|
|
153
222
|
// Toggle dropdown - close if already open for this match, otherwise show
|
|
@@ -167,6 +236,8 @@ export default class InteractionHandler {
|
|
|
167
236
|
if (this.options.onWordClick && !(behavior === 'dropdown' && this.dropdownManager.activeDropdownMatch === matchEl)) {
|
|
168
237
|
this.options.onWordClick(matchData);
|
|
169
238
|
}
|
|
239
|
+
|
|
240
|
+
console.log('[InteractionHandler] ===== CLICK EVENT END (interactive) =====');
|
|
170
241
|
}
|
|
171
242
|
|
|
172
243
|
/**
|
package/src/TrustQuery.js
CHANGED
|
@@ -284,12 +284,121 @@ export default class TrustQuery {
|
|
|
284
284
|
this.overlay.scrollLeft = this.textarea.scrollLeft;
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
+
// Keyboard event logging for debugging selection issues
|
|
288
|
+
this.textarea.addEventListener('keydown', (e) => {
|
|
289
|
+
const isCmdOrCtrl = e.metaKey || e.ctrlKey;
|
|
290
|
+
const isSelectAll = (e.metaKey || e.ctrlKey) && e.key === 'a';
|
|
291
|
+
|
|
292
|
+
if (isCmdOrCtrl || isSelectAll) {
|
|
293
|
+
console.log('[TrustQuery] ===== KEYBOARD EVENT =====');
|
|
294
|
+
console.log('[TrustQuery] Key pressed:', {
|
|
295
|
+
key: e.key,
|
|
296
|
+
code: e.code,
|
|
297
|
+
metaKey: e.metaKey,
|
|
298
|
+
ctrlKey: e.ctrlKey,
|
|
299
|
+
shiftKey: e.shiftKey,
|
|
300
|
+
altKey: e.altKey,
|
|
301
|
+
isSelectAll: isSelectAll
|
|
302
|
+
});
|
|
303
|
+
console.log('[TrustQuery] Active element:', document.activeElement.tagName, document.activeElement.id || '(no id)');
|
|
304
|
+
console.log('[TrustQuery] Textarea state BEFORE:', {
|
|
305
|
+
value: this.textarea.value,
|
|
306
|
+
valueLength: this.textarea.value.length,
|
|
307
|
+
selectionStart: this.textarea.selectionStart,
|
|
308
|
+
selectionEnd: this.textarea.selectionEnd,
|
|
309
|
+
selectedText: this.textarea.value.substring(this.textarea.selectionStart, this.textarea.selectionEnd)
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (isSelectAll) {
|
|
313
|
+
// Log state after select all (use setTimeout to let browser process the event)
|
|
314
|
+
setTimeout(() => {
|
|
315
|
+
console.log('[TrustQuery] Textarea state AFTER CMD+A:', {
|
|
316
|
+
selectionStart: this.textarea.selectionStart,
|
|
317
|
+
selectionEnd: this.textarea.selectionEnd,
|
|
318
|
+
selectedText: this.textarea.value.substring(this.textarea.selectionStart, this.textarea.selectionEnd),
|
|
319
|
+
selectedLength: this.textarea.selectionEnd - this.textarea.selectionStart
|
|
320
|
+
});
|
|
321
|
+
console.log('[TrustQuery] ===== KEYBOARD EVENT END =====');
|
|
322
|
+
}, 0);
|
|
323
|
+
} else {
|
|
324
|
+
console.log('[TrustQuery] ===== KEYBOARD EVENT END =====');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Selection change event
|
|
330
|
+
this.textarea.addEventListener('select', (e) => {
|
|
331
|
+
console.log('[TrustQuery] ===== SELECTION CHANGE EVENT =====');
|
|
332
|
+
console.log('[TrustQuery] Selection:', {
|
|
333
|
+
selectionStart: this.textarea.selectionStart,
|
|
334
|
+
selectionEnd: this.textarea.selectionEnd,
|
|
335
|
+
selectedText: this.textarea.value.substring(this.textarea.selectionStart, this.textarea.selectionEnd),
|
|
336
|
+
selectedLength: this.textarea.selectionEnd - this.textarea.selectionStart
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Context menu event - prevent keyboard-triggered context menu
|
|
341
|
+
this.textarea.addEventListener('contextmenu', (e) => {
|
|
342
|
+
console.log('[TrustQuery] ===== CONTEXTMENU EVENT =====');
|
|
343
|
+
console.log('[TrustQuery] Context menu triggered:', {
|
|
344
|
+
type: e.type,
|
|
345
|
+
isTrusted: e.isTrusted,
|
|
346
|
+
button: e.button,
|
|
347
|
+
buttons: e.buttons,
|
|
348
|
+
clientX: e.clientX,
|
|
349
|
+
clientY: e.clientY,
|
|
350
|
+
ctrlKey: e.ctrlKey,
|
|
351
|
+
metaKey: e.metaKey,
|
|
352
|
+
target: e.target.tagName
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Prevent context menu if triggered by keyboard (button === -1)
|
|
356
|
+
// This prevents the macOS context menu from opening after CMD+A
|
|
357
|
+
if (e.button === -1 && e.buttons === 0) {
|
|
358
|
+
console.log('[TrustQuery] Preventing keyboard-triggered context menu');
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
e.stopPropagation();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
console.log('[TrustQuery] Allowing mouse-triggered context menu');
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Also prevent context menu on overlay (it interferes with text selection)
|
|
368
|
+
this.overlay.addEventListener('contextmenu', (e) => {
|
|
369
|
+
console.log('[TrustQuery] ===== CONTEXTMENU EVENT ON OVERLAY =====');
|
|
370
|
+
console.log('[TrustQuery] Context menu on overlay - preventing');
|
|
371
|
+
|
|
372
|
+
// Always prevent context menu on overlay
|
|
373
|
+
// The overlay should be transparent to user interactions
|
|
374
|
+
e.preventDefault();
|
|
375
|
+
e.stopPropagation();
|
|
376
|
+
});
|
|
377
|
+
|
|
287
378
|
// Focus/blur events - add/remove focus class
|
|
288
|
-
this.textarea.addEventListener('focus', () => {
|
|
379
|
+
this.textarea.addEventListener('focus', (e) => {
|
|
380
|
+
console.log('[TrustQuery] ===== FOCUS EVENT =====');
|
|
381
|
+
console.log('[TrustQuery] Textarea focused. Active element:', document.activeElement.tagName, document.activeElement.id || '(no id)');
|
|
382
|
+
console.log('[TrustQuery] Current selection:', {
|
|
383
|
+
selectionStart: this.textarea.selectionStart,
|
|
384
|
+
selectionEnd: this.textarea.selectionEnd
|
|
385
|
+
});
|
|
289
386
|
this.wrapper.classList.add('tq-focused');
|
|
290
387
|
});
|
|
291
388
|
|
|
292
389
|
this.textarea.addEventListener('blur', (e) => {
|
|
390
|
+
console.log('[TrustQuery] ===== BLUR EVENT =====');
|
|
391
|
+
console.log('[TrustQuery] Textarea blurred. Related target:', e.relatedTarget?.tagName || '(none)');
|
|
392
|
+
console.log('[TrustQuery] Blur event details:', {
|
|
393
|
+
type: e.type,
|
|
394
|
+
isTrusted: e.isTrusted,
|
|
395
|
+
eventPhase: e.eventPhase,
|
|
396
|
+
target: e.target.tagName,
|
|
397
|
+
currentTarget: e.currentTarget.tagName
|
|
398
|
+
});
|
|
399
|
+
console.log('[TrustQuery] Stack trace at blur:');
|
|
400
|
+
console.trace();
|
|
401
|
+
|
|
293
402
|
// Close dropdown when textarea loses focus (unless interacting with dropdown)
|
|
294
403
|
if (this.interactionHandler) {
|
|
295
404
|
// Use setTimeout to let the new focus target be set and check if clicking on dropdown
|
|
@@ -300,6 +409,8 @@ export default class TrustQuery {
|
|
|
300
409
|
activeElement.closest('.tq-dropdown') // Check if clicking anywhere in dropdown
|
|
301
410
|
);
|
|
302
411
|
|
|
412
|
+
console.log('[TrustQuery] After blur - active element:', activeElement?.tagName || '(none)', 'isDropdownRelated:', isDropdownRelated);
|
|
413
|
+
|
|
303
414
|
// Only close if not interacting with dropdown
|
|
304
415
|
if (!isDropdownRelated) {
|
|
305
416
|
this.interactionHandler.hideDropdown();
|