ep_data_tables 0.0.6 → 0.0.7

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.
@@ -17,7 +17,6 @@ const ATTR_TABLE_JSON = 'tbljson';
17
17
  const ATTR_CELL = 'td';
18
18
  const ATTR_CLASS_PREFIX = 'tbljson-'; // For finding the class in DOM
19
19
  const log = (...m) => console.debug('[ep_data_tables:client_hooks]', ...m);
20
- log('version 0.0.6');
21
20
  const DELIMITER = '\u241F'; // Internal column delimiter (␟)
22
21
  // Use the same rare character inside the hidden span so acePostWriteDomLineHTML can
23
22
  // still find delimiters when it splits node.innerHTML.
@@ -67,14 +66,13 @@ let isAndroidChromeComposition = false;
67
66
  let handledCurrentComposition = false;
68
67
  // Suppress all beforeinput insertText events during an Android Chrome IME composition
69
68
  let suppressBeforeInputInsertTextDuringComposition = false;
70
- // Helper to detect Android Chromium-family browsers (exclude iOS and Firefox)
71
- function isAndroidChromiumUA() {
69
+ // Helper to detect any Android browser (exclude iOS/Safari)
70
+ function isAndroidUA() {
72
71
  const ua = (navigator.userAgent || '').toLowerCase();
73
72
  const isAndroid = ua.includes('android');
74
73
  const isIOS = ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod') || ua.includes('crios');
75
- const isFirefox = ua.includes('firefox');
76
- const isChromiumFamily = ua.includes('chrome') || ua.includes('edg') || ua.includes('opr') || ua.includes('samsungbrowser') || ua.includes('vivaldi') || ua.includes('brave');
77
- return isAndroid && !isIOS && !isFirefox && isChromiumFamily;
74
+ // Safari on Android is rare (WebKit ports), but our exclusion target is iOS Safari; we exclude all iOS above
75
+ return isAndroid && !isIOS;
78
76
  }
79
77
 
80
78
  // ─────────────────── Reusable Helper Functions ───────────────────
@@ -122,16 +120,16 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
122
120
  try {
123
121
  const metadata = JSON.parse(attribs);
124
122
  if (metadata && metadata.tblId) {
125
- // log(`${funcName}: Found metadata via attribute for line ${lineNum}`);
123
+ // log(`${funcName}: Found metadata via attribute for line ${lineNum}`);
126
124
  return metadata;
127
125
  }
128
126
  } catch (e) {
129
- // log(`${funcName}: Invalid JSON in tbljson attribute on line ${lineNum}:`, e.message);
127
+ // log(`${funcName}: Invalid JSON in tbljson attribute on line ${lineNum}:`, e.message);
130
128
  }
131
129
  }
132
130
 
133
131
  // Fallback for block-styled lines.
134
- // log(`${funcName}: No valid attribute on line ${lineNum}, checking DOM.`);
132
+ // log(`${funcName}: No valid attribute on line ${lineNum}, checking DOM.`);
135
133
  const rep = editorInfo.ace_getRep();
136
134
 
137
135
  // This is the fix: Get the lineNode directly from the rep. It's more reliable
@@ -140,7 +138,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
140
138
  const lineNode = lineEntry?.lineNode;
141
139
 
142
140
  if (!lineNode) {
143
- // log(`${funcName}: Could not find line node in rep for line ${lineNum}`);
141
+ // log(`${funcName}: Could not find line node in rep for line ${lineNum}`);
144
142
  return null;
145
143
  }
146
144
 
@@ -152,7 +150,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
152
150
  try {
153
151
  const decodedString = atob(encodedData);
154
152
  const metadata = JSON.parse(decodedString);
155
- // log(`${funcName}: Reconstructed metadata from DOM for line ${lineNum}:`, metadata);
153
+ // log(`${funcName}: Reconstructed metadata from DOM for line ${lineNum}:`, metadata);
156
154
  return metadata;
157
155
  } catch (e) {
158
156
  console.error(`${funcName}: Failed to decode/parse tbljson class on line ${lineNum}:`, e);
@@ -162,7 +160,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
162
160
  }
163
161
  }
164
162
 
165
- // log(`${funcName}: Could not find table metadata for line ${lineNum} in DOM.`);
163
+ // log(`${funcName}: Could not find table metadata for line ${lineNum} in DOM.`);
166
164
  return null;
167
165
  } catch (e) {
168
166
  console.error(`[ep_data_tables] ${funcName}: Error getting metadata for line ${lineNum}:`, e);
@@ -183,7 +181,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
183
181
  */
184
182
  function navigateToNextCell(currentLineNum, currentCellIndex, tableMetadata, shiftKey, editorInfo, docManager) {
185
183
  const funcName = 'navigateToNextCell';
186
- // log(`${funcName}: START - Current: Line=${currentLineNum}, Cell=${currentCellIndex}, Shift=${shiftKey}`);
184
+ // log(`${funcName}: START - Current: Line=${currentLineNum}, Cell=${currentCellIndex}, Shift=${shiftKey}`);
187
185
 
188
186
  try {
189
187
  let targetRow = tableMetadata.row;
@@ -207,12 +205,12 @@ function navigateToNextCell(currentLineNum, currentCellIndex, tableMetadata, shi
207
205
  }
208
206
  }
209
207
 
210
- // log(`${funcName}: Target coordinates - Row=${targetRow}, Col=${targetCol}`);
208
+ // log(`${funcName}: Target coordinates - Row=${targetRow}, Col=${targetCol}`);
211
209
 
212
210
  // Find the line number for the target row
213
211
  const targetLineNum = findLineForTableRow(tableMetadata.tblId, targetRow, editorInfo, docManager);
214
212
  if (targetLineNum === -1) {
215
- // log(`${funcName}: Could not find line for target row ${targetRow}`);
213
+ // log(`${funcName}: Could not find line for target row ${targetRow}`);
216
214
  return false;
217
215
  }
218
216
 
@@ -236,25 +234,25 @@ function navigateToNextCell(currentLineNum, currentCellIndex, tableMetadata, shi
236
234
  */
237
235
  function navigateToCellBelow(currentLineNum, currentCellIndex, tableMetadata, editorInfo, docManager) {
238
236
  const funcName = 'navigateToCellBelow';
239
- // log(`${funcName}: START - Current: Line=${currentLineNum}, Cell=${currentCellIndex}`);
237
+ // log(`${funcName}: START - Current: Line=${currentLineNum}, Cell=${currentCellIndex}`);
240
238
 
241
239
  try {
242
240
  const targetRow = tableMetadata.row + 1;
243
241
  const targetCol = currentCellIndex;
244
242
 
245
- // log(`${funcName}: Target coordinates - Row=${targetRow}, Col=${targetCol}`);
243
+ // log(`${funcName}: Target coordinates - Row=${targetRow}, Col=${targetCol}`);
246
244
 
247
245
  // Find the line number for the target row
248
246
  const targetLineNum = findLineForTableRow(tableMetadata.tblId, targetRow, editorInfo, docManager);
249
247
 
250
248
  if (targetLineNum !== -1) {
251
249
  // Found the row below, navigate to it.
252
- // log(`${funcName}: Found line for target row ${targetRow}, navigating.`);
250
+ // log(`${funcName}: Found line for target row ${targetRow}, navigating.`);
253
251
  return navigateToCell(targetLineNum, targetCol, editorInfo, docManager);
254
252
  } else {
255
253
  // Could not find the row below, we must be on the last line.
256
254
  // Create a new, empty line after the table.
257
- // log(`${funcName}: Could not find next row. Creating new line after table.`);
255
+ // log(`${funcName}: Could not find next row. Creating new line after table.`);
258
256
  const rep = editorInfo.ace_getRep();
259
257
  const lineTextLength = rep.lines.atIndex(currentLineNum).text.length;
260
258
  const endOfLinePos = [currentLineNum, lineTextLength];
@@ -272,7 +270,7 @@ function navigateToCellBelow(currentLineNum, currentCellIndex, tableMetadata, ed
272
270
  // We've now exited the table, so clear the last-clicked state.
273
271
  const editor = editorInfo.editor;
274
272
  if (editor) editor.ep_data_tables_last_clicked = null;
275
- // log(`${funcName}: Cleared last click info as we have exited the table.`);
273
+ // log(`${funcName}: Cleared last click info as we have exited the table.`);
276
274
 
277
275
  return true; // We handled it.
278
276
  }
@@ -292,12 +290,12 @@ function navigateToCellBelow(currentLineNum, currentCellIndex, tableMetadata, ed
292
290
  */
293
291
  function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
294
292
  const funcName = 'findLineForTableRow';
295
- // log(`${funcName}: Searching for tblId=${tblId}, row=${targetRow}`);
293
+ // log(`${funcName}: Searching for tblId=${tblId}, row=${targetRow}`);
296
294
 
297
295
  try {
298
296
  const rep = editorInfo.ace_getRep();
299
297
  if (!rep || !rep.lines) {
300
- // log(`${funcName}: Could not get rep or rep.lines`);
298
+ // log(`${funcName}: Could not get rep or rep.lines`);
301
299
  return -1;
302
300
  }
303
301
 
@@ -315,7 +313,7 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
315
313
  const domTblId = tableInDOM.getAttribute('data-tblId');
316
314
  const domRow = tableInDOM.getAttribute('data-row');
317
315
  if (domTblId === tblId && domRow !== null && parseInt(domRow, 10) === targetRow) {
318
- // log(`${funcName}: Found target via DOM: line ${lineIndex}`);
316
+ // log(`${funcName}: Found target via DOM: line ${lineIndex}`);
319
317
  return lineIndex;
320
318
  }
321
319
  }
@@ -325,7 +323,7 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
325
323
  if (lineAttrString) {
326
324
  const lineMetadata = JSON.parse(lineAttrString);
327
325
  if (lineMetadata.tblId === tblId && lineMetadata.row === targetRow) {
328
- // log(`${funcName}: Found target via attribute: line ${lineIndex}`);
326
+ // log(`${funcName}: Found target via attribute: line ${lineIndex}`);
329
327
  return lineIndex;
330
328
  }
331
329
  }
@@ -334,7 +332,7 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
334
332
  }
335
333
  }
336
334
 
337
- // log(`${funcName}: Target row not found`);
335
+ // log(`${funcName}: Target row not found`);
338
336
  return -1;
339
337
  } catch (e) {
340
338
  console.error(`[ep_data_tables] ${funcName}: Error searching for line:`, e);
@@ -352,19 +350,19 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
352
350
  */
353
351
  function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager) {
354
352
  const funcName = 'navigateToCell';
355
- // log(`${funcName}: START - Target: Line=${targetLineNum}, Cell=${targetCellIndex}`);
353
+ // log(`${funcName}: START - Target: Line=${targetLineNum}, Cell=${targetCellIndex}`);
356
354
  let targetPos;
357
355
 
358
356
  try {
359
357
  const rep = editorInfo.ace_getRep();
360
358
  if (!rep || !rep.lines) {
361
- // log(`${funcName}: Could not get rep or rep.lines`);
359
+ // log(`${funcName}: Could not get rep or rep.lines`);
362
360
  return false;
363
361
  }
364
362
 
365
363
  const lineEntry = rep.lines.atIndex(targetLineNum);
366
364
  if (!lineEntry) {
367
- // log(`${funcName}: Could not get line entry for line ${targetLineNum}`);
365
+ // log(`${funcName}: Could not get line entry for line ${targetLineNum}`);
368
366
  return false;
369
367
  }
370
368
 
@@ -372,7 +370,7 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
372
370
  const cells = lineText.split(DELIMITER);
373
371
 
374
372
  if (targetCellIndex >= cells.length) {
375
- // log(`${funcName}: Target cell ${targetCellIndex} doesn't exist (only ${cells.length} cells)`);
373
+ // log(`${funcName}: Target cell ${targetCellIndex} doesn't exist (only ${cells.length} cells)`);
376
374
  return false;
377
375
  }
378
376
 
@@ -399,12 +397,12 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
399
397
  cellIndex: targetCellIndex,
400
398
  relativePos: targetCellContent.length,
401
399
  };
402
- // log(`${funcName}: Pre-emptively updated stored click info:`, editor.ep_data_tables_last_clicked);
400
+ // log(`${funcName}: Pre-emptively updated stored click info:`, editor.ep_data_tables_last_clicked);
403
401
  } else {
404
- // log(`${funcName}: Could not get table metadata for target line ${targetLineNum}, cannot update click info.`);
402
+ // log(`${funcName}: Could not get table metadata for target line ${targetLineNum}, cannot update click info.`);
405
403
  }
406
404
  } catch (e) {
407
- // log(`${funcName}: Could not update stored click info before navigation:`, e.message);
405
+ // log(`${funcName}: Could not update stored click info before navigation:`, e.message);
408
406
  }
409
407
 
410
408
  // The previous attempts involving wrappers and poking the renderer have all
@@ -414,17 +412,17 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
414
412
  try {
415
413
  // 1. Update the internal representation of the selection.
416
414
  editorInfo.ace_performSelectionChange(targetPos, targetPos, false);
417
- // log(`${funcName}: Updated internal selection to [${targetPos}]`);
415
+ // log(`${funcName}: Updated internal selection to [${targetPos}]`);
418
416
 
419
417
  // 2. Explicitly tell the editor to update the browser's visual selection
420
418
  // to match the new internal representation. This is the correct way to
421
419
  // make the caret appear in the new location without causing a race condition.
422
420
  editorInfo.ace_updateBrowserSelectionFromRep();
423
- // log(`${funcName}: Called updateBrowserSelectionFromRep to sync visual caret.`);
421
+ // log(`${funcName}: Called updateBrowserSelectionFromRep to sync visual caret.`);
424
422
 
425
423
  // 3. Ensure the editor has focus.
426
424
  editorInfo.ace_focus();
427
- // log(`${funcName}: Editor focused.`);
425
+ // log(`${funcName}: Editor focused.`);
428
426
 
429
427
  } catch(e) {
430
428
  console.error(`[ep_data_tables] ${funcName}: Error during direct navigation update:`, e);
@@ -437,7 +435,7 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
437
435
  return false;
438
436
  }
439
437
 
440
- // log(`${funcName}: Navigation considered successful.`);
438
+ // log(`${funcName}: Navigation considered successful.`);
441
439
  return true;
442
440
  }
443
441
 
@@ -448,36 +446,36 @@ exports.collectContentPre = (hook, ctx) => {
448
446
  const state = ctx.state;
449
447
  const cc = ctx.cc; // ContentCollector instance
450
448
 
451
- // log(`${funcName}: *** ENTRY POINT *** Hook: ${hook}, Node: ${node?.tagName}.${node?.className}`);
449
+ // log(`${funcName}: *** ENTRY POINT *** Hook: ${hook}, Node: ${node?.tagName}.${node?.className}`);
452
450
 
453
451
  // ***** START Primary Path: Reconstruct from rendered table *****
454
452
  if (node?.classList?.contains('ace-line')) {
455
453
  const tableNode = node.querySelector('table.dataTable[data-tblId]');
456
454
  if (tableNode) {
457
- // log(`${funcName}: Found ace-line with rendered table. Attempting reconstruction from DOM.`);
455
+ // log(`${funcName}: Found ace-line with rendered table. Attempting reconstruction from DOM.`);
458
456
 
459
457
  const docManager = cc.documentAttributeManager;
460
458
  const rep = cc.rep;
461
459
  const lineNum = rep?.lines?.indexOfKey(node.id);
462
460
 
463
461
  if (typeof lineNum === 'number' && lineNum >= 0 && docManager) {
464
- // log(`${funcName}: Processing line ${lineNum} (NodeID: ${node.id}) for DOM reconstruction.`);
462
+ // log(`${funcName}: Processing line ${lineNum} (NodeID: ${node.id}) for DOM reconstruction.`);
465
463
  try {
466
464
  const existingAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
467
- // log(`${funcName}: Line ${lineNum} existing ${ATTR_TABLE_JSON} attribute: '${existingAttrString}'`);
465
+ // log(`${funcName}: Line ${lineNum} existing ${ATTR_TABLE_JSON} attribute: '${existingAttrString}'`);
468
466
 
469
467
  if (existingAttrString) {
470
468
  const existingMetadata = JSON.parse(existingAttrString);
471
469
  if (existingMetadata && typeof existingMetadata.tblId !== 'undefined' &&
472
470
  typeof existingMetadata.row !== 'undefined' && typeof existingMetadata.cols === 'number') {
473
- // log(`${funcName}: Line ${lineNum} existing metadata is valid:`, existingMetadata);
471
+ // log(`${funcName}: Line ${lineNum} existing metadata is valid:`, existingMetadata);
474
472
 
475
473
  const trNode = tableNode.querySelector('tbody > tr');
476
474
  if (trNode) {
477
- // log(`${funcName}: Line ${lineNum} found <tr> node for cell content extraction.`);
475
+ // log(`${funcName}: Line ${lineNum} found <tr> node for cell content extraction.`);
478
476
  let cellHTMLSegments = Array.from(trNode.children).map((td, index) => {
479
477
  let segmentHTML = td.innerHTML || '';
480
- // log(`${funcName}: Line ${lineNum} TD[${index}] raw innerHTML (first 100): "${segmentHTML.substring(0,100)}"`);
478
+ // log(`${funcName}: Line ${lineNum} TD[${index}] raw innerHTML (first 100): "${segmentHTML.substring(0,100)}"`);
481
479
 
482
480
  const resizeHandleRegex = /<div class="ep-data_tables-resize-handle"[^>]*><\/div>/ig;
483
481
  segmentHTML = segmentHTML.replace(resizeHandleRegex, '');
@@ -498,44 +496,44 @@ exports.collectContentPre = (hook, ctx) => {
498
496
  const hidden = index === 0 ? '' :
499
497
  /* keep the char in the DOM but make it visually disappear and non-editable */
500
498
  `<span class="ep-data_tables-delim" contenteditable="false">${HIDDEN_DELIM}</span>`;
501
- // log(`${funcName}: Line ${lineNum} TD[${index}] cleaned innerHTML (first 100): "${segmentHTML.substring(0,100)}"`);
499
+ // log(`${funcName}: Line ${lineNum} TD[${index}] cleaned innerHTML (first 100): "${segmentHTML.substring(0,100)}"`);
502
500
  return segmentHTML;
503
501
  });
504
502
 
505
503
  if (cellHTMLSegments.length !== existingMetadata.cols) {
506
- // log(`${funcName}: WARNING Line ${lineNum}: Reconstructed cell count (${cellHTMLSegments.length}) does not match metadata cols (${existingMetadata.cols}). Padding/truncating.`);
504
+ // log(`${funcName}: WARNING Line ${lineNum}: Reconstructed cell count (${cellHTMLSegments.length}) does not match metadata cols (${existingMetadata.cols}). Padding/truncating.`);
507
505
  while (cellHTMLSegments.length < existingMetadata.cols) cellHTMLSegments.push('');
508
506
  if (cellHTMLSegments.length > existingMetadata.cols) cellHTMLSegments.length = existingMetadata.cols;
509
507
  }
510
508
 
511
509
  const canonicalLineText = cellHTMLSegments.join(DELIMITER);
512
510
  state.line = canonicalLineText;
513
- // log(`${funcName}: Line ${lineNum} successfully reconstructed ctx.state.line: "${canonicalLineText.substring(0, 200)}..."`);
511
+ // log(`${funcName}: Line ${lineNum} successfully reconstructed ctx.state.line: "${canonicalLineText.substring(0, 200)}..."`);
514
512
 
515
513
  state.lineAttributes = state.lineAttributes || [];
516
514
  state.lineAttributes = state.lineAttributes.filter(attr => attr[0] !== ATTR_TABLE_JSON);
517
515
  state.lineAttributes.push([ATTR_TABLE_JSON, existingAttrString]);
518
- // log(`${funcName}: Line ${lineNum} ensured ${ATTR_TABLE_JSON} attribute is in state.lineAttributes.`);
516
+ // log(`${funcName}: Line ${lineNum} ensured ${ATTR_TABLE_JSON} attribute is in state.lineAttributes.`);
519
517
 
520
- // log(`${funcName}: Line ${lineNum} reconstruction complete. Returning undefined to prevent default DOM collection.`);
518
+ // log(`${funcName}: Line ${lineNum} reconstruction complete. Returning undefined to prevent default DOM collection.`);
521
519
  return undefined;
522
520
  } else {
523
- // log(`${funcName}: ERROR Line ${lineNum}: Could not find tbody > tr in rendered table for reconstruction.`);
521
+ // log(`${funcName}: ERROR Line ${lineNum}: Could not find tbody > tr in rendered table for reconstruction.`);
524
522
  }
525
523
  } else {
526
- // log(`${funcName}: ERROR Line ${lineNum}: Invalid or incomplete existing metadata from line attribute:`, existingMetadata);
524
+ // log(`${funcName}: ERROR Line ${lineNum}: Invalid or incomplete existing metadata from line attribute:`, existingMetadata);
527
525
  }
528
526
  } else {
529
- // log(`${funcName}: WARNING Line ${lineNum}: No existing ${ATTR_TABLE_JSON} attribute found for reconstruction, despite table DOM presence. Table may be malformed or attribute lost.`);
527
+ // log(`${funcName}: WARNING Line ${lineNum}: No existing ${ATTR_TABLE_JSON} attribute found for reconstruction, despite table DOM presence. Table may be malformed or attribute lost.`);
530
528
  const domTblId = tableNode.getAttribute('data-tblId');
531
529
  const domRow = tableNode.getAttribute('data-row');
532
530
  const trNode = tableNode.querySelector('tbody > tr');
533
531
  if (domTblId && domRow !== null && trNode && trNode.children.length > 0) {
534
- // log(`${funcName}: Line ${lineNum} FALLBACK: Attempting reconstruction using table DOM attributes as ${ATTR_TABLE_JSON} was missing.`);
532
+ // log(`${funcName}: Line ${lineNum} FALLBACK: Attempting reconstruction using table DOM attributes as ${ATTR_TABLE_JSON} was missing.`);
535
533
  const domCols = trNode.children.length;
536
534
  const tempMetadata = {tblId: domTblId, row: parseInt(domRow, 10), cols: domCols};
537
535
  const tempAttrString = JSON.stringify(tempMetadata);
538
- // log(`${funcName}: Line ${lineNum} FALLBACK: Constructed temporary metadata: ${tempAttrString}`);
536
+ // log(`${funcName}: Line ${lineNum} FALLBACK: Constructed temporary metadata: ${tempAttrString}`);
539
537
 
540
538
  let cellHTMLSegments = Array.from(trNode.children).map((td, index) => {
541
539
  let segmentHTML = td.innerHTML || '';
@@ -555,7 +553,7 @@ exports.collectContentPre = (hook, ctx) => {
555
553
  });
556
554
 
557
555
  if (cellHTMLSegments.length !== domCols) {
558
- // log(`${funcName}: WARNING Line ${lineNum} (Fallback): Reconstructed cell count (${cellHTMLSegments.length}) does not match DOM cols (${domCols}).`);
556
+ // log(`${funcName}: WARNING Line ${lineNum} (Fallback): Reconstructed cell count (${cellHTMLSegments.length}) does not match DOM cols (${domCols}).`);
559
557
  while(cellHTMLSegments.length < domCols) cellHTMLSegments.push('');
560
558
  if(cellHTMLSegments.length > domCols) cellHTMLSegments.length = domCols;
561
559
  }
@@ -565,24 +563,24 @@ exports.collectContentPre = (hook, ctx) => {
565
563
  state.lineAttributes = state.lineAttributes || [];
566
564
  state.lineAttributes = state.lineAttributes.filter(attr => attr[0] !== ATTR_TABLE_JSON);
567
565
  state.lineAttributes.push([ATTR_TABLE_JSON, tempAttrString]);
568
- // log(`${funcName}: Line ${lineNum} FALLBACK: Successfully reconstructed line using DOM attributes. Returning undefined.`);
566
+ // log(`${funcName}: Line ${lineNum} FALLBACK: Successfully reconstructed line using DOM attributes. Returning undefined.`);
569
567
  return undefined;
570
568
  } else {
571
- // log(`${funcName}: Line ${lineNum} FALLBACK: Could not reconstruct from DOM attributes due to missing info.`);
569
+ // log(`${funcName}: Line ${lineNum} FALLBACK: Could not reconstruct from DOM attributes due to missing info.`);
572
570
  }
573
571
  }
574
572
  } catch (e) {
575
573
  console.error(`[ep_data_tables] ${funcName}: Line ${lineNum} error during DOM reconstruction:`, e);
576
- // log(`${funcName}: Line ${lineNum} Exception details:`, { message: e.message, stack: e.stack });
574
+ // log(`${funcName}: Line ${lineNum} Exception details:`, { message: e.message, stack: e.stack });
577
575
  }
578
576
  } else {
579
- // log(`${funcName}: Could not get valid line number (${lineNum}), rep, or docManager for DOM reconstruction of ace-line.`);
577
+ // log(`${funcName}: Could not get valid line number (${lineNum}), rep, or docManager for DOM reconstruction of ace-line.`);
580
578
  }
581
579
  } else {
582
- // log(`${funcName}: Node is ace-line but no rendered table.dataTable[data-tblId] found. Allowing normal processing for: ${node?.className}`);
580
+ // log(`${funcName}: Node is ace-line but no rendered table.dataTable[data-tblId] found. Allowing normal processing for: ${node?.className}`);
583
581
  }
584
582
  } else {
585
- // log(`${funcName}: Node is not an ace-line (or node is null). Node: ${node?.tagName}.${node?.className}. Allowing normal processing.`);
583
+ // log(`${funcName}: Node is not an ace-line (or node is null). Node: ${node?.tagName}.${node?.className}. Allowing normal processing.`);
586
584
  }
587
585
  // ***** END Primary Path *****
588
586
 
@@ -591,19 +589,19 @@ exports.collectContentPre = (hook, ctx) => {
591
589
  const classes = ctx.cls ? ctx.cls.split(' ') : [];
592
590
  let appliedAttribFromClass = false;
593
591
  if (classes.length > 0) {
594
- // log(`${funcName}: Secondary path - Checking classes on node ${node?.tagName}.${node?.className}: [${classes.join(', ')}]`);
592
+ // log(`${funcName}: Secondary path - Checking classes on node ${node?.tagName}.${node?.className}: [${classes.join(', ')}]`);
595
593
  for (const cls of classes) {
596
594
  if (cls.startsWith('tbljson-')) {
597
- // log(`${funcName}: Secondary path - Found tbljson class: ${cls} on node ${node?.tagName}.${node?.className}`);
595
+ // log(`${funcName}: Secondary path - Found tbljson class: ${cls} on node ${node?.tagName}.${node?.className}`);
598
596
  const encodedMetadata = cls.substring(8);
599
597
  try {
600
598
  const decodedMetadata = dec(encodedMetadata);
601
599
  if (decodedMetadata) {
602
600
  cc.doAttrib(state, `${ATTR_TABLE_JSON}::${decodedMetadata}`);
603
601
  appliedAttribFromClass = true;
604
- // log(`${funcName}: Secondary path - Applied attribute to OP via cc.doAttrib for class ${cls.substring(0, 20)}... on ${node?.tagName}`);
602
+ // log(`${funcName}: Secondary path - Applied attribute to OP via cc.doAttrib for class ${cls.substring(0, 20)}... on ${node?.tagName}`);
605
603
  } else {
606
- // log(`${funcName}: Secondary path - ERROR - Decoded metadata is null or empty for class ${cls}`);
604
+ // log(`${funcName}: Secondary path - ERROR - Decoded metadata is null or empty for class ${cls}`);
607
605
  }
608
606
  } catch (e) {
609
607
  console.error(`[ep_data_tables] ${funcName}: Secondary path - Error processing tbljson class ${cls} on ${node?.tagName}:`, e);
@@ -612,49 +610,49 @@ exports.collectContentPre = (hook, ctx) => {
612
610
  }
613
611
  }
614
612
  if (!appliedAttribFromClass && classes.some(c => c.startsWith('tbljson-'))) {
615
- // log(`${funcName}: Secondary path - Found tbljson- class but failed to apply attribute.`);
613
+ // log(`${funcName}: Secondary path - Found tbljson- class but failed to apply attribute.`);
616
614
  } else if (!classes.some(c => c.startsWith('tbljson-'))) {
617
- // log(`${funcName}: Secondary path - No tbljson- class found on this node.`);
615
+ // log(`${funcName}: Secondary path - No tbljson- class found on this node.`);
618
616
  }
619
617
  } else {
620
- // log(`${funcName}: Secondary path - Node ${node?.tagName}.${node?.className} has no ctx.cls or classes array is empty.`);
618
+ // log(`${funcName}: Secondary path - Node ${node?.tagName}.${node?.className} has no ctx.cls or classes array is empty.`);
621
619
  }
622
620
 
623
- // log(`${funcName}: *** EXIT POINT *** For Node: ${node?.tagName}.${node?.className}. Applied from class: ${appliedAttribFromClass}`);
621
+ // log(`${funcName}: *** EXIT POINT *** For Node: ${node?.tagName}.${node?.className}. Applied from class: ${appliedAttribFromClass}`);
624
622
  };
625
623
 
626
624
  // ───────────── attribute → span‑class mapping (linestylefilter hook) ─────────
627
625
  exports.aceAttribsToClasses = (hook, ctx) => {
628
626
  const funcName = 'aceAttribsToClasses';
629
- // log(`>>>> ${funcName}: Called with key: ${ctx.key}`); // log entry
627
+ // log(`>>>> ${funcName}: Called with key: ${ctx.key}`); // log entry
630
628
  if (ctx.key === ATTR_TABLE_JSON) {
631
- // log(`${funcName}: Processing ATTR_TABLE_JSON.`);
629
+ // log(`${funcName}: Processing ATTR_TABLE_JSON.`);
632
630
  // ctx.value is the raw JSON string from Etherpad's attribute pool
633
631
  const rawJsonValue = ctx.value;
634
- // log(`${funcName}: Received raw attribute value (ctx.value):`, rawJsonValue);
632
+ // log(`${funcName}: Received raw attribute value (ctx.value):`, rawJsonValue);
635
633
 
636
634
  // Attempt to parse for logging purposes
637
635
  let parsedMetadataForLog = '[JSON Parse Error]';
638
636
  try {
639
637
  parsedMetadataForLog = JSON.parse(rawJsonValue);
640
- // log(`${funcName}: Value parsed for logging:`, parsedMetadataForLog);
638
+ // log(`${funcName}: Value parsed for logging:`, parsedMetadataForLog);
641
639
  } catch(e) {
642
- // log(`${funcName}: Error parsing raw JSON value for logging:`, e);
640
+ // log(`${funcName}: Error parsing raw JSON value for logging:`, e);
643
641
  // Continue anyway, enc() might still work if it's just a string
644
642
  }
645
643
 
646
644
  // Generate the class name by base64 encoding the raw JSON string.
647
645
  // This ensures acePostWriteDomLineHTML receives the expected encoded format.
648
646
  const className = `tbljson-${enc(rawJsonValue)}`;
649
- // log(`${funcName}: Generated class name by encoding raw JSON: ${className}`);
647
+ // log(`${funcName}: Generated class name by encoding raw JSON: ${className}`);
650
648
  return [className];
651
649
  }
652
650
  if (ctx.key === ATTR_CELL) {
653
651
  // Keep this in case we want cell-specific styling later
654
- // // log(`${funcName}: Processing ATTR_CELL: ${ctx.value}`); // Optional: Uncomment if needed
652
+ //// log(`${funcName}: Processing ATTR_CELL: ${ctx.value}`); // Optional: Uncomment if needed
655
653
  return [`tblCell-${ctx.value}`];
656
654
  }
657
- // // log(`${funcName}: Processing other key: ${ctx.key}`); // Optional: Uncomment if needed
655
+ //// log(`${funcName}: Processing other key: ${ctx.key}`); // Optional: Uncomment if needed
658
656
  return [];
659
657
  };
660
658
 
@@ -677,11 +675,11 @@ function escapeHtml(text = '') {
677
675
  // NEW Helper function to build table HTML from pre-rendered delimited content with resize handles
678
676
  function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
679
677
  const funcName = 'buildTableFromDelimitedHTML';
680
- // log(`${funcName}: START`, { metadata, innerHTMLSegments });
678
+ // log(`${funcName}: START`, { metadata, innerHTMLSegments });
681
679
 
682
680
  if (!metadata || typeof metadata.tblId === 'undefined' || typeof metadata.row === 'undefined') {
683
681
  console.error(`[ep_data_tables] ${funcName}: Invalid or missing metadata. Aborting.`);
684
- // log(`${funcName}: END - Error`);
682
+ // log(`${funcName}: END - Error`);
685
683
  return '<table class="dataTable dataTable-error"><tbody><tr><td>Error: Missing table metadata</td></tr></tbody></table>'; // Return error table
686
684
  }
687
685
 
@@ -747,17 +745,17 @@ function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
747
745
  const tdContent = `<td style="${cellStyle}" data-column="${index}" draggable="false">${modifiedSegment}${resizeHandle}</td>`;
748
746
  return tdContent;
749
747
  }).join('');
750
- // log(`${funcName}: Joined all cellsHtml:`, cellsHtml);
748
+ // log(`${funcName}: Joined all cellsHtml:`, cellsHtml);
751
749
 
752
750
  // Add 'dataTable-first-row' class if it's the logical first row (row index 0)
753
751
  const firstRowClass = metadata.row === 0 ? ' dataTable-first-row' : '';
754
- // log(`${funcName}: First row class applied: '${firstRowClass}'`);
752
+ // log(`${funcName}: First row class applied: '${firstRowClass}'`);
755
753
 
756
754
  // Construct the final table HTML
757
755
  // Rely on CSS for border-collapse, width etc. Add data attributes from metadata.
758
- const tableHtml = `<table class="dataTable${firstRowClass}" data-tblId="${metadata.tblId}" data-row="${metadata.row}" style="width:100%; border-collapse: collapse; table-layout: fixed;" draggable="false"><tbody><tr>${cellsHtml}</tr></tbody></table>`;
759
- // log(`${funcName}: Generated final table HTML:`, tableHtml);
760
- // log(`${funcName}: END - Success`);
756
+ const tableHtml = `<table class="dataTable${firstRowClass}" writingsuggestions="false" data-tblId="${metadata.tblId}" data-row="${metadata.row}" style="width:100%; border-collapse: collapse; table-layout: fixed;" draggable="false"><tbody><tr>${cellsHtml}</tr></tbody></table>`;
757
+ // log(`${funcName}: Generated final table HTML:`, tableHtml);
758
+ // log(`${funcName}: END - Success`);
761
759
  return tableHtml;
762
760
  }
763
761
 
@@ -770,28 +768,28 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
770
768
  const logPrefix = '[ep_data_tables:acePostWriteDomLineHTML]'; // Consistent prefix
771
769
 
772
770
  // *** STARTUP LOGGING ***
773
- // log(`${logPrefix} ----- START ----- NodeID: ${nodeId} LineNum: ${lineNum}`);
771
+ // log(`${logPrefix} ----- START ----- NodeID: ${nodeId} LineNum: ${lineNum}`);
774
772
  if (!node || !nodeId) {
775
- // log(`${logPrefix} ERROR - Received invalid node or node without ID. Aborting.`);
773
+ // log(`${logPrefix} ERROR - Received invalid node or node without ID. Aborting.`);
776
774
  console.error(`[ep_data_tables] ${funcName}: Received invalid node or node without ID.`);
777
775
  return cb();
778
776
  }
779
777
 
780
778
  // *** ENHANCED DEBUG: Log complete DOM state ***
781
- // log(`${logPrefix} NodeID#${nodeId}: COMPLETE DOM STRUCTURE DEBUG:`);
782
- // log(`${logPrefix} NodeID#${nodeId}: Node tagName: ${node.tagName}`);
783
- // log(`${logPrefix} NodeID#${nodeId}: Node className: ${node.className}`);
784
- // log(`${logPrefix} NodeID#${nodeId}: Node innerHTML length: ${node.innerHTML?.length || 0}`);
785
- // log(`${logPrefix} NodeID#${nodeId}: Node innerHTML (first 500 chars): "${(node.innerHTML || '').substring(0, 500)}"`);
786
- // log(`${logPrefix} NodeID#${nodeId}: Node children count: ${node.children?.length || 0}`);
779
+ // log(`${logPrefix} NodeID#${nodeId}: COMPLETE DOM STRUCTURE DEBUG:`);
780
+ // log(`${logPrefix} NodeID#${nodeId}: Node tagName: ${node.tagName}`);
781
+ // log(`${logPrefix} NodeID#${nodeId}: Node className: ${node.className}`);
782
+ // log(`${logPrefix} NodeID#${nodeId}: Node innerHTML length: ${node.innerHTML?.length || 0}`);
783
+ // log(`${logPrefix} NodeID#${nodeId}: Node innerHTML (first 500 chars): "${(node.innerHTML || '').substring(0, 500)}"`);
784
+ // log(`${logPrefix} NodeID#${nodeId}: Node children count: ${node.children?.length || 0}`);
787
785
 
788
786
  // log all child elements and their classes
789
787
  if (node.children) {
790
788
  for (let i = 0; i < Math.min(node.children.length, 10); i++) {
791
789
  const child = node.children[i];
792
- // log(`${logPrefix} NodeID#${nodeId}: Child[${i}] tagName: ${child.tagName}, className: "${child.className}", innerHTML length: ${child.innerHTML?.length || 0}`);
790
+ // log(`${logPrefix} NodeID#${nodeId}: Child[${i}] tagName: ${child.tagName}, className: "${child.className}", innerHTML length: ${child.innerHTML?.length || 0}`);
793
791
  if (child.className && child.className.includes('tbljson-')) {
794
- // log(`${logPrefix} NodeID#${nodeId}: *** FOUND TBLJSON CLASS ON CHILD[${i}] ***`);
792
+ // log(`${logPrefix} NodeID#${nodeId}: *** FOUND TBLJSON CLASS ON CHILD[${i}] ***`);
795
793
  }
796
794
  }
797
795
  }
@@ -800,69 +798,69 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
800
798
  let encodedJsonString = null;
801
799
 
802
800
  // --- 1. Find and Parse Metadata Attribute ---
803
- // log(`${logPrefix} NodeID#${nodeId}: Searching for tbljson-* class...`);
801
+ // log(`${logPrefix} NodeID#${nodeId}: Searching for tbljson-* class...`);
804
802
 
805
803
  // ENHANCED Helper function to recursively search for tbljson class in all descendants
806
804
  function findTbljsonClass(element, depth = 0, path = '') {
807
805
  const indent = ' '.repeat(depth);
808
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}Searching element: ${element.tagName || 'unknown'}, path: ${path}`);
806
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}Searching element: ${element.tagName || 'unknown'}, path: ${path}`);
809
807
 
810
808
  // Check the element itself
811
809
  if (element.classList) {
812
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has ${element.classList.length} classes: [${Array.from(element.classList).join(', ')}]`);
810
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has ${element.classList.length} classes: [${Array.from(element.classList).join(', ')}]`);
813
811
  for (const cls of element.classList) {
814
812
  if (cls.startsWith('tbljson-')) {
815
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}*** FOUND TBLJSON CLASS: ${cls.substring(8)} at depth ${depth}, path: ${path} ***`);
813
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}*** FOUND TBLJSON CLASS: ${cls.substring(8)} at depth ${depth}, path: ${path} ***`);
816
814
  return cls.substring(8);
817
815
  }
818
816
  }
819
817
  } else {
820
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has no classList`);
818
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has no classList`);
821
819
  }
822
820
 
823
821
  // Recursively check all descendants
824
822
  if (element.children) {
825
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has ${element.children.length} children`);
823
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has ${element.children.length} children`);
826
824
  for (let i = 0; i < element.children.length; i++) {
827
825
  const child = element.children[i];
828
826
  const childPath = `${path}>${child.tagName}[${i}]`;
829
827
  const found = findTbljsonClass(child, depth + 1, childPath);
830
828
  if (found) {
831
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}Returning found result from child: ${found}`);
829
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}Returning found result from child: ${found}`);
832
830
  return found;
833
831
  }
834
832
  }
835
833
  } else {
836
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has no children`);
834
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has no children`);
837
835
  }
838
836
 
839
- // log(`${logPrefix} NodeID#${nodeId}: ${indent}No tbljson class found in this element or its children`);
837
+ // log(`${logPrefix} NodeID#${nodeId}: ${indent}No tbljson class found in this element or its children`);
840
838
  return null;
841
839
  }
842
840
 
843
841
  // Search for tbljson class starting from the node
844
- // log(`${logPrefix} NodeID#${nodeId}: Starting recursive search for tbljson class...`);
842
+ // log(`${logPrefix} NodeID#${nodeId}: Starting recursive search for tbljson class...`);
845
843
  encodedJsonString = findTbljsonClass(node, 0, 'ROOT');
846
844
 
847
845
  if (encodedJsonString) {
848
- // log(`${logPrefix} NodeID#${nodeId}: *** SUCCESS: Found encoded tbljson class: ${encodedJsonString} ***`);
846
+ // log(`${logPrefix} NodeID#${nodeId}: *** SUCCESS: Found encoded tbljson class: ${encodedJsonString} ***`);
849
847
  } else {
850
- // log(`${logPrefix} NodeID#${nodeId}: *** NO TBLJSON CLASS FOUND ***`);
848
+ // log(`${logPrefix} NodeID#${nodeId}: *** NO TBLJSON CLASS FOUND ***`);
851
849
  }
852
850
 
853
851
  // If no attribute found, it's not a table line managed by us
854
852
  if (!encodedJsonString) {
855
- // log(`${logPrefix} NodeID#${nodeId}: No tbljson-* class found. Assuming not a table line. END.`);
853
+ // log(`${logPrefix} NodeID#${nodeId}: No tbljson-* class found. Assuming not a table line. END.`);
856
854
 
857
855
  // DEBUG: Add detailed logging to understand why tbljson class is missing
858
- // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Node tag: ${node.tagName}, Node classes:`, Array.from(node.classList || []));
859
- // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Node innerHTML (first 200 chars): "${(node.innerHTML || '').substring(0, 200)}"`);
856
+ // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Node tag: ${node.tagName}, Node classes:`, Array.from(node.classList || []));
857
+ // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Node innerHTML (first 200 chars): "${(node.innerHTML || '').substring(0, 200)}"`);
860
858
 
861
859
  // Check if there are any child elements with classes
862
860
  if (node.children && node.children.length > 0) {
863
861
  for (let i = 0; i < Math.min(node.children.length, 5); i++) {
864
862
  const child = node.children[i];
865
- // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Child ${i} tag: ${child.tagName}, classes:`, Array.from(child.classList || []));
863
+ // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Child ${i} tag: ${child.tagName}, classes:`, Array.from(child.classList || []));
866
864
  }
867
865
  }
868
866
 
@@ -871,24 +869,24 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
871
869
  if (existingTable) {
872
870
  const existingTblId = existingTable.getAttribute('data-tblId');
873
871
  const existingRow = existingTable.getAttribute('data-row');
874
- // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Found orphaned table! TblId: ${existingTblId}, Row: ${existingRow}`);
872
+ // log(`${logPrefix} NodeID#${nodeId}: DEBUG - Found orphaned table! TblId: ${existingTblId}, Row: ${existingRow}`);
875
873
 
876
874
  // This suggests the table exists but the tbljson class was lost
877
875
  // Check if we're in a post-resize situation
878
876
  if (existingTblId && existingRow !== null) {
879
- // log(`${logPrefix} NodeID#${nodeId}: POTENTIAL ISSUE - Table exists but no tbljson class. This may be a post-resize issue.`);
877
+ // log(`${logPrefix} NodeID#${nodeId}: POTENTIAL ISSUE - Table exists but no tbljson class. This may be a post-resize issue.`);
880
878
 
881
879
  // Try to look up what the metadata should be based on the table attributes
882
880
  const tableCells = existingTable.querySelectorAll('td');
883
- // log(`${logPrefix} NodeID#${nodeId}: Table has ${tableCells.length} cells`);
881
+ // log(`${logPrefix} NodeID#${nodeId}: Table has ${tableCells.length} cells`);
884
882
 
885
883
  // log the current line's attribute state if we can get line number
886
884
  if (lineNum !== undefined && args?.documentAttributeManager) {
887
885
  try {
888
886
  const currentLineAttr = args.documentAttributeManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
889
- // log(`${logPrefix} NodeID#${nodeId}: Current line ${lineNum} tbljson attribute: ${currentLineAttr || 'NULL'}`);
887
+ // log(`${logPrefix} NodeID#${nodeId}: Current line ${lineNum} tbljson attribute: ${currentLineAttr || 'NULL'}`);
890
888
  } catch (e) {
891
- // log(`${logPrefix} NodeID#${nodeId}: Error getting line attribute:`, e);
889
+ // log(`${logPrefix} NodeID#${nodeId}: Error getting line attribute:`, e);
892
890
  }
893
891
  }
894
892
  }
@@ -900,7 +898,7 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
900
898
  // *** NEW CHECK: If table already rendered, skip regeneration ***
901
899
  const existingTable = node.querySelector('table.dataTable[data-tblId]');
902
900
  if (existingTable) {
903
- // log(`${logPrefix} NodeID#${nodeId}: Table already exists in DOM. Skipping innerHTML replacement.`);
901
+ // log(`${logPrefix} NodeID#${nodeId}: Table already exists in DOM. Skipping innerHTML replacement.`);
904
902
  // Optionally, verify tblId matches metadata? For now, assume it's correct.
905
903
  // const existingTblId = existingTable.getAttribute('data-tblId');
906
904
  // try {
@@ -911,26 +909,26 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
911
909
  return cb(); // Do nothing further
912
910
  }
913
911
 
914
- // log(`${logPrefix} NodeID#${nodeId}: Decoding and parsing metadata...`);
912
+ // log(`${logPrefix} NodeID#${nodeId}: Decoding and parsing metadata...`);
915
913
  try {
916
914
  const decoded = dec(encodedJsonString);
917
- // log(`${logPrefix} NodeID#${nodeId}: Decoded string: ${decoded}`);
915
+ // log(`${logPrefix} NodeID#${nodeId}: Decoded string: ${decoded}`);
918
916
  if (!decoded) throw new Error('Decoded string is null or empty.');
919
917
  rowMetadata = JSON.parse(decoded);
920
- // log(`${logPrefix} NodeID#${nodeId}: Parsed rowMetadata:`, rowMetadata);
918
+ // log(`${logPrefix} NodeID#${nodeId}: Parsed rowMetadata:`, rowMetadata);
921
919
 
922
920
  // Validate essential metadata
923
921
  if (!rowMetadata || typeof rowMetadata.tblId === 'undefined' || typeof rowMetadata.row === 'undefined' || typeof rowMetadata.cols !== 'number') {
924
922
  throw new Error('Invalid or incomplete metadata (missing tblId, row, or cols).');
925
923
  }
926
- // log(`${logPrefix} NodeID#${nodeId}: Metadata validated successfully.`);
924
+ // log(`${logPrefix} NodeID#${nodeId}: Metadata validated successfully.`);
927
925
 
928
926
  } catch(e) {
929
- // log(`${logPrefix} NodeID#${nodeId}: FATAL ERROR - Failed to decode/parse/validate tbljson metadata. Rendering cannot proceed.`, e);
927
+ // log(`${logPrefix} NodeID#${nodeId}: FATAL ERROR - Failed to decode/parse/validate tbljson metadata. Rendering cannot proceed.`, e);
930
928
  console.error(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Failed to decode/parse/validate tbljson.`, encodedJsonString, e);
931
929
  // Optionally render an error state in the node?
932
930
  node.innerHTML = '<div style="color:red; border: 1px solid red; padding: 5px;">[ep_data_tables] Error: Invalid table metadata attribute found.</div>';
933
- // log(`${logPrefix} NodeID#${nodeId}: Rendered error message in node. END.`);
931
+ // log(`${logPrefix} NodeID#${nodeId}: Rendered error message in node. END.`);
934
932
  return cb();
935
933
  }
936
934
  // --- End Metadata Parsing ---
@@ -944,88 +942,66 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
944
942
  // After an edit, aceKeyEvent updates atext, and node.innerHTML reflects that new "EditedCell1|Cell2" string.
945
943
  // When styling is applied, it will include spans like: <span class="author-xxx bold">Cell1</span>|<span class="author-yyy italic">Cell2</span>
946
944
  const delimitedTextFromLine = node.innerHTML;
947
- // log(`${logPrefix} NodeID#${nodeId}: Using node.innerHTML for delimited text to preserve styling.`);
948
- // log(`${logPrefix} NodeID#${nodeId}: Raw innerHTML length: ${delimitedTextFromLine?.length || 0}`);
949
- // log(`${logPrefix} NodeID#${nodeId}: Raw innerHTML (first 1000 chars): "${(delimitedTextFromLine || '').substring(0, 1000)}"`);
945
+ // log(`${logPrefix} NodeID#${nodeId}: Using node.innerHTML for delimited text to preserve styling.`);
946
+ // log(`${logPrefix} NodeID#${nodeId}: Raw innerHTML length: ${delimitedTextFromLine?.length || 0}`);
947
+ // log(`${logPrefix} NodeID#${nodeId}: Raw innerHTML (first 1000 chars): "${(delimitedTextFromLine || '').substring(0, 1000)}"`);
950
948
 
951
949
  // *** ENHANCED DEBUG: Analyze delimiter presence ***
952
950
  const delimiterCount = (delimitedTextFromLine || '').split(DELIMITER).length - 1;
953
- // log(`${logPrefix} NodeID#${nodeId}: Delimiter '${DELIMITER}' count in innerHTML: ${delimiterCount}`);
954
- // log(`${logPrefix} NodeID#${nodeId}: Expected delimiters for ${rowMetadata.cols} columns: ${rowMetadata.cols - 1}`);
951
+ // log(`${logPrefix} NodeID#${nodeId}: Delimiter '${DELIMITER}' count in innerHTML: ${delimiterCount}`);
952
+ // log(`${logPrefix} NodeID#${nodeId}: Expected delimiters for ${rowMetadata.cols} columns: ${rowMetadata.cols - 1}`);
955
953
 
956
954
  // log all delimiter positions
957
955
  let pos = -1;
958
956
  const delimiterPositions = [];
959
957
  while ((pos = delimitedTextFromLine.indexOf(DELIMITER, pos + 1)) !== -1) {
960
958
  delimiterPositions.push(pos);
961
- // log(`${logPrefix} NodeID#${nodeId}: Delimiter found at position ${pos}, context: "${delimitedTextFromLine.substring(Math.max(0, pos - 20), pos + 21)}"`);
959
+ // log(`${logPrefix} NodeID#${nodeId}: Delimiter found at position ${pos}, context: "${delimitedTextFromLine.substring(Math.max(0, pos - 20), pos + 21)}"`);
962
960
  }
963
- // log(`${logPrefix} NodeID#${nodeId}: All delimiter positions: [${delimiterPositions.join(', ')}]`);
961
+ // log(`${logPrefix} NodeID#${nodeId}: All delimiter positions: [${delimiterPositions.join(', ')}]`);
964
962
 
965
963
  // The DELIMITER const is defined at the top of this file.
966
964
  // NEW: Remove all hidden-delimiter <span> wrappers **before** we split so
967
965
  // the embedded delimiter character they carry doesn't inflate or shrink
968
966
  // the segment count.
969
- const spanDelimRegex = new RegExp('<span class="ep-data_tables-delim"[^>]*>' + DELIMITER + '</span>', 'ig');
970
- // Safari-specific normalization: it may serialize the delimiter as entities and inject Apple spans
971
- const delimiterEntityHexRE = /&#x241f;/ig; // hex entity for U+241F
972
- const delimiterEntityDecRE = /&#9247;/g; // decimal entity for U+241F
973
- const appleConvertedSpaceRE = /<span class="Apple-converted-space">[\s\u00A0]*<\/span>/ig;
974
- const zeroWidthCharsRE = /[\u200B\u200C\u200D\uFEFF]/g;
975
-
976
- const hexMatches = ((delimitedTextFromLine || '').match(delimiterEntityHexRE) || []).length;
977
- const decMatches = ((delimitedTextFromLine || '').match(delimiterEntityDecRE) || []).length;
978
- const appleSpaceMatches = ((delimitedTextFromLine || '').match(appleConvertedSpaceRE) || []).length;
979
-
967
+ const spanDelimRegex = new RegExp('<span class="ep-data_tables-delim"[^>]*>' + DELIMITER + '<\\/span>', 'ig');
980
968
  const sanitizedHTMLForSplit = (delimitedTextFromLine || '')
981
969
  .replace(spanDelimRegex, '')
982
970
  // strip caret anchors from raw line html before split
983
- .replace(/<span class="ep-data_tables-caret-anchor"[^>]*><\/span>/ig, '')
984
- // Safari may serialize the delimiter as HTML entities – convert back to raw char
985
- .replace(delimiterEntityHexRE, DELIMITER)
986
- .replace(delimiterEntityDecRE, DELIMITER)
987
- // Safari sometimes injects Apple-converted-space wrappers; collapse to a normal space
988
- .replace(appleConvertedSpaceRE, ' ')
989
- // Guard against invisible characters that can disturb splitting
990
- .replace(zeroWidthCharsRE, '');
991
-
992
- if ((hexMatches + decMatches + appleSpaceMatches) > 0) {
993
- console.warn(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Normalized Safari entities/spans before splitting: hex=${hexMatches}, dec=${decMatches}, appleSpaces=${appleSpaceMatches}`);
994
- }
995
-
971
+ .replace(/<span class="ep-data_tables-caret-anchor"[^>]*><\/span>/ig, '');
996
972
  const htmlSegments = sanitizedHTMLForSplit.split(DELIMITER);
997
973
 
998
- // log(`${logPrefix} NodeID#${nodeId}: *** SEGMENT ANALYSIS ***`);
999
- // log(`${logPrefix} NodeID#${nodeId}: Split resulted in ${htmlSegments.length} segments`);
974
+ // log(`${logPrefix} NodeID#${nodeId}: *** SEGMENT ANALYSIS ***`);
975
+ // log(`${logPrefix} NodeID#${nodeId}: Split resulted in ${htmlSegments.length} segments`);
1000
976
  for (let i = 0; i < htmlSegments.length; i++) {
1001
977
  const segment = htmlSegments[i] || '';
1002
- // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] length: ${segment.length}`);
1003
- // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (first 200 chars): "${segment.substring(0, 200)}"`);
978
+ // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] length: ${segment.length}`);
979
+ // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (first 200 chars): "${segment.substring(0, 200)}"`);
1004
980
  if (segment.length > 200) {
1005
- // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (chars 200-400): "${segment.substring(200, 400)}"`);
981
+ // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (chars 200-400): "${segment.substring(200, 400)}"`);
1006
982
  }
1007
983
  if (segment.length > 400) {
1008
- // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (chars 400-600): "${segment.substring(400, 600)}"`);
984
+ // log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (chars 400-600): "${segment.substring(400, 600)}"`);
1009
985
  }
1010
986
  // Check if segment contains image-related content
1011
987
  if (segment.includes('image:') || segment.includes('image-placeholder') || segment.includes('currently-selected')) {
1012
- // log(`${logPrefix} NodeID#${nodeId}: *** SEGMENT[${i}] CONTAINS IMAGE CONTENT ***`);
988
+ // log(`${logPrefix} NodeID#${nodeId}: *** SEGMENT[${i}] CONTAINS IMAGE CONTENT ***`);
1013
989
  }
1014
990
  }
1015
991
 
1016
- // log(`${logPrefix} NodeID#${nodeId}: Parsed HTML segments (${htmlSegments.length}):`, htmlSegments.map(s => (s || '').substring(0,50) + (s && s.length > 50 ? '...' : '')));
992
+ // log(`${logPrefix} NodeID#${nodeId}: Parsed HTML segments (${htmlSegments.length}):`, htmlSegments.map(s => (s || '').substring(0,50) + (s && s.length > 50 ? '...' : '')));
1017
993
 
1018
994
  // --- Enhanced Validation with Automatic Structure Reconstruction ---
1019
995
  let finalHtmlSegments = htmlSegments;
1020
996
 
1021
997
  if (htmlSegments.length !== rowMetadata.cols) {
1022
- // log(`${logPrefix} NodeID#${nodeId}: *** MISMATCH DETECTED *** - Attempting reconstruction.`);
998
+ // log(`${logPrefix} NodeID#${nodeId}: *** MISMATCH DETECTED *** - Attempting reconstruction.`);
1023
999
 
1024
1000
  // Check if this is an image selection issue
1025
1001
  const hasImageSelected = delimitedTextFromLine.includes('currently-selected');
1026
1002
  const hasImageContent = delimitedTextFromLine.includes('image:');
1027
1003
  if (hasImageSelected) {
1028
- // log(`${logPrefix} NodeID#${nodeId}: *** POTENTIAL CAUSE: Image selection state may be affecting segment parsing ***`);
1004
+ // log(`${logPrefix} NodeID#${nodeId}: *** POTENTIAL CAUSE: Image selection state may be affecting segment parsing ***`);
1029
1005
  }
1030
1006
 
1031
1007
  // First attempt: reconstruct using DOM spans that carry tblCell-N classes
@@ -1097,14 +1073,14 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
1097
1073
  console.warn(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Could not reconstruct to expected ${rowMetadata.cols} segments. Got ${finalHtmlSegments.length}.`);
1098
1074
  }
1099
1075
  } else {
1100
- // log(`${logPrefix} NodeID#${nodeId}: Segment count matches metadata cols (${rowMetadata.cols}). Using original segments.`);
1076
+ // log(`${logPrefix} NodeID#${nodeId}: Segment count matches metadata cols (${rowMetadata.cols}). Using original segments.`);
1101
1077
  }
1102
1078
 
1103
1079
  // --- 3. Build and Render Table ---
1104
- // log(`${logPrefix} NodeID#${nodeId}: Calling buildTableFromDelimitedHTML...`);
1080
+ // log(`${logPrefix} NodeID#${nodeId}: Calling buildTableFromDelimitedHTML...`);
1105
1081
  try {
1106
1082
  const newTableHTML = buildTableFromDelimitedHTML(rowMetadata, finalHtmlSegments);
1107
- // log(`${logPrefix} NodeID#${nodeId}: Received new table HTML from helper. Replacing content.`);
1083
+ // log(`${logPrefix} NodeID#${nodeId}: Received new table HTML from helper. Replacing content.`);
1108
1084
 
1109
1085
  // The old local findTbljsonElement is removed from here. We use the global one now.
1110
1086
  const tbljsonElement = findTbljsonElement(node);
@@ -1117,24 +1093,24 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
1117
1093
  const blockElements = ['center', 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'right', 'left', 'ul', 'ol', 'li', 'code'];
1118
1094
 
1119
1095
  if (blockElements.includes(parentTag)) {
1120
- // log(`${logPrefix} NodeID#${nodeId}: Preserving block element ${parentTag} and replacing its content with table.`);
1096
+ // log(`${logPrefix} NodeID#${nodeId}: Preserving block element ${parentTag} and replacing its content with table.`);
1121
1097
  tbljsonElement.parentElement.innerHTML = newTableHTML;
1122
1098
  } else {
1123
- // log(`${logPrefix} NodeID#${nodeId}: Parent element ${parentTag} is not a block element, replacing entire node content.`);
1099
+ // log(`${logPrefix} NodeID#${nodeId}: Parent element ${parentTag} is not a block element, replacing entire node content.`);
1124
1100
  node.innerHTML = newTableHTML;
1125
1101
  }
1126
1102
  } else {
1127
1103
  // Replace the node's content entirely with the generated table
1128
- // log(`${logPrefix} NodeID#${nodeId}: No nested block element found, replacing entire node content.`);
1104
+ // log(`${logPrefix} NodeID#${nodeId}: No nested block element found, replacing entire node content.`);
1129
1105
  node.innerHTML = newTableHTML;
1130
1106
  }
1131
1107
 
1132
- // log(`${logPrefix} NodeID#${nodeId}: Successfully replaced content with new table structure.`);
1108
+ // log(`${logPrefix} NodeID#${nodeId}: Successfully replaced content with new table structure.`);
1133
1109
  } catch (renderError) {
1134
- // log(`${logPrefix} NodeID#${nodeId}: ERROR during table building or rendering.`, renderError);
1110
+ // log(`${logPrefix} NodeID#${nodeId}: ERROR during table building or rendering.`, renderError);
1135
1111
  console.error(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Error building/rendering table.`, renderError);
1136
1112
  node.innerHTML = '<div style="color:red; border: 1px solid red; padding: 5px;">[ep_data_tables] Error: Failed to render table structure.</div>';
1137
- // log(`${logPrefix} NodeID#${nodeId}: Rendered build/render error message in node. END.`);
1113
+ // log(`${logPrefix} NodeID#${nodeId}: Rendered build/render error message in node. END.`);
1138
1114
  return cb();
1139
1115
  }
1140
1116
  // --- End Table Building ---
@@ -1142,7 +1118,7 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
1142
1118
  // *** REMOVED CACHING LOGIC ***
1143
1119
  // The old logic based on tableRowNodes cache is completely removed.
1144
1120
 
1145
- // log(`${logPrefix}: ----- END ----- NodeID: ${nodeId}`);
1121
+ // log(`${logPrefix}: ----- END ----- NodeID: ${nodeId}`);
1146
1122
  return cb();
1147
1123
  };
1148
1124
 
@@ -1168,34 +1144,34 @@ exports.aceKeyEvent = (h, ctx) => {
1168
1144
 
1169
1145
  const startLogTime = Date.now();
1170
1146
  const logPrefix = '[ep_data_tables:aceKeyEvent]';
1171
- // log(`${logPrefix} START Key='${evt?.key}' Code=${evt?.keyCode} Type=${evt?.type} Modifiers={ctrl:${evt?.ctrlKey},alt:${evt?.altKey},meta:${evt?.metaKey},shift:${evt?.shiftKey}}`, { selStart: rep?.selStart, selEnd: rep?.selEnd });
1147
+ // log(`${logPrefix} START Key='${evt?.key}' Code=${evt?.keyCode} Type=${evt?.type} Modifiers={ctrl:${evt?.ctrlKey},alt:${evt?.altKey},meta:${evt?.metaKey},shift:${evt?.shiftKey}}`, { selStart: rep?.selStart, selEnd: rep?.selEnd });
1172
1148
 
1173
1149
  if (!rep || !rep.selStart || !editorInfo || !evt || !docManager) {
1174
- // log(`${logPrefix} Skipping - Missing critical context.`);
1150
+ // log(`${logPrefix} Skipping - Missing critical context.`);
1175
1151
  return false;
1176
1152
  }
1177
1153
 
1178
1154
  // Get caret info from event context - may be stale
1179
1155
  const reportedLineNum = rep.selStart[0];
1180
1156
  const reportedCol = rep.selStart[1];
1181
- // log(`${logPrefix} Reported caret from rep: Line=${reportedLineNum}, Col=${reportedCol}`);
1157
+ // log(`${logPrefix} Reported caret from rep: Line=${reportedLineNum}, Col=${reportedCol}`);
1182
1158
 
1183
1159
  // --- Get Table Metadata for the reported line ---
1184
1160
  let tableMetadata = null;
1185
1161
  let lineAttrString = null; // Store for potential use later
1186
1162
  try {
1187
1163
  // Add debugging to see what's happening with attribute retrieval
1188
- // log(`${logPrefix} DEBUG: Attempting to get ${ATTR_TABLE_JSON} attribute from line ${reportedLineNum}`);
1164
+ // log(`${logPrefix} DEBUG: Attempting to get ${ATTR_TABLE_JSON} attribute from line ${reportedLineNum}`);
1189
1165
  lineAttrString = docManager.getAttributeOnLine(reportedLineNum, ATTR_TABLE_JSON);
1190
- // log(`${logPrefix} DEBUG: getAttributeOnLine returned: ${lineAttrString ? `"${lineAttrString}"` : 'null/undefined'}`);
1166
+ // log(`${logPrefix} DEBUG: getAttributeOnLine returned: ${lineAttrString ? `"${lineAttrString}"` : 'null/undefined'}`);
1191
1167
 
1192
1168
  // Also check if there are any attributes on this line at all
1193
1169
  if (typeof docManager.getAttributesOnLine === 'function') {
1194
1170
  try {
1195
1171
  const allAttribs = docManager.getAttributesOnLine(reportedLineNum);
1196
- // log(`${logPrefix} DEBUG: All attributes on line ${reportedLineNum}:`, allAttribs);
1172
+ // log(`${logPrefix} DEBUG: All attributes on line ${reportedLineNum}:`, allAttribs);
1197
1173
  } catch(e) {
1198
- // log(`${logPrefix} DEBUG: Error getting all attributes:`, e);
1174
+ // log(`${logPrefix} DEBUG: Error getting all attributes:`, e);
1199
1175
  }
1200
1176
  }
1201
1177
 
@@ -1209,34 +1185,34 @@ exports.aceKeyEvent = (h, ctx) => {
1209
1185
  if (tableInDOM) {
1210
1186
  const domTblId = tableInDOM.getAttribute('data-tblId');
1211
1187
  const domRow = tableInDOM.getAttribute('data-row');
1212
- // log(`${logPrefix} DEBUG: Found table in DOM without attribute! TblId=${domTblId}, Row=${domRow}`);
1188
+ // log(`${logPrefix} DEBUG: Found table in DOM without attribute! TblId=${domTblId}, Row=${domRow}`);
1213
1189
  // Try to reconstruct the metadata from DOM
1214
1190
  const domCells = tableInDOM.querySelectorAll('td');
1215
1191
  if (domTblId && domRow !== null && domCells.length > 0) {
1216
- // log(`${logPrefix} DEBUG: Attempting to reconstruct metadata from DOM...`);
1192
+ // log(`${logPrefix} DEBUG: Attempting to reconstruct metadata from DOM...`);
1217
1193
  const reconstructedMetadata = {
1218
1194
  tblId: domTblId,
1219
1195
  row: parseInt(domRow, 10),
1220
1196
  cols: domCells.length
1221
1197
  };
1222
1198
  lineAttrString = JSON.stringify(reconstructedMetadata);
1223
- // log(`${logPrefix} DEBUG: Reconstructed metadata: ${lineAttrString}`);
1199
+ // log(`${logPrefix} DEBUG: Reconstructed metadata: ${lineAttrString}`);
1224
1200
  }
1225
1201
  }
1226
1202
  }
1227
1203
  } catch(e) {
1228
- // log(`${logPrefix} DEBUG: Error checking DOM for table:`, e);
1204
+ // log(`${logPrefix} DEBUG: Error checking DOM for table:`, e);
1229
1205
  }
1230
1206
  }
1231
1207
 
1232
1208
  if (lineAttrString) {
1233
1209
  tableMetadata = JSON.parse(lineAttrString);
1234
1210
  if (!tableMetadata || typeof tableMetadata.cols !== 'number') {
1235
- // log(`${logPrefix} Line ${reportedLineNum} has attribute, but metadata invalid/missing cols.`);
1211
+ // log(`${logPrefix} Line ${reportedLineNum} has attribute, but metadata invalid/missing cols.`);
1236
1212
  tableMetadata = null; // Ensure it's null if invalid
1237
1213
  }
1238
1214
  } else {
1239
- // log(`${logPrefix} DEBUG: No ${ATTR_TABLE_JSON} attribute found on line ${reportedLineNum}`);
1215
+ // log(`${logPrefix} DEBUG: No ${ATTR_TABLE_JSON} attribute found on line ${reportedLineNum}`);
1240
1216
  // Not a table line based on reported caret line
1241
1217
  }
1242
1218
  } catch(e) {
@@ -1247,7 +1223,7 @@ exports.aceKeyEvent = (h, ctx) => {
1247
1223
  // Get last known good state
1248
1224
  const editor = editorInfo.editor; // Get editor instance
1249
1225
  const lastClick = editor?.ep_data_tables_last_clicked; // Read shared state
1250
- // log(`${logPrefix} Reading stored click/caret info:`, lastClick);
1226
+ // log(`${logPrefix} Reading stored click/caret info:`, lastClick);
1251
1227
 
1252
1228
  // --- Determine the TRUE target line, cell, and caret position ---
1253
1229
  let currentLineNum = -1;
@@ -1262,22 +1238,22 @@ exports.aceKeyEvent = (h, ctx) => {
1262
1238
 
1263
1239
  // ** Scenario 1: Try to trust lastClick info **
1264
1240
  if (lastClick) {
1265
- // log(`${logPrefix} Attempting to validate stored click info for Line=${lastClick.lineNum}...`);
1241
+ // log(`${logPrefix} Attempting to validate stored click info for Line=${lastClick.lineNum}...`);
1266
1242
  let storedLineAttrString = null;
1267
1243
  let storedLineMetadata = null;
1268
1244
  try {
1269
- // log(`${logPrefix} DEBUG: Getting ${ATTR_TABLE_JSON} attribute from stored line ${lastClick.lineNum}`);
1245
+ // log(`${logPrefix} DEBUG: Getting ${ATTR_TABLE_JSON} attribute from stored line ${lastClick.lineNum}`);
1270
1246
  storedLineAttrString = docManager.getAttributeOnLine(lastClick.lineNum, ATTR_TABLE_JSON);
1271
- // log(`${logPrefix} DEBUG: Stored line attribute result: ${storedLineAttrString ? `"${storedLineAttrString}"` : 'null/undefined'}`);
1247
+ // log(`${logPrefix} DEBUG: Stored line attribute result: ${storedLineAttrString ? `"${storedLineAttrString}"` : 'null/undefined'}`);
1272
1248
 
1273
1249
  if (storedLineAttrString) {
1274
1250
  storedLineMetadata = JSON.parse(storedLineAttrString);
1275
- // log(`${logPrefix} DEBUG: Parsed stored metadata:`, storedLineMetadata);
1251
+ // log(`${logPrefix} DEBUG: Parsed stored metadata:`, storedLineMetadata);
1276
1252
  }
1277
1253
 
1278
1254
  // Check if metadata is valid and tblId matches
1279
1255
  if (storedLineMetadata && typeof storedLineMetadata.cols === 'number' && storedLineMetadata.tblId === lastClick.tblId) {
1280
- // log(`${logPrefix} Stored click info VALIDATED (Metadata OK and tblId matches). Trusting stored state.`);
1256
+ // log(`${logPrefix} Stored click info VALIDATED (Metadata OK and tblId matches). Trusting stored state.`);
1281
1257
  trustedLastClick = true;
1282
1258
  currentLineNum = lastClick.lineNum;
1283
1259
  targetCellIndex = lastClick.cellIndex;
@@ -1286,10 +1262,10 @@ exports.aceKeyEvent = (h, ctx) => {
1286
1262
 
1287
1263
  lineText = rep.lines.atIndex(currentLineNum)?.text || '';
1288
1264
  cellTexts = lineText.split(DELIMITER);
1289
- // log(`${logPrefix} Using Line=${currentLineNum}, CellIndex=${targetCellIndex}. Text: "${lineText}"`);
1265
+ // log(`${logPrefix} Using Line=${currentLineNum}, CellIndex=${targetCellIndex}. Text: "${lineText}"`);
1290
1266
 
1291
1267
  if (cellTexts.length !== metadataForTargetLine.cols) {
1292
- // log(`${logPrefix} WARNING: Stored cell count mismatch for trusted line ${currentLineNum}.`);
1268
+ // log(`${logPrefix} WARNING: Stored cell count mismatch for trusted line ${currentLineNum}.`);
1293
1269
  }
1294
1270
 
1295
1271
  cellStartCol = 0;
@@ -1297,20 +1273,20 @@ exports.aceKeyEvent = (h, ctx) => {
1297
1273
  cellStartCol += (cellTexts[i]?.length ?? 0) + DELIMITER.length;
1298
1274
  }
1299
1275
  precedingCellsOffset = cellStartCol;
1300
- // log(`${logPrefix} Calculated cellStartCol=${cellStartCol} from trusted cellIndex=${targetCellIndex}.`);
1276
+ // log(`${logPrefix} Calculated cellStartCol=${cellStartCol} from trusted cellIndex=${targetCellIndex}.`);
1301
1277
 
1302
1278
  if (typeof lastClick.relativePos === 'number' && lastClick.relativePos >= 0) {
1303
1279
  const currentCellTextLength = cellTexts[targetCellIndex]?.length ?? 0;
1304
1280
  relativeCaretPos = Math.max(0, Math.min(lastClick.relativePos, currentCellTextLength));
1305
- // log(`${logPrefix} Using and validated stored relative position: ${relativeCaretPos}.`);
1281
+ // log(`${logPrefix} Using and validated stored relative position: ${relativeCaretPos}.`);
1306
1282
  } else {
1307
1283
  relativeCaretPos = reportedCol - cellStartCol; // Use reportedCol for initial calc if relative is missing
1308
1284
  const currentCellTextLength = cellTexts[targetCellIndex]?.length ?? 0;
1309
1285
  relativeCaretPos = Math.max(0, Math.min(relativeCaretPos, currentCellTextLength));
1310
- // log(`${logPrefix} Stored relativePos missing, calculated from reportedCol (${reportedCol}): ${relativeCaretPos}`);
1286
+ // log(`${logPrefix} Stored relativePos missing, calculated from reportedCol (${reportedCol}): ${relativeCaretPos}`);
1311
1287
  }
1312
1288
  } else {
1313
- // log(`${logPrefix} Stored click info INVALID (Metadata missing/invalid or tblId mismatch). Clearing stored state.`);
1289
+ // log(`${logPrefix} Stored click info INVALID (Metadata missing/invalid or tblId mismatch). Clearing stored state.`);
1314
1290
  if (editor) editor.ep_data_tables_last_clicked = null;
1315
1291
  }
1316
1292
  } catch (e) {
@@ -1321,7 +1297,7 @@ exports.aceKeyEvent = (h, ctx) => {
1321
1297
 
1322
1298
  // ** Scenario 2: Fallback - Use reported line/col ONLY if stored info wasn't trusted **
1323
1299
  if (!trustedLastClick) {
1324
- // log(`${logPrefix} Fallback: Using reported caret position Line=${reportedLineNum}, Col=${reportedCol}.`);
1300
+ // log(`${logPrefix} Fallback: Using reported caret position Line=${reportedLineNum}, Col=${reportedCol}.`);
1325
1301
  // Fetch metadata for the reported line again, in case it wasn't fetched or was invalid earlier
1326
1302
  try {
1327
1303
  lineAttrString = docManager.getAttributeOnLine(reportedLineNum, ATTR_TABLE_JSON);
@@ -1338,11 +1314,11 @@ exports.aceKeyEvent = (h, ctx) => {
1338
1314
  if (tableInDOM) {
1339
1315
  const domTblId = tableInDOM.getAttribute('data-tblId');
1340
1316
  const domRow = tableInDOM.getAttribute('data-row');
1341
- // log(`${logPrefix} Fallback: Found table in DOM without attribute! TblId=${domTblId}, Row=${domRow}`);
1317
+ // log(`${logPrefix} Fallback: Found table in DOM without attribute! TblId=${domTblId}, Row=${domRow}`);
1342
1318
  // Try to reconstruct the metadata from DOM
1343
1319
  const domCells = tableInDOM.querySelectorAll('td');
1344
1320
  if (domTblId && domRow !== null && domCells.length > 0) {
1345
- // log(`${logPrefix} Fallback: Attempting to reconstruct metadata from DOM...`);
1321
+ // log(`${logPrefix} Fallback: Attempting to reconstruct metadata from DOM...`);
1346
1322
  const reconstructedMetadata = {
1347
1323
  tblId: domTblId,
1348
1324
  row: parseInt(domRow, 10),
@@ -1350,31 +1326,31 @@ exports.aceKeyEvent = (h, ctx) => {
1350
1326
  };
1351
1327
  lineAttrString = JSON.stringify(reconstructedMetadata);
1352
1328
  tableMetadata = reconstructedMetadata;
1353
- // log(`${logPrefix} Fallback: Reconstructed metadata: ${lineAttrString}`);
1329
+ // log(`${logPrefix} Fallback: Reconstructed metadata: ${lineAttrString}`);
1354
1330
  }
1355
1331
  }
1356
1332
  }
1357
1333
  } catch(e) {
1358
- // log(`${logPrefix} Fallback: Error checking DOM for table:`, e);
1334
+ // log(`${logPrefix} Fallback: Error checking DOM for table:`, e);
1359
1335
  }
1360
1336
  }
1361
1337
  } catch(e) { tableMetadata = null; } // Ignore errors here, handled below
1362
1338
 
1363
1339
  if (!tableMetadata) {
1364
- // log(`${logPrefix} Fallback: Reported line ${reportedLineNum} is not a valid table line. Allowing default.`);
1340
+ // log(`${logPrefix} Fallback: Reported line ${reportedLineNum} is not a valid table line. Allowing default.`);
1365
1341
  return false;
1366
1342
  }
1367
1343
 
1368
1344
  currentLineNum = reportedLineNum;
1369
1345
  metadataForTargetLine = tableMetadata;
1370
- // log(`${logPrefix} Fallback: Processing based on reported line ${currentLineNum}.`);
1346
+ // log(`${logPrefix} Fallback: Processing based on reported line ${currentLineNum}.`);
1371
1347
 
1372
1348
  lineText = rep.lines.atIndex(currentLineNum)?.text || '';
1373
1349
  cellTexts = lineText.split(DELIMITER);
1374
- // log(`${logPrefix} Fallback: Fetched text for reported line ${currentLineNum}: "${lineText}"`);
1350
+ // log(`${logPrefix} Fallback: Fetched text for reported line ${currentLineNum}: "${lineText}"`);
1375
1351
 
1376
1352
  if (cellTexts.length !== metadataForTargetLine.cols) {
1377
- // log(`${logPrefix} WARNING (Fallback): Cell count mismatch for reported line ${currentLineNum}.`);
1353
+ // log(`${logPrefix} WARNING (Fallback): Cell count mismatch for reported line ${currentLineNum}.`);
1378
1354
  }
1379
1355
 
1380
1356
  // Calculate target cell based on reportedCol
@@ -1388,7 +1364,7 @@ exports.aceKeyEvent = (h, ctx) => {
1388
1364
  relativeCaretPos = reportedCol - currentOffset;
1389
1365
  cellStartCol = currentOffset;
1390
1366
  precedingCellsOffset = cellStartCol;
1391
- // log(`${logPrefix} --> (Fallback Calc) Found target cell ${foundIndex}. RelativePos: ${relativeCaretPos}.`);
1367
+ // log(`${logPrefix} --> (Fallback Calc) Found target cell ${foundIndex}. RelativePos: ${relativeCaretPos}.`);
1392
1368
  break;
1393
1369
  }
1394
1370
  if (i < cellTexts.length - 1 && reportedCol === cellEndCol + DELIMITER.length) {
@@ -1396,7 +1372,7 @@ exports.aceKeyEvent = (h, ctx) => {
1396
1372
  relativeCaretPos = 0;
1397
1373
  cellStartCol = currentOffset + cellLength + DELIMITER.length;
1398
1374
  precedingCellsOffset = cellStartCol;
1399
- // log(`${logPrefix} --> (Fallback Calc) Caret at delimiter AFTER cell ${i}. Treating as start of cell ${foundIndex}.`);
1375
+ // log(`${logPrefix} --> (Fallback Calc) Caret at delimiter AFTER cell ${i}. Treating as start of cell ${foundIndex}.`);
1400
1376
  break;
1401
1377
  }
1402
1378
  currentOffset += cellLength + DELIMITER.length;
@@ -1409,9 +1385,9 @@ exports.aceKeyEvent = (h, ctx) => {
1409
1385
  for (let i = 0; i < foundIndex; i++) { cellStartCol += (cellTexts[i]?.length ?? 0) + DELIMITER.length; }
1410
1386
  precedingCellsOffset = cellStartCol;
1411
1387
  relativeCaretPos = cellTexts[foundIndex]?.length ?? 0;
1412
- // log(`${logPrefix} --> (Fallback Calc) Caret detected at END of last cell (${foundIndex}).`);
1388
+ // log(`${logPrefix} --> (Fallback Calc) Caret detected at END of last cell (${foundIndex}).`);
1413
1389
  } else {
1414
- // log(`${logPrefix} (Fallback Calc) FAILED to determine target cell for caret col ${reportedCol}. Allowing default handling.`);
1390
+ // log(`${logPrefix} (Fallback Calc) FAILED to determine target cell for caret col ${reportedCol}. Allowing default handling.`);
1415
1391
  return false;
1416
1392
  }
1417
1393
  }
@@ -1420,12 +1396,12 @@ exports.aceKeyEvent = (h, ctx) => {
1420
1396
 
1421
1397
  // --- Final Validation ---
1422
1398
  if (currentLineNum < 0 || targetCellIndex < 0 || !metadataForTargetLine || targetCellIndex >= metadataForTargetLine.cols) {
1423
- // log(`${logPrefix} FAILED final validation: Line=${currentLineNum}, Cell=${targetCellIndex}, Metadata=${!!metadataForTargetLine}. Allowing default.`);
1399
+ // log(`${logPrefix} FAILED final validation: Line=${currentLineNum}, Cell=${targetCellIndex}, Metadata=${!!metadataForTargetLine}. Allowing default.`);
1424
1400
  if (editor) editor.ep_data_tables_last_clicked = null;
1425
1401
  return false;
1426
1402
  }
1427
1403
 
1428
- // log(`${logPrefix} --> Final Target: Line=${currentLineNum}, CellIndex=${targetCellIndex}, RelativePos=${relativeCaretPos}`);
1404
+ // log(`${logPrefix} --> Final Target: Line=${currentLineNum}, CellIndex=${targetCellIndex}, RelativePos=${relativeCaretPos}`);
1429
1405
  // --- End Cell/Position Determination ---
1430
1406
 
1431
1407
  // --- START NEW: Handle Highlight Deletion/Replacement ---
@@ -1434,11 +1410,11 @@ exports.aceKeyEvent = (h, ctx) => {
1434
1410
  const hasSelection = selStartActual[0] !== selEndActual[0] || selStartActual[1] !== selEndActual[1];
1435
1411
 
1436
1412
  if (hasSelection) {
1437
- // log(`${logPrefix} [selection] Active selection detected. Start:[${selStartActual[0]},${selStartActual[1]}], End:[${selEndActual[0]},${selEndActual[1]}]`);
1438
- // log(`${logPrefix} [caretTrace] [selection] Initial rep.selStart: Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
1413
+ // log(`${logPrefix} [selection] Active selection detected. Start:[${selStartActual[0]},${selStartActual[1]}], End:[${selEndActual[0]},${selEndActual[1]}]`);
1414
+ // log(`${logPrefix} [caretTrace] [selection] Initial rep.selStart: Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
1439
1415
 
1440
1416
  if (selStartActual[0] !== currentLineNum || selEndActual[0] !== currentLineNum) {
1441
- // log(`${logPrefix} [selection] Selection spans multiple lines (${selStartActual[0]}-${selEndActual[0]}) or is not on the current focused table line (${currentLineNum}). Preventing default action.`);
1417
+ // log(`${logPrefix} [selection] Selection spans multiple lines (${selStartActual[0]}-${selEndActual[0]}) or is not on the current focused table line (${currentLineNum}). Preventing default action.`);
1442
1418
  evt.preventDefault();
1443
1419
  return true;
1444
1420
  }
@@ -1486,7 +1462,7 @@ exports.aceKeyEvent = (h, ctx) => {
1486
1462
  selectionEndColInLine = cellContentEndColInLine;
1487
1463
  }
1488
1464
 
1489
- // log(`${logPrefix} [selection] Cell context for selection: targetCellIndex=${targetCellIndex}, cellStartColInLine=${cellContentStartColInLine}, cellEndColInLine=${cellContentEndColInLine}, currentCellFullText='${currentCellFullText}'`);
1465
+ // log(`${logPrefix} [selection] Cell context for selection: targetCellIndex=${targetCellIndex}, cellStartColInLine=${cellContentStartColInLine}, cellEndColInLine=${cellContentEndColInLine}, currentCellFullText='${currentCellFullText}'`);
1490
1466
 
1491
1467
  const isSelectionEntirelyWithinCell =
1492
1468
  selectionStartColInLine >= cellContentStartColInLine &&
@@ -1518,10 +1494,10 @@ exports.aceKeyEvent = (h, ctx) => {
1518
1494
 
1519
1495
 
1520
1496
  if (isSelectionEntirelyWithinCell && (isCurrentKeyDelete || isCurrentKeyBackspace || isCurrentKeyTyping)) {
1521
- // log(`${logPrefix} [selection] Handling key='${evt.key}' (Type: ${evt.type}) for valid intra-cell selection.`);
1497
+ // log(`${logPrefix} [selection] Handling key='${evt.key}' (Type: ${evt.type}) for valid intra-cell selection.`);
1522
1498
 
1523
1499
  if (evt.type !== 'keydown') {
1524
- // log(`${logPrefix} [selection] Ignoring non-keydown event type ('${evt.type}') for selection handling. Allowing default.`);
1500
+ // log(`${logPrefix} [selection] Ignoring non-keydown event type ('${evt.type}') for selection handling. Allowing default.`);
1525
1501
  return false;
1526
1502
  }
1527
1503
  evt.preventDefault();
@@ -1531,20 +1507,20 @@ exports.aceKeyEvent = (h, ctx) => {
1531
1507
  let replacementText = '';
1532
1508
  let newAbsoluteCaretCol = selectionStartColInLine;
1533
1509
  const repBeforeEdit = editorInfo.ace_getRep(); // Get rep before edit for attribute helper
1534
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart before ace_performDocumentReplaceRange: Line=${repBeforeEdit.selStart[0]}, Col=${repBeforeEdit.selStart[1]}`);
1510
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart before ace_performDocumentReplaceRange: Line=${repBeforeEdit.selStart[0]}, Col=${repBeforeEdit.selStart[1]}`);
1535
1511
 
1536
1512
  if (isCurrentKeyTyping) {
1537
1513
  replacementText = evt.key;
1538
1514
  newAbsoluteCaretCol = selectionStartColInLine + replacementText.length;
1539
- // log(`${logPrefix} [selection] -> Replacing selected range [[${rangeStart[0]},${rangeStart[1]}],[${rangeEnd[0]},${rangeEnd[1]}]] with text '${replacementText}'`);
1515
+ // log(`${logPrefix} [selection] -> Replacing selected range [[${rangeStart[0]},${rangeStart[1]}],[${rangeEnd[0]},${rangeEnd[1]}]] with text '${replacementText}'`);
1540
1516
  } else { // Delete or Backspace
1541
- // log(`${logPrefix} [selection] -> Deleting selected range [[${rangeStart[0]},${rangeStart[1]}],[${rangeEnd[0]},${rangeEnd[1]}]]`);
1517
+ // log(`${logPrefix} [selection] -> Deleting selected range [[${rangeStart[0]},${rangeStart[1]}],[${rangeEnd[0]},${rangeEnd[1]}]]`);
1542
1518
  // If whole cell is being wiped, keep a single space so cell isn't empty
1543
1519
  const isWholeCell = selectionStartColInLine <= cellContentStartColInLine && selectionEndColInLine >= cellContentEndColInLine;
1544
1520
  if (isWholeCell) {
1545
1521
  replacementText = ' ';
1546
1522
  newAbsoluteCaretCol = selectionStartColInLine + 1;
1547
- // log(`${logPrefix} [selection] Whole cell cleared – inserting single space to preserve caret/author span.`);
1523
+ // log(`${logPrefix} [selection] Whole cell cleared – inserting single space to preserve caret/author span.`);
1548
1524
  }
1549
1525
  }
1550
1526
 
@@ -1563,44 +1539,44 @@ exports.aceKeyEvent = (h, ctx) => {
1563
1539
  );
1564
1540
  }
1565
1541
  const repAfterReplace = editorInfo.ace_getRep();
1566
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performDocumentReplaceRange: Line=${repAfterReplace.selStart[0]}, Col=${repAfterReplace.selStart[1]}`);
1542
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performDocumentReplaceRange: Line=${repAfterReplace.selStart[0]}, Col=${repAfterReplace.selStart[1]}`);
1567
1543
 
1568
1544
 
1569
- // log(`${logPrefix} [selection] -> Re-applying tbljson line attribute...`);
1545
+ // log(`${logPrefix} [selection] -> Re-applying tbljson line attribute...`);
1570
1546
  const applyHelper = editorInfo.ep_data_tables_applyMeta;
1571
1547
  if (applyHelper && typeof applyHelper === 'function' && repBeforeEdit) {
1572
1548
  const attrStringToApply = (trustedLastClick || reportedLineNum === currentLineNum) ? lineAttrString : null;
1573
1549
  applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, repBeforeEdit, editorInfo, attrStringToApply, docManager);
1574
- // log(`${logPrefix} [selection] -> tbljson line attribute re-applied (using rep before edit).`);
1550
+ // log(`${logPrefix} [selection] -> tbljson line attribute re-applied (using rep before edit).`);
1575
1551
  } else {
1576
1552
  console.error(`${logPrefix} [selection] -> FAILED to re-apply tbljson attribute (helper or repBeforeEdit missing).`);
1577
1553
  const currentRepFallback = editorInfo.ace_getRep();
1578
1554
  if (applyHelper && typeof applyHelper === 'function' && currentRepFallback) {
1579
- // log(`${logPrefix} [selection] -> Retrying attribute application with current rep...`);
1555
+ // log(`${logPrefix} [selection] -> Retrying attribute application with current rep...`);
1580
1556
  applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, currentRepFallback, editorInfo, null, docManager);
1581
- // log(`${logPrefix} [selection] -> tbljson line attribute re-applied (using current rep fallback).`);
1557
+ // log(`${logPrefix} [selection] -> tbljson line attribute re-applied (using current rep fallback).`);
1582
1558
  } else {
1583
1559
  console.error(`${logPrefix} [selection] -> FAILED to re-apply tbljson attribute even with fallback rep.`);
1584
1560
  }
1585
1561
  }
1586
1562
 
1587
- // log(`${logPrefix} [selection] -> Setting selection/caret to: [${currentLineNum}, ${newAbsoluteCaretCol}]`);
1588
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart before ace_performSelectionChange: Line=${editorInfo.ace_getRep().selStart[0]}, Col=${editorInfo.ace_getRep().selStart[1]}`);
1563
+ // log(`${logPrefix} [selection] -> Setting selection/caret to: [${currentLineNum}, ${newAbsoluteCaretCol}]`);
1564
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart before ace_performSelectionChange: Line=${editorInfo.ace_getRep().selStart[0]}, Col=${editorInfo.ace_getRep().selStart[1]}`);
1589
1565
  editorInfo.ace_performSelectionChange([currentLineNum, newAbsoluteCaretCol], [currentLineNum, newAbsoluteCaretCol], false);
1590
1566
  const repAfterSelectionChange = editorInfo.ace_getRep();
1591
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performSelectionChange: Line=${repAfterSelectionChange.selStart[0]}, Col=${repAfterSelectionChange.selStart[1]}`);
1567
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performSelectionChange: Line=${repAfterSelectionChange.selStart[0]}, Col=${repAfterSelectionChange.selStart[1]}`);
1592
1568
 
1593
1569
  // Add sync hint AFTER setting selection
1594
1570
  editorInfo.ace_fastIncorp(1);
1595
1571
  const repAfterFastIncorp = editorInfo.ace_getRep();
1596
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_fastIncorp: Line=${repAfterFastIncorp.selStart[0]}, Col=${repAfterFastIncorp.selStart[1]}`);
1597
- // log(`${logPrefix} [selection] -> Requested sync hint (fastIncorp 1).`);
1572
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_fastIncorp: Line=${repAfterFastIncorp.selStart[0]}, Col=${repAfterFastIncorp.selStart[1]}`);
1573
+ // log(`${logPrefix} [selection] -> Requested sync hint (fastIncorp 1).`);
1598
1574
 
1599
1575
  // --- Re-assert selection ---
1600
- // log(`${logPrefix} [caretTrace] [selection] Attempting to re-assert selection post-fastIncorp to [${currentLineNum}, ${newAbsoluteCaretCol}]`);
1576
+ // log(`${logPrefix} [caretTrace] [selection] Attempting to re-assert selection post-fastIncorp to [${currentLineNum}, ${newAbsoluteCaretCol}]`);
1601
1577
  editorInfo.ace_performSelectionChange([currentLineNum, newAbsoluteCaretCol], [currentLineNum, newAbsoluteCaretCol], false);
1602
1578
  const repAfterReassert = editorInfo.ace_getRep();
1603
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after re-asserting selection: Line=${repAfterReassert.selStart[0]}, Col=${repAfterReassert.selStart[1]}`);
1579
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after re-asserting selection: Line=${repAfterReassert.selStart[0]}, Col=${repAfterReassert.selStart[1]}`);
1604
1580
 
1605
1581
  const newRelativePos = newAbsoluteCaretCol - cellStartCol;
1606
1582
  if (editor) {
@@ -1610,15 +1586,15 @@ exports.aceKeyEvent = (h, ctx) => {
1610
1586
  cellIndex: targetCellIndex,
1611
1587
  relativePos: newRelativePos < 0 ? 0 : newRelativePos
1612
1588
  };
1613
- // log(`${logPrefix} [selection] -> Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
1589
+ // log(`${logPrefix} [selection] -> Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
1614
1590
  } else {
1615
- // log(`${logPrefix} [selection] -> Editor instance not found, cannot update ep_data_tables_last_clicked.`);
1591
+ // log(`${logPrefix} [selection] -> Editor instance not found, cannot update ep_data_tables_last_clicked.`);
1616
1592
  }
1617
1593
 
1618
- // log(`${logPrefix} END [selection] (Handled highlight modification) Key='${evt.key}' Type='${evt.type}'. Duration: ${Date.now() - startLogTime}ms`);
1594
+ // log(`${logPrefix} END [selection] (Handled highlight modification) Key='${evt.key}' Type='${evt.type}'. Duration: ${Date.now() - startLogTime}ms`);
1619
1595
  return true;
1620
1596
  } catch (error) {
1621
- // log(`${logPrefix} [selection] ERROR during highlight modification:`, error);
1597
+ // log(`${logPrefix} [selection] ERROR during highlight modification:`, error);
1622
1598
  console.error('[ep_data_tables] Error processing highlight modification:', error);
1623
1599
  return true; // Still return true as we prevented default.
1624
1600
  }
@@ -1629,12 +1605,12 @@ exports.aceKeyEvent = (h, ctx) => {
1629
1605
  // --- Check for Ctrl+X (Cut) key combination ---
1630
1606
  const isCutKey = (evt.ctrlKey || evt.metaKey) && (evt.key === 'x' || evt.key === 'X' || evt.keyCode === 88);
1631
1607
  if (isCutKey && hasSelection) {
1632
- log(`${logPrefix} Ctrl+X (Cut) detected with selection. Letting cut event handler manage this.`);
1608
+ // log(`${logPrefix} Ctrl+X (Cut) detected with selection. Letting cut event handler manage this.`);
1633
1609
  // Let the cut event handler handle this - we don't need to preventDefault here
1634
1610
  // as the cut event will handle the operation and prevent default
1635
1611
  return false; // Allow the cut event to be triggered
1636
1612
  } else if (isCutKey && !hasSelection) {
1637
- log(`${logPrefix} Ctrl+X (Cut) detected but no selection. Allowing default.`);
1613
+ // log(`${logPrefix} Ctrl+X (Cut) detected but no selection. Allowing default.`);
1638
1614
  return false; // Allow default - nothing to cut
1639
1615
  }
1640
1616
 
@@ -1645,7 +1621,7 @@ exports.aceKeyEvent = (h, ctx) => {
1645
1621
  const isNavigationKey = [33, 34, 35, 36, 37, 38, 39, 40].includes(evt.keyCode);
1646
1622
  const isTabKey = evt.key === 'Tab';
1647
1623
  const isEnterKey = evt.key === 'Enter';
1648
- // log(`${logPrefix} Key classification: Typing=${isTypingKey}, Backspace=${isBackspaceKey}, Delete=${isDeleteKey}, Nav=${isNavigationKey}, Tab=${isTabKey}, Enter=${isEnterKey}, Cut=${isCutKey}`);
1624
+ // log(`${logPrefix} Key classification: Typing=${isTypingKey}, Backspace=${isBackspaceKey}, Delete=${isDeleteKey}, Nav=${isNavigationKey}, Tab=${isTabKey}, Enter=${isEnterKey}, Cut=${isCutKey}`);
1649
1625
 
1650
1626
  /*
1651
1627
  * Prevent caret placement *after* the invisible caret-anchor.
@@ -1660,7 +1636,7 @@ exports.aceKeyEvent = (h, ctx) => {
1660
1636
  if (evt.type === 'keydown' && !evt.ctrlKey && !evt.metaKey && !evt.altKey) {
1661
1637
  // Right-arrow – if at the very end of a cell, move to the next cell.
1662
1638
  if (evt.keyCode === 39 && relativeCaretPos >= currentCellTextLengthEarly && targetCellIndex < metadataForTargetLine.cols - 1) {
1663
- // log(`${logPrefix} ArrowRight at cell boundary – navigating to next cell to avoid anchor zone.`);
1639
+ // log(`${logPrefix} ArrowRight at cell boundary – navigating to next cell to avoid anchor zone.`);
1664
1640
  evt.preventDefault();
1665
1641
  navigateToNextCell(currentLineNum, targetCellIndex, metadataForTargetLine, false, editorInfo, docManager);
1666
1642
  return true;
@@ -1668,7 +1644,7 @@ exports.aceKeyEvent = (h, ctx) => {
1668
1644
 
1669
1645
  // Left-arrow – if at the very start of a cell, move to the previous cell.
1670
1646
  if (evt.keyCode === 37 && relativeCaretPos === 0 && targetCellIndex > 0) {
1671
- // log(`${logPrefix} ArrowLeft at cell boundary – navigating to previous cell to avoid anchor zone.`);
1647
+ // log(`${logPrefix} ArrowLeft at cell boundary – navigating to previous cell to avoid anchor zone.`);
1672
1648
  evt.preventDefault();
1673
1649
  navigateToNextCell(currentLineNum, targetCellIndex, metadataForTargetLine, true, editorInfo, docManager);
1674
1650
  return true;
@@ -1679,45 +1655,45 @@ exports.aceKeyEvent = (h, ctx) => {
1679
1655
 
1680
1656
  // 1. Allow non-Tab navigation keys immediately
1681
1657
  if (isNavigationKey && !isTabKey) {
1682
- // log(`${logPrefix} Allowing navigation key: ${evt.key}. Clearing click state.`);
1658
+ // log(`${logPrefix} Allowing navigation key: ${evt.key}. Clearing click state.`);
1683
1659
  if (editor) editor.ep_data_tables_last_clicked = null; // Clear state on navigation
1684
1660
  return false;
1685
1661
  }
1686
1662
 
1687
1663
  // 2. Handle Tab - Navigate to next cell (only on keydown to avoid double navigation)
1688
1664
  if (isTabKey) {
1689
- // log(`${logPrefix} Tab key pressed. Event type: ${evt.type}`);
1665
+ // log(`${logPrefix} Tab key pressed. Event type: ${evt.type}`);
1690
1666
  evt.preventDefault();
1691
1667
 
1692
1668
  // Only process keydown events for navigation to avoid double navigation
1693
1669
  if (evt.type !== 'keydown') {
1694
- // log(`${logPrefix} Ignoring Tab ${evt.type} event to prevent double navigation.`);
1670
+ // log(`${logPrefix} Ignoring Tab ${evt.type} event to prevent double navigation.`);
1695
1671
  return true;
1696
1672
  }
1697
1673
 
1698
- // log(`${logPrefix} Processing Tab keydown - implementing cell navigation.`);
1674
+ // log(`${logPrefix} Processing Tab keydown - implementing cell navigation.`);
1699
1675
  const success = navigateToNextCell(currentLineNum, targetCellIndex, metadataForTargetLine, evt.shiftKey, editorInfo, docManager);
1700
1676
  if (!success) {
1701
- // log(`${logPrefix} Tab navigation failed, cell navigation not possible.`);
1677
+ // log(`${logPrefix} Tab navigation failed, cell navigation not possible.`);
1702
1678
  }
1703
1679
  return true;
1704
1680
  }
1705
1681
 
1706
1682
  // 3. Handle Enter - Navigate to cell below (only on keydown to avoid double navigation)
1707
1683
  if (isEnterKey) {
1708
- // log(`${logPrefix} Enter key pressed. Event type: ${evt.type}`);
1684
+ // log(`${logPrefix} Enter key pressed. Event type: ${evt.type}`);
1709
1685
  evt.preventDefault();
1710
1686
 
1711
1687
  // Only process keydown events for navigation to avoid double navigation
1712
1688
  if (evt.type !== 'keydown') {
1713
- // log(`${logPrefix} Ignoring Enter ${evt.type} event to prevent double navigation.`);
1689
+ // log(`${logPrefix} Ignoring Enter ${evt.type} event to prevent double navigation.`);
1714
1690
  return true;
1715
1691
  }
1716
1692
 
1717
- // log(`${logPrefix} Processing Enter keydown - implementing cell navigation.`);
1693
+ // log(`${logPrefix} Processing Enter keydown - implementing cell navigation.`);
1718
1694
  const success = navigateToCellBelow(currentLineNum, targetCellIndex, metadataForTargetLine, editorInfo, docManager);
1719
1695
  if (!success) {
1720
- // log(`${logPrefix} Enter navigation failed, cell navigation not possible.`);
1696
+ // log(`${logPrefix} Enter navigation failed, cell navigation not possible.`);
1721
1697
  }
1722
1698
  return true;
1723
1699
  }
@@ -1726,25 +1702,25 @@ exports.aceKeyEvent = (h, ctx) => {
1726
1702
  const currentCellTextLength = cellTexts[targetCellIndex]?.length ?? 0;
1727
1703
  // Backspace at the very beginning of cell > 0
1728
1704
  if (isBackspaceKey && relativeCaretPos === 0 && targetCellIndex > 0) {
1729
- // log(`${logPrefix} Intercepted Backspace at start of cell ${targetCellIndex}. Preventing default.`);
1705
+ // log(`${logPrefix} Intercepted Backspace at start of cell ${targetCellIndex}. Preventing default.`);
1730
1706
  evt.preventDefault();
1731
1707
  return true;
1732
1708
  }
1733
1709
  // NEW: Backspace at very beginning of first cell – would merge with previous line
1734
1710
  if (isBackspaceKey && relativeCaretPos === 0 && targetCellIndex === 0) {
1735
- // log(`${logPrefix} Intercepted Backspace at start of first cell (line boundary). Preventing merge.`);
1711
+ // log(`${logPrefix} Intercepted Backspace at start of first cell (line boundary). Preventing merge.`);
1736
1712
  evt.preventDefault();
1737
1713
  return true;
1738
1714
  }
1739
1715
  // Delete at the very end of cell < last cell
1740
1716
  if (isDeleteKey && relativeCaretPos === currentCellTextLength && targetCellIndex < metadataForTargetLine.cols - 1) {
1741
- // log(`${logPrefix} Intercepted Delete at end of cell ${targetCellIndex}. Preventing default.`);
1717
+ // log(`${logPrefix} Intercepted Delete at end of cell ${targetCellIndex}. Preventing default.`);
1742
1718
  evt.preventDefault();
1743
1719
  return true;
1744
1720
  }
1745
1721
  // NEW: Delete at very end of last cell – would merge with next line
1746
1722
  if (isDeleteKey && relativeCaretPos === currentCellTextLength && targetCellIndex === metadataForTargetLine.cols - 1) {
1747
- // log(`${logPrefix} Intercepted Delete at end of last cell (line boundary). Preventing merge.`);
1723
+ // log(`${logPrefix} Intercepted Delete at end of last cell (line boundary). Preventing merge.`);
1748
1724
  evt.preventDefault();
1749
1725
  return true;
1750
1726
  }
@@ -1756,7 +1732,7 @@ exports.aceKeyEvent = (h, ctx) => {
1756
1732
  // Guard: internal Backspace at relativePos 1 (would delete delimiter) & Delete at relativePos 0
1757
1733
  if ((isInternalBackspace && relativeCaretPos === 1 && targetCellIndex > 0) ||
1758
1734
  (isInternalDelete && relativeCaretPos === 0 && targetCellIndex > 0)) {
1759
- // log(`${logPrefix} Attempt to erase protected delimiter – operation blocked.`);
1735
+ // log(`${logPrefix} Attempt to erase protected delimiter – operation blocked.`);
1760
1736
  evt.preventDefault();
1761
1737
  return true;
1762
1738
  }
@@ -1764,25 +1740,25 @@ exports.aceKeyEvent = (h, ctx) => {
1764
1740
  if (isTypingKey || isInternalBackspace || isInternalDelete) {
1765
1741
  // --- PREVENT TYPING DIRECTLY AFTER DELIMITER (relativeCaretPos===0) ---
1766
1742
  if (isTypingKey && relativeCaretPos === 0 && targetCellIndex > 0) {
1767
- // log(`${logPrefix} Caret at forbidden position 0 (just after delimiter). Auto-advancing to position 1.`);
1743
+ // log(`${logPrefix} Caret at forbidden position 0 (just after delimiter). Auto-advancing to position 1.`);
1768
1744
  const safePosAbs = cellStartCol + 1;
1769
1745
  editorInfo.ace_performSelectionChange([currentLineNum, safePosAbs], [currentLineNum, safePosAbs], false);
1770
1746
  editorInfo.ace_updateBrowserSelectionFromRep();
1771
1747
  relativeCaretPos = 1;
1772
- // log(`${logPrefix} Caret moved to safe position. New relativeCaretPos=${relativeCaretPos}`);
1748
+ // log(`${logPrefix} Caret moved to safe position. New relativeCaretPos=${relativeCaretPos}`);
1773
1749
  }
1774
1750
  // *** Use the validated currentLineNum and currentCol derived from relativeCaretPos ***
1775
1751
  const currentCol = cellStartCol + relativeCaretPos;
1776
- // log(`${logPrefix} Handling INTERNAL key='${evt.key}' Type='${evt.type}' at Line=${currentLineNum}, Col=${currentCol} (CellIndex=${targetCellIndex}, RelativePos=${relativeCaretPos}).`);
1777
- // log(`${logPrefix} [caretTrace] Initial rep.selStart for internal edit: Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
1752
+ // log(`${logPrefix} Handling INTERNAL key='${evt.key}' Type='${evt.type}' at Line=${currentLineNum}, Col=${currentCol} (CellIndex=${targetCellIndex}, RelativePos=${relativeCaretPos}).`);
1753
+ // log(`${logPrefix} [caretTrace] Initial rep.selStart for internal edit: Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
1778
1754
 
1779
1755
  // Only process keydown events for modifications
1780
1756
  if (evt.type !== 'keydown') {
1781
- // log(`${logPrefix} Ignoring non-keydown event type ('${evt.type}') for handled key.`);
1757
+ // log(`${logPrefix} Ignoring non-keydown event type ('${evt.type}') for handled key.`);
1782
1758
  return false;
1783
1759
  }
1784
1760
 
1785
- // log(`${logPrefix} Preventing default browser action for keydown event.`);
1761
+ // log(`${logPrefix} Preventing default browser action for keydown event.`);
1786
1762
  evt.preventDefault();
1787
1763
 
1788
1764
  let newAbsoluteCaretCol = -1;
@@ -1790,56 +1766,56 @@ exports.aceKeyEvent = (h, ctx) => {
1790
1766
 
1791
1767
  try {
1792
1768
  repBeforeEdit = editorInfo.ace_getRep(); // Get rep *before* making changes
1793
- // log(`${logPrefix} [caretTrace] rep.selStart before ace_performDocumentReplaceRange: Line=${repBeforeEdit.selStart[0]}, Col=${repBeforeEdit.selStart[1]}`);
1769
+ // log(`${logPrefix} [caretTrace] rep.selStart before ace_performDocumentReplaceRange: Line=${repBeforeEdit.selStart[0]}, Col=${repBeforeEdit.selStart[1]}`);
1794
1770
 
1795
1771
  if (isTypingKey) {
1796
1772
  const insertPos = [currentLineNum, currentCol];
1797
- // log(`${logPrefix} -> Inserting text '${evt.key}' at [${insertPos}]`);
1773
+ // log(`${logPrefix} -> Inserting text '${evt.key}' at [${insertPos}]`);
1798
1774
  editorInfo.ace_performDocumentReplaceRange(insertPos, insertPos, evt.key);
1799
1775
  newAbsoluteCaretCol = currentCol + 1;
1800
1776
 
1801
1777
  } else if (isInternalBackspace) {
1802
1778
  const delRangeStart = [currentLineNum, currentCol - 1];
1803
1779
  const delRangeEnd = [currentLineNum, currentCol];
1804
- // log(`${logPrefix} -> Deleting (Backspace) range [${delRangeStart}]-[${delRangeEnd}]`);
1780
+ // log(`${logPrefix} -> Deleting (Backspace) range [${delRangeStart}]-[${delRangeEnd}]`);
1805
1781
  editorInfo.ace_performDocumentReplaceRange(delRangeStart, delRangeEnd, '');
1806
1782
  newAbsoluteCaretCol = currentCol - 1;
1807
1783
 
1808
1784
  } else if (isInternalDelete) {
1809
1785
  const delRangeStart = [currentLineNum, currentCol];
1810
1786
  const delRangeEnd = [currentLineNum, currentCol + 1];
1811
- // log(`${logPrefix} -> Deleting (Delete) range [${delRangeStart}]-[${delRangeEnd}]`);
1787
+ // log(`${logPrefix} -> Deleting (Delete) range [${delRangeStart}]-[${delRangeEnd}]`);
1812
1788
  editorInfo.ace_performDocumentReplaceRange(delRangeStart, delRangeEnd, '');
1813
1789
  newAbsoluteCaretCol = currentCol; // Caret stays at the same column for delete
1814
1790
  }
1815
1791
  const repAfterReplace = editorInfo.ace_getRep();
1816
- // log(`${logPrefix} [caretTrace] rep.selStart after ace_performDocumentReplaceRange: Line=${repAfterReplace.selStart[0]}, Col=${repAfterReplace.selStart[1]}`);
1792
+ // log(`${logPrefix} [caretTrace] rep.selStart after ace_performDocumentReplaceRange: Line=${repAfterReplace.selStart[0]}, Col=${repAfterReplace.selStart[1]}`);
1817
1793
 
1818
1794
 
1819
1795
  // *** CRITICAL: Re-apply the line attribute after ANY modification ***
1820
- // log(`${logPrefix} -> Re-applying tbljson line attribute...`);
1796
+ // log(`${logPrefix} -> Re-applying tbljson line attribute...`);
1821
1797
 
1822
1798
  // DEBUG: Log the values before calculating attrStringToApply
1823
- // log(`${logPrefix} DEBUG: Before calculating attrStringToApply - trustedLastClick=${trustedLastClick}, reportedLineNum=${reportedLineNum}, currentLineNum=${currentLineNum}`);
1824
- // log(`${logPrefix} DEBUG: lineAttrString value:`, lineAttrString ? `"${lineAttrString}"` : 'null/undefined');
1799
+ // log(`${logPrefix} DEBUG: Before calculating attrStringToApply - trustedLastClick=${trustedLastClick}, reportedLineNum=${reportedLineNum}, currentLineNum=${currentLineNum}`);
1800
+ // log(`${logPrefix} DEBUG: lineAttrString value:`, lineAttrString ? `"${lineAttrString}"` : 'null/undefined');
1825
1801
 
1826
1802
  const applyHelper = editorInfo.ep_data_tables_applyMeta;
1827
1803
  if (applyHelper && typeof applyHelper === 'function' && repBeforeEdit) {
1828
1804
  // Pass the original lineAttrString if available AND if it belongs to the currentLineNum
1829
1805
  const attrStringToApply = (trustedLastClick || reportedLineNum === currentLineNum) ? lineAttrString : null;
1830
1806
 
1831
- // log(`${logPrefix} DEBUG: Calculated attrStringToApply:`, attrStringToApply ? `"${attrStringToApply}"` : 'null/undefined');
1832
- // log(`${logPrefix} DEBUG: Condition result: (${trustedLastClick} || ${reportedLineNum} === ${currentLineNum}) = ${trustedLastClick || reportedLineNum === currentLineNum}`);
1807
+ // log(`${logPrefix} DEBUG: Calculated attrStringToApply:`, attrStringToApply ? `"${attrStringToApply}"` : 'null/undefined');
1808
+ // log(`${logPrefix} DEBUG: Condition result: (${trustedLastClick} || ${reportedLineNum} === ${currentLineNum}) = ${trustedLastClick || reportedLineNum === currentLineNum}`);
1833
1809
 
1834
1810
  applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, repBeforeEdit, editorInfo, attrStringToApply, docManager);
1835
- // log(`${logPrefix} -> tbljson line attribute re-applied (using rep before edit).`);
1811
+ // log(`${logPrefix} -> tbljson line attribute re-applied (using rep before edit).`);
1836
1812
  } else {
1837
1813
  console.error(`${logPrefix} -> FAILED to re-apply tbljson attribute (helper or repBeforeEdit missing).`);
1838
1814
  const currentRepFallback = editorInfo.ace_getRep();
1839
1815
  if (applyHelper && typeof applyHelper === 'function' && currentRepFallback) {
1840
- // log(`${logPrefix} -> Retrying attribute application with current rep...`);
1816
+ // log(`${logPrefix} -> Retrying attribute application with current rep...`);
1841
1817
  applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, currentRepFallback, editorInfo, null, docManager); // Cannot guarantee old attr string is valid here
1842
- // log(`${logPrefix} -> tbljson line attribute re-applied (using current rep fallback).`);
1818
+ // log(`${logPrefix} -> tbljson line attribute re-applied (using current rep fallback).`);
1843
1819
  } else {
1844
1820
  console.error(`${logPrefix} -> FAILED to re-apply tbljson attribute even with fallback rep.`);
1845
1821
  }
@@ -1848,26 +1824,26 @@ exports.aceKeyEvent = (h, ctx) => {
1848
1824
  // Set caret position immediately
1849
1825
  if (newAbsoluteCaretCol >= 0) {
1850
1826
  const newCaretPos = [currentLineNum, newAbsoluteCaretCol]; // Use the trusted currentLineNum
1851
- // log(`${logPrefix} -> Setting selection immediately to:`, newCaretPos);
1852
- // log(`${logPrefix} [caretTrace] rep.selStart before ace_performSelectionChange: Line=${editorInfo.ace_getRep().selStart[0]}, Col=${editorInfo.ace_getRep().selStart[1]}`);
1827
+ // log(`${logPrefix} -> Setting selection immediately to:`, newCaretPos);
1828
+ // log(`${logPrefix} [caretTrace] rep.selStart before ace_performSelectionChange: Line=${editorInfo.ace_getRep().selStart[0]}, Col=${editorInfo.ace_getRep().selStart[1]}`);
1853
1829
  try {
1854
1830
  editorInfo.ace_performSelectionChange(newCaretPos, newCaretPos, false);
1855
1831
  const repAfterSelectionChange = editorInfo.ace_getRep();
1856
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performSelectionChange: Line=${repAfterSelectionChange.selStart[0]}, Col=${repAfterSelectionChange.selStart[1]}`);
1857
- // log(`${logPrefix} -> Selection set immediately.`);
1832
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performSelectionChange: Line=${repAfterSelectionChange.selStart[0]}, Col=${repAfterSelectionChange.selStart[1]}`);
1833
+ // log(`${logPrefix} -> Selection set immediately.`);
1858
1834
 
1859
1835
  // Add sync hint AFTER setting selection
1860
1836
  editorInfo.ace_fastIncorp(1);
1861
1837
  const repAfterFastIncorp = editorInfo.ace_getRep();
1862
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_fastIncorp: Line=${repAfterFastIncorp.selStart[0]}, Col=${repAfterFastIncorp.selStart[1]}`);
1863
- // log(`${logPrefix} -> Requested sync hint (fastIncorp 1).`);
1838
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_fastIncorp: Line=${repAfterFastIncorp.selStart[0]}, Col=${repAfterFastIncorp.selStart[1]}`);
1839
+ // log(`${logPrefix} -> Requested sync hint (fastIncorp 1).`);
1864
1840
 
1865
1841
  // --- Re-assert selection ---
1866
1842
  const targetCaretPosForReassert = [currentLineNum, newAbsoluteCaretCol];
1867
- // log(`${logPrefix} [caretTrace] Attempting to re-assert selection post-fastIncorp to [${targetCaretPosForReassert[0]}, ${targetCaretPosForReassert[1]}]`);
1843
+ // log(`${logPrefix} [caretTrace] Attempting to re-assert selection post-fastIncorp to [${targetCaretPosForReassert[0]}, ${targetCaretPosForReassert[1]}]`);
1868
1844
  editorInfo.ace_performSelectionChange(targetCaretPosForReassert, targetCaretPosForReassert, false);
1869
1845
  const repAfterReassert = editorInfo.ace_getRep();
1870
- // log(`${logPrefix} [caretTrace] [selection] rep.selStart after re-asserting selection: Line=${repAfterReassert.selStart[0]}, Col=${repAfterReassert.selStart[1]}`);
1846
+ // log(`${logPrefix} [caretTrace] [selection] rep.selStart after re-asserting selection: Line=${repAfterReassert.selStart[0]}, Col=${repAfterReassert.selStart[1]}`);
1871
1847
 
1872
1848
  // Store the updated caret info for the next event
1873
1849
  const newRelativePos = newAbsoluteCaretCol - cellStartCol;
@@ -1877,19 +1853,19 @@ exports.aceKeyEvent = (h, ctx) => {
1877
1853
  cellIndex: targetCellIndex,
1878
1854
  relativePos: newRelativePos
1879
1855
  };
1880
- // log(`${logPrefix} -> Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
1881
- // log(`${logPrefix} [caretTrace] Updated ep_data_tables_last_clicked. Line=${editor.ep_data_tables_last_clicked.lineNum}, Cell=${editor.ep_data_tables_last_clicked.cellIndex}, RelPos=${editor.ep_data_tables_last_clicked.relativePos}`);
1856
+ // log(`${logPrefix} -> Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
1857
+ // log(`${logPrefix} [caretTrace] Updated ep_data_tables_last_clicked. Line=${editor.ep_data_tables_last_clicked.lineNum}, Cell=${editor.ep_data_tables_last_clicked.cellIndex}, RelPos=${editor.ep_data_tables_last_clicked.relativePos}`);
1882
1858
 
1883
1859
 
1884
1860
  } catch (selError) {
1885
1861
  console.error(`${logPrefix} -> ERROR setting selection immediately:`, selError);
1886
1862
  }
1887
1863
  } else {
1888
- // log(`${logPrefix} -> Warning: newAbsoluteCaretCol not set, skipping selection update.`);
1864
+ // log(`${logPrefix} -> Warning: newAbsoluteCaretCol not set, skipping selection update.`);
1889
1865
  }
1890
1866
 
1891
1867
  } catch (error) {
1892
- // log(`${logPrefix} ERROR during manual key handling:`, error);
1868
+ // log(`${logPrefix} ERROR during manual key handling:`, error);
1893
1869
  console.error('[ep_data_tables] Error processing key event update:', error);
1894
1870
  // Maybe return false to allow default as a fallback on error?
1895
1871
  // For now, return true as we prevented default.
@@ -1897,7 +1873,7 @@ exports.aceKeyEvent = (h, ctx) => {
1897
1873
  }
1898
1874
 
1899
1875
  const endLogTime = Date.now();
1900
- // log(`${logPrefix} END (Handled Internal Edit Manually) Key='${evt.key}' Type='${evt.type}' -> Returned true. Duration: ${endLogTime - startLogTime}ms`);
1876
+ // log(`${logPrefix} END (Handled Internal Edit Manually) Key='${evt.key}' Type='${evt.type}' -> Returned true. Duration: ${endLogTime - startLogTime}ms`);
1901
1877
  return true; // We handled the key event
1902
1878
 
1903
1879
  } // End if(isTypingKey || isInternalBackspace || isInternalDelete)
@@ -1905,90 +1881,90 @@ exports.aceKeyEvent = (h, ctx) => {
1905
1881
 
1906
1882
  // Fallback for any other keys or edge cases not handled above
1907
1883
  const endLogTimeFinal = Date.now();
1908
- // log(`${logPrefix} END (Fell Through / Unhandled Case) Key='${evt.key}' Type='${evt.type}'. Allowing default. Duration: ${endLogTimeFinal - startLogTime}ms`);
1884
+ // log(`${logPrefix} END (Fell Through / Unhandled Case) Key='${evt.key}' Type='${evt.type}'. Allowing default. Duration: ${endLogTimeFinal - startLogTime}ms`);
1909
1885
  // Clear click state if it wasn't handled?
1910
1886
  // if (editor?.ep_data_tables_last_clicked) editor.ep_data_tables_last_clicked = null;
1911
- // log(`${logPrefix} [caretTrace] Final rep.selStart at end of aceKeyEvent (if unhandled): Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
1887
+ // log(`${logPrefix} [caretTrace] Final rep.selStart at end of aceKeyEvent (if unhandled): Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
1912
1888
  return false; // Allow default browser/ACE handling
1913
1889
  };
1914
1890
 
1915
1891
  // ───────────────────── ace init + public helpers ─────────────────────
1916
1892
  exports.aceInitialized = (h, ctx) => {
1917
1893
  const logPrefix = '[ep_data_tables:aceInitialized]';
1918
- // log(`${logPrefix} START`, { hook_name: h, context: ctx });
1894
+ // log(`${logPrefix} START`, { hook_name: h, context: ctx });
1919
1895
  const ed = ctx.editorInfo;
1920
1896
  const docManager = ctx.documentAttributeManager;
1921
1897
 
1922
- // log(`${logPrefix} Attaching ep_data_tables_applyMeta helper to editorInfo.`);
1898
+ // log(`${logPrefix} Attaching ep_data_tables_applyMeta helper to editorInfo.`);
1923
1899
  ed.ep_data_tables_applyMeta = applyTableLineMetadataAttribute;
1924
- // log(`${logPrefix}: Attached applyTableLineMetadataAttribute helper to ed.ep_data_tables_applyMeta successfully.`);
1900
+ // log(`${logPrefix}: Attached applyTableLineMetadataAttribute helper to ed.ep_data_tables_applyMeta successfully.`);
1925
1901
 
1926
1902
  // Store the documentAttributeManager reference for later use
1927
- // log(`${logPrefix} Storing documentAttributeManager reference on editorInfo.`);
1903
+ // log(`${logPrefix} Storing documentAttributeManager reference on editorInfo.`);
1928
1904
  ed.ep_data_tables_docManager = docManager;
1929
- // log(`${logPrefix}: Stored documentAttributeManager reference as ed.ep_data_tables_docManager.`);
1905
+ // log(`${logPrefix}: Stored documentAttributeManager reference as ed.ep_data_tables_docManager.`);
1930
1906
 
1931
1907
  // *** ENHANCED: Paste event listener + Column resize listeners ***
1932
- // log(`${logPrefix} Preparing to attach paste and resize listeners via ace_callWithAce.`);
1908
+ // log(`${logPrefix} Preparing to attach paste and resize listeners via ace_callWithAce.`);
1933
1909
  ed.ace_callWithAce((ace) => {
1934
1910
  const callWithAceLogPrefix = '[ep_data_tables:aceInitialized:callWithAceForListeners]';
1935
- // log(`${callWithAceLogPrefix} Entered ace_callWithAce callback for listeners.`);
1911
+ // log(`${callWithAceLogPrefix} Entered ace_callWithAce callback for listeners.`);
1936
1912
 
1937
1913
  if (!ace || !ace.editor) {
1938
1914
  console.error(`${callWithAceLogPrefix} ERROR: ace or ace.editor is not available. Cannot attach listeners.`);
1939
- // log(`${callWithAceLogPrefix} Aborting listener attachment due to missing ace.editor.`);
1915
+ // log(`${callWithAceLogPrefix} Aborting listener attachment due to missing ace.editor.`);
1940
1916
  return;
1941
1917
  }
1942
1918
  const editor = ace.editor;
1943
- // log(`${callWithAceLogPrefix} ace.editor obtained successfully.`);
1919
+ // log(`${callWithAceLogPrefix} ace.editor obtained successfully.`);
1944
1920
 
1945
1921
  // Store editor reference for later use in table operations
1946
- // log(`${logPrefix} Storing editor reference on editorInfo.`);
1922
+ // log(`${logPrefix} Storing editor reference on editorInfo.`);
1947
1923
  ed.ep_data_tables_editor = editor;
1948
- // log(`${logPrefix}: Stored editor reference as ed.ep_data_tables_editor.`);
1924
+ // log(`${logPrefix}: Stored editor reference as ed.ep_data_tables_editor.`);
1949
1925
 
1950
1926
  // Attempt to find the inner iframe body, similar to ep_image_insert
1951
1927
  let $inner;
1952
1928
  try {
1953
- // log(`${callWithAceLogPrefix} Attempting to find inner iframe body for listener attachment.`);
1929
+ // log(`${callWithAceLogPrefix} Attempting to find inner iframe body for listener attachment.`);
1954
1930
  const $iframeOuter = $('iframe[name="ace_outer"]');
1955
1931
  if ($iframeOuter.length === 0) {
1956
1932
  console.error(`${callWithAceLogPrefix} ERROR: Could not find outer iframe (ace_outer).`);
1957
- // log(`${callWithAceLogPrefix} Failed to find ace_outer.`);
1933
+ // log(`${callWithAceLogPrefix} Failed to find ace_outer.`);
1958
1934
  return;
1959
1935
  }
1960
- // log(`${callWithAceLogPrefix} Found ace_outer:`, $iframeOuter);
1936
+ // log(`${callWithAceLogPrefix} Found ace_outer:`, $iframeOuter);
1961
1937
 
1962
1938
  const $iframeInner = $iframeOuter.contents().find('iframe[name="ace_inner"]');
1963
1939
  if ($iframeInner.length === 0) {
1964
1940
  console.error(`${callWithAceLogPrefix} ERROR: Could not find inner iframe (ace_inner).`);
1965
- // log(`${callWithAceLogPrefix} Failed to find ace_inner within ace_outer.`);
1941
+ // log(`${callWithAceLogPrefix} Failed to find ace_inner within ace_outer.`);
1966
1942
  return;
1967
1943
  }
1968
- // log(`${callWithAceLogPrefix} Found ace_inner:`, $iframeInner);
1944
+ // log(`${callWithAceLogPrefix} Found ace_inner:`, $iframeInner);
1969
1945
 
1970
1946
  const innerDocBody = $iframeInner.contents().find('body');
1971
1947
  if (innerDocBody.length === 0) {
1972
1948
  console.error(`${callWithAceLogPrefix} ERROR: Could not find body element in inner iframe.`);
1973
- // log(`${callWithAceLogPrefix} Failed to find body in ace_inner.`);
1949
+ // log(`${callWithAceLogPrefix} Failed to find body in ace_inner.`);
1974
1950
  return;
1975
1951
  }
1976
1952
  $inner = $(innerDocBody[0]); // Ensure it's a jQuery object of the body itself
1977
- // log(`${callWithAceLogPrefix} Successfully found inner iframe body:`, $inner);
1953
+ // log(`${callWithAceLogPrefix} Successfully found inner iframe body:`, $inner);
1978
1954
  } catch (e) {
1979
1955
  console.error(`${callWithAceLogPrefix} ERROR: Exception while trying to find inner iframe body:`, e);
1980
- // log(`${callWithAceLogPrefix} Exception details:`, { message: e.message, stack: e.stack });
1956
+ // log(`${callWithAceLogPrefix} Exception details:`, { message: e.message, stack: e.stack });
1981
1957
  return;
1982
1958
  }
1983
1959
 
1984
1960
  if (!$inner || $inner.length === 0) {
1985
1961
  console.error(`${callWithAceLogPrefix} ERROR: $inner is not valid after attempting to find iframe body. Cannot attach listeners.`);
1986
- // log(`${callWithAceLogPrefix} $inner is invalid. Aborting.`);
1962
+ // log(`${callWithAceLogPrefix} $inner is invalid. Aborting.`);
1987
1963
  return;
1988
1964
  }
1989
1965
 
1990
1966
  // *** CUT EVENT LISTENER ***
1991
- log(`${callWithAceLogPrefix} Attaching cut event listener to $inner (inner iframe body).`);
1967
+ // log(`${callWithAceLogPrefix} Attaching cut event listener to $inner (inner iframe body).`);
1992
1968
  $inner.on('cut', (evt) => {
1993
1969
  const cutLogPrefix = '[ep_data_tables:cutHandler]';
1994
1970
  console.log(`${cutLogPrefix} CUT EVENT TRIGGERED. Event object:`, evt);
@@ -2194,32 +2170,32 @@ exports.aceInitialized = (h, ctx) => {
2194
2170
  });
2195
2171
 
2196
2172
  // *** BEFOREINPUT EVENT LISTENER FOR CONTEXT-MENU DELETE ***
2197
- // log(`${callWithAceLogPrefix} Attaching beforeinput event listener to $inner (inner iframe body).`);
2173
+ // log(`${callWithAceLogPrefix} Attaching beforeinput event listener to $inner (inner iframe body).`);
2198
2174
  $inner.on('beforeinput', (evt) => {
2199
2175
  const deleteLogPrefix = '[ep_data_tables:beforeinputDeleteHandler]';
2200
- // log(`${deleteLogPrefix} BEFOREINPUT EVENT TRIGGERED. inputType: "${evt.originalEvent.inputType}", event object:`, evt);
2176
+ // log(`${deleteLogPrefix} BEFOREINPUT EVENT TRIGGERED. inputType: "${evt.originalEvent.inputType}", event object:`, evt);
2201
2177
 
2202
2178
  // Only intercept deletion-related input events
2203
2179
  if (!evt.originalEvent.inputType || !evt.originalEvent.inputType.startsWith('delete')) {
2204
- // log(`${deleteLogPrefix} Not a deletion event (inputType: "${evt.originalEvent.inputType}"). Allowing default.`);
2180
+ // log(`${deleteLogPrefix} Not a deletion event (inputType: "${evt.originalEvent.inputType}"). Allowing default.`);
2205
2181
  return; // Allow default for non-delete events
2206
2182
  }
2207
2183
 
2208
- // log(`${deleteLogPrefix} Getting current editor representation (rep).`);
2184
+ // log(`${deleteLogPrefix} Getting current editor representation (rep).`);
2209
2185
  const rep = ed.ace_getRep();
2210
2186
  if (!rep || !rep.selStart) {
2211
- // log(`${deleteLogPrefix} WARNING: Could not get representation or selection. Allowing default delete.`);
2187
+ // log(`${deleteLogPrefix} WARNING: Could not get representation or selection. Allowing default delete.`);
2212
2188
  console.warn(`${deleteLogPrefix} Could not get rep or selStart.`);
2213
2189
  return; // Allow default
2214
2190
  }
2215
- // log(`${deleteLogPrefix} Rep obtained. selStart:`, rep.selStart, `selEnd:`, rep.selEnd);
2191
+ // log(`${deleteLogPrefix} Rep obtained. selStart:`, rep.selStart, `selEnd:`, rep.selEnd);
2216
2192
  const selStart = rep.selStart;
2217
2193
  const selEnd = rep.selEnd;
2218
2194
  const lineNum = selStart[0];
2219
- // log(`${deleteLogPrefix} Current line number: ${lineNum}. Column start: ${selStart[1]}, Column end: ${selEnd[1]}.`);
2195
+ // log(`${deleteLogPrefix} Current line number: ${lineNum}. Column start: ${selStart[1]}, Column end: ${selEnd[1]}.`);
2220
2196
 
2221
2197
  // Android Chrome IME: collapsed backspace/forward-delete often comes via beforeinput
2222
- const isAndroidChrome = isAndroidChromiumUA();
2198
+ const isAndroidChrome = isAndroidUA();
2223
2199
  const inputType = (evt.originalEvent && evt.originalEvent.inputType) || '';
2224
2200
 
2225
2201
  // Handle collapsed deletes on Android Chrome inside a table line to protect delimiters
@@ -2315,12 +2291,12 @@ exports.aceInitialized = (h, ctx) => {
2315
2291
 
2316
2292
  // Check if selection spans multiple lines
2317
2293
  if (selStart[0] !== selEnd[0]) {
2318
- // log(`${deleteLogPrefix} WARNING: Selection spans multiple lines. Preventing delete to protect table structure.`);
2294
+ // log(`${deleteLogPrefix} WARNING: Selection spans multiple lines. Preventing delete to protect table structure.`);
2319
2295
  evt.preventDefault();
2320
2296
  return;
2321
2297
  }
2322
2298
 
2323
- // log(`${deleteLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2299
+ // log(`${deleteLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2324
2300
  let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
2325
2301
  let tableMetadata = null;
2326
2302
 
@@ -2337,11 +2313,11 @@ exports.aceInitialized = (h, ctx) => {
2337
2313
  }
2338
2314
 
2339
2315
  if (!tableMetadata || typeof tableMetadata.cols !== 'number' || typeof tableMetadata.tblId === 'undefined' || typeof tableMetadata.row === 'undefined') {
2340
- // log(`${deleteLogPrefix} Line ${lineNum} is NOT a recognised table line. Allowing default delete.`);
2316
+ // log(`${deleteLogPrefix} Line ${lineNum} is NOT a recognised table line. Allowing default delete.`);
2341
2317
  return; // Not a table line
2342
2318
  }
2343
2319
 
2344
- // log(`${deleteLogPrefix} Line ${lineNum} IS a table line. Metadata:`, tableMetadata);
2320
+ // log(`${deleteLogPrefix} Line ${lineNum} IS a table line. Metadata:`, tableMetadata);
2345
2321
 
2346
2322
  // Validate selection is within cell boundaries
2347
2323
  const lineText = rep.lines.atIndex(lineNum)?.text || '';
@@ -2392,25 +2368,25 @@ exports.aceInitialized = (h, ctx) => {
2392
2368
  }
2393
2369
 
2394
2370
  if (targetCellIndex === -1 || selEnd[1] > cellEndCol) {
2395
- // log(`${deleteLogPrefix} WARNING: Selection spans cell boundaries or is outside cells. Preventing delete to protect table structure.`);
2371
+ // log(`${deleteLogPrefix} WARNING: Selection spans cell boundaries or is outside cells. Preventing delete to protect table structure.`);
2396
2372
  evt.preventDefault();
2397
2373
  return;
2398
2374
  }
2399
2375
 
2400
2376
  // If we reach here, the selection is entirely within a single cell - intercept delete and preserve table structure
2401
- // log(`${deleteLogPrefix} Selection is entirely within cell ${targetCellIndex}. Intercepting delete to preserve table structure.`);
2377
+ // log(`${deleteLogPrefix} Selection is entirely within cell ${targetCellIndex}. Intercepting delete to preserve table structure.`);
2402
2378
  evt.preventDefault();
2403
2379
 
2404
2380
  try {
2405
2381
  // No clipboard operations needed for delete - just perform the deletion within the cell using ace operations
2406
- // log(`${deleteLogPrefix} Performing deletion via ed.ace_callWithAce.`);
2382
+ // log(`${deleteLogPrefix} Performing deletion via ed.ace_callWithAce.`);
2407
2383
  ed.ace_callWithAce((aceInstance) => {
2408
2384
  const callAceLogPrefix = `${deleteLogPrefix}[ace_callWithAceOps]`;
2409
- // log(`${callAceLogPrefix} Entered ace_callWithAce for delete operations. selStart:`, selStart, `selEnd:`, selEnd);
2385
+ // log(`${callAceLogPrefix} Entered ace_callWithAce for delete operations. selStart:`, selStart, `selEnd:`, selEnd);
2410
2386
 
2411
- // log(`${callAceLogPrefix} Calling aceInstance.ace_performDocumentReplaceRange to delete selected text.`);
2387
+ // log(`${callAceLogPrefix} Calling aceInstance.ace_performDocumentReplaceRange to delete selected text.`);
2412
2388
  aceInstance.ace_performDocumentReplaceRange(selStart, selEnd, '');
2413
- // log(`${callAceLogPrefix} ace_performDocumentReplaceRange successful.`);
2389
+ // log(`${callAceLogPrefix} ace_performDocumentReplaceRange successful.`);
2414
2390
 
2415
2391
  // --- Ensure cell is not left empty (zero-length) ---
2416
2392
  const repAfterDeletion = aceInstance.ace_getRep();
@@ -2419,7 +2395,7 @@ exports.aceInitialized = (h, ctx) => {
2419
2395
  const cellTextAfterDeletion = cellsAfterDeletion[targetCellIndex] || '';
2420
2396
 
2421
2397
  if (cellTextAfterDeletion.length === 0) {
2422
- // log(`${callAceLogPrefix} Cell ${targetCellIndex} became empty after delete – inserting single space to preserve structure.`);
2398
+ // log(`${callAceLogPrefix} Cell ${targetCellIndex} became empty after delete – inserting single space to preserve structure.`);
2423
2399
  const insertPos = [lineNum, selStart[1]]; // Start of the now-empty cell
2424
2400
  aceInstance.ace_performDocumentReplaceRange(insertPos, insertPos, ' ');
2425
2401
 
@@ -2431,9 +2407,9 @@ exports.aceInitialized = (h, ctx) => {
2431
2407
  );
2432
2408
  }
2433
2409
 
2434
- // log(`${callAceLogPrefix} Preparing to re-apply tbljson attribute to line ${lineNum}.`);
2410
+ // log(`${callAceLogPrefix} Preparing to re-apply tbljson attribute to line ${lineNum}.`);
2435
2411
  const repAfterDelete = aceInstance.ace_getRep();
2436
- // log(`${callAceLogPrefix} Fetched rep after delete for applyMeta. Line ${lineNum} text now: "${repAfterDelete.lines.atIndex(lineNum).text}"`);
2412
+ // log(`${callAceLogPrefix} Fetched rep after delete for applyMeta. Line ${lineNum} text now: "${repAfterDelete.lines.atIndex(lineNum).text}"`);
2437
2413
 
2438
2414
  ed.ep_data_tables_applyMeta(
2439
2415
  lineNum,
@@ -2445,22 +2421,22 @@ exports.aceInitialized = (h, ctx) => {
2445
2421
  null,
2446
2422
  docManager
2447
2423
  );
2448
- // log(`${callAceLogPrefix} tbljson attribute re-applied successfully via ep_data_tables_applyMeta.`);
2424
+ // log(`${callAceLogPrefix} tbljson attribute re-applied successfully via ep_data_tables_applyMeta.`);
2449
2425
 
2450
2426
  // Determine new caret position – one char forward if we inserted a space
2451
2427
  const newCaretAbsoluteCol = (cellTextAfterDeletion.length === 0) ? selStart[1] + 1 : selStart[1];
2452
2428
  const newCaretPos = [lineNum, newCaretAbsoluteCol];
2453
- // log(`${callAceLogPrefix} Setting caret position to: [${newCaretPos}].`);
2429
+ // log(`${callAceLogPrefix} Setting caret position to: [${newCaretPos}].`);
2454
2430
  aceInstance.ace_performSelectionChange(newCaretPos, newCaretPos, false);
2455
- // log(`${callAceLogPrefix} Selection change successful.`);
2431
+ // log(`${callAceLogPrefix} Selection change successful.`);
2456
2432
 
2457
- // log(`${callAceLogPrefix} Delete operations within ace_callWithAce completed successfully.`);
2433
+ // log(`${callAceLogPrefix} Delete operations within ace_callWithAce completed successfully.`);
2458
2434
  }, 'tableDeleteTextOperations', true);
2459
2435
 
2460
- // log(`${deleteLogPrefix} Delete operation completed successfully.`);
2436
+ // log(`${deleteLogPrefix} Delete operation completed successfully.`);
2461
2437
  } catch (error) {
2462
2438
  console.error(`${deleteLogPrefix} ERROR during delete operation:`, error);
2463
- // log(`${deleteLogPrefix} Delete operation failed. Error details:`, { message: error.message, stack: error.stack });
2439
+ // log(`${deleteLogPrefix} Delete operation failed. Error details:`, { message: error.message, stack: error.stack });
2464
2440
  }
2465
2441
  });
2466
2442
 
@@ -2472,8 +2448,8 @@ exports.aceInitialized = (h, ctx) => {
2472
2448
  // Only intercept insert types
2473
2449
  if (!inputType || !inputType.startsWith('insert')) return;
2474
2450
 
2475
- // Target only Android Chromium-family browsers (exclude iOS and Firefox)
2476
- if (!isAndroidChromiumUA()) return;
2451
+ // Target only Android browsers (exclude iOS)
2452
+ if (!isAndroidUA()) return;
2477
2453
 
2478
2454
  // Get current selection and ensure we are inside a table line
2479
2455
  const rep = ed.ace_getRep();
@@ -2638,7 +2614,7 @@ exports.aceInitialized = (h, ctx) => {
2638
2614
 
2639
2615
  // Composition start marker (Android Chrome/table only)
2640
2616
  $inner.on('compositionstart', (evt) => {
2641
- if (!isAndroidChromiumUA()) return;
2617
+ if (!isAndroidUA()) return;
2642
2618
  const rep = ed.ace_getRep();
2643
2619
  if (!rep || !rep.selStart) return;
2644
2620
  const lineNum = rep.selStart[0];
@@ -2655,7 +2631,7 @@ exports.aceInitialized = (h, ctx) => {
2655
2631
  $inner.on('compositionupdate', (evt) => {
2656
2632
  const compLogPrefix = '[ep_data_tables:compositionHandler]';
2657
2633
 
2658
- if (!isAndroidChromiumUA()) return;
2634
+ if (!isAndroidUA()) return;
2659
2635
 
2660
2636
  const rep = ed.ace_getRep();
2661
2637
  if (!rep || !rep.selStart) return;
@@ -2778,12 +2754,12 @@ exports.aceInitialized = (h, ctx) => {
2778
2754
  });
2779
2755
 
2780
2756
  // *** DRAG AND DROP EVENT LISTENERS ***
2781
- // log(`${callWithAceLogPrefix} Attaching drag and drop event listeners to $inner (inner iframe body).`);
2757
+ // log(`${callWithAceLogPrefix} Attaching drag and drop event listeners to $inner (inner iframe body).`);
2782
2758
 
2783
2759
  // Prevent drops that could damage table structure
2784
2760
  $inner.on('drop', (evt) => {
2785
2761
  const dropLogPrefix = '[ep_data_tables:dropHandler]';
2786
- // log(`${dropLogPrefix} DROP EVENT TRIGGERED. Event object:`, evt);
2762
+ // log(`${dropLogPrefix} DROP EVENT TRIGGERED. Event object:`, evt);
2787
2763
 
2788
2764
  // Block drops directly targeted at a table element regardless of selection state
2789
2765
  const targetEl = evt.target;
@@ -2797,19 +2773,19 @@ exports.aceInitialized = (h, ctx) => {
2797
2773
  return;
2798
2774
  }
2799
2775
 
2800
- // log(`${dropLogPrefix} Getting current editor representation (rep).`);
2776
+ // log(`${dropLogPrefix} Getting current editor representation (rep).`);
2801
2777
  const rep = ed.ace_getRep();
2802
2778
  if (!rep || !rep.selStart) {
2803
- // log(`${dropLogPrefix} WARNING: Could not get representation or selection. Allowing default drop.`);
2779
+ // log(`${dropLogPrefix} WARNING: Could not get representation or selection. Allowing default drop.`);
2804
2780
  return; // Allow default
2805
2781
  }
2806
2782
 
2807
2783
  const selStart = rep.selStart;
2808
2784
  const lineNum = selStart[0];
2809
- // log(`${dropLogPrefix} Current line number: ${lineNum}.`);
2785
+ // log(`${dropLogPrefix} Current line number: ${lineNum}.`);
2810
2786
 
2811
2787
  // Check if we're dropping onto a table line
2812
- // log(`${dropLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2788
+ // log(`${dropLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2813
2789
  let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
2814
2790
  let isTableLine = !!lineAttrString;
2815
2791
 
@@ -2819,7 +2795,7 @@ exports.aceInitialized = (h, ctx) => {
2819
2795
  }
2820
2796
 
2821
2797
  if (isTableLine) {
2822
- // log(`${dropLogPrefix} Line ${lineNum} IS a table line. Preventing drop to protect table structure.`);
2798
+ // log(`${dropLogPrefix} Line ${lineNum} IS a table line. Preventing drop to protect table structure.`);
2823
2799
  evt.preventDefault();
2824
2800
  evt.stopPropagation();
2825
2801
  console.warn('[ep_data_tables] Drop operation prevented to protect table structure. Please use copy/paste within table cells.');
@@ -2850,7 +2826,7 @@ exports.aceInitialized = (h, ctx) => {
2850
2826
  const lineNum = selStart[0];
2851
2827
 
2852
2828
  // Check if we're dragging over a table line
2853
- // log(`${dragLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2829
+ // log(`${dragLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2854
2830
  let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
2855
2831
  let isTableLine = !!lineAttrString;
2856
2832
 
@@ -2859,7 +2835,7 @@ exports.aceInitialized = (h, ctx) => {
2859
2835
  }
2860
2836
 
2861
2837
  if (isTableLine) {
2862
- // log(`${dragLogPrefix} Preventing dragover on table line ${lineNum} to control drop handling.`);
2838
+ // log(`${dragLogPrefix} Preventing dragover on table line ${lineNum} to control drop handling.`);
2863
2839
  evt.preventDefault();
2864
2840
  }
2865
2841
  });
@@ -2877,67 +2853,67 @@ exports.aceInitialized = (h, ctx) => {
2877
2853
  });
2878
2854
 
2879
2855
  // *** EXISTING PASTE LISTENER ***
2880
- // log(`${callWithAceLogPrefix} Attaching paste event listener to $inner (inner iframe body).`);
2856
+ // log(`${callWithAceLogPrefix} Attaching paste event listener to $inner (inner iframe body).`);
2881
2857
  $inner.on('paste', (evt) => {
2882
2858
  const pasteLogPrefix = '[ep_data_tables:pasteHandler]';
2883
- // log(`${pasteLogPrefix} PASTE EVENT TRIGGERED. Event object:`, evt);
2859
+ // log(`${pasteLogPrefix} PASTE EVENT TRIGGERED. Event object:`, evt);
2884
2860
 
2885
- // log(`${pasteLogPrefix} Getting current editor representation (rep).`);
2861
+ // log(`${pasteLogPrefix} Getting current editor representation (rep).`);
2886
2862
  const rep = ed.ace_getRep();
2887
2863
  if (!rep || !rep.selStart) {
2888
- // log(`${pasteLogPrefix} WARNING: Could not get representation or selection. Allowing default paste.`);
2864
+ // log(`${pasteLogPrefix} WARNING: Could not get representation or selection. Allowing default paste.`);
2889
2865
  console.warn(`${pasteLogPrefix} Could not get rep or selStart.`);
2890
2866
  return; // Allow default
2891
2867
  }
2892
- // log(`${pasteLogPrefix} Rep obtained. selStart:`, rep.selStart, `selEnd:`, rep.selEnd);
2868
+ // log(`${pasteLogPrefix} Rep obtained. selStart:`, rep.selStart, `selEnd:`, rep.selEnd);
2893
2869
  const selStart = rep.selStart;
2894
2870
  const selEnd = rep.selEnd;
2895
2871
  const lineNum = selStart[0];
2896
- // log(`${pasteLogPrefix} Current line number: ${lineNum}. Column start: ${selStart[1]}, Column end: ${selEnd[1]}.`);
2872
+ // log(`${pasteLogPrefix} Current line number: ${lineNum}. Column start: ${selStart[1]}, Column end: ${selEnd[1]}.`);
2897
2873
 
2898
2874
  // NEW: Check if selection spans multiple lines
2899
2875
  if (selStart[0] !== selEnd[0]) {
2900
- // log(`${pasteLogPrefix} WARNING: Selection spans multiple lines. Preventing paste to protect table structure.`);
2876
+ // log(`${pasteLogPrefix} WARNING: Selection spans multiple lines. Preventing paste to protect table structure.`);
2901
2877
  evt.preventDefault();
2902
2878
  return;
2903
2879
  }
2904
2880
 
2905
- // log(`${pasteLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2881
+ // log(`${pasteLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
2906
2882
  let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
2907
2883
  let tableMetadata = null;
2908
2884
 
2909
2885
  if (!lineAttrString) {
2910
2886
  // Block-styled row? Reconstruct metadata from the DOM.
2911
- // log(`${pasteLogPrefix} No '${ATTR_TABLE_JSON}' attribute found. Checking if this is a block-styled table row via DOM reconstruction.`);
2887
+ // log(`${pasteLogPrefix} No '${ATTR_TABLE_JSON}' attribute found. Checking if this is a block-styled table row via DOM reconstruction.`);
2912
2888
  const fallbackMeta = getTableLineMetadata(lineNum, ed, docManager);
2913
2889
  if (fallbackMeta) {
2914
2890
  tableMetadata = fallbackMeta;
2915
2891
  lineAttrString = JSON.stringify(fallbackMeta);
2916
- // log(`${pasteLogPrefix} Block-styled table row detected. Reconstructed metadata:`, fallbackMeta);
2892
+ // log(`${pasteLogPrefix} Block-styled table row detected. Reconstructed metadata:`, fallbackMeta);
2917
2893
  }
2918
2894
  }
2919
2895
 
2920
2896
  if (!lineAttrString) {
2921
- // log(`${pasteLogPrefix} Line ${lineNum} is NOT a table line (no '${ATTR_TABLE_JSON}' attribute found and no DOM reconstruction possible). Allowing default paste.`);
2897
+ // log(`${pasteLogPrefix} Line ${lineNum} is NOT a table line (no '${ATTR_TABLE_JSON}' attribute found and no DOM reconstruction possible). Allowing default paste.`);
2922
2898
  return; // Not a table line
2923
2899
  }
2924
- // log(`${pasteLogPrefix} Line ${lineNum} IS a table line. Attribute string: "${lineAttrString}".`);
2900
+ // log(`${pasteLogPrefix} Line ${lineNum} IS a table line. Attribute string: "${lineAttrString}".`);
2925
2901
 
2926
2902
  try {
2927
- // log(`${pasteLogPrefix} Parsing table metadata from attribute string.`);
2903
+ // log(`${pasteLogPrefix} Parsing table metadata from attribute string.`);
2928
2904
  if (!tableMetadata) {
2929
2905
  tableMetadata = JSON.parse(lineAttrString);
2930
2906
  }
2931
- // log(`${pasteLogPrefix} Parsed table metadata:`, tableMetadata);
2907
+ // log(`${pasteLogPrefix} Parsed table metadata:`, tableMetadata);
2932
2908
  if (!tableMetadata || typeof tableMetadata.cols !== 'number' || typeof tableMetadata.tblId === 'undefined' || typeof tableMetadata.row === 'undefined') {
2933
- // log(`${pasteLogPrefix} WARNING: Invalid or incomplete table metadata on line ${lineNum}. Allowing default paste. Metadata:`, tableMetadata);
2909
+ // log(`${pasteLogPrefix} WARNING: Invalid or incomplete table metadata on line ${lineNum}. Allowing default paste. Metadata:`, tableMetadata);
2934
2910
  console.warn(`${pasteLogPrefix} Invalid table metadata for line ${lineNum}.`);
2935
2911
  return; // Allow default
2936
2912
  }
2937
- // log(`${pasteLogPrefix} Table metadata validated successfully: tblId=${tableMetadata.tblId}, row=${tableMetadata.row}, cols=${tableMetadata.cols}.`);
2913
+ // log(`${pasteLogPrefix} Table metadata validated successfully: tblId=${tableMetadata.tblId}, row=${tableMetadata.row}, cols=${tableMetadata.cols}.`);
2938
2914
  } catch(e) {
2939
2915
  console.error(`${pasteLogPrefix} ERROR parsing table metadata for line ${lineNum}:`, e);
2940
- // log(`${pasteLogPrefix} Metadata parse error. Allowing default paste. Error details:`, { message: e.message, stack: e.stack });
2916
+ // log(`${pasteLogPrefix} Metadata parse error. Allowing default paste. Error details:`, { message: e.message, stack: e.stack });
2941
2917
  return; // Allow default
2942
2918
  }
2943
2919
 
@@ -2968,29 +2944,29 @@ exports.aceInitialized = (h, ctx) => {
2968
2944
  selEnd[1] = cellEndCol; // clamp
2969
2945
  }
2970
2946
  if (targetCellIndex === -1 || selEnd[1] > cellEndCol) {
2971
- // log(`${pasteLogPrefix} WARNING: Selection spans cell boundaries or is outside cells. Preventing paste to protect table structure.`);
2947
+ // log(`${pasteLogPrefix} WARNING: Selection spans cell boundaries or is outside cells. Preventing paste to protect table structure.`);
2972
2948
  evt.preventDefault();
2973
2949
  return;
2974
2950
  }
2975
2951
 
2976
- // log(`${pasteLogPrefix} Accessing clipboard data.`);
2952
+ // log(`${pasteLogPrefix} Accessing clipboard data.`);
2977
2953
  const clipboardData = evt.originalEvent.clipboardData || window.clipboardData;
2978
2954
  if (!clipboardData) {
2979
- // log(`${pasteLogPrefix} WARNING: No clipboard data found. Allowing default paste.`);
2955
+ // log(`${pasteLogPrefix} WARNING: No clipboard data found. Allowing default paste.`);
2980
2956
  return; // Allow default
2981
2957
  }
2982
- // log(`${pasteLogPrefix} Clipboard data object obtained:`, clipboardData);
2958
+ // log(`${pasteLogPrefix} Clipboard data object obtained:`, clipboardData);
2983
2959
 
2984
2960
  // Allow default handling (so ep_hyperlinked_text plugin can process) if rich HTML is present
2985
2961
  const types = clipboardData.types || [];
2986
2962
  if (types.includes('text/html') && clipboardData.getData('text/html')) {
2987
- // log(`${pasteLogPrefix} Detected text/html in clipboard – deferring to other plugins and default paste.`);
2963
+ // log(`${pasteLogPrefix} Detected text/html in clipboard – deferring to other plugins and default paste.`);
2988
2964
  return; // Do not intercept
2989
2965
  }
2990
2966
 
2991
- // log(`${pasteLogPrefix} Getting 'text/plain' from clipboard.`);
2967
+ // log(`${pasteLogPrefix} Getting 'text/plain' from clipboard.`);
2992
2968
  const pastedTextRaw = clipboardData.getData('text/plain');
2993
- // log(`${pasteLogPrefix} Pasted text raw: "${pastedTextRaw}" (Type: ${typeof pastedTextRaw})`);
2969
+ // log(`${pasteLogPrefix} Pasted text raw: "${pastedTextRaw}" (Type: ${typeof pastedTextRaw})`);
2994
2970
 
2995
2971
  // ENHANCED: More thorough sanitization of pasted content
2996
2972
  let pastedText = pastedTextRaw
@@ -3000,18 +2976,18 @@ exports.aceInitialized = (h, ctx) => {
3000
2976
  .replace(/\s+/g, " ") // Normalize whitespace
3001
2977
  .trim(); // Trim leading/trailing whitespace
3002
2978
 
3003
- // log(`${pasteLogPrefix} Pasted text after sanitization: "${pastedText}"`);
2979
+ // log(`${pasteLogPrefix} Pasted text after sanitization: "${pastedText}"`);
3004
2980
 
3005
2981
  if (typeof pastedText !== 'string' || pastedText.length === 0) {
3006
- // log(`${pasteLogPrefix} No plain text in clipboard or text is empty (after sanitization). Allowing default paste.`);
2982
+ // log(`${pasteLogPrefix} No plain text in clipboard or text is empty (after sanitization). Allowing default paste.`);
3007
2983
  const types = clipboardData.types;
3008
- // log(`${pasteLogPrefix} Clipboard types available:`, types);
2984
+ // log(`${pasteLogPrefix} Clipboard types available:`, types);
3009
2985
  if (types && types.includes('text/html')) {
3010
- // log(`${pasteLogPrefix} Clipboard also contains HTML:`, clipboardData.getData('text/html'));
2986
+ // log(`${pasteLogPrefix} Clipboard also contains HTML:`, clipboardData.getData('text/html'));
3011
2987
  }
3012
2988
  return; // Allow default if no plain text
3013
2989
  }
3014
- // log(`${pasteLogPrefix} Plain text obtained from clipboard: "${pastedText}". Length: ${pastedText.length}.`);
2990
+ // log(`${pasteLogPrefix} Plain text obtained from clipboard: "${pastedText}". Length: ${pastedText.length}.`);
3015
2991
 
3016
2992
  // NEW: Check if paste would exceed cell boundaries
3017
2993
  const currentCellText = cells[targetCellIndex] || '';
@@ -3025,18 +3001,18 @@ exports.aceInitialized = (h, ctx) => {
3025
3001
  // see fit or set to `Infinity` to remove the cap entirely.
3026
3002
  const MAX_CELL_LENGTH = 8000;
3027
3003
  if (newCellLength > MAX_CELL_LENGTH) {
3028
- // log(`${pasteLogPrefix} WARNING: Paste would exceed maximum cell length (${newCellLength} > ${MAX_CELL_LENGTH}). Truncating paste.`);
3004
+ // log(`${pasteLogPrefix} WARNING: Paste would exceed maximum cell length (${newCellLength} > ${MAX_CELL_LENGTH}). Truncating paste.`);
3029
3005
  const truncatedPaste = pastedText.substring(0, MAX_CELL_LENGTH - (currentCellText.length - selectionLength));
3030
3006
  if (truncatedPaste.length === 0) {
3031
- // log(`${pasteLogPrefix} Paste would be completely truncated. Preventing paste.`);
3007
+ // log(`${pasteLogPrefix} Paste would be completely truncated. Preventing paste.`);
3032
3008
  evt.preventDefault();
3033
3009
  return;
3034
3010
  }
3035
- // log(`${pasteLogPrefix} Using truncated paste: "${truncatedPaste}"`);
3011
+ // log(`${pasteLogPrefix} Using truncated paste: "${truncatedPaste}"`);
3036
3012
  pastedText = truncatedPaste;
3037
3013
  }
3038
3014
 
3039
- // log(`${pasteLogPrefix} INTERCEPTING paste of plain text into table line ${lineNum}. PREVENTING DEFAULT browser action.`);
3015
+ // log(`${pasteLogPrefix} INTERCEPTING paste of plain text into table line ${lineNum}. PREVENTING DEFAULT browser action.`);
3040
3016
  evt.preventDefault();
3041
3017
  // Prevent other plugins from handling the same paste event once we
3042
3018
  // have intercepted it inside a table cell.
@@ -3044,20 +3020,20 @@ exports.aceInitialized = (h, ctx) => {
3044
3020
  if (typeof evt.stopImmediatePropagation === 'function') evt.stopImmediatePropagation();
3045
3021
 
3046
3022
  try {
3047
- // log(`${pasteLogPrefix} Preparing to perform paste operations via ed.ace_callWithAce.`);
3023
+ // log(`${pasteLogPrefix} Preparing to perform paste operations via ed.ace_callWithAce.`);
3048
3024
  ed.ace_callWithAce((aceInstance) => {
3049
3025
  const callAceLogPrefix = `${pasteLogPrefix}[ace_callWithAceOps]`;
3050
- // log(`${callAceLogPrefix} Entered ace_callWithAce for paste operations. selStart:`, selStart, `selEnd:`, selEnd);
3026
+ // log(`${callAceLogPrefix} Entered ace_callWithAce for paste operations. selStart:`, selStart, `selEnd:`, selEnd);
3051
3027
 
3052
- // log(`${callAceLogPrefix} Original line text from initial rep: "${rep.lines.atIndex(lineNum).text}". SelStartCol: ${selStart[1]}, SelEndCol: ${selEnd[1]}.`);
3028
+ // log(`${callAceLogPrefix} Original line text from initial rep: "${rep.lines.atIndex(lineNum).text}". SelStartCol: ${selStart[1]}, SelEndCol: ${selEnd[1]}.`);
3053
3029
 
3054
- // log(`${callAceLogPrefix} Calling aceInstance.ace_performDocumentReplaceRange to insert text: "${pastedText}".`);
3030
+ // log(`${callAceLogPrefix} Calling aceInstance.ace_performDocumentReplaceRange to insert text: "${pastedText}".`);
3055
3031
  aceInstance.ace_performDocumentReplaceRange(selStart, selEnd, pastedText);
3056
- // log(`${callAceLogPrefix} ace_performDocumentReplaceRange successful.`);
3032
+ // log(`${callAceLogPrefix} ace_performDocumentReplaceRange successful.`);
3057
3033
 
3058
- // log(`${callAceLogPrefix} Preparing to re-apply tbljson attribute to line ${lineNum}.`);
3034
+ // log(`${callAceLogPrefix} Preparing to re-apply tbljson attribute to line ${lineNum}.`);
3059
3035
  const repAfterReplace = aceInstance.ace_getRep();
3060
- // log(`${callAceLogPrefix} Fetched rep after replace for applyMeta. Line ${lineNum} text now: "${repAfterReplace.lines.atIndex(lineNum).text}"`);
3036
+ // log(`${callAceLogPrefix} Fetched rep after replace for applyMeta. Line ${lineNum} text now: "${repAfterReplace.lines.atIndex(lineNum).text}"`);
3061
3037
 
3062
3038
  ed.ep_data_tables_applyMeta(
3063
3039
  lineNum,
@@ -3069,17 +3045,17 @@ exports.aceInitialized = (h, ctx) => {
3069
3045
  null,
3070
3046
  docManager
3071
3047
  );
3072
- // log(`${callAceLogPrefix} tbljson attribute re-applied successfully via ep_data_tables_applyMeta.`);
3048
+ // log(`${callAceLogPrefix} tbljson attribute re-applied successfully via ep_data_tables_applyMeta.`);
3073
3049
 
3074
3050
  const newCaretCol = selStart[1] + pastedText.length;
3075
3051
  const newCaretPos = [lineNum, newCaretCol];
3076
- // log(`${callAceLogPrefix} New calculated caret position: [${newCaretPos}]. Setting selection.`);
3052
+ // log(`${callAceLogPrefix} New calculated caret position: [${newCaretPos}]. Setting selection.`);
3077
3053
  aceInstance.ace_performSelectionChange(newCaretPos, newCaretPos, false);
3078
- // log(`${callAceLogPrefix} Selection change successful.`);
3054
+ // log(`${callAceLogPrefix} Selection change successful.`);
3079
3055
 
3080
- // log(`${callAceLogPrefix} Requesting fastIncorp(10) for sync.`);
3056
+ // log(`${callAceLogPrefix} Requesting fastIncorp(10) for sync.`);
3081
3057
  aceInstance.ace_fastIncorp(10);
3082
- // log(`${callAceLogPrefix} fastIncorp requested.`);
3058
+ // log(`${callAceLogPrefix} fastIncorp requested.`);
3083
3059
 
3084
3060
  // Update stored click/caret info
3085
3061
  if (editor && editor.ep_data_tables_last_clicked && editor.ep_data_tables_last_clicked.tblId === tableMetadata.tblId) {
@@ -3090,23 +3066,23 @@ exports.aceInitialized = (h, ctx) => {
3090
3066
  cellIndex: targetCellIndex,
3091
3067
  relativePos: newRelativePos < 0 ? 0 : newRelativePos,
3092
3068
  };
3093
- // log(`${callAceLogPrefix} Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
3069
+ // log(`${callAceLogPrefix} Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
3094
3070
  }
3095
3071
 
3096
- // log(`${callAceLogPrefix} Paste operations within ace_callWithAce completed successfully.`);
3072
+ // log(`${callAceLogPrefix} Paste operations within ace_callWithAce completed successfully.`);
3097
3073
  }, 'tablePasteTextOperations', true);
3098
- // log(`${pasteLogPrefix} ed.ace_callWithAce for paste operations was called.`);
3074
+ // log(`${pasteLogPrefix} ed.ace_callWithAce for paste operations was called.`);
3099
3075
 
3100
3076
  } catch (error) {
3101
3077
  console.error(`${pasteLogPrefix} CRITICAL ERROR during paste handling operation:`, error);
3102
- // log(`${pasteLogPrefix} Error details:`, { message: error.message, stack: error.stack });
3103
- // log(`${pasteLogPrefix} Paste handling FAILED. END OF HANDLER.`);
3078
+ // log(`${pasteLogPrefix} Error details:`, { message: error.message, stack: error.stack });
3079
+ // log(`${pasteLogPrefix} Paste handling FAILED. END OF HANDLER.`);
3104
3080
  }
3105
3081
  });
3106
- // log(`${callWithAceLogPrefix} Paste event listener attached.`);
3082
+ // log(`${callWithAceLogPrefix} Paste event listener attached.`);
3107
3083
 
3108
3084
  // *** NEW: Column resize listeners ***
3109
- // log(`${callWithAceLogPrefix} Attaching column resize listeners...`);
3085
+ // log(`${callWithAceLogPrefix} Attaching column resize listeners...`);
3110
3086
 
3111
3087
  // Get the iframe documents for proper event delegation
3112
3088
  const $iframeOuter = $('iframe[name="ace_outer"]');
@@ -3114,16 +3090,16 @@ exports.aceInitialized = (h, ctx) => {
3114
3090
  const innerDoc = $iframeInner.contents();
3115
3091
  const outerDoc = $iframeOuter.contents();
3116
3092
 
3117
- // log(`${callWithAceLogPrefix} Found iframe documents: outer=${outerDoc.length}, inner=${innerDoc.length}`);
3093
+ // log(`${callWithAceLogPrefix} Found iframe documents: outer=${outerDoc.length}, inner=${innerDoc.length}`);
3118
3094
 
3119
3095
  // Mousedown on resize handles
3120
3096
  $inner.on('mousedown', '.ep-data_tables-resize-handle', (evt) => {
3121
3097
  const resizeLogPrefix = '[ep_data_tables:resizeMousedown]';
3122
- // log(`${resizeLogPrefix} Resize handle mousedown detected`);
3098
+ // log(`${resizeLogPrefix} Resize handle mousedown detected`);
3123
3099
 
3124
3100
  // Only handle left mouse button clicks
3125
3101
  if (evt.button !== 0) {
3126
- // log(`${resizeLogPrefix} Ignoring non-left mouse button: ${evt.button}`);
3102
+ // log(`${resizeLogPrefix} Ignoring non-left mouse button: ${evt.button}`);
3127
3103
  return;
3128
3104
  }
3129
3105
 
@@ -3134,7 +3110,7 @@ exports.aceInitialized = (h, ctx) => {
3134
3110
  const isImageResizeHandle = $target.hasClass('image-resize-handle') || $target.closest('.image-resize-handle').length > 0;
3135
3111
 
3136
3112
  if (isImageRelated || isImageResizeHandle) {
3137
- // log(`${resizeLogPrefix} Click detected on image-related element or image resize handle, ignoring for table resize`);
3113
+ // log(`${resizeLogPrefix} Click detected on image-related element or image resize handle, ignoring for table resize`);
3138
3114
  return;
3139
3115
  }
3140
3116
 
@@ -3146,7 +3122,7 @@ exports.aceInitialized = (h, ctx) => {
3146
3122
  const table = handle.closest('table.dataTable');
3147
3123
  const lineNode = table.closest('div.ace-line');
3148
3124
 
3149
- // log(`${resizeLogPrefix} Parsed resize target: columnIndex=${columnIndex}, table=${!!table}, lineNode=${!!lineNode}`);
3125
+ // log(`${resizeLogPrefix} Parsed resize target: columnIndex=${columnIndex}, table=${!!table}, lineNode=${!!lineNode}`);
3150
3126
 
3151
3127
  if (table && lineNode && !isNaN(columnIndex)) {
3152
3128
  // Get table metadata
@@ -3160,7 +3136,7 @@ exports.aceInitialized = (h, ctx) => {
3160
3136
 
3161
3137
  const lineNum = rep.lines.indexOfKey(lineNode.id);
3162
3138
 
3163
- // log(`${resizeLogPrefix} Table info: tblId=${tblId}, lineNum=${lineNum}`);
3139
+ // log(`${resizeLogPrefix} Table info: tblId=${tblId}, lineNum=${lineNum}`);
3164
3140
 
3165
3141
  if (tblId && lineNum !== -1) {
3166
3142
  try {
@@ -3168,17 +3144,17 @@ exports.aceInitialized = (h, ctx) => {
3168
3144
  if (lineAttrString) {
3169
3145
  const metadata = JSON.parse(lineAttrString);
3170
3146
  if (metadata.tblId === tblId) {
3171
- // log(`${resizeLogPrefix} Starting resize with metadata:`, metadata);
3147
+ // log(`${resizeLogPrefix} Starting resize with metadata:`, metadata);
3172
3148
  startColumnResize(table, columnIndex, evt.clientX, metadata, lineNum);
3173
- // log(`${resizeLogPrefix} Started resize for column ${columnIndex}`);
3149
+ // log(`${resizeLogPrefix} Started resize for column ${columnIndex}`);
3174
3150
 
3175
3151
  // DEBUG: Verify global state is set
3176
- // log(`${resizeLogPrefix} Global resize state: isResizing=${isResizing}, targetTable=${!!resizeTargetTable}, targetColumn=${resizeTargetColumn}`);
3152
+ // log(`${resizeLogPrefix} Global resize state: isResizing=${isResizing}, targetTable=${!!resizeTargetTable}, targetColumn=${resizeTargetColumn}`);
3177
3153
  } else {
3178
- // log(`${resizeLogPrefix} Table ID mismatch: ${metadata.tblId} vs ${tblId}`);
3154
+ // log(`${resizeLogPrefix} Table ID mismatch: ${metadata.tblId} vs ${tblId}`);
3179
3155
  }
3180
3156
  } else {
3181
- // log(`${resizeLogPrefix} No table metadata found for line ${lineNum}, trying DOM reconstruction...`);
3157
+ // log(`${resizeLogPrefix} No table metadata found for line ${lineNum}, trying DOM reconstruction...`);
3182
3158
 
3183
3159
  // Fallback: Reconstruct metadata from DOM (same logic as ace_doDatatableOptions)
3184
3160
  const rep = ed.ace_getRep();
@@ -3212,37 +3188,37 @@ exports.aceInitialized = (h, ctx) => {
3212
3188
  cols: domCells.length,
3213
3189
  columnWidths: columnWidths
3214
3190
  };
3215
- // log(`${resizeLogPrefix} Reconstructed metadata from DOM:`, reconstructedMetadata);
3191
+ // log(`${resizeLogPrefix} Reconstructed metadata from DOM:`, reconstructedMetadata);
3216
3192
 
3217
3193
  startColumnResize(table, columnIndex, evt.clientX, reconstructedMetadata, lineNum);
3218
- // log(`${resizeLogPrefix} Started resize for column ${columnIndex} using reconstructed metadata`);
3194
+ // log(`${resizeLogPrefix} Started resize for column ${columnIndex} using reconstructed metadata`);
3219
3195
 
3220
3196
  // DEBUG: Verify global state is set
3221
- // log(`${resizeLogPrefix} Global resize state: isResizing=${isResizing}, targetTable=${!!resizeTargetTable}, targetColumn=${resizeTargetColumn}`);
3197
+ // log(`${resizeLogPrefix} Global resize state: isResizing=${isResizing}, targetTable=${!!resizeTargetTable}, targetColumn=${resizeTargetColumn}`);
3222
3198
  } else {
3223
- // log(`${resizeLogPrefix} DOM table found but no cells detected`);
3199
+ // log(`${resizeLogPrefix} DOM table found but no cells detected`);
3224
3200
  }
3225
3201
  } else {
3226
- // log(`${resizeLogPrefix} DOM table found but tblId mismatch or missing row: domTblId=${domTblId}, domRow=${domRow}`);
3202
+ // log(`${resizeLogPrefix} DOM table found but tblId mismatch or missing row: domTblId=${domTblId}, domRow=${domRow}`);
3227
3203
  }
3228
3204
  } else {
3229
- // log(`${resizeLogPrefix} No table found in DOM for line ${lineNum}`);
3205
+ // log(`${resizeLogPrefix} No table found in DOM for line ${lineNum}`);
3230
3206
  }
3231
3207
  } else {
3232
- // log(`${resizeLogPrefix} Could not get line entry or lineNode for line ${lineNum}`);
3208
+ // log(`${resizeLogPrefix} Could not get line entry or lineNode for line ${lineNum}`);
3233
3209
  }
3234
3210
  } else {
3235
- // log(`${resizeLogPrefix} Could not get rep or rep.lines for DOM reconstruction`);
3211
+ // log(`${resizeLogPrefix} Could not get rep or rep.lines for DOM reconstruction`);
3236
3212
  }
3237
3213
  }
3238
3214
  } catch (e) {
3239
3215
  console.error(`${resizeLogPrefix} Error getting table metadata:`, e);
3240
3216
  }
3241
3217
  } else {
3242
- // log(`${resizeLogPrefix} Invalid line number (${lineNum}) or table ID (${tblId})`);
3218
+ // log(`${resizeLogPrefix} Invalid line number (${lineNum}) or table ID (${tblId})`);
3243
3219
  }
3244
3220
  } else {
3245
- // log(`${resizeLogPrefix} Invalid resize target:`, { table: !!table, lineNode: !!lineNode, columnIndex });
3221
+ // log(`${resizeLogPrefix} Invalid resize target:`, { table: !!table, lineNode: !!lineNode, columnIndex });
3246
3222
  }
3247
3223
  });
3248
3224
 
@@ -3261,57 +3237,57 @@ exports.aceInitialized = (h, ctx) => {
3261
3237
 
3262
3238
  // Mouseup handler with enhanced debugging
3263
3239
  const handleMouseup = (evt) => {
3264
- // log(`${mouseupLogPrefix} Mouseup detected on ${evt.target.tagName || 'unknown'}. isResizing: ${isResizing}`);
3240
+ // log(`${mouseupLogPrefix} Mouseup detected on ${evt.target.tagName || 'unknown'}. isResizing: ${isResizing}`);
3265
3241
 
3266
3242
  if (isResizing) {
3267
- // log(`${mouseupLogPrefix} Processing resize completion...`);
3243
+ // log(`${mouseupLogPrefix} Processing resize completion...`);
3268
3244
  evt.preventDefault();
3269
3245
  evt.stopPropagation();
3270
3246
 
3271
3247
  // Add a small delay to ensure all DOM updates are complete
3272
3248
  setTimeout(() => {
3273
- // log(`${mouseupLogPrefix} Executing finishColumnResize after delay...`);
3249
+ // log(`${mouseupLogPrefix} Executing finishColumnResize after delay...`);
3274
3250
  finishColumnResize(ed, docManager);
3275
- // log(`${mouseupLogPrefix} Resize completion finished.`);
3251
+ // log(`${mouseupLogPrefix} Resize completion finished.`);
3276
3252
  }, 50);
3277
3253
  } else {
3278
- // log(`${mouseupLogPrefix} Not in resize mode, ignoring mouseup.`);
3254
+ // log(`${mouseupLogPrefix} Not in resize mode, ignoring mouseup.`);
3279
3255
  }
3280
3256
  };
3281
3257
 
3282
3258
  // Attach to multiple contexts to ensure we catch the event
3283
- // log(`${callWithAceLogPrefix} Attaching global mousemove/mouseup handlers to multiple contexts...`);
3259
+ // log(`${callWithAceLogPrefix} Attaching global mousemove/mouseup handlers to multiple contexts...`);
3284
3260
 
3285
3261
  // 1. Main document (outside iframes)
3286
3262
  $(document).on('mousemove', handleMousemove);
3287
3263
  $(document).on('mouseup', handleMouseup);
3288
- // log(`${callWithAceLogPrefix} Attached to main document`);
3264
+ // log(`${callWithAceLogPrefix} Attached to main document`);
3289
3265
 
3290
3266
  // 2. Outer iframe document
3291
3267
  if (outerDoc.length > 0) {
3292
3268
  outerDoc.on('mousemove', handleMousemove);
3293
3269
  outerDoc.on('mouseup', handleMouseup);
3294
- // log(`${callWithAceLogPrefix} Attached to outer iframe document`);
3270
+ // log(`${callWithAceLogPrefix} Attached to outer iframe document`);
3295
3271
  }
3296
3272
 
3297
3273
  // 3. Inner iframe document
3298
3274
  if (innerDoc.length > 0) {
3299
3275
  innerDoc.on('mousemove', handleMousemove);
3300
3276
  innerDoc.on('mouseup', handleMouseup);
3301
- // log(`${callWithAceLogPrefix} Attached to inner iframe document`);
3277
+ // log(`${callWithAceLogPrefix} Attached to inner iframe document`);
3302
3278
  }
3303
3279
 
3304
3280
  // 4. Inner iframe body (the editing area)
3305
3281
  $inner.on('mousemove', handleMousemove);
3306
3282
  $inner.on('mouseup', handleMouseup);
3307
- // log(`${callWithAceLogPrefix} Attached to inner iframe body`);
3283
+ // log(`${callWithAceLogPrefix} Attached to inner iframe body`);
3308
3284
 
3309
3285
  // 5. Add a failsafe - listen for any mouse events during resize
3310
3286
  const failsafeMouseup = (evt) => {
3311
3287
  if (isResizing) {
3312
- // log(`${mouseupLogPrefix} FAILSAFE: Detected mouse event during resize: ${evt.type}`);
3288
+ // log(`${mouseupLogPrefix} FAILSAFE: Detected mouse event during resize: ${evt.type}`);
3313
3289
  if (evt.type === 'mouseup' || evt.type === 'mousedown' || evt.type === 'click') {
3314
- // log(`${mouseupLogPrefix} FAILSAFE: Triggering resize completion due to ${evt.type}`);
3290
+ // log(`${mouseupLogPrefix} FAILSAFE: Triggering resize completion due to ${evt.type}`);
3315
3291
  setTimeout(() => {
3316
3292
  if (isResizing) { // Double-check we're still resizing
3317
3293
  finishColumnResize(ed, docManager);
@@ -3325,7 +3301,7 @@ exports.aceInitialized = (h, ctx) => {
3325
3301
  document.addEventListener('mouseup', failsafeMouseup, true);
3326
3302
  document.addEventListener('mousedown', failsafeMouseup, true);
3327
3303
  document.addEventListener('click', failsafeMouseup, true);
3328
- // log(`${callWithAceLogPrefix} Attached failsafe event handlers`);
3304
+ // log(`${callWithAceLogPrefix} Attached failsafe event handlers`);
3329
3305
 
3330
3306
  // *** DRAG PREVENTION FOR TABLE ELEMENTS ***
3331
3307
  const preventTableDrag = (evt) => {
@@ -3333,7 +3309,7 @@ exports.aceInitialized = (h, ctx) => {
3333
3309
  // Block ANY drag gesture that originates within a table (including spans/text inside cells)
3334
3310
  const inTable = target && typeof target.closest === 'function' && target.closest('table.dataTable');
3335
3311
  if (inTable) {
3336
- // log('[ep_data_tables:dragPrevention] Preventing drag operation originating from inside table');
3312
+ // log('[ep_data_tables:dragPrevention] Preventing drag operation originating from inside table');
3337
3313
  evt.preventDefault();
3338
3314
  evt.stopPropagation();
3339
3315
  if (evt.originalEvent && evt.originalEvent.dataTransfer) {
@@ -3347,7 +3323,7 @@ exports.aceInitialized = (h, ctx) => {
3347
3323
  $inner.on('dragstart', preventTableDrag);
3348
3324
  $inner.on('drag', preventTableDrag);
3349
3325
  $inner.on('dragend', preventTableDrag);
3350
- // log(`${callWithAceLogPrefix} Attached drag prevention handlers to inner body`);
3326
+ // log(`${callWithAceLogPrefix} Attached drag prevention handlers to inner body`);
3351
3327
 
3352
3328
  // Attach drag prevention broadly to cover iframe boundaries and the host document
3353
3329
  if (innerDoc.length > 0) {
@@ -3368,15 +3344,15 @@ exports.aceInitialized = (h, ctx) => {
3368
3344
  // Setup the global handlers
3369
3345
  setupGlobalHandlers();
3370
3346
 
3371
- // log(`${callWithAceLogPrefix} Column resize listeners attached successfully.`);
3347
+ // log(`${callWithAceLogPrefix} Column resize listeners attached successfully.`);
3372
3348
 
3373
3349
  }, 'tablePasteAndResizeListeners', true);
3374
- // log(`${logPrefix} ace_callWithAce for listeners setup completed.`);
3350
+ // log(`${logPrefix} ace_callWithAce for listeners setup completed.`);
3375
3351
 
3376
3352
  // Helper function to apply the metadata attribute to a line
3377
3353
  function applyTableLineMetadataAttribute (lineNum, tblId, rowIndex, numCols, rep, editorInfo, attributeString = null, documentAttributeManager = null) {
3378
3354
  const funcName = 'applyTableLineMetadataAttribute';
3379
- // log(`${logPrefix}:${funcName}: START - Applying METADATA attribute to line ${lineNum}`, {tblId, rowIndex, numCols});
3355
+ // log(`${logPrefix}:${funcName}: START - Applying METADATA attribute to line ${lineNum}`, {tblId, rowIndex, numCols});
3380
3356
 
3381
3357
  let finalMetadata;
3382
3358
 
@@ -3387,14 +3363,14 @@ exports.aceInitialized = (h, ctx) => {
3387
3363
  if (providedMetadata.columnWidths && Array.isArray(providedMetadata.columnWidths) && providedMetadata.columnWidths.length === numCols) {
3388
3364
  // Already has valid columnWidths, use as-is
3389
3365
  finalMetadata = providedMetadata;
3390
- // log(`${logPrefix}:${funcName}: Using provided metadata with existing columnWidths`);
3366
+ // log(`${logPrefix}:${funcName}: Using provided metadata with existing columnWidths`);
3391
3367
  } else {
3392
3368
  // Has metadata but missing/invalid columnWidths, extract from DOM
3393
3369
  finalMetadata = providedMetadata;
3394
- // log(`${logPrefix}:${funcName}: Provided metadata missing columnWidths, attempting DOM extraction`);
3370
+ // log(`${logPrefix}:${funcName}: Provided metadata missing columnWidths, attempting DOM extraction`);
3395
3371
  }
3396
3372
  } catch (e) {
3397
- // log(`${logPrefix}:${funcName}: Error parsing provided attributeString, will reconstruct:`, e);
3373
+ // log(`${logPrefix}:${funcName}: Error parsing provided attributeString, will reconstruct:`, e);
3398
3374
  finalMetadata = null;
3399
3375
  }
3400
3376
  }
@@ -3425,13 +3401,13 @@ exports.aceInitialized = (h, ctx) => {
3425
3401
  columnWidths.push(100 / numCols);
3426
3402
  }
3427
3403
  });
3428
- // log(`${logPrefix}:${funcName}: Extracted column widths from DOM: ${columnWidths.map(w => w.toFixed(1) + '%').join(', ')}`);
3404
+ // log(`${logPrefix}:${funcName}: Extracted column widths from DOM: ${columnWidths.map(w => w.toFixed(1) + '%').join(', ')}`);
3429
3405
  }
3430
3406
  }
3431
3407
  }
3432
3408
  }
3433
3409
  } catch (e) {
3434
- // log(`${logPrefix}:${funcName}: Error extracting column widths from DOM:`, e);
3410
+ // log(`${logPrefix}:${funcName}: Error extracting column widths from DOM:`, e);
3435
3411
  }
3436
3412
 
3437
3413
  // Build final metadata
@@ -3448,26 +3424,26 @@ exports.aceInitialized = (h, ctx) => {
3448
3424
  }
3449
3425
 
3450
3426
  const finalAttributeString = JSON.stringify(finalMetadata);
3451
- // log(`${logPrefix}:${funcName}: Final metadata attribute string: ${finalAttributeString}`);
3427
+ // log(`${logPrefix}:${funcName}: Final metadata attribute string: ${finalAttributeString}`);
3452
3428
 
3453
3429
  try {
3454
3430
  // Get current line info
3455
3431
  const lineEntry = rep.lines.atIndex(lineNum);
3456
3432
  if (!lineEntry) {
3457
- // log(`${logPrefix}:${funcName}: ERROR - Could not find line entry for line number ${lineNum}`);
3433
+ // log(`${logPrefix}:${funcName}: ERROR - Could not find line entry for line number ${lineNum}`);
3458
3434
  return;
3459
3435
  }
3460
3436
  const lineLength = Math.max(1, lineEntry.text.length);
3461
- // log(`${logPrefix}:${funcName}: Line ${lineNum} text length: ${lineLength}`);
3437
+ // log(`${logPrefix}:${funcName}: Line ${lineNum} text length: ${lineLength}`);
3462
3438
 
3463
3439
  // Simple attribute application - just add the tbljson attribute
3464
3440
  const attributes = [[ATTR_TABLE_JSON, finalAttributeString]];
3465
3441
  const start = [lineNum, 0];
3466
3442
  const end = [lineNum, lineLength];
3467
3443
 
3468
- // log(`${logPrefix}:${funcName}: Applying tbljson attribute to range [${start}]-[${end}]`);
3444
+ // log(`${logPrefix}:${funcName}: Applying tbljson attribute to range [${start}]-[${end}]`);
3469
3445
  editorInfo.ace_performDocumentApplyAttributesToRange(start, end, attributes);
3470
- // log(`${logPrefix}:${funcName}: Successfully applied tbljson attribute to line ${lineNum}`);
3446
+ // log(`${logPrefix}:${funcName}: Successfully applied tbljson attribute to line ${lineNum}`);
3471
3447
 
3472
3448
  } catch(e) {
3473
3449
  console.error(`[ep_data_tables] ${logPrefix}:${funcName}: Error applying metadata attribute on line ${lineNum}:`, e);
@@ -3477,59 +3453,59 @@ exports.aceInitialized = (h, ctx) => {
3477
3453
  /** Insert a fresh rows×cols blank table at the caret */
3478
3454
  ed.ace_createTableViaAttributes = (rows = 2, cols = 2) => {
3479
3455
  const funcName = 'ace_createTableViaAttributes';
3480
- // log(`${funcName}: START - Refactored Phase 4 (Get Selection Fix)`, { rows, cols });
3456
+ // log(`${funcName}: START - Refactored Phase 4 (Get Selection Fix)`, { rows, cols });
3481
3457
  rows = Math.max(1, rows); cols = Math.max(1, cols);
3482
- // log(`${funcName}: Ensuring minimum 1 row, 1 col.`);
3458
+ // log(`${funcName}: Ensuring minimum 1 row, 1 col.`);
3483
3459
 
3484
3460
  // --- Phase 1: Prepare Data ---
3485
3461
  const tblId = rand();
3486
- // log(`${funcName}: Generated table ID: ${tblId}`);
3462
+ // log(`${funcName}: Generated table ID: ${tblId}`);
3487
3463
  const initialCellContent = ' '; // Start with a single space per cell
3488
3464
  const lineTxt = Array.from({ length: cols }).fill(initialCellContent).join(DELIMITER);
3489
- // log(`${funcName}: Constructed initial line text for ${cols} cols: "${lineTxt}"`);
3465
+ // log(`${funcName}: Constructed initial line text for ${cols} cols: "${lineTxt}"`);
3490
3466
  const block = Array.from({ length: rows }).fill(lineTxt).join('\n') + '\n';
3491
- // log(`${funcName}: Constructed block for ${rows} rows:\n${block}`);
3467
+ // log(`${funcName}: Constructed block for ${rows} rows:\n${block}`);
3492
3468
 
3493
3469
  // Get current selection BEFORE making changes using ace_getRep()
3494
- // log(`${funcName}: Getting current representation and selection...`);
3470
+ // log(`${funcName}: Getting current representation and selection...`);
3495
3471
  const currentRepInitial = ed.ace_getRep();
3496
3472
  if (!currentRepInitial || !currentRepInitial.selStart || !currentRepInitial.selEnd) {
3497
3473
  console.error(`[ep_data_tables] ${funcName}: Could not get current representation or selection via ace_getRep(). Aborting.`);
3498
- // log(`${funcName}: END - Error getting initial rep/selection`);
3474
+ // log(`${funcName}: END - Error getting initial rep/selection`);
3499
3475
  return;
3500
3476
  }
3501
3477
  const start = currentRepInitial.selStart;
3502
3478
  const end = currentRepInitial.selEnd;
3503
3479
  const initialStartLine = start[0]; // Store the starting line number
3504
- // log(`${funcName}: Current selection from initial rep:`, { start, end });
3480
+ // log(`${funcName}: Current selection from initial rep:`, { start, end });
3505
3481
 
3506
3482
  // --- Phase 2: Insert Text Block ---
3507
- // log(`${funcName}: Phase 2 - Inserting text block...`);
3483
+ // log(`${funcName}: Phase 2 - Inserting text block...`);
3508
3484
  ed.ace_performDocumentReplaceRange(start, end, block);
3509
- // log(`${funcName}: Inserted block of delimited text lines.`);
3510
- // log(`${funcName}: Requesting text sync (ace_fastIncorp 20)...`);
3485
+ // log(`${funcName}: Inserted block of delimited text lines.`);
3486
+ // log(`${funcName}: Requesting text sync (ace_fastIncorp 20)...`);
3511
3487
  ed.ace_fastIncorp(20); // Sync text insertion
3512
- // log(`${funcName}: Text sync requested.`);
3488
+ // log(`${funcName}: Text sync requested.`);
3513
3489
 
3514
3490
  // --- Phase 3: Apply Metadata Attributes ---
3515
- // log(`${funcName}: Phase 3 - Applying metadata attributes to ${rows} inserted lines...`);
3491
+ // log(`${funcName}: Phase 3 - Applying metadata attributes to ${rows} inserted lines...`);
3516
3492
  // Need rep to be updated after text insertion to apply attributes correctly
3517
3493
  const currentRep = ed.ace_getRep(); // Get potentially updated rep
3518
3494
  if (!currentRep || !currentRep.lines) {
3519
3495
  console.error(`[ep_data_tables] ${funcName}: Could not get updated rep after text insertion. Cannot apply attributes reliably.`);
3520
- // log(`${funcName}: END - Error getting updated rep`);
3496
+ // log(`${funcName}: END - Error getting updated rep`);
3521
3497
  // Maybe attempt to continue without rep? Risky.
3522
3498
  return;
3523
3499
  }
3524
- // log(`${funcName}: Fetched updated rep for attribute application.`);
3500
+ // log(`${funcName}: Fetched updated rep for attribute application.`);
3525
3501
 
3526
3502
  for (let r = 0; r < rows; r++) {
3527
3503
  const lineNumToApply = initialStartLine + r;
3528
- // log(`${funcName}: -> Processing row ${r} on line ${lineNumToApply}`);
3504
+ // log(`${funcName}: -> Processing row ${r} on line ${lineNumToApply}`);
3529
3505
 
3530
3506
  const lineEntry = currentRep.lines.atIndex(lineNumToApply);
3531
3507
  if (!lineEntry) {
3532
- // log(`${funcName}: Could not find line entry for ${lineNumToApply}, skipping attribute application.`);
3508
+ // log(`${funcName}: Could not find line entry for ${lineNumToApply}, skipping attribute application.`);
3533
3509
  continue;
3534
3510
  }
3535
3511
  const lineText = lineEntry.text || '';
@@ -3542,7 +3518,7 @@ exports.aceInitialized = (h, ctx) => {
3542
3518
  if (cellContent.length > 0) { // Only apply to non-empty cells
3543
3519
  const cellStart = [lineNumToApply, offset];
3544
3520
  const cellEnd = [lineNumToApply, offset + cellContent.length];
3545
- // log(`${funcName}: Applying ${ATTR_CELL} attribute to Line ${lineNumToApply} Col ${c} Range ${offset}-${offset + cellContent.length}`);
3521
+ // log(`${funcName}: Applying ${ATTR_CELL} attribute to Line ${lineNumToApply} Col ${c} Range ${offset}-${offset + cellContent.length}`);
3546
3522
  ed.ace_performDocumentApplyAttributesToRange(cellStart, cellEnd, [[ATTR_CELL, String(c)]]);
3547
3523
  }
3548
3524
  offset += cellContent.length;
@@ -3555,30 +3531,30 @@ exports.aceInitialized = (h, ctx) => {
3555
3531
  // Note: documentAttributeManager not available in this context for new table creation
3556
3532
  applyTableLineMetadataAttribute(lineNumToApply, tblId, r, cols, currentRep, ed, null, null);
3557
3533
  }
3558
- // log(`${funcName}: Finished applying metadata attributes.`);
3559
- // log(`${funcName}: Requesting attribute sync (ace_fastIncorp 20)...`);
3534
+ // log(`${funcName}: Finished applying metadata attributes.`);
3535
+ // log(`${funcName}: Requesting attribute sync (ace_fastIncorp 20)...`);
3560
3536
  ed.ace_fastIncorp(20); // Final sync after attributes
3561
- // log(`${funcName}: Attribute sync requested.`);
3537
+ // log(`${funcName}: Attribute sync requested.`);
3562
3538
 
3563
3539
  // --- Phase 4: Set Caret Position ---
3564
- // log(`${funcName}: Phase 4 - Setting final caret position...`);
3540
+ // log(`${funcName}: Phase 4 - Setting final caret position...`);
3565
3541
  const finalCaretLine = initialStartLine + rows; // Line number after the last inserted row
3566
3542
  const finalCaretPos = [finalCaretLine, 0];
3567
- // log(`${funcName}: Target caret position:`, finalCaretPos);
3543
+ // log(`${funcName}: Target caret position:`, finalCaretPos);
3568
3544
  try {
3569
3545
  ed.ace_performSelectionChange(finalCaretPos, finalCaretPos, false);
3570
- // log(`${funcName}: Successfully set caret position.`);
3546
+ // log(`${funcName}: Successfully set caret position.`);
3571
3547
  } catch(e) {
3572
3548
  console.error(`[ep_data_tables] ${funcName}: Error setting caret position after table creation:`, e);
3573
- // log(`[ep_data_tables] ${funcName}: Error details:`, { message: e.message, stack: e.stack });
3549
+ // log(`[ep_data_tables] ${funcName}: Error details:`, { message: e.message, stack: e.stack });
3574
3550
  }
3575
3551
 
3576
- // log(`${funcName}: END - Refactored Phase 4`);
3552
+ // log(`${funcName}: END - Refactored Phase 4`);
3577
3553
  };
3578
3554
 
3579
3555
  ed.ace_doDatatableOptions = (action) => {
3580
3556
  const funcName = 'ace_doDatatableOptions';
3581
- // log(`${funcName}: START - Processing action: ${action}`);
3557
+ // log(`${funcName}: START - Processing action: ${action}`);
3582
3558
 
3583
3559
  // Get the last clicked cell info to determine which table to operate on
3584
3560
  const editor = ed.ep_data_tables_editor;
@@ -3589,12 +3565,12 @@ exports.aceInitialized = (h, ctx) => {
3589
3565
 
3590
3566
  const lastClick = editor.ep_data_tables_last_clicked;
3591
3567
  if (!lastClick || !lastClick.tblId) {
3592
- // log(`${funcName}: No table selected. Please click on a table cell first.`);
3568
+ // log(`${funcName}: No table selected. Please click on a table cell first.`);
3593
3569
  console.warn('[ep_data_tables] No table selected. Please click on a table cell first.');
3594
3570
  return;
3595
3571
  }
3596
3572
 
3597
- // log(`${funcName}: Operating on table ${lastClick.tblId}, clicked line ${lastClick.lineNum}, cell ${lastClick.cellIndex}`);
3573
+ // log(`${funcName}: Operating on table ${lastClick.tblId}, clicked line ${lastClick.lineNum}, cell ${lastClick.cellIndex}`);
3598
3574
 
3599
3575
  try {
3600
3576
  // Get current representation and document manager
@@ -3611,7 +3587,7 @@ exports.aceInitialized = (h, ctx) => {
3611
3587
  return;
3612
3588
  }
3613
3589
 
3614
- // log(`${funcName}: Successfully obtained documentAttributeManager from stored reference.`);
3590
+ // log(`${funcName}: Successfully obtained documentAttributeManager from stored reference.`);
3615
3591
 
3616
3592
  // Find all lines that belong to this table
3617
3593
  const tableLines = [];
@@ -3640,7 +3616,7 @@ exports.aceInitialized = (h, ctx) => {
3640
3616
  cols: domCells.length
3641
3617
  };
3642
3618
  lineAttrString = JSON.stringify(reconstructedMetadata);
3643
- // log(`${funcName}: Reconstructed metadata from DOM for line ${lineIndex}: ${lineAttrString}`);
3619
+ // log(`${funcName}: Reconstructed metadata from DOM for line ${lineIndex}: ${lineAttrString}`);
3644
3620
  }
3645
3621
  }
3646
3622
  }
@@ -3668,13 +3644,13 @@ exports.aceInitialized = (h, ctx) => {
3668
3644
  }
3669
3645
 
3670
3646
  if (tableLines.length === 0) {
3671
- // log(`${funcName}: No table lines found for table ${lastClick.tblId}`);
3647
+ // log(`${funcName}: No table lines found for table ${lastClick.tblId}`);
3672
3648
  return;
3673
3649
  }
3674
3650
 
3675
3651
  // Sort by row number to ensure correct order
3676
3652
  tableLines.sort((a, b) => a.row - b.row);
3677
- // log(`${funcName}: Found ${tableLines.length} table lines`);
3653
+ // log(`${funcName}: Found ${tableLines.length} table lines`);
3678
3654
 
3679
3655
  // Determine table dimensions and target indices with robust matching
3680
3656
  const numRows = tableLines.length;
@@ -3688,7 +3664,7 @@ exports.aceInitialized = (h, ctx) => {
3688
3664
 
3689
3665
  // If that fails, try to match by finding the row that contains the clicked table
3690
3666
  if (targetRowIndex === -1) {
3691
- // log(`${funcName}: Direct line number match failed, searching by DOM structure...`);
3667
+ // log(`${funcName}: Direct line number match failed, searching by DOM structure...`);
3692
3668
  const clickedLineEntry = currentRep.lines.atIndex(lastClick.lineNum);
3693
3669
  if (clickedLineEntry && clickedLineEntry.lineNode) {
3694
3670
  const clickedTable = clickedLineEntry.lineNode.querySelector('table.dataTable[data-tblId="' + lastClick.tblId + '"]');
@@ -3697,7 +3673,7 @@ exports.aceInitialized = (h, ctx) => {
3697
3673
  if (clickedRowAttr !== null) {
3698
3674
  const clickedRowNum = parseInt(clickedRowAttr, 10);
3699
3675
  targetRowIndex = tableLines.findIndex(line => line.row === clickedRowNum);
3700
- // log(`${funcName}: Found target row by DOM attribute matching: row ${clickedRowNum}, index ${targetRowIndex}`);
3676
+ // log(`${funcName}: Found target row by DOM attribute matching: row ${clickedRowNum}, index ${targetRowIndex}`);
3701
3677
  }
3702
3678
  }
3703
3679
  }
@@ -3705,13 +3681,13 @@ exports.aceInitialized = (h, ctx) => {
3705
3681
 
3706
3682
  // If still not found, default to first row but log the issue
3707
3683
  if (targetRowIndex === -1) {
3708
- // log(`${funcName}: Warning: Could not find target row, defaulting to row 0`);
3684
+ // log(`${funcName}: Warning: Could not find target row, defaulting to row 0`);
3709
3685
  targetRowIndex = 0;
3710
3686
  }
3711
3687
 
3712
3688
  const targetColIndex = lastClick.cellIndex || 0;
3713
3689
 
3714
- // log(`${funcName}: Table dimensions: ${numRows} rows x ${numCols} cols. Target: row ${targetRowIndex}, col ${targetColIndex}`);
3690
+ // log(`${funcName}: Table dimensions: ${numRows} rows x ${numCols} cols. Target: row ${targetRowIndex}, col ${targetColIndex}`);
3715
3691
 
3716
3692
  // Perform table operations with both text and metadata updates
3717
3693
  let newNumCols = numCols;
@@ -3719,23 +3695,23 @@ exports.aceInitialized = (h, ctx) => {
3719
3695
 
3720
3696
  switch (action) {
3721
3697
  case 'addTblRowA': // Insert row above
3722
- // log(`${funcName}: Inserting row above row ${targetRowIndex}`);
3698
+ // log(`${funcName}: Inserting row above row ${targetRowIndex}`);
3723
3699
  success = addTableRowAboveWithText(tableLines, targetRowIndex, numCols, lastClick.tblId, ed, docManager);
3724
3700
  break;
3725
3701
 
3726
3702
  case 'addTblRowB': // Insert row below
3727
- // log(`${funcName}: Inserting row below row ${targetRowIndex}`);
3703
+ // log(`${funcName}: Inserting row below row ${targetRowIndex}`);
3728
3704
  success = addTableRowBelowWithText(tableLines, targetRowIndex, numCols, lastClick.tblId, ed, docManager);
3729
3705
  break;
3730
3706
 
3731
3707
  case 'addTblColL': // Insert column left
3732
- // log(`${funcName}: Inserting column left of column ${targetColIndex}`);
3708
+ // log(`${funcName}: Inserting column left of column ${targetColIndex}`);
3733
3709
  newNumCols = numCols + 1;
3734
3710
  success = addTableColumnLeftWithText(tableLines, targetColIndex, ed, docManager);
3735
3711
  break;
3736
3712
 
3737
3713
  case 'addTblColR': // Insert column right
3738
- // log(`${funcName}: Inserting column right of column ${targetColIndex}`);
3714
+ // log(`${funcName}: Inserting column right of column ${targetColIndex}`);
3739
3715
  newNumCols = numCols + 1;
3740
3716
  success = addTableColumnRightWithText(tableLines, targetColIndex, ed, docManager);
3741
3717
  break;
@@ -3744,10 +3720,10 @@ exports.aceInitialized = (h, ctx) => {
3744
3720
  // Show confirmation prompt for row deletion
3745
3721
  const rowConfirmMessage = `Are you sure you want to delete Row ${targetRowIndex + 1} and all content within?`;
3746
3722
  if (!confirm(rowConfirmMessage)) {
3747
- // log(`${funcName}: Row deletion cancelled by user`);
3723
+ // log(`${funcName}: Row deletion cancelled by user`);
3748
3724
  return;
3749
3725
  }
3750
- // log(`${funcName}: Deleting row ${targetRowIndex}`);
3726
+ // log(`${funcName}: Deleting row ${targetRowIndex}`);
3751
3727
  success = deleteTableRowWithText(tableLines, targetRowIndex, ed, docManager);
3752
3728
  break;
3753
3729
 
@@ -3755,16 +3731,16 @@ exports.aceInitialized = (h, ctx) => {
3755
3731
  // Show confirmation prompt for column deletion
3756
3732
  const colConfirmMessage = `Are you sure you want to delete Column ${targetColIndex + 1} and all content within?`;
3757
3733
  if (!confirm(colConfirmMessage)) {
3758
- // log(`${funcName}: Column deletion cancelled by user`);
3734
+ // log(`${funcName}: Column deletion cancelled by user`);
3759
3735
  return;
3760
3736
  }
3761
- // log(`${funcName}: Deleting column ${targetColIndex}`);
3737
+ // log(`${funcName}: Deleting column ${targetColIndex}`);
3762
3738
  newNumCols = numCols - 1;
3763
3739
  success = deleteTableColumnWithText(tableLines, targetColIndex, ed, docManager);
3764
3740
  break;
3765
3741
 
3766
3742
  default:
3767
- // log(`${funcName}: Unknown action: ${action}`);
3743
+ // log(`${funcName}: Unknown action: ${action}`);
3768
3744
  return;
3769
3745
  }
3770
3746
 
@@ -3773,11 +3749,11 @@ exports.aceInitialized = (h, ctx) => {
3773
3749
  return;
3774
3750
  }
3775
3751
 
3776
- // log(`${funcName}: Table operation completed successfully with text and metadata synchronization`);
3752
+ // log(`${funcName}: Table operation completed successfully with text and metadata synchronization`);
3777
3753
 
3778
3754
  } catch (error) {
3779
3755
  console.error(`[ep_data_tables] ${funcName}: Error during table operation:`, error);
3780
- // log(`${funcName}: Error details:`, { message: error.message, stack: error.stack });
3756
+ // log(`${funcName}: Error details:`, { message: error.message, stack: error.stack });
3781
3757
  }
3782
3758
  };
3783
3759
 
@@ -3830,7 +3806,7 @@ exports.aceInitialized = (h, ctx) => {
3830
3806
  columnWidths.push(100 / numCols);
3831
3807
  }
3832
3808
  });
3833
- // log('[ep_data_tables] addTableRowAbove: Extracted column widths from DOM:', columnWidths);
3809
+ // log('[ep_data_tables] addTableRowAbove: Extracted column widths from DOM:', columnWidths);
3834
3810
  }
3835
3811
  }
3836
3812
  }
@@ -3908,7 +3884,7 @@ exports.aceInitialized = (h, ctx) => {
3908
3884
  columnWidths.push(100 / numCols);
3909
3885
  }
3910
3886
  });
3911
- // log('[ep_data_tables] addTableRowBelow: Extracted column widths from DOM:', columnWidths);
3887
+ // log('[ep_data_tables] addTableRowBelow: Extracted column widths from DOM:', columnWidths);
3912
3888
  }
3913
3889
  }
3914
3890
  }
@@ -3973,7 +3949,7 @@ exports.aceInitialized = (h, ctx) => {
3973
3949
  if (cellContent.length > 0) { // Only apply to non-empty cells
3974
3950
  const cellStart = [tableLine.lineIndex, offset];
3975
3951
  const cellEnd = [tableLine.lineIndex, offset + cellContent.length];
3976
- // log(`[ep_data_tables] ${funcName}: Applying ${ATTR_CELL} attribute to Line ${tableLine.lineIndex} Col ${c} Range ${offset}-${offset + cellContent.length}`);
3952
+ // log(`[ep_data_tables] ${funcName}: Applying ${ATTR_CELL} attribute to Line ${tableLine.lineIndex} Col ${c} Range ${offset}-${offset + cellContent.length}`);
3977
3953
  editorInfo.ace_performDocumentApplyAttributesToRange(cellStart, cellEnd, [[ATTR_CELL, String(c)]]);
3978
3954
  }
3979
3955
  offset += cellContent.length;
@@ -3988,7 +3964,7 @@ exports.aceInitialized = (h, ctx) => {
3988
3964
  const newColCount = tableLine.cols + 1;
3989
3965
  const equalWidth = 100 / newColCount;
3990
3966
  const normalizedWidths = Array(newColCount).fill(equalWidth);
3991
- // log(`[ep_data_tables] addTableColumnLeft: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
3967
+ // log(`[ep_data_tables] addTableColumnLeft: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
3992
3968
 
3993
3969
  // Apply updated metadata
3994
3970
  const newMetadata = { ...tableLine.metadata, cols: tableLine.cols + 1, columnWidths: normalizedWidths };
@@ -4040,7 +4016,7 @@ exports.aceInitialized = (h, ctx) => {
4040
4016
  if (cellContent.length > 0) { // Only apply to non-empty cells
4041
4017
  const cellStart = [tableLine.lineIndex, offset];
4042
4018
  const cellEnd = [tableLine.lineIndex, offset + cellContent.length];
4043
- // log(`[ep_data_tables] ${funcName}: Applying ${ATTR_CELL} attribute to Line ${tableLine.lineIndex} Col ${c} Range ${offset}-${offset + cellContent.length}`);
4019
+ // log(`[ep_data_tables] ${funcName}: Applying ${ATTR_CELL} attribute to Line ${tableLine.lineIndex} Col ${c} Range ${offset}-${offset + cellContent.length}`);
4044
4020
  editorInfo.ace_performDocumentApplyAttributesToRange(cellStart, cellEnd, [[ATTR_CELL, String(c)]]);
4045
4021
  }
4046
4022
  offset += cellContent.length;
@@ -4055,7 +4031,7 @@ exports.aceInitialized = (h, ctx) => {
4055
4031
  const newColCount = tableLine.cols + 1;
4056
4032
  const equalWidth = 100 / newColCount;
4057
4033
  const normalizedWidths = Array(newColCount).fill(equalWidth);
4058
- // log(`[ep_data_tables] addTableColumnRight: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
4034
+ // log(`[ep_data_tables] addTableColumnRight: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
4059
4035
 
4060
4036
  // Apply updated metadata
4061
4037
  const newMetadata = { ...tableLine.metadata, cols: tableLine.cols + 1, columnWidths: normalizedWidths };
@@ -4078,7 +4054,7 @@ exports.aceInitialized = (h, ctx) => {
4078
4054
  // Special handling for deleting the first row (row index 0)
4079
4055
  // Insert a blank line to prevent the table from getting stuck at line 1
4080
4056
  if (targetRowIndex === 0) {
4081
- // log('[ep_data_tables] Deleting first row (row 0) - inserting blank line to prevent table from getting stuck');
4057
+ // log('[ep_data_tables] Deleting first row (row 0) - inserting blank line to prevent table from getting stuck');
4082
4058
  const insertStart = [targetLine.lineIndex, 0];
4083
4059
  editorInfo.ace_performDocumentReplaceRange(insertStart, insertStart, '\n');
4084
4060
 
@@ -4118,7 +4094,7 @@ exports.aceInitialized = (h, ctx) => {
4118
4094
  columnWidths.push(100 / targetLine.metadata.cols);
4119
4095
  }
4120
4096
  });
4121
- // log('[ep_data_tables] deleteTableRow: Extracted column widths from DOM:', columnWidths);
4097
+ // log('[ep_data_tables] deleteTableRow: Extracted column widths from DOM:', columnWidths);
4122
4098
  break;
4123
4099
  }
4124
4100
  }
@@ -4155,7 +4131,7 @@ exports.aceInitialized = (h, ctx) => {
4155
4131
  const cells = lineText.split(DELIMITER);
4156
4132
 
4157
4133
  if (targetColIndex >= cells.length) {
4158
- // log(`[ep_data_tables] Warning: Target column ${targetColIndex} doesn't exist in line with ${cells.length} columns`);
4134
+ // log(`[ep_data_tables] Warning: Target column ${targetColIndex} doesn't exist in line with ${cells.length} columns`);
4159
4135
  continue;
4160
4136
  }
4161
4137
 
@@ -4180,7 +4156,7 @@ exports.aceInitialized = (h, ctx) => {
4180
4156
  deleteStart -= DELIMITER.length;
4181
4157
  }
4182
4158
 
4183
- // log(`[ep_data_tables] Deleting column ${targetColIndex} from line ${tableLine.lineIndex}: chars ${deleteStart}-${deleteEnd} from "${lineText}"`);
4159
+ // log(`[ep_data_tables] Deleting column ${targetColIndex} from line ${tableLine.lineIndex}: chars ${deleteStart}-${deleteEnd} from "${lineText}"`);
4184
4160
 
4185
4161
  // Perform the precise deletion
4186
4162
  const rangeStart = [tableLine.lineIndex, deleteStart];
@@ -4194,7 +4170,7 @@ exports.aceInitialized = (h, ctx) => {
4194
4170
  if (newColCount > 0) {
4195
4171
  const equalWidth = 100 / newColCount;
4196
4172
  const normalizedWidths = Array(newColCount).fill(equalWidth);
4197
- // log(`[ep_data_tables] deleteTableColumn: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
4173
+ // log(`[ep_data_tables] deleteTableColumn: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
4198
4174
 
4199
4175
  // Update metadata
4200
4176
  const newMetadata = { ...tableLine.metadata, cols: newColCount, columnWidths: normalizedWidths };
@@ -4212,7 +4188,7 @@ exports.aceInitialized = (h, ctx) => {
4212
4188
 
4213
4189
  // ... existing code ...
4214
4190
 
4215
- // log('aceInitialized: END - helpers defined.');
4191
+ // log('aceInitialized: END - helpers defined.');
4216
4192
  };
4217
4193
 
4218
4194
  // ───────────────────── required no‑op stubs ─────────────────────
@@ -4222,11 +4198,11 @@ exports.aceEndLineAndCharForPoint = () => { return undefined; };
4222
4198
  // NEW: Style protection for table cells
4223
4199
  exports.aceSetAuthorStyle = (hook, ctx) => {
4224
4200
  const logPrefix = '[ep_data_tables:aceSetAuthorStyle]';
4225
- // log(`${logPrefix} START`, { hook, ctx });
4201
+ // log(`${logPrefix} START`, { hook, ctx });
4226
4202
 
4227
4203
  // If no selection or no style to apply, allow default
4228
4204
  if (!ctx || !ctx.rep || !ctx.rep.selStart || !ctx.rep.selEnd || !ctx.key) {
4229
- // log(`${logPrefix} No selection or style key. Allowing default.`);
4205
+ // log(`${logPrefix} No selection or style key. Allowing default.`);
4230
4206
  return;
4231
4207
  }
4232
4208
 
@@ -4236,14 +4212,14 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
4236
4212
 
4237
4213
  // If selection spans multiple lines, prevent style application
4238
4214
  if (startLine !== endLine) {
4239
- // log(`${logPrefix} Selection spans multiple lines. Preventing style application to protect table structure.`);
4215
+ // log(`${logPrefix} Selection spans multiple lines. Preventing style application to protect table structure.`);
4240
4216
  return false;
4241
4217
  }
4242
4218
 
4243
4219
  // Check if the line is a table line
4244
4220
  const lineAttrString = ctx.documentAttributeManager?.getAttributeOnLine(startLine, ATTR_TABLE_JSON);
4245
4221
  if (!lineAttrString) {
4246
- // log(`${logPrefix} Line ${startLine} is not a table line. Allowing default style application.`);
4222
+ // log(`${logPrefix} Line ${startLine} is not a table line. Allowing default style application.`);
4247
4223
  return;
4248
4224
  }
4249
4225
 
@@ -4254,7 +4230,7 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
4254
4230
  ];
4255
4231
 
4256
4232
  if (BLOCKED_STYLES.includes(ctx.key)) {
4257
- // log(`${logPrefix} Blocked potentially harmful style '${ctx.key}' from being applied to table cell.`);
4233
+ // log(`${logPrefix} Blocked potentially harmful style '${ctx.key}' from being applied to table cell.`);
4258
4234
  return false;
4259
4235
  }
4260
4236
 
@@ -4262,7 +4238,7 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
4262
4238
  try {
4263
4239
  const tableMetadata = JSON.parse(lineAttrString);
4264
4240
  if (!tableMetadata || typeof tableMetadata.cols !== 'number') {
4265
- // log(`${logPrefix} Invalid table metadata. Preventing style application.`);
4241
+ // log(`${logPrefix} Invalid table metadata. Preventing style application.`);
4266
4242
  return false;
4267
4243
  }
4268
4244
 
@@ -4288,7 +4264,7 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
4288
4264
 
4289
4265
  // If selection spans multiple cells, prevent style application
4290
4266
  if (selectionStartCell !== selectionEndCell) {
4291
- // log(`${logPrefix} Selection spans multiple cells. Preventing style application to protect table structure.`);
4267
+ // log(`${logPrefix} Selection spans multiple cells. Preventing style application to protect table structure.`);
4292
4268
  return false;
4293
4269
  }
4294
4270
 
@@ -4297,15 +4273,15 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
4297
4273
  const cellEndCol = cellStartCol + cells[selectionStartCell].length;
4298
4274
 
4299
4275
  if (ctx.rep.selStart[1] <= cellStartCol || ctx.rep.selEnd[1] >= cellEndCol) {
4300
- // log(`${logPrefix} Selection includes cell delimiters. Preventing style application to protect table structure.`);
4276
+ // log(`${logPrefix} Selection includes cell delimiters. Preventing style application to protect table structure.`);
4301
4277
  return false;
4302
4278
  }
4303
4279
 
4304
- // log(`${logPrefix} Style '${ctx.key}' allowed within cell boundaries.`);
4280
+ // log(`${logPrefix} Style '${ctx.key}' allowed within cell boundaries.`);
4305
4281
  return; // Allow the style to be applied
4306
4282
  } catch (e) {
4307
4283
  console.error(`${logPrefix} Error processing style application:`, e);
4308
- // log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
4284
+ // log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
4309
4285
  return false; // Prevent style application on error
4310
4286
  }
4311
4287
  };
@@ -4322,7 +4298,7 @@ exports.aceRegisterBlockElements = () => ['table'];
4322
4298
  // NEW: Column resize helper functions (adapted from images plugin)
4323
4299
  const startColumnResize = (table, columnIndex, startX, metadata, lineNum) => {
4324
4300
  const funcName = 'startColumnResize';
4325
- // log(`${funcName}: Starting resize for column ${columnIndex}`);
4301
+ // log(`${funcName}: Starting resize for column ${columnIndex}`);
4326
4302
 
4327
4303
  isResizing = true;
4328
4304
  resizeStartX = startX;
@@ -4336,7 +4312,7 @@ const startColumnResize = (table, columnIndex, startX, metadata, lineNum) => {
4336
4312
  const numCols = metadata.cols;
4337
4313
  resizeOriginalWidths = metadata.columnWidths ? [...metadata.columnWidths] : Array(numCols).fill(100 / numCols);
4338
4314
 
4339
- // log(`${funcName}: Original widths:`, resizeOriginalWidths);
4315
+ // log(`${funcName}: Original widths:`, resizeOriginalWidths);
4340
4316
 
4341
4317
  // Create visual overlay instead of modifying table directly
4342
4318
  createResizeOverlay(table, columnIndex);
@@ -4402,7 +4378,7 @@ const createResizeOverlay = (table, columnIndex) => {
4402
4378
 
4403
4379
  const totalTableHeight = maxBottom - minTop;
4404
4380
 
4405
- // log(`createResizeOverlay: Found ${allTableRows.length} table rows, total height: ${totalTableHeight}px`);
4381
+ // log(`createResizeOverlay: Found ${allTableRows.length} table rows, total height: ${totalTableHeight}px`);
4406
4382
 
4407
4383
  // Calculate positioning using the same method as image plugin
4408
4384
  let innerBodyRect, innerIframeRect, outerBodyRect;
@@ -4489,7 +4465,7 @@ const createResizeOverlay = (table, columnIndex) => {
4489
4465
  // Append to outer body like image plugin does with its outline
4490
4466
  padOuter.append(resizeOverlay);
4491
4467
 
4492
- // log('createResizeOverlay: Created Google Docs style blue line overlay spanning entire table height');
4468
+ // log('createResizeOverlay: Created Google Docs style blue line overlay spanning entire table height');
4493
4469
  };
4494
4470
 
4495
4471
  const updateColumnResize = (currentX) => {
@@ -4549,19 +4525,19 @@ const updateColumnResize = (currentX) => {
4549
4525
 
4550
4526
  const finishColumnResize = (editorInfo, docManager) => {
4551
4527
  if (!isResizing || !resizeTargetTable) {
4552
- // log('finishColumnResize: Not in resize mode');
4528
+ // log('finishColumnResize: Not in resize mode');
4553
4529
  return;
4554
4530
  }
4555
4531
 
4556
4532
  const funcName = 'finishColumnResize';
4557
- // log(`${funcName}: Finishing resize`);
4533
+ // log(`${funcName}: Finishing resize`);
4558
4534
 
4559
4535
  // Calculate final widths from actual mouse movement
4560
4536
  const tableRect = resizeTargetTable.getBoundingClientRect();
4561
4537
  const deltaX = resizeCurrentX - resizeStartX;
4562
4538
  const deltaPercent = (deltaX / tableRect.width) * 100;
4563
4539
 
4564
- // log(`${funcName}: Mouse moved ${deltaX}px (${deltaPercent.toFixed(1)}%)`);
4540
+ // log(`${funcName}: Mouse moved ${deltaX}px (${deltaPercent.toFixed(1)}%)`);
4565
4541
 
4566
4542
  const finalWidths = [...resizeOriginalWidths];
4567
4543
  const currentColumn = resizeTargetColumn;
@@ -4575,7 +4551,7 @@ const finishColumnResize = (editorInfo, docManager) => {
4575
4551
  finalWidths[currentColumn] += actualTransfer;
4576
4552
  finalWidths[nextColumn] -= actualTransfer;
4577
4553
 
4578
- // log(`${funcName}: Transferred ${actualTransfer.toFixed(1)}% from column ${nextColumn} to column ${currentColumn}`);
4554
+ // log(`${funcName}: Transferred ${actualTransfer.toFixed(1)}% from column ${nextColumn} to column ${currentColumn}`);
4579
4555
  }
4580
4556
 
4581
4557
  // Normalize widths
@@ -4586,7 +4562,7 @@ const finishColumnResize = (editorInfo, docManager) => {
4586
4562
  });
4587
4563
  }
4588
4564
 
4589
- // log(`${funcName}: Final normalized widths:`, finalWidths.map(w => w.toFixed(1) + '%'));
4565
+ // log(`${funcName}: Final normalized widths:`, finalWidths.map(w => w.toFixed(1) + '%'));
4590
4566
 
4591
4567
  // Clean up overlay
4592
4568
  if (resizeOverlay) {
@@ -4606,7 +4582,7 @@ const finishColumnResize = (editorInfo, docManager) => {
4606
4582
  // Apply updated metadata to ALL rows in the table (not just the resized row)
4607
4583
  editorInfo.ace_callWithAce((ace) => {
4608
4584
  const callWithAceLogPrefix = `${funcName}[ace_callWithAce]`;
4609
- // log(`${callWithAceLogPrefix}: Finding and updating all table rows with tblId: ${resizeTableMetadata.tblId}`);
4585
+ // log(`${callWithAceLogPrefix}: Finding and updating all table rows with tblId: ${resizeTableMetadata.tblId}`);
4610
4586
 
4611
4587
  try {
4612
4588
  const rep = ace.ace_getRep();
@@ -4663,7 +4639,7 @@ const finishColumnResize = (editorInfo, docManager) => {
4663
4639
  cols: domCells.length,
4664
4640
  columnWidths: columnWidths
4665
4641
  };
4666
- // log(`${callWithAceLogPrefix}: Reconstructed metadata from DOM for line ${lineIndex}:`, reconstructedMetadata);
4642
+ // log(`${callWithAceLogPrefix}: Reconstructed metadata from DOM for line ${lineIndex}:`, reconstructedMetadata);
4667
4643
  tableLines.push({
4668
4644
  lineIndex,
4669
4645
  metadata: reconstructedMetadata
@@ -4678,7 +4654,7 @@ const finishColumnResize = (editorInfo, docManager) => {
4678
4654
  }
4679
4655
  }
4680
4656
 
4681
- // log(`${callWithAceLogPrefix}: Found ${tableLines.length} table lines to update`);
4657
+ // log(`${callWithAceLogPrefix}: Found ${tableLines.length} table lines to update`);
4682
4658
 
4683
4659
  // Update all table lines with new column widths
4684
4660
  for (const tableLine of tableLines) {
@@ -4696,7 +4672,7 @@ const finishColumnResize = (editorInfo, docManager) => {
4696
4672
  const rangeStart = [tableLine.lineIndex, 0];
4697
4673
  const rangeEnd = [tableLine.lineIndex, lineLength];
4698
4674
 
4699
- // log(`${callWithAceLogPrefix}: Updating line ${tableLine.lineIndex} (row ${tableLine.metadata.row}) with new column widths`);
4675
+ // log(`${callWithAceLogPrefix}: Updating line ${tableLine.lineIndex} (row ${tableLine.metadata.row}) with new column widths`);
4700
4676
 
4701
4677
  // Apply the updated metadata attribute directly
4702
4678
  ace.ace_performDocumentApplyAttributesToRange(rangeStart, rangeEnd, [
@@ -4704,15 +4680,15 @@ const finishColumnResize = (editorInfo, docManager) => {
4704
4680
  ]);
4705
4681
  }
4706
4682
 
4707
- // log(`${callWithAceLogPrefix}: Successfully applied updated column widths to all ${tableLines.length} table rows`);
4683
+ // log(`${callWithAceLogPrefix}: Successfully applied updated column widths to all ${tableLines.length} table rows`);
4708
4684
 
4709
4685
  } catch (error) {
4710
4686
  console.error(`${callWithAceLogPrefix}: Error applying updated metadata:`, error);
4711
- // log(`${callWithAceLogPrefix}: Error details:`, { message: error.message, stack: error.stack });
4687
+ // log(`${callWithAceLogPrefix}: Error details:`, { message: error.message, stack: error.stack });
4712
4688
  }
4713
4689
  }, 'applyTableResizeToAllRows', true);
4714
4690
 
4715
- // log(`${funcName}: Column width update initiated for all table rows via ace_callWithAce`);
4691
+ // log(`${funcName}: Column width update initiated for all table rows via ace_callWithAce`);
4716
4692
 
4717
4693
  // Reset state
4718
4694
  resizeStartX = 0;
@@ -4723,16 +4699,16 @@ const finishColumnResize = (editorInfo, docManager) => {
4723
4699
  resizeTableMetadata = null;
4724
4700
  resizeLineNum = -1;
4725
4701
 
4726
- // log(`${funcName}: Resize complete - state reset`);
4702
+ // log(`${funcName}: Resize complete - state reset`);
4727
4703
  };
4728
4704
 
4729
4705
  // NEW: Undo/Redo protection
4730
4706
  exports.aceUndoRedo = (hook, ctx) => {
4731
4707
  const logPrefix = '[ep_data_tables:aceUndoRedo]';
4732
- // log(`${logPrefix} START`, { hook, ctx });
4708
+ // log(`${logPrefix} START`, { hook, ctx });
4733
4709
 
4734
4710
  if (!ctx || !ctx.rep || !ctx.rep.selStart || !ctx.rep.selEnd) {
4735
- // log(`${logPrefix} No selection or context. Allowing default.`);
4711
+ // log(`${logPrefix} No selection or context. Allowing default.`);
4736
4712
  return;
4737
4713
  }
4738
4714
 
@@ -4753,11 +4729,11 @@ exports.aceUndoRedo = (hook, ctx) => {
4753
4729
  }
4754
4730
 
4755
4731
  if (!hasTableLines) {
4756
- // log(`${logPrefix} No table lines affected. Allowing default undo/redo.`);
4732
+ // log(`${logPrefix} No table lines affected. Allowing default undo/redo.`);
4757
4733
  return;
4758
4734
  }
4759
4735
 
4760
- // log(`${logPrefix} Table lines affected:`, { tableLines });
4736
+ // log(`${logPrefix} Table lines affected:`, { tableLines });
4761
4737
 
4762
4738
  // Validate table structure after undo/redo
4763
4739
  try {
@@ -4767,7 +4743,7 @@ exports.aceUndoRedo = (hook, ctx) => {
4767
4743
 
4768
4744
  const tableMetadata = JSON.parse(lineAttrString);
4769
4745
  if (!tableMetadata || typeof tableMetadata.cols !== 'number') {
4770
- // log(`${logPrefix} Invalid table metadata after undo/redo. Attempting recovery.`);
4746
+ // log(`${logPrefix} Invalid table metadata after undo/redo. Attempting recovery.`);
4771
4747
  // Attempt to recover table structure
4772
4748
  const lineText = ctx.rep.lines.atIndex(line)?.text || '';
4773
4749
  const cells = lineText.split(DELIMITER);
@@ -4782,16 +4758,16 @@ exports.aceUndoRedo = (hook, ctx) => {
4782
4758
 
4783
4759
  // Apply the recovered metadata
4784
4760
  ctx.documentAttributeManager.setAttributeOnLine(line, ATTR_TABLE_JSON, JSON.stringify(newMetadata));
4785
- // log(`${logPrefix} Recovered table structure for line ${line}`);
4761
+ // log(`${logPrefix} Recovered table structure for line ${line}`);
4786
4762
  } else {
4787
4763
  // If we can't recover, remove the table attribute
4788
4764
  ctx.documentAttributeManager.removeAttributeOnLine(line, ATTR_TABLE_JSON);
4789
- // log(`${logPrefix} Removed invalid table attribute from line ${line}`);
4765
+ // log(`${logPrefix} Removed invalid table attribute from line ${line}`);
4790
4766
  }
4791
4767
  }
4792
4768
  }
4793
4769
  } catch (e) {
4794
4770
  console.error(`${logPrefix} Error during undo/redo validation:`, e);
4795
- // log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
4771
+ // log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
4796
4772
  }
4797
4773
  };