├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ 3 | 4 | # TypeScript compilation output 5 | dist/ 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Environment files 15 | .env 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | # IDE files 22 | .idea/ 23 | .vscode/ 24 | *.swp 25 | *.swo 26 | 27 | # OS Files 28 | .DS_Store 29 | Thumbs.db -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to beautiful-console-ts 2 | 3 | Thank you for your interest in contributing to beautiful-console-ts! 🎉 4 | 5 | beautiful-console-ts에 기여하고 싶어 주셔서 감사합니다! 🎉 6 | 7 | ## Issues | 이슈 8 | 9 | If you find a bug or want to suggest a new feature: 10 | 11 | 버그를 발견하셨거나 새로운 기능을 제안하고 싶으시다면: 12 | 13 | 1. Check if there's already an issue for it 14 | 이미 같은 이슈가 있는지 확인해주세요. 15 | 16 | 2. For bug reports, please include: 17 | 버그 제보의 경우 다음 내용을 포함해주세요: 18 | - Steps to reproduce | 재현 방법 19 | - Expected behavior | 예상 동작 20 | - Actual behavior | 실제 동작 21 | - Environment (browser, OS) | 환경 (브라우저, OS) 22 | 23 | 3. For feature requests, please include: 24 | 새로운 기능 제안의 경우 다음 내용을 포함해주세요: 25 | - Use case | 사용 사례 26 | - Implementation ideas (if any) | 구현 아이디어 (있는 경우) 27 | 28 | ## Pull Requests | 풀 리퀘스트 29 | 30 | 1. Create an issue first to discuss your changes 31 | 먼저 이슈를 생성하여 변경 사항에 대해 논의해주세요. 32 | 33 | 2. Fork the repository and create a new branch 34 | 저장소를 포크하고 새로운 브랜치를 생성해주세요. 35 | 36 | 3. Follow the commit message convention: 37 | 커밋 메시지 규칙을 따라주세요: 38 | ``` 39 | type: subject 40 | 41 | body (optional) 42 | ``` 43 | Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` 44 | 45 | 4. Update documentation if needed 46 | 필요한 경우 문서를 업데이트해주세요. 47 | 48 | 49 | ## Code Style | 코드 스타일 50 | 51 | - Use TypeScript strict mode 52 | TypeScript strict 모드를 사용합니다. 53 | - Document public APIs with JSDoc (English and Korean) 54 | 공개 API는 JSDoc으로 문서화합니다 (영어와 한국어). 55 | - Keep it simple and focused 56 | 간단하고 집중된 코드를 작성해주세요. 57 | 58 | ## Questions? | 질문이 있으신가요? 59 | 60 | Feel free to create an issue with the `question` label. 61 | 'question' 라벨과 함께 이슈를 생성해주세요. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 lazy-sky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beautiful-console-ts 2 | 3 | ![npm](https://img.shields.io/npm/v/beautiful-console-ts) 4 | ![npm bundle size](https://img.shields.io/bundlephobia/min/beautiful-console-ts) 5 | ![npm downloads](https://img.shields.io/npm/dm/beautiful-console-ts) 6 | ![GitHub stars](https://img.shields.io/github/stars/lazy-sky/beautiful-console-ts) 7 | ![License](https://img.shields.io/npm/l/beautiful-console-ts) 8 | 9 | Enhanced browser console library with styling capabilities for frontend developers. Make your debugging more beautiful and readable. 10 | 11 | 프론트엔드 개발자를 위한 스타일링 기능이 강화된 브라우저 콘솔 라이브러리입니다. 디버깅을 더 예쁘고, 가독성 있게 만들어보세요. 12 | 13 | ## Features | 기능 14 | 15 | - Custom styled log output | 커스텀 스타일을 적용한 로그 출력 16 | - Status-based log styling (success, error, warning, info) | 상태별 로그 스타일링 (성공, 에러, 경고, 정보) 17 | - Grouped log output | 그룹화된 로그 출력 18 | - Enhanced table output | 향상된 테이블 출력 19 | - Gradient text support | 그라데이션 텍스트 지원 20 | - Object tree visualization | 객체 트리 시각화 21 | - Time measurement and performance analysis | 시간 측정 및 성능 분석 22 | - Progress bar output | 프로그레스 바 출력 23 | 24 | ## Installation | 설치 25 | 26 | ```bash 27 | npm install beautiful-console-ts 28 | ``` 29 | 30 | or | 또는 31 | 32 | ```bash 33 | yarn add beautiful-console-ts 34 | ``` 35 | 36 | ## Usage | 사용법 37 | 38 | ### Basic Usage | 기본 사용법 39 | 40 | ```typescript 41 | import { beautifulConsole as bc } from 'beautiful-console-ts'; 42 | 43 | // Basic log output | 기본 로그 출력 44 | bc.log('Hello, this is beautiful-console-ts by lazy-sky!'); 45 | 46 | // Log with custom style | 스타일이 적용된 로그 출력 47 | bc.log('Message with custom style', { 48 | color: '#FF5722', 49 | fontSize: '16px', 50 | fontWeight: 'bold', 51 | backgroundColor: '#FFFDE7', 52 | padding: '5px 10px', 53 | borderRadius: '4px', 54 | }); 55 | 56 | // Log with specific theme | 특정 테마로 로그 출력 57 | bc.log('Message in dark theme', { theme: 'dark' }); 58 | bc.log('Message in light theme', { theme: 'light' }); 59 | 60 | // Apply theme with custom style | 테마와 스타일을 함께 적용 61 | bc.log('Message with theme and custom style', { 62 | theme: 'dark', 63 | fontSize: '16px', 64 | fontWeight: 'bold' 65 | }); 66 | ``` 67 | ![image](https://github.com/user-attachments/assets/5fe27281-849c-4b68-a252-fb11695f5cdf) 68 | 69 | ### Status Logs | 상태별 로그 출력 70 | 71 | ```typescript 72 | // Status-based log output | 상태별 로그 출력 73 | bc.success('Operation completed successfully!'); 74 | bc.error('An error occurred.'); 75 | bc.warn('Warning: Please check the input.'); 76 | bc.info('Additional information.'); 77 | ``` 78 | ![image](https://github.com/user-attachments/assets/8af3c1c2-07d5-4aa5-877a-5d9f5b35e32c) 79 | 80 | ### Box Style and Gradient | 박스 스타일과 그라데이션 81 | 82 | ```typescript 83 | // Box style message | 박스 스타일의 메시지 84 | bc.box('Important Notice: System update available.'); 85 | 86 | // Gradient style | 그라데이션 스타일 87 | bc.gradient('Gradient message'); 88 | ``` 89 | ![image](https://github.com/user-attachments/assets/d75db4d4-b188-4afb-8519-47f8ab6a3296) 90 | 91 | ### Group Feature | 그룹 기능 92 | 93 | ```typescript 94 | // Group functionality | 그룹 기능 95 | bc.group({ 96 | title: 'User Information', 97 | collapsed: false, 98 | style: { color: '#E91E63' } 99 | }, () => { 100 | bc.log('Name: John Doe'); 101 | bc.log('Email: john@example.com'); 102 | bc.log('Role: Admin'); 103 | }); 104 | 105 | // Nested group example | 중첩 그룹 예제 106 | bc.group({ 107 | title: 'Application Status', 108 | style: { color: '#4CAF50' } 109 | }, () => { 110 | bc.log('Version: 1.2.3'); 111 | 112 | bc.group({ 113 | title: 'Network Status', 114 | collapsed: true, 115 | style: { color: '#2196F3' } 116 | }, () => { 117 | bc.log('Connection: Online'); 118 | bc.log('Latency: 120ms'); 119 | }); 120 | }); 121 | ``` 122 | ![image](https://github.com/user-attachments/assets/c2853de1-d320-42a3-b2d9-615cdc89f94d) 123 | 124 | ### Table Output | 테이블 출력 125 | 126 | ```typescript 127 | const users = [ 128 | { id: 1, name: 'John Doe', role: 'Admin', active: true }, 129 | { id: 2, name: 'Jane Smith', role: 'User', active: false }, 130 | { id: 3, name: 'Bob Johnson', role: 'Editor', active: true } 131 | ]; 132 | 133 | bc.table(users, { 134 | headers: ['id', 'name', 'role', 'active'], 135 | alternateRowColors: true, 136 | headerStyle: { 137 | backgroundColor: '#3F51B5', 138 | color: 'white' 139 | } 140 | }); 141 | ``` 142 | ![image](https://github.com/user-attachments/assets/5354d343-b81f-434d-825c-6ef406be6524) 143 | 144 | ### Time Measurement | 시간 측정 145 | 146 | ```typescript 147 | bc.timeStart('Data Loading'); 148 | // Perform time-consuming task | 시간이 걸리는 작업 수행 149 | fetchData().then(() => { 150 | bc.timeEnd('Data Loading'); 151 | }); 152 | ``` 153 | 154 | ### JSON Data and Object Tree | JSON 데이터 및 객체 트리 155 | 156 | ```typescript 157 | const config = { 158 | server: { host: 'localhost', port: 3000 }, 159 | database: { host: 'db.example.com', user: 'admin' } 160 | }; 161 | 162 | // Output JSON data | JSON 데이터 출력 163 | bc.json(config); 164 | 165 | // Output as object tree | 객체 트리 형태로 출력 166 | bc.objectTree(config, 'Application Config'); 167 | ``` 168 | ![image](https://github.com/user-attachments/assets/24ffbef8-2af3-453f-b485-26d1c4248f76) 169 | 170 | ### Progress Bar | 프로그레스 바 171 | 172 | ```typescript 173 | bc.log('File upload progress:'); 174 | bc.progress(25, 100); 175 | bc.progress(50, 100); 176 | bc.progress(75, 100); 177 | bc.progress(100, 100); 178 | ``` 179 | ![image](https://github.com/user-attachments/assets/56ab1d28-0edc-4741-a1dc-be9a3b7fd94a) 180 | 181 | ## Style Properties | 스타일 속성 182 | 183 | | Property | Example Values | Description | 184 | |----------|---------------|-------------| 185 | | color | '#FF5722', 'red' | Text color | 186 | | backgroundColor | '#FFFDE7', 'lightblue' | Background color | 187 | | fontSize | '14px', '1.2em' | Font size | 188 | | fontWeight | 'bold', 'normal' | Font weight | 189 | | fontStyle | 'italic', 'normal' | Font style | 190 | | textDecoration | 'underline', 'none' | Text decoration | 191 | | border | '1px solid #ccc' | Border style | 192 | | borderRadius | '5px', '50%' | Border radius | 193 | | padding | '5px 10px' | Inner spacing | 194 | | margin | '10px' | Outer spacing | 195 | 196 | ## 🤝 Contributing | 기여하기 197 | 198 | 이슈와 풀 리퀘스트는 언제나 환영합니다! 자세한 내용은 [CONTRIBUTING.md](CONTRIBUTING.md)를 참고해주세요. 199 | 200 | ## 💖 Supporting | 후원하기 201 | 202 | 이 프로젝트가 도움이 되었다면, ⭐️ 스타를 눌러주세요! 203 | 204 | ## 📜 License | 라이센스 205 | 206 | MIT © [lazy-sky](LICENSE) 207 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beautiful-console-ts", 3 | "version": "0.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "beautiful-console-ts", 9 | "version": "0.1.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "typescript": "^5.8.2" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beautiful-console-ts", 3 | "version": "0.1.6", 4 | "description": "스타일링 기능이 강화된 브라우저 콘솔 라이브러리", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "prepare": "npm run build" 10 | }, 11 | "keywords": [ 12 | "console", 13 | "debug", 14 | "typescript", 15 | "browser", 16 | "developer-tools", 17 | "logging", 18 | "styled-console" 19 | ], 20 | "author": "lazy-sky", 21 | "license": "MIT", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/lazy-sky/beautiful-console-ts.git" 25 | }, 26 | "files": [ 27 | "dist", 28 | "README.md", 29 | "LICENSE" 30 | ], 31 | "devDependencies": { 32 | "typescript": "^5.8.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | type Theme = 'light' | 'dark' | 'system'; 2 | type ThemeExcludeSystem = Exclude; 3 | type ConsoleStyle = { 4 | theme?: Theme; 5 | color?: string; 6 | backgroundColor?: string; 7 | fontSize?: string; 8 | fontWeight?: string; 9 | fontStyle?: string; 10 | textDecoration?: string; 11 | border?: string; 12 | borderRadius?: string; 13 | padding?: string; 14 | margin?: string; 15 | }; 16 | 17 | type GroupOptions = { 18 | title: string; 19 | collapsed?: boolean; 20 | style?: ConsoleStyle; 21 | }; 22 | 23 | type TableOptions = { 24 | headers?: string[]; 25 | style?: ConsoleStyle; 26 | headerStyle?: ConsoleStyle; 27 | rowStyles?: ConsoleStyle[]; 28 | alternateRowColors?: boolean; 29 | }; 30 | 31 | /** 32 | * Utility class for browser console styling 33 | * @example 34 | * ```ts 35 | * import { beautifulConsole as bc } from 'beautiful-console-ts'; 36 | * 37 | * // Basic usage 38 | * bc.log('Hello, World!'); 39 | * 40 | * // With custom style 41 | * bc.log('Styled message', { 42 | * color: '#FF5722', 43 | * fontSize: '16px', 44 | * backgroundColor: '#FFFDE7' 45 | * }); 46 | * 47 | * // With theme 48 | * bc.log('Dark theme message', { theme: 'dark' }); 49 | * ``` 50 | */ 51 | class BeautifulConsole { 52 | private readonly defaultTheme: Theme = 'system'; 53 | private readonly themeStyles: Record = { 54 | light: { 55 | backgroundColor: '#333333', 56 | color: '#d3d3d3', 57 | }, 58 | dark: { 59 | backgroundColor: '#ffffff', 60 | color: '#333333', 61 | }, 62 | }; 63 | 64 | private get currentTheme(): ThemeExcludeSystem { 65 | return this.isDarkMode ? 'dark' : 'light'; 66 | } 67 | 68 | private get isDarkMode(): boolean { 69 | if (typeof window !== 'undefined') { 70 | return window.matchMedia('(prefers-color-scheme: dark)').matches; 71 | } 72 | return false; 73 | } 74 | 75 | /** 76 | * Basic log output with styling 77 | * @param message - Message to output 78 | * @param style - Style to apply 79 | * @example 80 | * ```ts 81 | * // Basic log 82 | * bc.log('Hello, World!'); 83 | * 84 | * // With theme 85 | * bc.log('Dark theme', { theme: 'dark' }); 86 | * 87 | * // With custom style 88 | * bc.log('Custom style', { 89 | * color: 'white', 90 | * backgroundColor: '#2196F3', 91 | * padding: '5px' 92 | * }); 93 | * ``` 94 | */ 95 | log(message: any, style?: ConsoleStyle): void { 96 | const theme = style?.theme || this.defaultTheme; 97 | const themeStyle = 98 | theme === 'system' 99 | ? this.themeStyles[this.currentTheme] 100 | : this.themeStyles[theme as ThemeExcludeSystem]; 101 | 102 | const combinedStyle = { ...themeStyle, ...style }; 103 | delete combinedStyle.theme; 104 | 105 | const styleString = this.styleObjectToString(combinedStyle); 106 | 107 | if (typeof message === 'object') { 108 | console.log('%c Object:', styleString, message); 109 | } else { 110 | console.log(`%c${message}`, styleString); 111 | } 112 | } 113 | 114 | /** 115 | * Output error message 116 | * @param message - Error message to output 117 | * @param style - Additional style 118 | * @example 119 | * ```ts 120 | * bc.error('Failed to fetch data'); 121 | * 122 | * // With custom style 123 | * bc.error('Critical error', { 124 | * fontSize: '16px', 125 | * padding: '10px' 126 | * }); 127 | * ``` 128 | */ 129 | error(message: any, style?: ConsoleStyle): void { 130 | const errorStyle: ConsoleStyle = { 131 | color: 'white', 132 | backgroundColor: '#FF5555', 133 | fontWeight: 'bold', 134 | padding: '2px 5px', 135 | borderRadius: '3px', 136 | ...style, 137 | }; 138 | 139 | this.log(message, errorStyle); 140 | } 141 | 142 | /** 143 | * Output warning message 144 | * @param message - Warning message to output 145 | * @param style - Additional style 146 | * @example 147 | * ```ts 148 | * bc.warn('Deprecated feature'); 149 | * 150 | * // With custom style 151 | * bc.warn('Please update', { 152 | * fontSize: '14px', 153 | * fontWeight: 'bold' 154 | * }); 155 | * ``` 156 | */ 157 | warn(message: any, style?: ConsoleStyle): void { 158 | const warnStyle: ConsoleStyle = { 159 | color: 'black', 160 | backgroundColor: '#FFDD00', 161 | fontWeight: 'bold', 162 | padding: '2px 5px', 163 | borderRadius: '3px', 164 | ...style, 165 | }; 166 | 167 | this.log(message, warnStyle); 168 | } 169 | 170 | /** 171 | * Output success message 172 | * @param message - Success message to output 173 | * @param style - Additional style 174 | * @example 175 | * ```ts 176 | * bc.success('Data saved successfully'); 177 | * 178 | * // With custom style 179 | * bc.success('Task completed', { 180 | * padding: '8px', 181 | * borderRadius: '4px' 182 | * }); 183 | * ``` 184 | */ 185 | success(message: any, style?: ConsoleStyle): void { 186 | const successStyle: ConsoleStyle = { 187 | color: 'white', 188 | backgroundColor: '#4CAF50', 189 | fontWeight: 'bold', 190 | padding: '2px 5px', 191 | borderRadius: '3px', 192 | ...style, 193 | }; 194 | 195 | this.log(message, successStyle); 196 | } 197 | 198 | /** 199 | * Output info message 200 | * @param message - Info message to output 201 | * @param style - Additional style 202 | */ 203 | info(message: any, style?: ConsoleStyle): void { 204 | const infoStyle: ConsoleStyle = { 205 | color: 'white', 206 | backgroundColor: '#2196F3', 207 | fontWeight: 'bold', 208 | padding: '2px 5px', 209 | borderRadius: '3px', 210 | ...style, 211 | }; 212 | 213 | this.log(message, infoStyle); 214 | } 215 | 216 | /** 217 | * Output message in box style 218 | * @param message - Message to output 219 | * @param style - Additional style 220 | */ 221 | box(message: any, style?: ConsoleStyle): void { 222 | const boxStyle: ConsoleStyle = { 223 | border: '1px solid #ccc', 224 | borderRadius: '5px', 225 | padding: '10px', 226 | backgroundColor: '#f8f8f8', 227 | ...style, 228 | }; 229 | 230 | this.log(message, boxStyle); 231 | } 232 | 233 | /** 234 | * Output message in gradient style 235 | * @param message - Message to output 236 | * @param fromColor - Start color 237 | * @param toColor - End color 238 | * @example 239 | * ```ts 240 | * // Default gradient 241 | * bc.gradient('Beautiful gradient'); 242 | * 243 | * // Custom gradient colors 244 | * bc.gradient('Custom gradient', '#FF0080', '#7928CA'); 245 | * ``` 246 | */ 247 | gradient(message: string, fromColor = '#FF5722', toColor = '#2196F3'): void { 248 | console.log( 249 | `%c${message}`, 250 | `background: linear-gradient(to right, ${fromColor}, ${toColor}); color: white; padding: 5px; border-radius: 3px;`, 251 | ); 252 | } 253 | 254 | /** 255 | * Output grouped messages 256 | * @param options - Group options 257 | * @param callback - Callback function to execute inside the group 258 | * @example 259 | * ```ts 260 | * bc.group({ 261 | * title: 'User Info', 262 | * style: { color: '#E91E63' } 263 | * }, () => { 264 | * bc.log('Name: John'); 265 | * bc.log('Role: Admin'); 266 | * }); 267 | * 268 | * // Collapsed group 269 | * bc.group({ 270 | * title: 'Details', 271 | * collapsed: true 272 | * }, () => { 273 | * bc.log('Created: 2024-03-22'); 274 | * }); 275 | * ``` 276 | */ 277 | group(options: GroupOptions, callback: () => void): void { 278 | const { title, collapsed = false, style } = options; 279 | 280 | const groupStyle: ConsoleStyle = { 281 | color: '#444', 282 | fontWeight: 'bold', 283 | ...style, 284 | }; 285 | 286 | const styleString = this.styleObjectToString(groupStyle); 287 | 288 | if (collapsed) { 289 | console.groupCollapsed(`%c${title}`, styleString); 290 | } else { 291 | console.group(`%c${title}`, styleString); 292 | } 293 | 294 | callback(); 295 | console.groupEnd(); 296 | } 297 | 298 | /** 299 | * Output data in table format 300 | * @param data - Array of data to output 301 | * @param options - Table style options 302 | * @example 303 | * ```ts 304 | * const users = [ 305 | * { id: 1, name: 'John', role: 'Admin' }, 306 | * { id: 2, name: 'Jane', role: 'User' } 307 | * ]; 308 | * 309 | * bc.table(users, { 310 | * headers: ['id', 'name', 'role'], 311 | * alternateRowColors: true, 312 | * headerStyle: { 313 | * backgroundColor: '#3F51B5', 314 | * color: 'white' 315 | * } 316 | * }); 317 | * ``` 318 | */ 319 | table(data: any[], options?: TableOptions): void { 320 | if (!data.length) { 321 | console.log('No data to display'); 322 | return; 323 | } 324 | 325 | const { 326 | headers = Object.keys(data[0]), 327 | style, 328 | headerStyle, 329 | rowStyles, 330 | alternateRowColors = true, 331 | } = options || {}; 332 | 333 | const defaultHeaderStyle: ConsoleStyle = { 334 | color: 'white', 335 | backgroundColor: '#444', 336 | fontWeight: 'bold', 337 | padding: '5px', 338 | ...headerStyle, 339 | }; 340 | 341 | console.log( 342 | '%cTable Data:', 343 | this.styleObjectToString({ 344 | fontWeight: 'bold', 345 | fontSize: '14px', 346 | ...style, 347 | }), 348 | ); 349 | 350 | const headerStyleString = this.styleObjectToString(defaultHeaderStyle); 351 | console.log('%c' + headers.join('\t|\t'), headerStyleString); 352 | 353 | data.forEach((row, index) => { 354 | let rowStyle: ConsoleStyle = {}; 355 | 356 | if (rowStyles && rowStyles[index]) { 357 | rowStyle = rowStyles[index]; 358 | } else if (alternateRowColors) { 359 | rowStyle = { 360 | backgroundColor: index % 2 === 0 ? '#f5f5f5' : '#e9e9e9', 361 | }; 362 | } 363 | 364 | const rowData = headers.map((header) => row[header]).join('\t|\t'); 365 | console.log('%c' + rowData, this.styleObjectToString(rowStyle)); 366 | }); 367 | } 368 | 369 | /** 370 | * Start time measurement 371 | * @param label - Label for the task being measured 372 | * @param style - Additional style 373 | */ 374 | timeStart(label: string, style?: ConsoleStyle): void { 375 | const timeStyle: ConsoleStyle = { 376 | color: '#9C27B0', 377 | fontStyle: 'italic', 378 | ...style, 379 | }; 380 | 381 | this.log(`⏱️ Timer started: ${label}`, timeStyle); 382 | console.time(label); 383 | } 384 | 385 | /** 386 | * End time measurement and output result 387 | * @param label - Label for the task being measured 388 | * @param style - Additional style 389 | */ 390 | timeEnd(label: string, style?: ConsoleStyle): void { 391 | const timeStyle: ConsoleStyle = { 392 | color: '#9C27B0', 393 | fontStyle: 'italic', 394 | ...style, 395 | }; 396 | 397 | console.timeEnd(label); 398 | this.log(`⏱️ Timer ended: ${label}`, timeStyle); 399 | } 400 | 401 | /** 402 | * Output formatted JSON data 403 | * @param data - JSON data to output 404 | * @param expanded - Whether to output in expanded format 405 | */ 406 | json(data: any, expanded = true): void { 407 | if (expanded) { 408 | console.log(JSON.stringify(data, null, 2)); 409 | } else { 410 | console.log(JSON.stringify(data)); 411 | } 412 | } 413 | 414 | /** 415 | * Output object in tree format 416 | * @param obj - Object to output 417 | * @param name - Root name of the tree 418 | */ 419 | objectTree(obj: any, name = 'Object'): void { 420 | console.groupCollapsed(`%c${name}`, 'font-weight: bold; color: #2196F3;'); 421 | 422 | for (const key in obj) { 423 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 424 | const value = obj[key]; 425 | 426 | if (typeof value === 'object' && value !== null) { 427 | this.objectTree(value, key); 428 | } else { 429 | console.log(`%c${key}:`, 'font-weight: bold;', value); 430 | } 431 | } 432 | } 433 | 434 | console.groupEnd(); 435 | } 436 | 437 | /** 438 | * Output progress visualization 439 | * @param value - Current progress value 440 | * @param max - Maximum value 441 | * @param length - Length of progress bar 442 | * @example 443 | * ```ts 444 | * // Basic progress 445 | * bc.progress(50, 100); 446 | * 447 | * // Custom length 448 | * bc.progress(7, 10, 30); 449 | * 450 | * // Progress updates 451 | * let progress = 0; 452 | * const interval = setInterval(() => { 453 | * progress += 25; 454 | * bc.progress(progress); 455 | * if (progress >= 100) clearInterval(interval); 456 | * }, 1000); 457 | * ``` 458 | */ 459 | progress(value: number, max = 100, length = 20): void { 460 | const percentage = Math.round((value / max) * 100); 461 | const filledLength = Math.round((value / max) * length); 462 | 463 | const filledBar = '█'.repeat(filledLength); 464 | const emptyBar = '░'.repeat(length - filledLength); 465 | 466 | const progressBar = `${filledBar}${emptyBar} ${percentage}%`; 467 | 468 | let color = '#2196F3'; 469 | if (percentage < 30) { 470 | color = '#F44336'; 471 | } else if (percentage < 70) { 472 | color = '#FF9800'; 473 | } else { 474 | color = '#4CAF50'; 475 | } 476 | 477 | this.log(progressBar, { color }); 478 | } 479 | 480 | /** 481 | * Convert ConsoleStyle object to CSS style string 482 | * @param style - Style object to convert 483 | * @returns CSS style string 484 | */ 485 | private styleObjectToString(style: ConsoleStyle): string { 486 | return Object.entries(style) 487 | .filter(([, value]) => value !== undefined) 488 | .map(([key, value]) => { 489 | // camelCase to kebab-case conversion (e.g., backgroundColor -> background-color) 490 | const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase(); 491 | return `${cssKey}: ${value}`; 492 | }) 493 | .join('; '); 494 | } 495 | } 496 | 497 | export const beautifulConsole = new BeautifulConsole(); 498 | export default beautifulConsole; 499 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "./dist", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "lib": ["dom", "es2015", "es2016", "es2017"] 11 | }, 12 | "include": ["src"], 13 | "exclude": ["node_modules", "**/__tests__/*"] 14 | } --------------------------------------------------------------------------------