@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustquery/browser",
3
- "version": "0.2.5",
3
+ "version": "0.2.8",
4
4
  "description": "Turn any textarea into an interactive trigger-based editor with inline styling",
5
5
  "type": "module",
6
6
  "main": "dist/trustquery.js",
@@ -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] Match clicked:', matchData);
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
- // Only prevent default for interactive elements (dropdown/action)
145
- // Let bubbles (error/warning/info messages) pass through for normal cursor positioning
146
- if (behavior === 'dropdown' || behavior === 'action') {
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();