From 65f69d3e3ed9261578d60c372d79cbfe7326cb14 Mon Sep 17 00:00:00 2001 From: Neragoth Date: Mon, 18 Aug 2025 11:00:54 +0200 Subject: [PATCH] feat: add Windows support and clean up codebase - Add Windows compatibility for terminal width detection - Remove console.error statements for cleaner output - Remove DEBUG_FONT_INSTALL debug code - Remove unnecessary react-devtools-core dependency - Fix TypeScript/ESLint warnings - Add .gitattributes for consistent line endings (LF) - Improve error handling with silent failures Tested on Windows 11. Unable to test on macOS/Linux platforms. --- .gitattributes | 13 ++++++ CLAUDE.md | 18 +++++++- bun.lock | 67 ++++++++++++++++++++++++++- package.json | 4 +- src/ccstatusline.ts | 5 +-- src/tui/App.tsx | 7 +-- src/utils/claude-settings.ts | 4 +- src/utils/config.ts | 16 +++---- src/utils/powerline.ts | 24 ---------- src/utils/terminal.ts | 87 ++---------------------------------- src/widgets/GitBranch.ts | 2 +- src/widgets/GitChanges.ts | 4 +- 12 files changed, 113 insertions(+), 138 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ea316c8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +# Normalize line endings to LF for all text files +* text=auto eol=lf + +# Binary files +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.ttf binary +*.otf binary +*.woff binary +*.woff2 binary \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 9aeff20..1776b4b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,9 @@ bun run build # Creates dist/ccstatusline.js with Node.js 14+ compatibility # Lint and type check bun run lint # Runs TypeScript type checking and ESLint with auto-fix + +# Run specific components during development +bun run patch # Apply patches (required before development) ``` ## Architecture @@ -55,20 +58,31 @@ The project has dual runtime compatibility - works with both Bun and Node.js: - Handles terminal width detection and truncation - Applies colors, padding, and separators - Manages flex separator expansion + - Supports both normal and Powerline rendering modes - **powerline.ts**: Powerline font detection and installation - **claude-settings.ts**: Integration with Claude Code settings.json - **colors.ts**: Color definitions and ANSI code mapping +- **widgets.ts**: Widget registry and factory functions +- **terminal.ts**: Terminal width detection utilities +- **jsonl.ts**: JSONL parsing for token metrics and session data + +### Widgets (src/widgets/) +- Individual widget implementations for status line items +- Each widget exports render() and getDefaultColor() functions +- Includes built-in widgets: Model, GitBranch, TokensTotal, OutputStyle, BlockTimer, etc. +- Custom widgets: CustomText and CustomCommand for user-defined content ## Key Implementation Details -- **Cross-platform stdin reading**: Detects Bun vs Node.js environment and uses appropriate stdin API +- **Dual runtime support**: Detects Bun vs Node.js environment and uses appropriate stdin API - **Token metrics**: Parses Claude Code transcript files (JSONL format) to calculate token usage - **Git integration**: Uses child_process.execSync to get current branch and changes - **Terminal width management**: Three modes for handling width (full, full-minus-40, full-until-compact) - **Flex separators**: Special separator type that expands to fill available space -- **Powerline mode**: Optional Powerline-style rendering with arrow separators +- **Powerline mode**: Optional Powerline-style rendering with arrow separators and theme support - **Custom commands**: Execute shell commands and display output in status line - **Mergeable items**: Items can be merged together with or without padding +- **Widget registry**: Factory pattern for widget creation and type safety ## Bun Usage Preferences diff --git a/bun.lock b/bun.lock index 00e9146..e3d3e3d 100644 --- a/bun.lock +++ b/bun.lock @@ -25,6 +25,7 @@ "patch-package": "^8.0.0", "react": "^19.1.1", "react-devtools-core": "^6.1.5", + "rimraf": "^5.0.5", "tinyglobby": "^0.2.14", "typescript": "^5.9.2", "typescript-eslint": "^8.39.1", @@ -69,6 +70,8 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -77,6 +80,8 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.2.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/types": "^8.38.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-oY7GVkJGVMI5benlBDCaRrSC1qPasafyv5dOBLLv5MTilMGnErKhO6ziEfodDDIZbo5QxPUNW360VudJOFODMw=="], @@ -255,6 +260,8 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], @@ -339,6 +346,8 @@ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + "fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -359,7 +368,7 @@ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -477,6 +486,8 @@ "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], @@ -509,6 +520,8 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], @@ -521,6 +534,8 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "napi-postinstall": ["napi-postinstall@0.3.3", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow=="], @@ -559,6 +574,8 @@ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], @@ -573,6 +590,8 @@ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], @@ -607,7 +626,7 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -655,6 +674,8 @@ "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], @@ -667,6 +688,8 @@ "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], @@ -735,6 +758,8 @@ "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], @@ -751,6 +776,10 @@ "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -771,6 +800,10 @@ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "gradient-string/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "is-bun-module/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], @@ -779,6 +812,8 @@ "patch-package/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "patch-package/rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], + "patch-package/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], @@ -789,12 +824,40 @@ "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "gradient-string/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "patch-package/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "patch-package/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], } } diff --git a/package.json b/package.json index 878da43..84608ed 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "start": "bun run patch && bun run src/ccstatusline.ts", "statusline": "bun run src/ccstatusline.ts", "patch": "patch-package", - "build": "bun run patch && rm -rf dist/* && bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14", + "build": "bun run patch && rimraf dist && bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14", "postbuild": "bun run scripts/replace-version.ts", "prepublishOnly": "bun run build", "lint": "bun tsc --noEmit; eslint . --config eslint.config.js --max-warnings=999999 --fix" @@ -35,8 +35,8 @@ "ink-gradient": "^3.0.0", "ink-select-input": "^6.2.0", "patch-package": "^8.0.0", + "rimraf": "^5.0.5", "react": "^19.1.1", - "react-devtools-core": "^6.1.5", "tinyglobby": "^0.2.14", "typescript": "^5.9.2", "typescript-eslint": "^8.39.1" diff --git a/src/ccstatusline.ts b/src/ccstatusline.ts index 2c87ea6..b66ddaf 100644 --- a/src/ccstatusline.ts +++ b/src/ccstatusline.ts @@ -156,17 +156,14 @@ async function main() { // Parse and validate JSON in one step const result = StatusJSONSchema.safeParse(JSON.parse(input)); if (!result.success) { - console.error('Invalid status JSON format:', result.error.message); process.exit(1); } await renderMultipleLines(result.data); - } catch (error) { - console.error('Error parsing JSON:', error); + } catch { process.exit(1); } } else { - console.error('No input received'); process.exit(1); } } else { diff --git a/src/tui/App.tsx b/src/tui/App.tsx index 76787a6..134e4b2 100644 --- a/src/tui/App.tsx +++ b/src/tui/App.tsx @@ -26,7 +26,6 @@ import { saveSettings } from '../utils/config'; import { - checkPowerlineFonts, checkPowerlineFontsAsync, installPowerlineFonts, type PowerlineFontStatus @@ -81,11 +80,7 @@ export const App: React.FC = () => { }); void isInstalled().then(setIsClaudeInstalled); - // Check for Powerline fonts on startup (use sync version that doesn't call execSync) - const fontStatus = checkPowerlineFonts(); - setPowerlineFontStatus(fontStatus); - - // Optionally do the async check later (but not blocking React) + // Check for Powerline fonts on startup void checkPowerlineFontsAsync().then((asyncStatus) => { setPowerlineFontStatus(asyncStatus); }); diff --git a/src/utils/claude-settings.ts b/src/utils/claude-settings.ts index e205409..e4655d9 100644 --- a/src/utils/claude-settings.ts +++ b/src/utils/claude-settings.ts @@ -43,7 +43,9 @@ export async function isInstalled(): Promise { export function isBunxAvailable(): boolean { try { - execSync('which bunx', { stdio: 'ignore' }); + // The `where` command is the equivalent of `which` on Windows. + const command = os.platform() === 'win32' ? 'where' : 'which'; + execSync(`${command} bunx`, { stdio: 'ignore' }); return true; } catch { return false; diff --git a/src/utils/config.ts b/src/utils/config.ts index 5d60d79..83c05e1 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -28,10 +28,9 @@ async function backupBadSettings(): Promise { if (fs.existsSync(SETTINGS_PATH)) { const content = await readFile(SETTINGS_PATH, 'utf-8'); await writeFile(SETTINGS_BACKUP_PATH, content, 'utf-8'); - console.error(`Bad settings backed up to ${SETTINGS_BACKUP_PATH}`); } - } catch (error) { - console.error('Failed to backup bad settings:', error); + } catch { + // Ignore backup errors } } @@ -45,9 +44,8 @@ async function writeDefaultSettings(): Promise { try { await mkdir(CONFIG_DIR, { recursive: true }); await writeFile(SETTINGS_PATH, JSON.stringify(settingsWithVersion, null, 2), 'utf-8'); - console.error(`Default settings written to ${SETTINGS_PATH}`); - } catch (error) { - console.error('Failed to write default settings:', error); + } catch { + // Ignore write errors } return defaults; @@ -66,7 +64,6 @@ export async function loadSettings(): Promise { rawData = JSON.parse(content); } catch { // If we can't parse the JSON, backup and write defaults - console.error('Failed to parse settings.json, backing up and using defaults'); await backupBadSettings(); return await writeDefaultSettings(); } @@ -77,7 +74,6 @@ export async function loadSettings(): Promise { // Parse as v1 to validate before migration const v1Result = SettingsSchema_v1.safeParse(rawData); if (!v1Result.success) { - console.error('Invalid v1 settings format:', v1Result.error); await backupBadSettings(); return await writeDefaultSettings(); } @@ -95,15 +91,13 @@ export async function loadSettings(): Promise { // Parse with main schema which will apply all defaults const result = SettingsSchema.safeParse(rawData); if (!result.success) { - console.error('Failed to parse settings:', result.error); await backupBadSettings(); return await writeDefaultSettings(); } return result.data; - } catch (error) { + } catch { // Any other error, backup and write defaults - console.error('Error loading settings:', error); await backupBadSettings(); return await writeDefaultSettings(); } diff --git a/src/utils/powerline.ts b/src/utils/powerline.ts index 5076c12..245fdf1 100644 --- a/src/utils/powerline.ts +++ b/src/utils/powerline.ts @@ -8,21 +8,10 @@ import type { PowerlineFontStatus } from '../types/PowerlineFontStatus'; // Re-export for backward compatibility export type { PowerlineFontStatus }; -// Track if fonts were installed during this session (for DEBUG_FONT_INSTALL) -let fontsInstalledThisSession = false; - /** * Check if Powerline fonts are installed by testing if Powerline symbols render correctly */ export function checkPowerlineFonts(): PowerlineFontStatus { - // Debug mode: pretend fonts aren't installed (unless we installed them this session) - if (process.env.DEBUG_FONT_INSTALL === '1' && !fontsInstalledThisSession) { - return { - installed: false, - checkedSymbol: '\uE0B0' - }; - } - try { // Test if we can display the common Powerline separator symbols // These are the key characters that require Powerline fonts @@ -108,14 +97,6 @@ export async function checkPowerlineFontsAsync(): Promise { // Ensure this is always async await Promise.resolve(); - // Debug mode: pretend fonts aren't installed (unless we installed them this session) - if (process.env.DEBUG_FONT_INSTALL === '1' && !fontsInstalledThisSession) { - return { - installed: false, - checkedSymbol: '\uE0B0' - }; - } - try { // First do the quick synchronous check const quickCheck = checkPowerlineFonts(); @@ -232,11 +213,6 @@ export async function installPowerlineFonts(): Promise<{ success: boolean; messa } } - // Mark as installed for DEBUG_FONT_INSTALL mode - if (process.env.DEBUG_FONT_INSTALL === '1') { - fontsInstalledThisSession = true; - } - return { success: true, message: 'Powerline fonts installed successfully! Please restart your terminal and select a Powerline font (e.g., "Source Code Pro for Powerline", "Meslo LG S for Powerline", etc.)' diff --git a/src/utils/terminal.ts b/src/utils/terminal.ts index 394f5e3..cb26b0a 100644 --- a/src/utils/terminal.ts +++ b/src/utils/terminal.ts @@ -1,4 +1,3 @@ -import { execSync } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; @@ -34,48 +33,9 @@ export function getPackageVersion(): string { // Get terminal width export function getTerminalWidth(): number | null { - try { - // First try to get the tty of the parent process - const tty = execSync('ps -o tty= -p $(ps -o ppid= -p $$)', { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'], - shell: '/bin/sh' - }).trim(); - - // Check if we got a valid tty (not ?? which means no tty) - if (tty && tty !== '??' && tty !== '?') { - // Now get the terminal size - const width = execSync( - `stty size < /dev/${tty} | awk '{print $2}'`, - { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'], - shell: '/bin/sh' - } - ).trim(); - - const parsed = parseInt(width, 10); - if (!isNaN(parsed) && parsed > 0) { - return parsed; - } - } - } catch { - // Command failed, width detection not available - } - - // Fallback: try tput cols which might work in some environments - try { - const width = execSync('tput cols 2>/dev/null', { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'] - }).trim(); - - const parsed = parseInt(width, 10); - if (!isNaN(parsed) && parsed > 0) { - return parsed; - } - } catch { - // tput also failed + const width = process.stdout.columns; + if (width) { + return width; } return null; @@ -83,44 +43,5 @@ export function getTerminalWidth(): number | null { // Check if terminal width detection is available export function canDetectTerminalWidth(): boolean { - try { - // First try to get the tty of the parent process - const tty = execSync('ps -o tty= -p $(ps -o ppid= -p $$)', { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'], - shell: '/bin/sh' - }).trim(); - - // Check if we got a valid tty - if (tty && tty !== '??' && tty !== '?') { - const width = execSync( - `stty size < /dev/${tty} | awk '{print $2}'`, - { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'], - shell: '/bin/sh' - } - ).trim(); - - const parsed = parseInt(width, 10); - if (!isNaN(parsed) && parsed > 0) { - return true; - } - } - } catch { - // Try fallback - } - - // Fallback: try tput cols - try { - const width = execSync('tput cols 2>/dev/null', { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'ignore'] - }).trim(); - - const parsed = parseInt(width, 10); - return !isNaN(parsed) && parsed > 0; - } catch { - return false; - } + return Boolean(process.stdout.columns); } \ No newline at end of file diff --git a/src/widgets/GitBranch.ts b/src/widgets/GitBranch.ts index aceeaa0..c61984d 100644 --- a/src/widgets/GitBranch.ts +++ b/src/widgets/GitBranch.ts @@ -30,7 +30,7 @@ export class GitBranchWidget implements Widget { private getGitBranch(): string | null { try { - const branch = execSync('git branch --show-current 2>/dev/null', { + const branch = execSync('git branch --show-current', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim(); diff --git a/src/widgets/GitChanges.ts b/src/widgets/GitChanges.ts index 1234fb5..c2d8609 100644 --- a/src/widgets/GitChanges.ts +++ b/src/widgets/GitChanges.ts @@ -33,12 +33,12 @@ export class GitChangesWidget implements Widget { let totalInsertions = 0; let totalDeletions = 0; - const unstagedStat = execSync('git diff --shortstat 2>/dev/null', { + const unstagedStat = execSync('git diff --shortstat', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim(); - const stagedStat = execSync('git diff --cached --shortstat 2>/dev/null', { + const stagedStat = execSync('git diff --cached --shortstat', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();