@leftium/gg 0.0.25 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eruda/loader.js +14 -1
- package/dist/eruda/plugin.js +228 -69
- package/dist/gg.d.ts +8 -1
- package/dist/gg.js +104 -24
- package/package.json +1 -1
package/dist/eruda/loader.js
CHANGED
|
@@ -62,6 +62,17 @@ export async function loadEruda(options) {
|
|
|
62
62
|
// Dynamic import of Eruda
|
|
63
63
|
const erudaModule = await import('eruda');
|
|
64
64
|
const eruda = erudaModule.default;
|
|
65
|
+
// Clear Eruda position state to prevent icon from being stuck in wrong position
|
|
66
|
+
// Eruda stores draggable icon position in localStorage which can get corrupted
|
|
67
|
+
// This ensures the icon always appears in the default bottom-right corner
|
|
68
|
+
try {
|
|
69
|
+
// Eruda uses keys like 'eruda-entry-button' for position state
|
|
70
|
+
const positionKeys = ['eruda-entry-button', 'eruda-position'];
|
|
71
|
+
positionKeys.forEach((key) => localStorage.removeItem(key));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// localStorage might not be available
|
|
75
|
+
}
|
|
65
76
|
// Initialize Eruda
|
|
66
77
|
eruda.init({
|
|
67
78
|
...options.erudaOptions,
|
|
@@ -81,13 +92,15 @@ export async function loadEruda(options) {
|
|
|
81
92
|
}
|
|
82
93
|
// Register gg plugin
|
|
83
94
|
// Import gg and pass it to the plugin directly
|
|
84
|
-
const { gg } = await import('../gg.js');
|
|
95
|
+
const { gg, runGgDiagnostics } = await import('../gg.js');
|
|
85
96
|
const { createGgPlugin } = await import('./plugin.js');
|
|
86
97
|
const ggPlugin = createGgPlugin(options, gg);
|
|
87
98
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
99
|
eruda.add(ggPlugin);
|
|
89
100
|
// Make GG tab the default selected tab
|
|
90
101
|
eruda.show('GG');
|
|
102
|
+
// Run diagnostics after Eruda is ready so they appear in Console tab
|
|
103
|
+
await runGgDiagnostics();
|
|
91
104
|
}
|
|
92
105
|
catch (error) {
|
|
93
106
|
console.error('[gg] Failed to load Eruda:', error);
|
package/dist/eruda/plugin.js
CHANGED
|
@@ -172,29 +172,37 @@ export function createGgPlugin(options, gg) {
|
|
|
172
172
|
}
|
|
173
173
|
function gridColumns() {
|
|
174
174
|
const ns = nsColWidth !== null ? `${nsColWidth}px` : 'auto';
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return `24px auto ${ns} 4px 1fr`;
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
// diff | ns | handle | content
|
|
181
|
-
return `auto ${ns} 4px 1fr`;
|
|
182
|
-
}
|
|
175
|
+
// diff | ns | handle | content (× is now inside ns)
|
|
176
|
+
return `auto ${ns} 4px 1fr`;
|
|
183
177
|
}
|
|
184
178
|
function buildHTML() {
|
|
185
179
|
return `
|
|
186
180
|
<style>
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
181
|
+
.gg-log-grid {
|
|
182
|
+
display: grid;
|
|
183
|
+
grid-template-columns: ${gridColumns()};
|
|
184
|
+
column-gap: 0;
|
|
185
|
+
align-items: start !important;
|
|
186
|
+
}
|
|
187
|
+
/* Desktop: hide wrapper divs, show direct children */
|
|
188
|
+
.gg-log-entry {
|
|
189
|
+
display: contents;
|
|
190
|
+
}
|
|
191
|
+
.gg-log-header {
|
|
192
|
+
display: contents;
|
|
193
|
+
}
|
|
194
|
+
.gg-log-diff,
|
|
195
|
+
.gg-log-ns,
|
|
196
|
+
.gg-log-handle,
|
|
197
|
+
.gg-log-content {
|
|
198
|
+
min-width: 0;
|
|
199
|
+
align-self: start !important;
|
|
200
|
+
border-top: 1px solid rgba(0,0,0,0.05);
|
|
201
|
+
}
|
|
202
|
+
.gg-details {
|
|
203
|
+
grid-column: 1 / -1;
|
|
204
|
+
border-top: none;
|
|
205
|
+
}
|
|
198
206
|
.gg-details {
|
|
199
207
|
align-self: stretch !important;
|
|
200
208
|
border-bottom: none;
|
|
@@ -234,19 +242,15 @@ export function createGgPlugin(options, gg) {
|
|
|
234
242
|
word-break: break-word;
|
|
235
243
|
padding: 4px 0;
|
|
236
244
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
.gg-row-filter:hover {
|
|
247
|
-
opacity: 1;
|
|
248
|
-
background: rgba(0,0,0,0.05);
|
|
249
|
-
}
|
|
245
|
+
/* Make header clickable for filtering when filters are expanded */
|
|
246
|
+
.gg-log-header.clickable {
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
}
|
|
249
|
+
/* Desktop: highlight child elements since header has display: contents */
|
|
250
|
+
.gg-log-header.clickable:hover .gg-log-diff,
|
|
251
|
+
.gg-log-header.clickable:hover .gg-log-ns {
|
|
252
|
+
background: rgba(0,0,0,0.05);
|
|
253
|
+
}
|
|
250
254
|
.gg-filter-panel {
|
|
251
255
|
background: #f5f5f5;
|
|
252
256
|
padding: 10px;
|
|
@@ -262,7 +266,7 @@ export function createGgPlugin(options, gg) {
|
|
|
262
266
|
width: 100%;
|
|
263
267
|
padding: 4px 8px;
|
|
264
268
|
font-family: monospace;
|
|
265
|
-
font-size:
|
|
269
|
+
font-size: 16px;
|
|
266
270
|
margin-bottom: 8px;
|
|
267
271
|
}
|
|
268
272
|
.gg-filter-checkboxes {
|
|
@@ -281,16 +285,125 @@ export function createGgPlugin(options, gg) {
|
|
|
281
285
|
font-family: monospace;
|
|
282
286
|
white-space: nowrap;
|
|
283
287
|
}
|
|
288
|
+
/* Mobile responsive styles */
|
|
289
|
+
.gg-toolbar {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
gap: 8px;
|
|
293
|
+
margin-bottom: 8px;
|
|
294
|
+
flex-shrink: 0;
|
|
295
|
+
overflow-x: auto;
|
|
296
|
+
-webkit-overflow-scrolling: touch;
|
|
297
|
+
}
|
|
298
|
+
.gg-toolbar button {
|
|
299
|
+
padding: 4px 10px;
|
|
300
|
+
cursor: pointer;
|
|
301
|
+
flex-shrink: 0;
|
|
302
|
+
}
|
|
303
|
+
.gg-btn-text {
|
|
304
|
+
display: inline;
|
|
305
|
+
}
|
|
306
|
+
.gg-btn-icon {
|
|
307
|
+
display: none;
|
|
308
|
+
}
|
|
309
|
+
@media (max-width: 640px) {
|
|
310
|
+
.gg-btn-text {
|
|
311
|
+
display: none;
|
|
312
|
+
}
|
|
313
|
+
.gg-btn-icon {
|
|
314
|
+
display: inline;
|
|
315
|
+
}
|
|
316
|
+
.gg-toolbar button {
|
|
317
|
+
padding: 4px 8px;
|
|
318
|
+
min-width: 32px;
|
|
319
|
+
}
|
|
320
|
+
.gg-filter-btn {
|
|
321
|
+
font-family: monospace;
|
|
322
|
+
font-size: 12px;
|
|
323
|
+
}
|
|
324
|
+
/* Stack log entries vertically on mobile */
|
|
325
|
+
.gg-log-grid {
|
|
326
|
+
display: block;
|
|
327
|
+
}
|
|
328
|
+
.gg-log-entry {
|
|
329
|
+
display: block;
|
|
330
|
+
padding: 8px 0;
|
|
331
|
+
}
|
|
332
|
+
/* Remove double borders on mobile - only border on entry wrapper */
|
|
333
|
+
.gg-log-entry:not(:first-child) {
|
|
334
|
+
border-top: 1px solid rgba(0,0,0,0.05);
|
|
335
|
+
}
|
|
336
|
+
.gg-log-diff,
|
|
337
|
+
.gg-log-ns,
|
|
338
|
+
.gg-log-handle,
|
|
339
|
+
.gg-log-content,
|
|
340
|
+
.gg-details {
|
|
341
|
+
border-top: none !important;
|
|
342
|
+
}
|
|
343
|
+
.gg-log-header {
|
|
344
|
+
display: flex;
|
|
345
|
+
align-items: center;
|
|
346
|
+
gap: 8px;
|
|
347
|
+
margin-bottom: 4px;
|
|
348
|
+
min-width: 0;
|
|
349
|
+
}
|
|
350
|
+
/* Mobile: hover on container since it's not display: contents */
|
|
351
|
+
.gg-log-header.clickable {
|
|
352
|
+
padding: 2px 0;
|
|
353
|
+
}
|
|
354
|
+
.gg-log-header.clickable:hover {
|
|
355
|
+
background: rgba(0,0,0,0.05);
|
|
356
|
+
}
|
|
357
|
+
/* Override desktop child hover on mobile */
|
|
358
|
+
.gg-log-header.clickable:hover .gg-log-diff,
|
|
359
|
+
.gg-log-header.clickable:hover .gg-log-ns {
|
|
360
|
+
background: transparent;
|
|
361
|
+
}
|
|
362
|
+
.gg-log-diff {
|
|
363
|
+
padding: 0;
|
|
364
|
+
text-align: left;
|
|
365
|
+
flex-shrink: 0;
|
|
366
|
+
white-space: nowrap;
|
|
367
|
+
}
|
|
368
|
+
.gg-log-ns {
|
|
369
|
+
padding: 0;
|
|
370
|
+
flex: 1;
|
|
371
|
+
min-width: 0;
|
|
372
|
+
overflow: hidden;
|
|
373
|
+
text-overflow: ellipsis;
|
|
374
|
+
white-space: nowrap;
|
|
375
|
+
}
|
|
376
|
+
.gg-log-handle {
|
|
377
|
+
display: none;
|
|
378
|
+
}
|
|
379
|
+
.gg-log-content {
|
|
380
|
+
padding: 0;
|
|
381
|
+
padding-left: 0;
|
|
382
|
+
}
|
|
383
|
+
.gg-details {
|
|
384
|
+
margin-top: 4px;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
284
387
|
</style>
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
<
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
388
|
+
<div class="eruda-gg" style="padding: 10px; height: 100%; display: flex; flex-direction: column; font-size: 14px; touch-action: none; overscroll-behavior: contain;">
|
|
389
|
+
<div class="gg-toolbar">
|
|
390
|
+
<button class="gg-copy-btn">
|
|
391
|
+
<span class="gg-btn-text">Copy</span>
|
|
392
|
+
<span class="gg-btn-icon" title="Copy">📋</span>
|
|
393
|
+
</button>
|
|
394
|
+
<button class="gg-filter-btn" style="text-align: left; white-space: nowrap;">
|
|
395
|
+
<span class="gg-btn-text">Namespaces: </span>
|
|
396
|
+
<span class="gg-btn-icon">NS: </span>
|
|
397
|
+
<span class="gg-filter-summary"></span>
|
|
398
|
+
</button>
|
|
399
|
+
<span class="gg-count" style="opacity: 0.6; white-space: nowrap; flex: 1; text-align: right;"></span>
|
|
400
|
+
<button class="gg-clear-btn">
|
|
401
|
+
<span class="gg-btn-text">Clear</span>
|
|
402
|
+
<span class="gg-btn-icon" title="Clear">⊘</span>
|
|
403
|
+
</button>
|
|
404
|
+
</div>
|
|
292
405
|
<div class="gg-filter-panel"></div>
|
|
293
|
-
<div class="gg-log-container" style="flex: 1; overflow-y: auto; font-family: monospace; font-size: 12px;"></div>
|
|
406
|
+
<div class="gg-log-container" style="flex: 1; overflow-y: auto; font-family: monospace; font-size: 12px; touch-action: pan-y; overscroll-behavior: contain;"></div>
|
|
294
407
|
</div>
|
|
295
408
|
`;
|
|
296
409
|
}
|
|
@@ -340,6 +453,27 @@ export function createGgPlugin(options, gg) {
|
|
|
340
453
|
// Wire up checkboxes
|
|
341
454
|
filterPanel.addEventListener('change', (e) => {
|
|
342
455
|
const target = e.target;
|
|
456
|
+
// Handle ALL checkbox
|
|
457
|
+
if (target.classList.contains('gg-all-checkbox')) {
|
|
458
|
+
const allNamespaces = getAllCapturedNamespaces();
|
|
459
|
+
if (target.checked) {
|
|
460
|
+
// Select all
|
|
461
|
+
filterPattern = 'gg:*';
|
|
462
|
+
enabledNamespaces.clear();
|
|
463
|
+
allNamespaces.forEach((ns) => enabledNamespaces.add(ns));
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
// Deselect all
|
|
467
|
+
const exclusions = allNamespaces.map((ns) => `-${ns}`).join(',');
|
|
468
|
+
filterPattern = `gg:*,${exclusions}`;
|
|
469
|
+
enabledNamespaces.clear();
|
|
470
|
+
}
|
|
471
|
+
localStorage.setItem('debug', filterPattern);
|
|
472
|
+
renderFilterUI();
|
|
473
|
+
renderLogs();
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
// Handle individual namespace checkboxes
|
|
343
477
|
if (target.classList.contains('gg-ns-checkbox')) {
|
|
344
478
|
const namespace = target.getAttribute('data-namespace');
|
|
345
479
|
if (!namespace)
|
|
@@ -355,11 +489,13 @@ export function createGgPlugin(options, gg) {
|
|
|
355
489
|
function renderFilterUI() {
|
|
356
490
|
if (!$el)
|
|
357
491
|
return;
|
|
358
|
-
|
|
492
|
+
const allNamespaces = getAllCapturedNamespaces();
|
|
493
|
+
const enabledCount = enabledNamespaces.size;
|
|
494
|
+
const totalCount = allNamespaces.length;
|
|
495
|
+
// Update button summary with count of enabled namespaces
|
|
359
496
|
const filterSummary = $el.find('.gg-filter-summary').get(0);
|
|
360
497
|
if (filterSummary) {
|
|
361
|
-
|
|
362
|
-
filterSummary.textContent = summary;
|
|
498
|
+
filterSummary.textContent = `${enabledCount}/${totalCount}`;
|
|
363
499
|
}
|
|
364
500
|
// Update panel
|
|
365
501
|
const filterPanel = $el.find('.gg-filter-panel').get(0);
|
|
@@ -374,32 +510,37 @@ export function createGgPlugin(options, gg) {
|
|
|
374
510
|
const effectivePattern = filterPattern || 'gg:*';
|
|
375
511
|
let checkboxesHTML = '';
|
|
376
512
|
if (simple && allNamespaces.length > 0) {
|
|
513
|
+
const allChecked = enabledCount === totalCount;
|
|
377
514
|
checkboxesHTML = `
|
|
378
|
-
|
|
379
|
-
|
|
515
|
+
<div class="gg-filter-checkboxes">
|
|
516
|
+
<label class="gg-filter-checkbox" style="font-weight: bold;">
|
|
517
|
+
<input type="checkbox" class="gg-all-checkbox" ${allChecked ? 'checked' : ''}>
|
|
518
|
+
<span>ALL</span>
|
|
519
|
+
</label>
|
|
520
|
+
${allNamespaces
|
|
380
521
|
.map((ns) => {
|
|
381
522
|
// Check if namespace matches the current pattern
|
|
382
523
|
const checked = namespaceMatchesPattern(ns, effectivePattern);
|
|
383
524
|
return `
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
525
|
+
<label class="gg-filter-checkbox">
|
|
526
|
+
<input type="checkbox" class="gg-ns-checkbox" data-namespace="${escapeHtml(ns)}" ${checked ? 'checked' : ''}>
|
|
527
|
+
<span>${escapeHtml(ns)}</span>
|
|
528
|
+
</label>
|
|
529
|
+
`;
|
|
389
530
|
})
|
|
390
531
|
.join('')}
|
|
391
|
-
|
|
392
|
-
|
|
532
|
+
</div>
|
|
533
|
+
`;
|
|
393
534
|
}
|
|
394
535
|
else if (!simple) {
|
|
395
536
|
checkboxesHTML = `<div style="opacity: 0.6; font-size: 11px; margin: 8px 0;">⚠️ Complex pattern - edit manually (quick filters disabled)</div>`;
|
|
396
537
|
}
|
|
397
538
|
filterPanel.innerHTML = `
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
539
|
+
<div style="margin-bottom: 8px;">
|
|
540
|
+
<input type="text" class="gg-filter-pattern" value="${escapeHtml(filterPattern)}" placeholder="gg:*" style="width: 100%;">
|
|
541
|
+
</div>
|
|
542
|
+
${checkboxesHTML}
|
|
543
|
+
`;
|
|
403
544
|
}
|
|
404
545
|
else {
|
|
405
546
|
// Hide panel
|
|
@@ -470,9 +611,12 @@ export function createGgPlugin(options, gg) {
|
|
|
470
611
|
}
|
|
471
612
|
return;
|
|
472
613
|
}
|
|
473
|
-
// Handle
|
|
474
|
-
if
|
|
475
|
-
|
|
614
|
+
// Handle clickable header (when filters expanded)
|
|
615
|
+
// Skip if clicking on resize handle
|
|
616
|
+
if (!target?.classList?.contains('gg-log-handle') &&
|
|
617
|
+
target?.closest('.gg-log-header.clickable')) {
|
|
618
|
+
const header = target.closest('.gg-log-header.clickable');
|
|
619
|
+
const namespace = header.getAttribute('data-namespace');
|
|
476
620
|
if (!namespace)
|
|
477
621
|
return;
|
|
478
622
|
// Toggle this namespace off
|
|
@@ -567,25 +711,40 @@ export function createGgPlugin(options, gg) {
|
|
|
567
711
|
const jsonStr = escapeHtml(JSON.stringify(arg, null, 2));
|
|
568
712
|
const uniqueId = `${index}-${argIdx}`;
|
|
569
713
|
// Store details separately to render after the row
|
|
570
|
-
detailsHTML += `<div class="gg-details" data-index="${uniqueId}" style="display: none;
|
|
714
|
+
detailsHTML += `<div class="gg-details" data-index="${uniqueId}" style="display: none; margin: 4px 0 8px 0; padding: 8px; background: #f8f8f8; border-left: 3px solid ${color}; font-size: 11px; overflow-x: auto;"><pre style="margin: 0;">${jsonStr}</pre></div>`;
|
|
571
715
|
return `<span style="color: #888; cursor: pointer; text-decoration: underline;" class="gg-expand" data-index="${uniqueId}">${preview}</span>`;
|
|
572
716
|
}
|
|
573
717
|
else {
|
|
574
|
-
|
|
718
|
+
// Convert URLs to clickable links
|
|
719
|
+
const argStr = String(arg);
|
|
720
|
+
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
|
721
|
+
const linkedText = argStr.replace(urlRegex, (url) => {
|
|
722
|
+
return `<a href="${escapeHtml(url)}" target="_blank" style="color: #0066cc; text-decoration: underline;">${escapeHtml(url)}</a>`;
|
|
723
|
+
});
|
|
724
|
+
return `<span>${linkedText}</span>`;
|
|
575
725
|
}
|
|
576
726
|
})
|
|
577
727
|
.join(' ');
|
|
578
728
|
}
|
|
579
|
-
//
|
|
580
|
-
const
|
|
581
|
-
|
|
729
|
+
// Make header clickable when filters expanded
|
|
730
|
+
const headerClass = filterExpanded ? 'gg-log-header clickable' : 'gg-log-header';
|
|
731
|
+
const headerAttrs = filterExpanded
|
|
732
|
+
? ` data-namespace="${ns}" title="Click to hide this namespace"`
|
|
733
|
+
: '';
|
|
734
|
+
// Add × at start of diff when filters expanded (bold, darker)
|
|
735
|
+
const filterIcon = filterExpanded
|
|
736
|
+
? '<span style="font-weight: bold; color: #000; opacity: 0.6;">× </span>'
|
|
582
737
|
: '';
|
|
583
|
-
|
|
584
|
-
|
|
738
|
+
// Desktop: grid layout, Mobile: stacked layout
|
|
739
|
+
return (`<div class="gg-log-entry">` +
|
|
740
|
+
`<div class="${headerClass}"${headerAttrs}>` +
|
|
741
|
+
`<div class="gg-log-diff" style="color: ${color};">${filterIcon}${diff}</div>` +
|
|
585
742
|
`<div class="gg-log-ns" style="color: ${color};">${ns}</div>` +
|
|
586
743
|
`<div class="gg-log-handle"></div>` +
|
|
744
|
+
`</div>` +
|
|
587
745
|
`<div class="gg-log-content">${argsHTML}</div>` +
|
|
588
|
-
detailsHTML
|
|
746
|
+
detailsHTML +
|
|
747
|
+
`</div>`);
|
|
589
748
|
})
|
|
590
749
|
.join('')}</div>`;
|
|
591
750
|
logContainer.html(logsHTML);
|
package/dist/gg.d.ts
CHANGED
|
@@ -22,6 +22,13 @@ export declare namespace gg {
|
|
|
22
22
|
var disable: () => void;
|
|
23
23
|
var enable: (namespaces: string) => void;
|
|
24
24
|
var clearPersist: () => void;
|
|
25
|
-
var _onLog: OnLogCallback | null;
|
|
26
25
|
}
|
|
26
|
+
export declare namespace gg {
|
|
27
|
+
let _onLog: OnLogCallback | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run gg diagnostics and log configuration status
|
|
31
|
+
* Can be called immediately or delayed (e.g., after Eruda loads)
|
|
32
|
+
*/
|
|
33
|
+
export declare function runGgDiagnostics(): Promise<void>;
|
|
27
34
|
export {};
|
package/dist/gg.js
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import debugFactory from './debug.js';
|
|
2
2
|
import ErrorStackParser from 'error-stack-parser';
|
|
3
3
|
import { BROWSER, DEV } from 'esm-env';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a debug instance with custom formatArgs to add namespace padding
|
|
6
|
+
* Padding is done at format time, not in the namespace itself, to keep colors stable
|
|
7
|
+
*/
|
|
8
|
+
function createGgDebugger(namespace) {
|
|
9
|
+
const dbg = debugFactory(namespace);
|
|
10
|
+
// Store the original formatArgs (if it exists)
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
const originalFormatArgs = dbg.formatArgs;
|
|
13
|
+
// Override formatArgs to add padding to the namespace display
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
dbg.formatArgs = function (args) {
|
|
16
|
+
// Call original formatArgs first
|
|
17
|
+
if (originalFormatArgs) {
|
|
18
|
+
originalFormatArgs.call(this, args);
|
|
19
|
+
}
|
|
20
|
+
// Extract the callpoint from namespace (strip 'gg:' prefix and any URL suffix)
|
|
21
|
+
const nsMatch = this.namespace.match(/^gg:([^h]+?)(?:http|$)/);
|
|
22
|
+
const callpoint = nsMatch ? nsMatch[1] : this.namespace.replace(/^gg:/, '');
|
|
23
|
+
const paddedCallpoint = callpoint.padEnd(maxCallpointLength, ' ');
|
|
24
|
+
// Replace the namespace in the formatted string with padded version
|
|
25
|
+
if (typeof args[0] === 'string') {
|
|
26
|
+
args[0] = args[0].replace(this.namespace, `gg:${paddedCallpoint}`);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return dbg;
|
|
30
|
+
}
|
|
4
31
|
// Helper to detect if we're running in CloudFlare Workers
|
|
5
32
|
const isCloudflareWorker = () => {
|
|
6
33
|
// Check for CloudFlare Workers-specific global
|
|
@@ -176,40 +203,57 @@ export function gg(...args) {
|
|
|
176
203
|
if (callpoint.length < 80 && callpoint.length > maxCallpointLength) {
|
|
177
204
|
maxCallpointLength = callpoint.length;
|
|
178
205
|
}
|
|
179
|
-
|
|
206
|
+
// Namespace without padding - keeps colors stable
|
|
207
|
+
// Editor link appended if enabled
|
|
208
|
+
namespace = `gg:${callpoint}${ggConfig.editorLink ? url : ''}`;
|
|
180
209
|
}
|
|
181
210
|
const ggLogFunction = namespaceToLogFunction.get(namespace) ||
|
|
182
|
-
namespaceToLogFunction.set(namespace,
|
|
211
|
+
namespaceToLogFunction.set(namespace, createGgDebugger(namespace)).get(namespace);
|
|
212
|
+
// Prepare args for logging
|
|
213
|
+
let logArgs;
|
|
214
|
+
let returnValue;
|
|
183
215
|
if (!args.length) {
|
|
184
|
-
|
|
185
|
-
|
|
216
|
+
// No arguments: log editor link
|
|
217
|
+
logArgs = [` 📝📝 ${url} 👀👀`];
|
|
218
|
+
returnValue = {
|
|
186
219
|
fileName,
|
|
187
220
|
functionName,
|
|
188
221
|
url,
|
|
189
222
|
stack
|
|
190
223
|
};
|
|
191
224
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
225
|
+
else if (args.length === 1) {
|
|
226
|
+
logArgs = [args[0]];
|
|
227
|
+
returnValue = args[0];
|
|
195
228
|
}
|
|
196
229
|
else {
|
|
197
|
-
|
|
230
|
+
logArgs = [args[0], ...args.slice(1)];
|
|
231
|
+
returnValue = args[0];
|
|
232
|
+
}
|
|
233
|
+
// Log to console via debug
|
|
234
|
+
if (logArgs.length === 1) {
|
|
235
|
+
ggLogFunction(logArgs[0]);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
ggLogFunction(logArgs[0], ...logArgs.slice(1));
|
|
198
239
|
}
|
|
199
240
|
// Call capture hook if registered (for Eruda plugin)
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
});
|
|
241
|
+
const entry = {
|
|
242
|
+
namespace,
|
|
243
|
+
color: ggLogFunction.color,
|
|
244
|
+
diff: ggLogFunction.diff || 0, // Millisecond diff from debug library
|
|
245
|
+
message: logArgs.length === 1 ? String(logArgs[0]) : logArgs.map(String).join(' '),
|
|
246
|
+
args: logArgs, // Keep raw args for object inspection
|
|
247
|
+
timestamp: Date.now()
|
|
248
|
+
};
|
|
249
|
+
if (_onLogCallback) {
|
|
250
|
+
_onLogCallback(entry);
|
|
211
251
|
}
|
|
212
|
-
|
|
252
|
+
else {
|
|
253
|
+
// Buffer early logs before Eruda initializes
|
|
254
|
+
earlyLogBuffer.push(entry);
|
|
255
|
+
}
|
|
256
|
+
return returnValue;
|
|
213
257
|
}
|
|
214
258
|
gg.disable = isCloudflareWorker() ? () => { } : debugFactory.disable;
|
|
215
259
|
gg.enable = isCloudflareWorker() ? () => { } : debugFactory.enable;
|
|
@@ -232,21 +276,47 @@ gg.clearPersist = () => {
|
|
|
232
276
|
* Hook for capturing gg() output (used by Eruda plugin)
|
|
233
277
|
* Set this to a callback function to receive log entries
|
|
234
278
|
*/
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
279
|
+
// Buffer for capturing early logs before Eruda initializes
|
|
280
|
+
const earlyLogBuffer = [];
|
|
281
|
+
let _onLogCallback = null;
|
|
282
|
+
// Proxy property that replays buffered logs when hook is registered
|
|
283
|
+
Object.defineProperty(gg, '_onLog', {
|
|
284
|
+
get() {
|
|
285
|
+
return _onLogCallback;
|
|
286
|
+
},
|
|
287
|
+
set(callback) {
|
|
288
|
+
_onLogCallback = callback;
|
|
289
|
+
// Replay buffered logs when callback is first registered
|
|
290
|
+
if (callback && earlyLogBuffer.length > 0) {
|
|
291
|
+
earlyLogBuffer.forEach((entry) => callback(entry));
|
|
292
|
+
earlyLogBuffer.length = 0; // Clear buffer after replay
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
// Namespace for adding properties to the gg function
|
|
297
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
298
|
+
(function (gg) {
|
|
299
|
+
})(gg || (gg = {}));
|
|
300
|
+
/**
|
|
301
|
+
* Run gg diagnostics and log configuration status
|
|
302
|
+
* Can be called immediately or delayed (e.g., after Eruda loads)
|
|
303
|
+
*/
|
|
304
|
+
export async function runGgDiagnostics() {
|
|
305
|
+
if (!ggConfig.showHints || isCloudflareWorker())
|
|
306
|
+
return;
|
|
238
307
|
const ggLogTest = debugFactory('gg:TEST');
|
|
239
308
|
let ggMessage = '\n';
|
|
240
309
|
// Utilities for forming ggMessage:
|
|
241
310
|
const message = (s) => (ggMessage += `${s}\n`);
|
|
242
311
|
const checkbox = (test) => (test ? '✅' : '❌');
|
|
243
312
|
const makeHint = (test, ifTrue, ifFalse = '') => (test ? ifTrue : ifFalse);
|
|
313
|
+
// Use plain console.log for diagnostics - appears in Eruda's Console tab
|
|
244
314
|
console.log(`Loaded gg module. Checking configuration...`);
|
|
245
315
|
if (ggConfig.enabled && ggLogTest.enabled) {
|
|
246
316
|
gg('If you can see this logg, gg configured correctly!');
|
|
247
317
|
message(`No problems detected:`);
|
|
248
318
|
if (BROWSER) {
|
|
249
|
-
message(`ℹ️ If gg output
|
|
319
|
+
message(`ℹ️ If gg output not visible: enable "Verbose" log level in DevTools, or check Eruda's GG tab.`);
|
|
250
320
|
}
|
|
251
321
|
}
|
|
252
322
|
else {
|
|
@@ -277,8 +347,18 @@ if (ggConfig.showHints && !isCloudflareWorker()) {
|
|
|
277
347
|
}
|
|
278
348
|
message(`${checkbox(ggLogTest.enabled)} DEBUG env variable: ${process?.env?.DEBUG}${hint}`);
|
|
279
349
|
}
|
|
350
|
+
// Use plain console.log for diagnostics - appears in Eruda's Console tab
|
|
280
351
|
console.log(ggMessage);
|
|
281
352
|
// Reset namespace width after configuration check
|
|
282
353
|
// This prevents the long callpoint from the config check from affecting subsequent logs
|
|
283
354
|
resetNamespaceWidth();
|
|
284
355
|
}
|
|
356
|
+
// Run diagnostics immediately on module load if Eruda is not being used
|
|
357
|
+
// (If Eruda will load, the loader will call runGgDiagnostics after Eruda is ready)
|
|
358
|
+
if (ggConfig.showHints && !isCloudflareWorker()) {
|
|
359
|
+
// Only run immediately if we're not in a context where Eruda might load
|
|
360
|
+
// In browser dev mode, assume Eruda might load and skip immediate diagnostics
|
|
361
|
+
if (!BROWSER || !DEV) {
|
|
362
|
+
runGgDiagnostics();
|
|
363
|
+
}
|
|
364
|
+
}
|