@leftium/gg 0.0.38 → 0.0.40
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/README.md +43 -0
- package/dist/eruda/plugin.js +210 -10
- package/dist/gg-call-sites-plugin.js +3 -0
- package/dist/gg.d.ts +41 -0
- package/dist/gg.js +70 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,6 +159,49 @@ gg(fg('rgb(255,99,71)')`Tomato text`);
|
|
|
159
159
|
- Node.js terminal
|
|
160
160
|
- All environments that support ANSI escape codes
|
|
161
161
|
|
|
162
|
+
## Text Styling (ANSI)
|
|
163
|
+
|
|
164
|
+
Add visual emphasis to logs with `bold()`, `italic()`, `underline()`, and `dim()`. These can be used standalone or chained with colors:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
import { gg, fg, bg, bold, italic, underline, dim } from '@leftium/gg';
|
|
168
|
+
|
|
169
|
+
// Standalone text styles
|
|
170
|
+
gg(bold()`Bold text`);
|
|
171
|
+
gg(italic()`Italic text`);
|
|
172
|
+
gg(underline()`Underlined text`);
|
|
173
|
+
gg(dim()`Dimmed/faint text`);
|
|
174
|
+
|
|
175
|
+
// Combined with colors
|
|
176
|
+
gg(fg('red').bold()`Bold red error`);
|
|
177
|
+
gg(fg('green').bold()`Bold green success`);
|
|
178
|
+
gg(bg('yellow').italic()`Italic on yellow background`);
|
|
179
|
+
gg(fg('blue').underline()`Blue underlined text`);
|
|
180
|
+
|
|
181
|
+
// Multiple styles chained
|
|
182
|
+
gg(bold().italic()`Bold and italic`);
|
|
183
|
+
gg(fg('red').bold().underline()`Bold underlined red`);
|
|
184
|
+
|
|
185
|
+
// Reusable style presets
|
|
186
|
+
const finalStyle = fg('green').bold();
|
|
187
|
+
const interimStyle = fg('gray');
|
|
188
|
+
|
|
189
|
+
gg(finalStyle`final` + ' seg=0 "my name is John Kim Murphy" 96%');
|
|
190
|
+
gg(interimStyle`interim` + ' seg=0 "my name is John Kim Murphy" 90%');
|
|
191
|
+
|
|
192
|
+
// Mixed inline styling
|
|
193
|
+
gg(bold()`Important:` + ' normal text ' + italic()`with emphasis`);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Available styles:**
|
|
197
|
+
|
|
198
|
+
- `bold()` - Bold/strong text (font-weight: bold)
|
|
199
|
+
- `italic()` - Italic/emphasized text (font-style: italic)
|
|
200
|
+
- `underline()` - Underlined text (text-decoration: underline)
|
|
201
|
+
- `dim()` - Dimmed/faint text (opacity: 0.6)
|
|
202
|
+
|
|
203
|
+
Text styles work in the same environments as colors (browser console, GgConsole, terminal).
|
|
204
|
+
|
|
162
205
|
## Other Frameworks
|
|
163
206
|
|
|
164
207
|
`gg()` works in any JavaScript project. The Vite plugins work with any Vite-based framework (React, Vue, Solid, etc.).
|
package/dist/eruda/plugin.js
CHANGED
|
@@ -205,6 +205,41 @@ export function createGgPlugin(options, gg) {
|
|
|
205
205
|
}
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
|
+
function toggleNamespaces(namespaces, enable) {
|
|
209
|
+
const currentPattern = filterPattern || 'gg:*';
|
|
210
|
+
let parts = currentPattern
|
|
211
|
+
.split(',')
|
|
212
|
+
.map((p) => p.trim())
|
|
213
|
+
.filter(Boolean);
|
|
214
|
+
namespaces.forEach((namespace) => {
|
|
215
|
+
const ns = namespace.trim();
|
|
216
|
+
if (enable) {
|
|
217
|
+
// Remove any exclusion for this namespace
|
|
218
|
+
parts = parts.filter((p) => p !== `-${ns}`);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Add exclusion if not already present
|
|
222
|
+
const exclusion = `-${ns}`;
|
|
223
|
+
if (!parts.includes(exclusion)) {
|
|
224
|
+
parts.push(exclusion);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
filterPattern = parts.join(',');
|
|
229
|
+
// Simplify pattern
|
|
230
|
+
filterPattern = simplifyPattern(filterPattern);
|
|
231
|
+
// Sync enabledNamespaces from the NEW pattern
|
|
232
|
+
const allNamespaces = getAllCapturedNamespaces();
|
|
233
|
+
enabledNamespaces.clear();
|
|
234
|
+
const effectivePattern = filterPattern || 'gg:*';
|
|
235
|
+
allNamespaces.forEach((ns) => {
|
|
236
|
+
if (namespaceMatchesPattern(ns, effectivePattern)) {
|
|
237
|
+
enabledNamespaces.add(ns);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
// Persist the new pattern
|
|
241
|
+
localStorage.setItem(FILTER_KEY, filterPattern);
|
|
242
|
+
}
|
|
208
243
|
function soloNamespace(namespace) {
|
|
209
244
|
const ns = namespace.trim();
|
|
210
245
|
// Toggle: if already soloed on this namespace, restore all
|
|
@@ -377,6 +412,20 @@ export function createGgPlugin(options, gg) {
|
|
|
377
412
|
.gg-log-ns[data-file]:hover::after {
|
|
378
413
|
opacity: 1;
|
|
379
414
|
}
|
|
415
|
+
/* Clickable namespace segments */
|
|
416
|
+
.gg-ns-segment {
|
|
417
|
+
cursor: pointer;
|
|
418
|
+
padding: 1px 2px;
|
|
419
|
+
border-radius: 2px;
|
|
420
|
+
transition: background 0.1s;
|
|
421
|
+
}
|
|
422
|
+
/* Only show segment hover styling when in filter mode */
|
|
423
|
+
.gg-log-grid.filter-mode .gg-ns-segment:hover {
|
|
424
|
+
background: rgba(0,0,0,0.1);
|
|
425
|
+
text-decoration: underline;
|
|
426
|
+
text-decoration-style: solid;
|
|
427
|
+
text-underline-offset: 2px;
|
|
428
|
+
}
|
|
380
429
|
.gg-details {
|
|
381
430
|
grid-column: 1 / -1;
|
|
382
431
|
border-top: none;
|
|
@@ -870,6 +919,19 @@ export function createGgPlugin(options, gg) {
|
|
|
870
919
|
renderLogs();
|
|
871
920
|
return;
|
|
872
921
|
}
|
|
922
|
+
// Handle "other" checkbox
|
|
923
|
+
if (target.classList.contains('gg-other-checkbox')) {
|
|
924
|
+
const otherNamespacesJson = target.getAttribute('data-other-namespaces');
|
|
925
|
+
if (!otherNamespacesJson)
|
|
926
|
+
return;
|
|
927
|
+
const otherNamespaces = JSON.parse(otherNamespacesJson);
|
|
928
|
+
// Toggle all "other" namespaces at once
|
|
929
|
+
toggleNamespaces(otherNamespaces, target.checked);
|
|
930
|
+
// localStorage already saved in toggleNamespaces()
|
|
931
|
+
renderFilterUI();
|
|
932
|
+
renderLogs();
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
873
935
|
// Handle individual namespace checkboxes
|
|
874
936
|
if (target.classList.contains('gg-ns-checkbox')) {
|
|
875
937
|
const namespace = target.getAttribute('data-namespace');
|
|
@@ -908,24 +970,50 @@ export function createGgPlugin(options, gg) {
|
|
|
908
970
|
let checkboxesHTML = '';
|
|
909
971
|
if (simple && allNamespaces.length > 0) {
|
|
910
972
|
const allChecked = enabledCount === totalCount;
|
|
973
|
+
// Count frequency of each namespace in the buffer
|
|
974
|
+
const allEntries = buffer.getEntries();
|
|
975
|
+
const nsCounts = new Map();
|
|
976
|
+
allEntries.forEach((entry) => {
|
|
977
|
+
nsCounts.set(entry.namespace, (nsCounts.get(entry.namespace) || 0) + 1);
|
|
978
|
+
});
|
|
979
|
+
// Sort ALL namespaces by frequency (most common first)
|
|
980
|
+
const sortedAllNamespaces = [...allNamespaces].sort((a, b) => (nsCounts.get(b) || 0) - (nsCounts.get(a) || 0));
|
|
981
|
+
// Take top 5 most common (regardless of enabled state)
|
|
982
|
+
const displayedNamespaces = sortedAllNamespaces.slice(0, 5);
|
|
983
|
+
// Calculate "other" namespaces (not in top 5)
|
|
984
|
+
const displayedSet = new Set(displayedNamespaces);
|
|
985
|
+
const otherNamespaces = allNamespaces.filter((ns) => !displayedSet.has(ns));
|
|
986
|
+
const otherEnabledCount = otherNamespaces.filter((ns) => enabledNamespaces.has(ns)).length;
|
|
987
|
+
const otherTotalCount = otherNamespaces.length;
|
|
988
|
+
const otherChecked = otherEnabledCount > 0;
|
|
989
|
+
const otherCount = otherNamespaces.reduce((sum, ns) => sum + (nsCounts.get(ns) || 0), 0);
|
|
911
990
|
checkboxesHTML = `
|
|
912
991
|
<div class="gg-filter-checkboxes">
|
|
913
992
|
<label class="gg-filter-checkbox" style="font-weight: bold;">
|
|
914
993
|
<input type="checkbox" class="gg-all-checkbox" ${allChecked ? 'checked' : ''}>
|
|
915
994
|
<span>ALL</span>
|
|
916
995
|
</label>
|
|
917
|
-
${
|
|
996
|
+
${displayedNamespaces
|
|
918
997
|
.map((ns) => {
|
|
919
998
|
// Check if namespace matches the current pattern
|
|
920
999
|
const checked = namespaceMatchesPattern(ns, effectivePattern);
|
|
1000
|
+
const count = nsCounts.get(ns) || 0;
|
|
921
1001
|
return `
|
|
922
1002
|
<label class="gg-filter-checkbox">
|
|
923
1003
|
<input type="checkbox" class="gg-ns-checkbox" data-namespace="${escapeHtml(ns)}" ${checked ? 'checked' : ''}>
|
|
924
|
-
<span>${escapeHtml(ns)}</span>
|
|
1004
|
+
<span>${escapeHtml(ns)} (${count})</span>
|
|
925
1005
|
</label>
|
|
926
1006
|
`;
|
|
927
1007
|
})
|
|
928
1008
|
.join('')}
|
|
1009
|
+
${otherTotalCount > 0
|
|
1010
|
+
? `
|
|
1011
|
+
<label class="gg-filter-checkbox" style="opacity: 0.7;">
|
|
1012
|
+
<input type="checkbox" class="gg-other-checkbox" ${otherChecked ? 'checked' : ''} data-other-namespaces='${JSON.stringify(otherNamespaces)}'>
|
|
1013
|
+
<span>other (${otherCount})</span>
|
|
1014
|
+
</label>
|
|
1015
|
+
`
|
|
1016
|
+
: ''}
|
|
929
1017
|
</div>
|
|
930
1018
|
`;
|
|
931
1019
|
}
|
|
@@ -1282,6 +1370,38 @@ export function createGgPlugin(options, gg) {
|
|
|
1282
1370
|
}
|
|
1283
1371
|
return;
|
|
1284
1372
|
}
|
|
1373
|
+
// Handle clicking namespace segments
|
|
1374
|
+
if (target?.classList?.contains('gg-ns-segment')) {
|
|
1375
|
+
// When filter is collapsed, open in editor instead of filtering
|
|
1376
|
+
if (!filterExpanded) {
|
|
1377
|
+
const nsContainer = target.closest('.gg-log-ns');
|
|
1378
|
+
if (nsContainer?.hasAttribute('data-file')) {
|
|
1379
|
+
handleNamespaceClick(nsContainer);
|
|
1380
|
+
}
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
// When filter is expanded, apply hierarchical filtering
|
|
1384
|
+
const filter = target.getAttribute('data-filter');
|
|
1385
|
+
if (!filter)
|
|
1386
|
+
return;
|
|
1387
|
+
// Toggle behavior: if already at this filter, restore all
|
|
1388
|
+
if (filterPattern === filter) {
|
|
1389
|
+
filterPattern = 'gg:*';
|
|
1390
|
+
enabledNamespaces.clear();
|
|
1391
|
+
getAllCapturedNamespaces().forEach((ns) => enabledNamespaces.add(ns));
|
|
1392
|
+
}
|
|
1393
|
+
else {
|
|
1394
|
+
filterPattern = filter;
|
|
1395
|
+
enabledNamespaces.clear();
|
|
1396
|
+
getAllCapturedNamespaces()
|
|
1397
|
+
.filter((ns) => namespaceMatchesPattern(ns, filter))
|
|
1398
|
+
.forEach((ns) => enabledNamespaces.add(ns));
|
|
1399
|
+
}
|
|
1400
|
+
localStorage.setItem(FILTER_KEY, filterPattern);
|
|
1401
|
+
renderFilterUI();
|
|
1402
|
+
renderLogs();
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1285
1405
|
// Handle clicking namespace to open in editor (when filter collapsed)
|
|
1286
1406
|
if (target?.classList?.contains('gg-log-ns') &&
|
|
1287
1407
|
target.hasAttribute('data-file') &&
|
|
@@ -1462,11 +1582,45 @@ export function createGgPlugin(options, gg) {
|
|
|
1462
1582
|
logContainer.html('<div style="padding: 20px; text-align: center; opacity: 0.5;">No logs captured yet. Call gg() to see output here.</div>');
|
|
1463
1583
|
return;
|
|
1464
1584
|
}
|
|
1465
|
-
const logsHTML = `<div class="gg-log-grid" style="grid-template-columns: ${gridColumns()};">${entries
|
|
1585
|
+
const logsHTML = `<div class="gg-log-grid${filterExpanded ? ' filter-mode' : ''}" style="grid-template-columns: ${gridColumns()};">${entries
|
|
1466
1586
|
.map((entry, index) => {
|
|
1467
1587
|
const color = entry.color || '#0066cc';
|
|
1468
1588
|
const diff = `+${humanize(entry.diff)}`;
|
|
1469
1589
|
const ns = escapeHtml(entry.namespace);
|
|
1590
|
+
// Split namespace into clickable segments on multiple delimiters: : @ / - _
|
|
1591
|
+
const parts = entry.namespace.split(/([:\/@\-_])/);
|
|
1592
|
+
const nsSegments = [];
|
|
1593
|
+
const delimiters = [];
|
|
1594
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1595
|
+
if (i % 2 === 0) {
|
|
1596
|
+
// Even indices are segments
|
|
1597
|
+
if (parts[i])
|
|
1598
|
+
nsSegments.push(parts[i]);
|
|
1599
|
+
}
|
|
1600
|
+
else {
|
|
1601
|
+
// Odd indices are delimiters
|
|
1602
|
+
delimiters.push(parts[i]);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
let nsHTML = '';
|
|
1606
|
+
for (let i = 0; i < nsSegments.length; i++) {
|
|
1607
|
+
const segment = escapeHtml(nsSegments[i]);
|
|
1608
|
+
// Build filter pattern: reconstruct namespace up to this point
|
|
1609
|
+
let filterPattern = '';
|
|
1610
|
+
for (let j = 0; j <= i; j++) {
|
|
1611
|
+
filterPattern += nsSegments[j];
|
|
1612
|
+
if (j < i) {
|
|
1613
|
+
filterPattern += delimiters[j];
|
|
1614
|
+
}
|
|
1615
|
+
else if (j < nsSegments.length - 1) {
|
|
1616
|
+
filterPattern += delimiters[j] + '*';
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
nsHTML += `<span class="gg-ns-segment" data-filter="${escapeHtml(filterPattern)}">${segment}</span>`;
|
|
1620
|
+
if (i < nsSegments.length - 1) {
|
|
1621
|
+
nsHTML += escapeHtml(delimiters[i]);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1470
1624
|
// Format each arg individually - objects are expandable
|
|
1471
1625
|
let argsHTML = '';
|
|
1472
1626
|
let detailsHTML = '';
|
|
@@ -1569,7 +1723,7 @@ export function createGgPlugin(options, gg) {
|
|
|
1569
1723
|
`<div class="gg-log-header">` +
|
|
1570
1724
|
iconsCol +
|
|
1571
1725
|
`<div class="gg-log-diff${soloClass}" style="color: ${color};"${soloAttr}>${diff}</div>` +
|
|
1572
|
-
`<div class="gg-log-ns${soloClass}" style="color: ${color};"${
|
|
1726
|
+
`<div class="gg-log-ns${soloClass}" style="color: ${color};" data-namespace="${escapeHtml(entry.namespace)}"${fileAttr}${lineAttr}${colAttr}${fileTitle}>${nsHTML}</div>` +
|
|
1573
1727
|
`<div class="gg-log-handle"></div>` +
|
|
1574
1728
|
`</div>` +
|
|
1575
1729
|
`<div class="gg-log-content"${!entry.level && entry.src?.trim() && !/^['"`]/.test(entry.src) ? ` data-src="${escapeHtml(entry.src)}"` : ''}>${argsHTML}${stackHTML}</div>` +
|
|
@@ -1663,6 +1817,7 @@ export function createGgPlugin(options, gg) {
|
|
|
1663
1817
|
* Supports:
|
|
1664
1818
|
* - Basic 3/4-bit colors: \x1b[31m (fg red), \x1b[41m (bg red), \x1b[91m (bright fg), etc.
|
|
1665
1819
|
* - 24-bit RGB: \x1b[38;2;r;g;bm (foreground), \x1b[48;2;r;g;bm (background)
|
|
1820
|
+
* - Text styles: \x1b[1m (bold), \x1b[2m (dim), \x1b[3m (italic), \x1b[4m (underline)
|
|
1666
1821
|
* - Reset: \x1b[0m
|
|
1667
1822
|
*/
|
|
1668
1823
|
function parseAnsiToHtml(text) {
|
|
@@ -1673,20 +1828,57 @@ export function createGgPlugin(options, gg) {
|
|
|
1673
1828
|
let lastIndex = 0;
|
|
1674
1829
|
let currentFg = null;
|
|
1675
1830
|
let currentBg = null;
|
|
1831
|
+
let currentBold = false;
|
|
1832
|
+
let currentDim = false;
|
|
1833
|
+
let currentItalic = false;
|
|
1834
|
+
let currentUnderline = false;
|
|
1676
1835
|
let match;
|
|
1677
1836
|
while ((match = ansiRegex.exec(text)) !== null) {
|
|
1678
1837
|
// Add text before this code (with current styling)
|
|
1679
1838
|
const textBefore = text.slice(lastIndex, match.index);
|
|
1680
1839
|
if (textBefore) {
|
|
1681
|
-
html += wrapWithStyle(escapeHtml(textBefore), currentFg, currentBg);
|
|
1840
|
+
html += wrapWithStyle(escapeHtml(textBefore), currentFg, currentBg, currentBold, currentDim, currentItalic, currentUnderline);
|
|
1682
1841
|
}
|
|
1683
1842
|
// Parse the ANSI code
|
|
1684
1843
|
const code = match[1];
|
|
1685
1844
|
const parts = code.split(';').map(Number);
|
|
1686
1845
|
if (parts[0] === 0) {
|
|
1687
|
-
// Reset
|
|
1846
|
+
// Reset all
|
|
1688
1847
|
currentFg = null;
|
|
1689
1848
|
currentBg = null;
|
|
1849
|
+
currentBold = false;
|
|
1850
|
+
currentDim = false;
|
|
1851
|
+
currentItalic = false;
|
|
1852
|
+
currentUnderline = false;
|
|
1853
|
+
}
|
|
1854
|
+
else if (parts[0] === 1) {
|
|
1855
|
+
// Bold
|
|
1856
|
+
currentBold = true;
|
|
1857
|
+
}
|
|
1858
|
+
else if (parts[0] === 2) {
|
|
1859
|
+
// Dim/Faint
|
|
1860
|
+
currentDim = true;
|
|
1861
|
+
}
|
|
1862
|
+
else if (parts[0] === 3) {
|
|
1863
|
+
// Italic
|
|
1864
|
+
currentItalic = true;
|
|
1865
|
+
}
|
|
1866
|
+
else if (parts[0] === 4) {
|
|
1867
|
+
// Underline
|
|
1868
|
+
currentUnderline = true;
|
|
1869
|
+
}
|
|
1870
|
+
else if (parts[0] === 22) {
|
|
1871
|
+
// Normal intensity (not bold, not dim)
|
|
1872
|
+
currentBold = false;
|
|
1873
|
+
currentDim = false;
|
|
1874
|
+
}
|
|
1875
|
+
else if (parts[0] === 23) {
|
|
1876
|
+
// Not italic
|
|
1877
|
+
currentItalic = false;
|
|
1878
|
+
}
|
|
1879
|
+
else if (parts[0] === 24) {
|
|
1880
|
+
// Not underlined
|
|
1881
|
+
currentUnderline = false;
|
|
1690
1882
|
}
|
|
1691
1883
|
else if (parts[0] === 38 && parts[1] === 2 && parts.length >= 5) {
|
|
1692
1884
|
// Foreground RGB: 38;2;r;g;b
|
|
@@ -1720,21 +1912,29 @@ export function createGgPlugin(options, gg) {
|
|
|
1720
1912
|
// Add remaining text
|
|
1721
1913
|
const remaining = text.slice(lastIndex);
|
|
1722
1914
|
if (remaining) {
|
|
1723
|
-
html += wrapWithStyle(escapeHtml(remaining), currentFg, currentBg);
|
|
1915
|
+
html += wrapWithStyle(escapeHtml(remaining), currentFg, currentBg, currentBold, currentDim, currentItalic, currentUnderline);
|
|
1724
1916
|
}
|
|
1725
1917
|
return html || escapeHtml(text);
|
|
1726
1918
|
}
|
|
1727
1919
|
/**
|
|
1728
|
-
* Wrap text with inline color
|
|
1920
|
+
* Wrap text with inline color and text style CSS
|
|
1729
1921
|
*/
|
|
1730
|
-
function wrapWithStyle(text, fg, bg) {
|
|
1731
|
-
if (!fg && !bg)
|
|
1922
|
+
function wrapWithStyle(text, fg, bg, bold, dim, italic, underline) {
|
|
1923
|
+
if (!fg && !bg && !bold && !dim && !italic && !underline)
|
|
1732
1924
|
return text;
|
|
1733
1925
|
const styles = [];
|
|
1734
1926
|
if (fg)
|
|
1735
1927
|
styles.push(`color: ${fg}`);
|
|
1736
1928
|
if (bg)
|
|
1737
1929
|
styles.push(`background-color: ${bg}`);
|
|
1930
|
+
if (bold)
|
|
1931
|
+
styles.push('font-weight: bold');
|
|
1932
|
+
if (dim)
|
|
1933
|
+
styles.push('opacity: 0.6');
|
|
1934
|
+
if (italic)
|
|
1935
|
+
styles.push('font-style: italic');
|
|
1936
|
+
if (underline)
|
|
1937
|
+
styles.push('text-decoration: underline');
|
|
1738
1938
|
return `<span style="${styles.join('; ')}">${text}</span>`;
|
|
1739
1939
|
}
|
|
1740
1940
|
return plugin;
|
|
@@ -42,6 +42,9 @@ export default function ggCallSitesPlugin(options = {}) {
|
|
|
42
42
|
// This prevents rewriting library code (including gg itself when published)
|
|
43
43
|
if (id.includes('/node_modules/'))
|
|
44
44
|
return null;
|
|
45
|
+
// Don't transform the gg.ts file itself (contains gg function definitions)
|
|
46
|
+
if (id.includes('/gg.ts') || id.includes('/gg.js'))
|
|
47
|
+
return null;
|
|
45
48
|
// Quick bail: no gg calls in this file
|
|
46
49
|
if (!code.includes('gg(') &&
|
|
47
50
|
!code.includes('gg.ns(') &&
|
package/dist/gg.d.ts
CHANGED
|
@@ -70,6 +70,10 @@ type ColorTagFunction = (strings: TemplateStringsArray, ...values: unknown[]) =>
|
|
|
70
70
|
interface ChainableColorFn extends ColorTagFunction {
|
|
71
71
|
fg: (color: string) => ChainableColorFn;
|
|
72
72
|
bg: (color: string) => ChainableColorFn;
|
|
73
|
+
bold: () => ChainableColorFn;
|
|
74
|
+
italic: () => ChainableColorFn;
|
|
75
|
+
underline: () => ChainableColorFn;
|
|
76
|
+
dim: () => ChainableColorFn;
|
|
73
77
|
}
|
|
74
78
|
/**
|
|
75
79
|
* Foreground (text) color helper
|
|
@@ -91,6 +95,43 @@ export declare function fg(color: string): ChainableColorFn;
|
|
|
91
95
|
* gg(bg('green').fg('white')`Success!`);
|
|
92
96
|
*/
|
|
93
97
|
export declare function bg(color: string): ChainableColorFn;
|
|
98
|
+
/**
|
|
99
|
+
* Bold text style
|
|
100
|
+
* Can be used directly or chained with colors
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* gg(bold()`Important text`);
|
|
104
|
+
* gg(bold().fg('red')`Bold red error`);
|
|
105
|
+
* gg(fg('green').bold()`Bold green success`);
|
|
106
|
+
*/
|
|
107
|
+
export declare function bold(): ChainableColorFn;
|
|
108
|
+
/**
|
|
109
|
+
* Italic text style
|
|
110
|
+
* Can be used directly or chained with colors
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* gg(italic()`Emphasized text`);
|
|
114
|
+
* gg(italic().fg('blue')`Italic blue`);
|
|
115
|
+
*/
|
|
116
|
+
export declare function italic(): ChainableColorFn;
|
|
117
|
+
/**
|
|
118
|
+
* Underline text style
|
|
119
|
+
* Can be used directly or chained with colors
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* gg(underline()`Underlined text`);
|
|
123
|
+
* gg(underline().fg('cyan')`Underlined cyan`);
|
|
124
|
+
*/
|
|
125
|
+
export declare function underline(): ChainableColorFn;
|
|
126
|
+
/**
|
|
127
|
+
* Dim text style (faint/dimmed appearance)
|
|
128
|
+
* Can be used directly or chained with colors
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* gg(dim()`Less important text`);
|
|
132
|
+
* gg(dim().fg('white')`Dimmed white`);
|
|
133
|
+
*/
|
|
134
|
+
export declare function dim(): ChainableColorFn;
|
|
94
135
|
export declare namespace gg {
|
|
95
136
|
let _onLog: OnLogCallback | null;
|
|
96
137
|
let ns: (nsLabel: string, ...args: unknown[]) => unknown;
|
package/dist/gg.js
CHANGED
|
@@ -806,24 +806,45 @@ function parseColor(color) {
|
|
|
806
806
|
}
|
|
807
807
|
return null;
|
|
808
808
|
}
|
|
809
|
+
/**
|
|
810
|
+
* ANSI style codes for text formatting
|
|
811
|
+
*/
|
|
812
|
+
const STYLE_CODES = {
|
|
813
|
+
bold: '\x1b[1m',
|
|
814
|
+
dim: '\x1b[2m',
|
|
815
|
+
italic: '\x1b[3m',
|
|
816
|
+
underline: '\x1b[4m'
|
|
817
|
+
};
|
|
809
818
|
/**
|
|
810
819
|
* Internal helper to create chainable color function with method chaining
|
|
811
820
|
*/
|
|
812
|
-
function createColorFunction(fgCode = '', bgCode = '') {
|
|
821
|
+
function createColorFunction(fgCode = '', bgCode = '', styleCode = '') {
|
|
813
822
|
const tagFn = function (strings, ...values) {
|
|
814
823
|
const text = strings.reduce((acc, str, i) => acc + str + (values[i] !== undefined ? String(values[i]) : ''), '');
|
|
815
|
-
return fgCode + bgCode + text + '\x1b[0m';
|
|
824
|
+
return fgCode + bgCode + styleCode + text + '\x1b[0m';
|
|
816
825
|
};
|
|
817
826
|
// Add method chaining
|
|
818
827
|
tagFn.fg = (color) => {
|
|
819
828
|
const rgb = parseColor(color);
|
|
820
829
|
const newFgCode = rgb ? `\x1b[38;2;${rgb.r};${rgb.g};${rgb.b}m` : '';
|
|
821
|
-
return createColorFunction(newFgCode, bgCode);
|
|
830
|
+
return createColorFunction(newFgCode, bgCode, styleCode);
|
|
822
831
|
};
|
|
823
832
|
tagFn.bg = (color) => {
|
|
824
833
|
const rgb = parseColor(color);
|
|
825
834
|
const newBgCode = rgb ? `\x1b[48;2;${rgb.r};${rgb.g};${rgb.b}m` : '';
|
|
826
|
-
return createColorFunction(fgCode, newBgCode);
|
|
835
|
+
return createColorFunction(fgCode, newBgCode, styleCode);
|
|
836
|
+
};
|
|
837
|
+
tagFn.bold = () => {
|
|
838
|
+
return createColorFunction(fgCode, bgCode, styleCode + STYLE_CODES.bold);
|
|
839
|
+
};
|
|
840
|
+
tagFn.italic = () => {
|
|
841
|
+
return createColorFunction(fgCode, bgCode, styleCode + STYLE_CODES.italic);
|
|
842
|
+
};
|
|
843
|
+
tagFn.underline = () => {
|
|
844
|
+
return createColorFunction(fgCode, bgCode, styleCode + STYLE_CODES.underline);
|
|
845
|
+
};
|
|
846
|
+
tagFn.dim = () => {
|
|
847
|
+
return createColorFunction(fgCode, bgCode, styleCode + STYLE_CODES.dim);
|
|
827
848
|
};
|
|
828
849
|
return tagFn;
|
|
829
850
|
}
|
|
@@ -855,6 +876,51 @@ export function bg(color) {
|
|
|
855
876
|
const bgCode = rgb ? `\x1b[48;2;${rgb.r};${rgb.g};${rgb.b}m` : '';
|
|
856
877
|
return createColorFunction('', bgCode);
|
|
857
878
|
}
|
|
879
|
+
/**
|
|
880
|
+
* Bold text style
|
|
881
|
+
* Can be used directly or chained with colors
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* gg(bold()`Important text`);
|
|
885
|
+
* gg(bold().fg('red')`Bold red error`);
|
|
886
|
+
* gg(fg('green').bold()`Bold green success`);
|
|
887
|
+
*/
|
|
888
|
+
export function bold() {
|
|
889
|
+
return createColorFunction('', '', STYLE_CODES.bold);
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Italic text style
|
|
893
|
+
* Can be used directly or chained with colors
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* gg(italic()`Emphasized text`);
|
|
897
|
+
* gg(italic().fg('blue')`Italic blue`);
|
|
898
|
+
*/
|
|
899
|
+
export function italic() {
|
|
900
|
+
return createColorFunction('', '', STYLE_CODES.italic);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Underline text style
|
|
904
|
+
* Can be used directly or chained with colors
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* gg(underline()`Underlined text`);
|
|
908
|
+
* gg(underline().fg('cyan')`Underlined cyan`);
|
|
909
|
+
*/
|
|
910
|
+
export function underline() {
|
|
911
|
+
return createColorFunction('', '', STYLE_CODES.underline);
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Dim text style (faint/dimmed appearance)
|
|
915
|
+
* Can be used directly or chained with colors
|
|
916
|
+
*
|
|
917
|
+
* @example
|
|
918
|
+
* gg(dim()`Less important text`);
|
|
919
|
+
* gg(dim().fg('white')`Dimmed white`);
|
|
920
|
+
*/
|
|
921
|
+
export function dim() {
|
|
922
|
+
return createColorFunction('', '', STYLE_CODES.dim);
|
|
923
|
+
}
|
|
858
924
|
/**
|
|
859
925
|
* Hook for capturing gg() output (used by Eruda plugin)
|
|
860
926
|
* Set this to a callback function to receive log entries
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { gg, fg, bg } from './gg.js';
|
|
1
|
+
import { gg, fg, bg, bold, italic, underline, dim } from './gg.js';
|
|
2
2
|
import openInEditorPlugin from './open-in-editor.js';
|
|
3
3
|
import ggCallSitesPlugin from './gg-call-sites-plugin.js';
|
|
4
4
|
export { default as GgConsole } from './GgConsole.svelte';
|
|
5
|
-
export { gg, fg, bg, openInEditorPlugin, ggCallSitesPlugin };
|
|
5
|
+
export { gg, fg, bg, bold, italic, underline, dim, openInEditorPlugin, ggCallSitesPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Reexport your entry components here
|
|
2
|
-
import { gg, fg, bg } from './gg.js';
|
|
2
|
+
import { gg, fg, bg, bold, italic, underline, dim } from './gg.js';
|
|
3
3
|
import openInEditorPlugin from './open-in-editor.js';
|
|
4
4
|
import ggCallSitesPlugin from './gg-call-sites-plugin.js';
|
|
5
5
|
export { default as GgConsole } from './GgConsole.svelte';
|
|
6
|
-
export { gg, fg, bg, openInEditorPlugin, ggCallSitesPlugin };
|
|
6
|
+
export { gg, fg, bg, bold, italic, underline, dim, openInEditorPlugin, ggCallSitesPlugin };
|