@leftium/gg 0.0.37 → 0.0.39

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 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.).
@@ -1663,6 +1663,7 @@ export function createGgPlugin(options, gg) {
1663
1663
  * Supports:
1664
1664
  * - Basic 3/4-bit colors: \x1b[31m (fg red), \x1b[41m (bg red), \x1b[91m (bright fg), etc.
1665
1665
  * - 24-bit RGB: \x1b[38;2;r;g;bm (foreground), \x1b[48;2;r;g;bm (background)
1666
+ * - Text styles: \x1b[1m (bold), \x1b[2m (dim), \x1b[3m (italic), \x1b[4m (underline)
1666
1667
  * - Reset: \x1b[0m
1667
1668
  */
1668
1669
  function parseAnsiToHtml(text) {
@@ -1673,20 +1674,57 @@ export function createGgPlugin(options, gg) {
1673
1674
  let lastIndex = 0;
1674
1675
  let currentFg = null;
1675
1676
  let currentBg = null;
1677
+ let currentBold = false;
1678
+ let currentDim = false;
1679
+ let currentItalic = false;
1680
+ let currentUnderline = false;
1676
1681
  let match;
1677
1682
  while ((match = ansiRegex.exec(text)) !== null) {
1678
1683
  // Add text before this code (with current styling)
1679
1684
  const textBefore = text.slice(lastIndex, match.index);
1680
1685
  if (textBefore) {
1681
- html += wrapWithStyle(escapeHtml(textBefore), currentFg, currentBg);
1686
+ html += wrapWithStyle(escapeHtml(textBefore), currentFg, currentBg, currentBold, currentDim, currentItalic, currentUnderline);
1682
1687
  }
1683
1688
  // Parse the ANSI code
1684
1689
  const code = match[1];
1685
1690
  const parts = code.split(';').map(Number);
1686
1691
  if (parts[0] === 0) {
1687
- // Reset
1692
+ // Reset all
1688
1693
  currentFg = null;
1689
1694
  currentBg = null;
1695
+ currentBold = false;
1696
+ currentDim = false;
1697
+ currentItalic = false;
1698
+ currentUnderline = false;
1699
+ }
1700
+ else if (parts[0] === 1) {
1701
+ // Bold
1702
+ currentBold = true;
1703
+ }
1704
+ else if (parts[0] === 2) {
1705
+ // Dim/Faint
1706
+ currentDim = true;
1707
+ }
1708
+ else if (parts[0] === 3) {
1709
+ // Italic
1710
+ currentItalic = true;
1711
+ }
1712
+ else if (parts[0] === 4) {
1713
+ // Underline
1714
+ currentUnderline = true;
1715
+ }
1716
+ else if (parts[0] === 22) {
1717
+ // Normal intensity (not bold, not dim)
1718
+ currentBold = false;
1719
+ currentDim = false;
1720
+ }
1721
+ else if (parts[0] === 23) {
1722
+ // Not italic
1723
+ currentItalic = false;
1724
+ }
1725
+ else if (parts[0] === 24) {
1726
+ // Not underlined
1727
+ currentUnderline = false;
1690
1728
  }
1691
1729
  else if (parts[0] === 38 && parts[1] === 2 && parts.length >= 5) {
1692
1730
  // Foreground RGB: 38;2;r;g;b
@@ -1720,21 +1758,29 @@ export function createGgPlugin(options, gg) {
1720
1758
  // Add remaining text
1721
1759
  const remaining = text.slice(lastIndex);
1722
1760
  if (remaining) {
1723
- html += wrapWithStyle(escapeHtml(remaining), currentFg, currentBg);
1761
+ html += wrapWithStyle(escapeHtml(remaining), currentFg, currentBg, currentBold, currentDim, currentItalic, currentUnderline);
1724
1762
  }
1725
1763
  return html || escapeHtml(text);
1726
1764
  }
1727
1765
  /**
1728
- * Wrap text with inline color styles
1766
+ * Wrap text with inline color and text style CSS
1729
1767
  */
1730
- function wrapWithStyle(text, fg, bg) {
1731
- if (!fg && !bg)
1768
+ function wrapWithStyle(text, fg, bg, bold, dim, italic, underline) {
1769
+ if (!fg && !bg && !bold && !dim && !italic && !underline)
1732
1770
  return text;
1733
1771
  const styles = [];
1734
1772
  if (fg)
1735
1773
  styles.push(`color: ${fg}`);
1736
1774
  if (bg)
1737
1775
  styles.push(`background-color: ${bg}`);
1776
+ if (bold)
1777
+ styles.push('font-weight: bold');
1778
+ if (dim)
1779
+ styles.push('opacity: 0.6');
1780
+ if (italic)
1781
+ styles.push('font-style: italic');
1782
+ if (underline)
1783
+ styles.push('text-decoration: underline');
1738
1784
  return `<span style="${styles.join('; ')}">${text}</span>`;
1739
1785
  }
1740
1786
  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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leftium/gg",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Leftium/gg.git"
@@ -49,7 +49,6 @@
49
49
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
50
50
  "@types/node": "^25.2.2",
51
51
  "add": "^2.0.6",
52
- "eruda": "^3.4.3",
53
52
  "eslint": "^10.0.0",
54
53
  "eslint-config-prettier": "^10.1.8",
55
54
  "eslint-plugin-svelte": "^3.14.0",
@@ -74,6 +73,7 @@
74
73
  "@types/estree": "^1.0.8",
75
74
  "acorn": "^8.15.0",
76
75
  "dotenv": "^17.2.4",
76
+ "eruda": "^3.4.3",
77
77
  "esm-env": "^1.2.2",
78
78
  "launch-editor": "^2.12.0"
79
79
  },