diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66c586e..bda8173 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,11 @@ on: branches: [ master, main ] pull_request: branches: [ master, main ] + workflow_dispatch: env: CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 jobs: test: @@ -40,18 +42,194 @@ jobs: - name: Run clippy run: cargo clippy -- -D warnings - build: - name: Build Check + build-nightly: + name: Build Nightly (${{ matrix.target }}) runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-22.04 + name: ccline-88cc-nightly-linux-x64 + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + name: ccline-88cc-nightly-linux-x64-static + - target: x86_64-pc-windows-msvc + os: windows-latest + name: ccline-88cc-nightly-windows-x64 + - target: x86_64-apple-darwin + os: macos-latest + name: ccline-88cc-nightly-macos-x64 + - target: aarch64-apple-darwin + os: macos-latest + name: ccline-88cc-nightly-macos-arm64 + steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Install musl tools + if: matrix.target == 'x86_64-unknown-linux-musl' + run: | + sudo apt-get update + sudo apt-get install -y musl-tools + + - name: Build binary + run: cargo build --release --target ${{ matrix.target }} + + - name: Package Linux/macOS + if: matrix.os != 'windows-latest' + run: | + mkdir -p dist + cp target/${{ matrix.target }}/release/ccline-88cc dist/ccline-88cc + cd dist + tar czf ../${{ matrix.name }}-${{ github.sha }}.tar.gz ccline-88cc + + - name: Package Windows + if: matrix.os == 'windows-latest' + run: | + mkdir dist + copy "target\${{ matrix.target }}\release\ccline-88cc.exe" "dist\ccline-88cc.exe" + cd dist + 7z a "..\${{ matrix.name }}-${{ github.sha }}.zip" ccline-88cc.exe + + - name: Upload nightly build artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }}-${{ github.sha }} + path: | + ${{ matrix.name }}-${{ github.sha }}.tar.gz + ${{ matrix.name }}-${{ github.sha }}.zip + retention-days: 30 + + build-summary: + name: Build Summary + runs-on: ubuntu-latest + needs: [test, build-nightly] + if: always() + steps: + - name: Print build summary + run: | + echo "## 🚀 Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [[ "${{ needs.test.result }}" == "success" ]]; then + echo "✅ **Tests:** Passed" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Tests:** Failed" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.build-nightly.result }}" == "success" ]]; then + echo "✅ **Build:** All platforms built successfully" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Nightly Builds Available:" >> $GITHUB_STEP_SUMMARY + echo "- Linux x64 (GNU libc)" >> $GITHUB_STEP_SUMMARY + echo "- Linux x64 (static/musl)" >> $GITHUB_STEP_SUMMARY + echo "- Windows x64" >> $GITHUB_STEP_SUMMARY + echo "- macOS x64 (Intel)" >> $GITHUB_STEP_SUMMARY + echo "- macOS ARM64 (Apple Silicon)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "🔗 Download artifacts from the [Actions tab](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Build:** Some platforms failed" >> $GITHUB_STEP_SUMMARY + fi + + nightly-release: + name: Create Nightly Release + runs-on: ubuntu-latest + needs: [test, build-nightly] + if: github.ref == 'refs/heads/master' && needs.test.result == 'success' && needs.build-nightly.result == 'success' + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: nightly-artifacts + + - name: Generate release date + id: date + run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + + - name: Generate nightly tag + id: tag + run: echo "tag=nightly-${{ steps.date.outputs.date }}-${{ github.sha }}" >> $GITHUB_OUTPUT + + - name: Delete existing nightly releases + run: | + # Delete previous nightly releases (keep only the latest) + gh release list --json tagName,isDraft | jq -r '.[] | select(.tagName | startswith("nightly-")) | select(.isDraft == false) | .tagName' | head -n 5 | while read tag; do + echo "Deleting old nightly release: $tag" + gh release delete "$tag" --yes || true + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create nightly release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.tag.outputs.tag }} + name: "Nightly Build (${{ steps.date.outputs.date }})" + body: | + 🌙 **Nightly Build** - ${{ steps.date.outputs.date }} + + **Commit:** ${{ github.sha }} + **Branch:** ${{ github.ref_name }} + **Build Time:** ${{ steps.date.outputs.date }} + + ## 📦 Available Downloads + + ### Linux + - **ccline-88cc-nightly-linux-x64**: Standard Linux x64 build (Ubuntu 22.04+) + - **ccline-88cc-nightly-linux-x64-static**: Static Linux x64 build (Universal compatibility) + + ### macOS + - **ccline-88cc-nightly-macos-x64**: Intel Mac build + - **ccline-88cc-nightly-macos-arm64**: Apple Silicon Mac build + + ### Windows + - **ccline-88cc-nightly-windows-x64**: Windows x64 build + + ## ⚠️ Important Notes + + - This is a **development build** from the latest `master` branch + - Use at your own risk - may contain bugs or incomplete features + - For stable releases, see the [Releases](https://github.com/${{ github.repository }}/releases) page + - Install via NPM: `npm install -g @byebyecode/ccline-88cc` (stable) + + ## 🚀 What's New + + Recent commits in this build: + + ``` + ${{ github.event.head_commit.message }} + ``` - - name: Build - run: cargo build --release \ No newline at end of file + --- + *Generated automatically from commit ${{ github.sha }}* + files: nightly-artifacts/*/* + draft: false + prerelease: true + make_latest: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8f517a..d12ef48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,19 +18,19 @@ jobs: include: - target: x86_64-unknown-linux-gnu os: ubuntu-22.04 - name: ccline-linux-x64.tar.gz + name: ccline-88cc-linux-x64.tar.gz - target: x86_64-unknown-linux-musl os: ubuntu-latest - name: ccline-linux-x64-static.tar.gz + name: ccline-88cc-linux-x64-static.tar.gz - target: x86_64-pc-windows-gnu os: ubuntu-latest - name: ccline-windows-x64.zip + name: ccline-88cc-windows-x64.zip - target: x86_64-apple-darwin os: macos-latest - name: ccline-macos-x64.tar.gz + name: ccline-88cc-macos-x64.tar.gz - target: aarch64-apple-darwin os: macos-latest - name: ccline-macos-arm64.tar.gz + name: ccline-88cc-macos-arm64.tar.gz steps: - name: Checkout @@ -60,17 +60,17 @@ jobs: if: matrix.os != 'windows-latest' && matrix.target != 'x86_64-pc-windows-gnu' run: | mkdir -p dist - cp target/${{ matrix.target }}/release/ccometixline dist/ccline + cp target/${{ matrix.target }}/release/ccline-88cc dist/ccline-88cc cd dist - tar czf ../${{ matrix.name }} ccline + tar czf ../${{ matrix.name }} ccline-88cc - name: Package Windows if: matrix.target == 'x86_64-pc-windows-gnu' run: | mkdir -p dist - cp target/${{ matrix.target }}/release/ccometixline.exe dist/ccline.exe + cp target/${{ matrix.target }}/release/ccline-88cc.exe dist/ccline-88cc.exe cd dist - zip ../${{ matrix.name }} ccline.exe + zip ../${{ matrix.name }} ccline-88cc.exe - name: Upload artifact uses: actions/upload-artifact@v4 @@ -111,24 +111,24 @@ jobs: mkdir -p extracted # macOS x64 - tar -xzf artifacts/ccline-macos-x64.tar.gz/ccline-macos-x64.tar.gz -C extracted - mv extracted/ccline extracted/ccline-darwin-x64 + tar -xzf artifacts/ccline-88cc-macos-x64.tar.gz/ccline-88cc-macos-x64.tar.gz -C extracted + mv extracted/ccline-88cc extracted/ccline-88cc-darwin-x64 # macOS ARM64 - tar -xzf artifacts/ccline-macos-arm64.tar.gz/ccline-macos-arm64.tar.gz -C extracted - mv extracted/ccline extracted/ccline-darwin-arm64 + tar -xzf artifacts/ccline-88cc-macos-arm64.tar.gz/ccline-88cc-macos-arm64.tar.gz -C extracted + mv extracted/ccline-88cc extracted/ccline-88cc-darwin-arm64 # Linux x64 - tar -xzf artifacts/ccline-linux-x64.tar.gz/ccline-linux-x64.tar.gz -C extracted - mv extracted/ccline extracted/ccline-linux-x64 + tar -xzf artifacts/ccline-88cc-linux-x64.tar.gz/ccline-88cc-linux-x64.tar.gz -C extracted + mv extracted/ccline-88cc extracted/ccline-88cc-linux-x64 # Linux musl (static) - tar -xzf artifacts/ccline-linux-x64-static.tar.gz/ccline-linux-x64-static.tar.gz -C extracted - mv extracted/ccline extracted/ccline-linux-x64-musl + tar -xzf artifacts/ccline-88cc-linux-x64-static.tar.gz/ccline-88cc-linux-x64-static.tar.gz -C extracted + mv extracted/ccline-88cc extracted/ccline-88cc-linux-x64-musl # Windows - unzip artifacts/ccline-windows-x64.zip/ccline-windows-x64.zip -d extracted - mv extracted/ccline.exe extracted/ccline-win32-x64.exe + unzip artifacts/ccline-88cc-windows-x64.zip/ccline-88cc-windows-x64.zip -d extracted + mv extracted/ccline-88cc.exe extracted/ccline-88cc-win32-x64.exe # List extracted files ls -la extracted/ @@ -137,20 +137,20 @@ jobs: run: | # Prepare packages with version management node npm/scripts/prepare-packages.js - + # Copy binaries to platform directories - cp extracted/ccline-darwin-x64 npm-publish/darwin-x64/ccline - cp extracted/ccline-darwin-arm64 npm-publish/darwin-arm64/ccline - cp extracted/ccline-linux-x64 npm-publish/linux-x64/ccline - cp extracted/ccline-linux-x64-musl npm-publish/linux-x64-musl/ccline - cp extracted/ccline-win32-x64.exe npm-publish/win32-x64/ccline.exe - + cp extracted/ccline-88cc-darwin-x64 npm-publish/darwin-x64/ccline-88cc + cp extracted/ccline-88cc-darwin-arm64 npm-publish/darwin-arm64/ccline-88cc + cp extracted/ccline-88cc-linux-x64 npm-publish/linux-x64/ccline-88cc + cp extracted/ccline-88cc-linux-x64-musl npm-publish/linux-x64-musl/ccline-88cc + cp extracted/ccline-88cc-win32-x64.exe npm-publish/win32-x64/ccline-88cc.exe + # Set executable permissions for Unix binaries - chmod +x npm-publish/darwin-x64/ccline - chmod +x npm-publish/darwin-arm64/ccline - chmod +x npm-publish/linux-x64/ccline - chmod +x npm-publish/linux-x64-musl/ccline - + chmod +x npm-publish/darwin-x64/ccline-88cc + chmod +x npm-publish/darwin-arm64/ccline-88cc + chmod +x npm-publish/linux-x64/ccline-88cc + chmod +x npm-publish/linux-x64-musl/ccline-88cc + # Verify packages echo "Package structure:" find npm-publish -name "package.json" -exec echo "=== {} ===" \; -exec head -5 {} \; @@ -161,11 +161,11 @@ jobs: run: | # Publish platform packages first for platform in darwin-x64 darwin-arm64 linux-x64 linux-x64-musl win32-x64; do - echo "📦 Publishing @cometix/ccline-$platform" + echo "📦 Publishing @byebyecode/ccline-88cc-$platform" cd npm-publish/$platform npm publish --access public cd ../.. - echo "✅ Published @cometix/ccline-$platform" + echo "✅ Published @byebyecode/ccline-88cc-$platform" done - name: Wait for NPM registry @@ -178,9 +178,9 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | cd npm-publish/main - echo "📦 Publishing @cometix/ccline" + echo "📦 Publishing @byebyecode/ccline-88cc" npm publish --access public - echo "✅ Published @cometix/ccline" + echo "✅ Published @byebyecode/ccline-88cc" echo "" echo "🎉 NPM packages published successfully!" - echo "Install with: npm install -g @cometix/ccline" \ No newline at end of file + echo "Install with: npm install -g @byebyecode/ccline-88cc" diff --git a/Cargo.lock b/Cargo.lock index ade23d8..f681f13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,8 +159,8 @@ dependencies = [ ] [[package]] -name = "ccometixline" -version = "1.0.7" +name = "ccometixline-88cc" +version = "1.0.13" dependencies = [ "ansi-to-tui", "ansi_term", diff --git a/Cargo.toml b/Cargo.toml index dd8e655..bf4c8dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "ccometixline" -version = "1.0.7" +name = "ccometixline-88cc" +version = "1.0.13" edition = "2021" -description = "CCometixLine (ccline) - High-performance Claude Code StatusLine tool written in Rust" -authors = ["Haleclipse"] +description = "CCometixLine (ccline) - High-performance Claude Code StatusLine tool written in Rust (88Code version)" +authors = ["Hobee Liu"] license = "MIT" -repository = "https://github.com/username/CCometixLine" +repository = "https://github.com/byebye-code/ccline-88cc" readme = "README.md" keywords = ["claude", "statusline", "powerline", "rust", "claude-code"] categories = ["command-line-utilities", "development-tools"] @@ -28,9 +28,12 @@ chrono = { version = "0.4", features = ["serde"], optional = true } dirs = { version = "5.0", optional = true } regex = "1.0" - +[[bin]] +name = "ccline-88cc" +path = "src/main.rs" [features] -default = ["tui", "self-update", "dirs"] +default = ["tui", "self-update", "dirs", "quota"] tui = ["ratatui", "crossterm", "ansi_term", "ansi-to-tui", "chrono"] self-update = ["ureq", "semver", "chrono", "dirs"] +quota = ["ureq", "dirs", "chrono"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..66d46c9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2024 Hobee Liu +Copyright (c) 2024 CCometixLine contributors (https://github.com/Haleclipse/CCometixLine) +Copyright (c) 2024 ccline-packycc contributors (https://github.com/ding113/ccline-packycc) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 91512b7..ef287ae 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ -# CCometixLine +# CCometixLine-88CC [English](README.md) | [中文](README.zh.md) -A high-performance Claude Code statusline tool written in Rust with Git integration, usage tracking, interactive TUI configuration, and Claude Code enhancement utilities. +A high-performance Claude Code statusline tool written in Rust with Git integration, usage tracking, interactive TUI configuration, API quota monitoring and Claude Code enhancement utilities. + +> **Maintained by [HoBeedzc](https://github.com/HoBeedzc)** - This is a specially adapted version of CCometixLine for 88Code service. The original CCometixLine was created by [Haleclipse](https://github.com/Haleclipse/CCometixLine) under MIT License. This project is also released under MIT License. +> +> This project also incorporates code from another MIT-licensed project, [ccline-packycc](https://github.com/ding113/ccline-packycc), with attribution retained. +> +> 88Code is a third-party Claude Code proxy service. This project is a voluntary third-party adaptation and is not affiliated with Anthropic or 88Code. 88Code websites: [88code](https://www.88code.org/). This project implements automatic adaptation for both endpoints. ![Language:Rust](https://img.shields.io/static/v1?label=Language&message=Rust&color=orange&style=flat-square) ![License:MIT](https://img.shields.io/static/v1?label=License&message=MIT&color=blue&style=flat-square) +[![CI](https://github.com/byebye-code/ccline-88cc/actions/workflows/ci.yml/badge.svg)](https://github.com/byebye-code/ccline-88cc/actions/workflows/ci.yml) ## Screenshots ![CCometixLine](assets/img1.png) -The statusline shows: Model | Directory | Git Branch Status | Context Window Information +The statusline shows: Model | Directory | Git Branch Status | Context Window | API Quota ## Features @@ -20,6 +27,7 @@ The statusline shows: Model | Directory | Git Branch Status | Context Window Inf - **Model display** with simplified Claude model names - **Usage tracking** based on transcript analysis - **Directory display** showing current workspace +- **API Quota display** showing current API quota - **Minimal design** using Nerd Font icons ### Interactive TUI Features @@ -43,24 +51,24 @@ Install via npm (works on all platforms): ```bash # Install globally -npm install -g @cometix/ccline +npm install -g @byebyecode/ccline-88cc # Or using yarn -yarn global add @cometix/ccline +yarn global add @byebyecode/ccline-88cc # Or using pnpm -pnpm add -g @cometix/ccline +pnpm add -g @byebyecode/ccline-88cc ``` Use npm mirror for faster download: ```bash -npm install -g @cometix/ccline --registry https://registry.npmmirror.com +npm install -g @byebyecode/ccline-88cc --registry https://registry.npmmirror.com ``` After installation: -- ✅ Global command `ccline` is available everywhere +- ✅ Global command `ccline-88cc` is available everywhere - ⚙️ Follow the configuration steps below to integrate with Claude Code -- 🎨 Run `ccline -c` to open configuration panel for theme selection +- 🎨 Run `ccline-88cc -c` to open configuration panel for theme selection ### Claude Code Configuration @@ -71,7 +79,7 @@ Add to your Claude Code `settings.json`: { "statusLine": { "type": "command", - "command": "~/.claude/ccline/ccline", + "command": "~/.claude/ccline/ccline-88cc", "padding": 0 } } @@ -82,7 +90,7 @@ Add to your Claude Code `settings.json`: { "statusLine": { "type": "command", - "command": "%USERPROFILE%\\.claude\\ccline\\ccline.exe", + "command": "%USERPROFILE%\\.claude\\ccline\\ccline-88cc.exe", "padding": 0 } } @@ -93,7 +101,7 @@ Add to your Claude Code `settings.json`: { "statusLine": { "type": "command", - "command": "ccline", + "command": "ccline-88cc", "padding": 0 } } @@ -103,33 +111,33 @@ Add to your Claude Code `settings.json`: ### Update ```bash -npm update -g @cometix/ccline +npm update -g @byebyecode/ccline-88cc ```
Manual Installation (Click to expand) -Alternatively, download from [Releases](https://github.com/Haleclipse/CCometixLine/releases): +Alternatively, download from [Releases](https://github.com/byebye-code/ccline-88cc/releases): #### Linux #### Option 1: Dynamic Binary (Recommended) ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-linux-x64.tar.gz -tar -xzf ccline-linux-x64.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-linux-x64.tar.gz +tar -xzf ccline-88cc-linux-x64.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` *Requires: Ubuntu 22.04+, CentOS 9+, Debian 11+, RHEL 9+ (glibc 2.35+)* #### Option 2: Static Binary (Universal Compatibility) ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-linux-x64-static.tar.gz -tar -xzf ccline-linux-x64-static.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-linux-x64-static.tar.gz +tar -xzf ccline-88cc-linux-x64-static.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` *Works on any Linux distribution (static, no dependencies)* @@ -137,30 +145,30 @@ chmod +x ~/.claude/ccline/ccline ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-macos-x64.tar.gz -tar -xzf ccline-macos-x64.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-macos-x64.tar.gz +tar -xzf ccline-88cc-macos-x64.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` #### macOS (Apple Silicon) ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-macos-arm64.tar.gz -tar -xzf ccline-macos-arm64.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-macos-arm64.tar.gz +tar -xzf ccline-88cc-macos-arm64.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` #### Windows ```powershell # Create directory and download -New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline" -Invoke-WebRequest -Uri "https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-windows-x64.zip" -OutFile "ccline-windows-x64.zip" -Expand-Archive -Path "ccline-windows-x64.zip" -DestinationPath "." -Move-Item "ccline.exe" "$env:USERPROFILE\.claude\ccline\" +New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline-88cc" +Invoke-WebRequest -Uri "https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-windows-x64.zip" -OutFile "ccline-88cc-windows-x64.zip" +Expand-Archive -Path "ccline-88cc-windows-x64.zip" -DestinationPath "." +Move-Item "ccline-88cc.exe" "$env:USERPROFILE\.claude\ccline-88cc\" ```
@@ -168,18 +176,18 @@ Move-Item "ccline.exe" "$env:USERPROFILE\.claude\ccline\" ### Build from Source ```bash -git clone https://github.com/Haleclipse/CCometixLine.git -cd CCometixLine +git clone https://github.com/byebye-code/ccline-88cc.git +cd ccline-88cc cargo build --release # Linux/macOS mkdir -p ~/.claude/ccline -cp target/release/ccometixline ~/.claude/ccline/ccline -chmod +x ~/.claude/ccline/ccline +cp target/release/ccometixline ~/.claude/ccline-88cc/ccline-88cc +chmod +x ~/.claude/ccline/ccline-88cc # Windows (PowerShell) -New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline" -copy target\release\ccometixline.exe "$env:USERPROFILE\.claude\ccline\ccline.exe" +New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline-88cc" +copy target\release\ccometixline.exe "$env:USERPROFILE\.claude\ccline-88cc\ccline-88cc.exe" ``` ## Usage @@ -188,45 +196,45 @@ copy target\release\ccometixline.exe "$env:USERPROFILE\.claude\ccline\ccline.exe ```bash # Initialize configuration file -ccline --init +ccline-88cc --init # Check configuration validity -ccline --check +ccline-88cc --check # Print current configuration -ccline --print +ccline-88cc --print # Enter TUI configuration mode -ccline --config +ccline-88cc --config ``` ### Theme Override ```bash # Temporarily use specific theme (overrides config file) -ccline --theme cometix -ccline --theme minimal -ccline --theme gruvbox -ccline --theme nord -ccline --theme powerline-dark - -# Or use custom theme files from ~/.claude/ccline/themes/ -ccline --theme my-custom-theme +ccline-88cc --theme cometix +ccline-88cc --theme minimal +ccline-88cc --theme gruvbox +ccline-88cc --theme nord +ccline-88cc --theme powerline-dark + +# Or use custom theme files from ~/.claude/ccline-88cc/themes/ +ccline-88cc --theme my-custom-theme ``` ### Claude Code Enhancement ```bash # Disable context warnings and enable verbose mode -ccline --patch /path/to/claude-code/cli.js +ccline-88cc --patch /path/to/claude-code/cli.js # Example for common installation -ccline --patch ~/.local/share/fnm/node-versions/v24.4.1/installation/lib/node_modules/@anthropic-ai/claude-code/cli.js +ccline-88cc --patch ~/.local/share/fnm/node-versions/v24.4.1/installation/lib/node_modules/@anthropic-ai/claude-code/cli.js ``` ## Default Segments -Displays: `Directory | Git Branch Status | Model | Context Window` +Displays: `Directory | Git Branch Status | Model | Context Window | API Quota` ### Git Status Indicators @@ -244,14 +252,27 @@ Shows simplified Claude model names: Token usage percentage based on transcript analysis with context limit tracking. +### API Quota Display +Smart monitoring of API usage: + +- **Usage display**: Shows subscription name and used/total credits (e.g., `Pro $0.06/$20.25`) +- **Auto-detection**: Automatically detects the correct API endpoint +- **Zero configuration**: Just provide your API key, everything else is automatic + +Supports multiple API key sources: + +- Environment variables: `C88_API_KEY`, `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` +- Claude Code settings.json +- Local API key file: `~/.claude/api_key` + ## Configuration CCometixLine supports full configuration via TOML files and interactive TUI: -- **Configuration file**: `~/.claude/ccline/config.toml` -- **Interactive TUI**: `ccline --config` for real-time editing with preview -- **Theme files**: `~/.claude/ccline/themes/*.toml` for custom themes -- **Automatic initialization**: `ccline --init` creates default configuration +- **Configuration file**: `~/.claude/ccline-88cc/config.toml` +- **Interactive TUI**: `ccline-88cc --config` for real-time editing with preview +- **Theme files**: `~/.claude/ccline-88cc/themes/*.toml` for custom themes +- **Automatic initialization**: `ccline-88cc --init` creates default configuration ### Available Segments @@ -300,6 +321,8 @@ Contributions are welcome! Please feel free to submit issues or pull requests. ## Related Projects - [tweakcc](https://github.com/Piebald-AI/tweakcc) - Command-line tool to customize your Claude Code themes, thinking verbs, and more. +- [CCometixLine](https://github.com/Haleclipse/CCometixLine) - Original high-performance Claude Code status line tool written in Rust (upstream project). +- [ccline-packycc](https://github.com/ding113/ccline-packycc) - Another high-performance Claude Code status line tool written in Rust. ## License @@ -307,4 +330,4 @@ This project is licensed under the [MIT License](LICENSE). ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=Haleclipse/CCometixLine&type=Date)](https://star-history.com/#Haleclipse/CCometixLine&Date) +[![Star History Chart](https://api.star-history.com/svg?repos=byebye-code/ccline-88cc&type=Date)](https://star-history.com/#byebye-code/ccline-88cc&Date) diff --git a/README.zh.md b/README.zh.md index 88656db..7684bd3 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,25 +1,33 @@ -# CCometixLine +# CCometixLine-88CC [English](README.md) | [中文](README.zh.md) -基于 Rust 的高性能 Claude Code 状态栏工具,集成 Git 信息、使用量跟踪、交互式 TUI 配置和 Claude Code 补丁工具。 +基于 Rust 的高性能 Claude Code 状态栏工具,集成 Git 信息、使用量跟踪、交互式 TUI 配置、API 配额监控和 Claude Code 增强工具。 + +> **由 [HoBeedzc](https://github.com/HoBeedzc) 维护** - 这是专为 88Code 服务特别适配的 CCometixLine 版本。原始 CCometixLine 由 [Haleclipse](https://github.com/Haleclipse/CCometixLine) 在 MIT 许可证下创建。本项目同样在 MIT 许可证下发布。 +> +> 本项目还整合了另一个 MIT 许可证项目 [ccline-packycc](https://github.com/ding113/ccline-packycc) 的代码,并保留了相应归属。 +> +> 88Code 是第三方 Claude Code 代理服务。本项目是自发的第三方适配,与 Anthropic 或 88Code 无关。88Code 网站:[88code](https://www.88code.org/)。本项目实现了对两个端点的自动适配。 ![Language:Rust](https://img.shields.io/static/v1?label=Language&message=Rust&color=orange&style=flat-square) ![License:MIT](https://img.shields.io/static/v1?label=License&message=MIT&color=blue&style=flat-square) +[![CI](https://github.com/byebye-code/ccline-88cc/actions/workflows/ci.yml/badge.svg)](https://github.com/byebye-code/ccline-88cc/actions/workflows/ci.yml) ## 截图 ![CCometixLine](assets/img1.png) -状态栏显示:模型 | 目录 | Git 分支状态 | 上下文窗口信息 +状态栏显示:模型 | 目录 | Git 分支状态 | 上下文窗口 | API 配额 ## 特性 ### 核心功能 - **Git 集成** 显示分支、状态和跟踪信息 - **模型显示** 简化的 Claude 模型名称 -- **使用量跟踪** 基于转录文件分析 +- **使用量跟踪** 基于转录文件分析 - **目录显示** 显示当前工作空间 +- **API 配额显示** 显示当前 API 配额 - **简洁设计** 使用 Nerd Font 图标 ### 交互式 TUI 功能 @@ -43,24 +51,24 @@ ```bash # 全局安装 -npm install -g @cometix/ccline +npm install -g @byebyecode/ccline-88cc # 或使用 yarn -yarn global add @cometix/ccline +yarn global add @byebyecode/ccline-88cc # 或使用 pnpm -pnpm add -g @cometix/ccline +pnpm add -g @byebyecode/ccline-88cc ``` 使用镜像源加速下载: ```bash -npm install -g @cometix/ccline --registry https://registry.npmmirror.com +npm install -g @byebyecode/ccline-88cc --registry https://registry.npmmirror.com ``` 安装后: -- ✅ 全局命令 `ccline` 可在任何地方使用 +- ✅ 全局命令 `ccline-88cc` 可在任何地方使用 - ⚙️ 按照下方提示进行配置以集成到 Claude Code -- 🎨 运行 `ccline -c` 打开配置面板进行主题选择 +- 🎨 运行 `ccline-88cc -c` 打开配置面板进行主题选择 ### Claude Code 配置 @@ -71,7 +79,7 @@ npm install -g @cometix/ccline --registry https://registry.npmmirror.com { "statusLine": { "type": "command", - "command": "~/.claude/ccline/ccline", + "command": "~/.claude/ccline/ccline-88cc", "padding": 0 } } @@ -82,7 +90,7 @@ npm install -g @cometix/ccline --registry https://registry.npmmirror.com { "statusLine": { "type": "command", - "command": "%USERPROFILE%\\.claude\\ccline\\ccline.exe", + "command": "%USERPROFILE%\\.claude\\ccline\\ccline-88cc.exe", "padding": 0 } } @@ -93,7 +101,7 @@ npm install -g @cometix/ccline --registry https://registry.npmmirror.com { "statusLine": { "type": "command", - "command": "ccline", + "command": "ccline-88cc", "padding": 0 } } @@ -103,33 +111,33 @@ npm install -g @cometix/ccline --registry https://registry.npmmirror.com ### 更新 ```bash -npm update -g @cometix/ccline +npm update -g @byebyecode/ccline-88cc ```
手动安装(点击展开) -或者从 [Releases](https://github.com/Haleclipse/CCometixLine/releases) 手动下载: +或者从 [Releases](https://github.com/byebye-code/ccline-88cc/releases) 手动下载: #### Linux #### 选项 1: 动态链接版本(推荐) ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-linux-x64.tar.gz -tar -xzf ccline-linux-x64.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-linux-x64.tar.gz +tar -xzf ccline-88cc-linux-x64.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` *系统要求: Ubuntu 22.04+, CentOS 9+, Debian 11+, RHEL 9+ (glibc 2.35+)* #### 选项 2: 静态链接版本(通用兼容) ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-linux-x64-static.tar.gz -tar -xzf ccline-linux-x64-static.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-linux-x64-static.tar.gz +tar -xzf ccline-88cc-linux-x64-static.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` *适用于任何 Linux 发行版(静态链接,无依赖)* @@ -137,30 +145,30 @@ chmod +x ~/.claude/ccline/ccline ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-macos-x64.tar.gz -tar -xzf ccline-macos-x64.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-macos-x64.tar.gz +tar -xzf ccline-88cc-macos-x64.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` #### macOS (Apple Silicon) ```bash mkdir -p ~/.claude/ccline -wget https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-macos-arm64.tar.gz -tar -xzf ccline-macos-arm64.tar.gz -cp ccline ~/.claude/ccline/ -chmod +x ~/.claude/ccline/ccline +wget https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-macos-arm64.tar.gz +tar -xzf ccline-88cc-macos-arm64.tar.gz +cp ccline-88cc ~/.claude/ccline/ +chmod +x ~/.claude/ccline/ccline-88cc ``` #### Windows ```powershell # 创建目录并下载 -New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline" -Invoke-WebRequest -Uri "https://github.com/Haleclipse/CCometixLine/releases/latest/download/ccline-windows-x64.zip" -OutFile "ccline-windows-x64.zip" -Expand-Archive -Path "ccline-windows-x64.zip" -DestinationPath "." -Move-Item "ccline.exe" "$env:USERPROFILE\.claude\ccline\" +New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline-88cc" +Invoke-WebRequest -Uri "https://github.com/byebye-code/ccline-88cc/releases/latest/download/ccline-88cc-windows-x64.zip" -OutFile "ccline-88cc-windows-x64.zip" +Expand-Archive -Path "ccline-88cc-windows-x64.zip" -DestinationPath "." +Move-Item "ccline-88cc.exe" "$env:USERPROFILE\.claude\ccline-88cc\" ```
@@ -168,10 +176,18 @@ Move-Item "ccline.exe" "$env:USERPROFILE\.claude\ccline\" ### 从源码构建 ```bash -git clone https://github.com/Haleclipse/CCometixLine.git -cd CCometixLine +git clone https://github.com/byebye-code/ccline-88cc.git +cd ccline-88cc cargo build --release -cp target/release/ccometixline ~/.claude/ccline/ccline + +# Linux/macOS +mkdir -p ~/.claude/ccline +cp target/release/ccometixline ~/.claude/ccline-88cc/ccline-88cc +chmod +x ~/.claude/ccline/ccline-88cc + +# Windows (PowerShell) +New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\ccline-88cc" +copy target\release\ccometixline.exe "$env:USERPROFILE\.claude\ccline-88cc\ccline-88cc.exe" ``` ## 使用 @@ -180,45 +196,45 @@ cp target/release/ccometixline ~/.claude/ccline/ccline ```bash # 初始化配置文件 -ccline --init +ccline-88cc --init # 检查配置有效性 -ccline --check +ccline-88cc --check # 打印当前配置 -ccline --print +ccline-88cc --print # 进入 TUI 配置模式 -ccline --config +ccline-88cc --config ``` ### 主题覆盖 ```bash # 临时使用指定主题(覆盖配置文件设置) -ccline --theme cometix -ccline --theme minimal -ccline --theme gruvbox -ccline --theme nord -ccline --theme powerline-dark - -# 或使用 ~/.claude/ccline/themes/ 目录下的自定义主题 -ccline --theme my-custom-theme +ccline-88cc --theme cometix +ccline-88cc --theme minimal +ccline-88cc --theme gruvbox +ccline-88cc --theme nord +ccline-88cc --theme powerline-dark + +# 或使用 ~/.claude/ccline-88cc/themes/ 目录下的自定义主题 +ccline-88cc --theme my-custom-theme ``` ### Claude Code 增强 ```bash # 禁用上下文警告并启用详细模式 -ccline --patch /path/to/claude-code/cli.js +ccline-88cc --patch /path/to/claude-code/cli.js # 常见安装路径示例 -ccline --patch ~/.local/share/fnm/node-versions/v24.4.1/installation/lib/node_modules/@anthropic-ai/claude-code/cli.js +ccline-88cc --patch ~/.local/share/fnm/node-versions/v24.4.1/installation/lib/node_modules/@anthropic-ai/claude-code/cli.js ``` ## 默认段落 -显示:`目录 | Git 分支状态 | 模型 | 上下文窗口` +显示:`目录 | Git 分支状态 | 模型 | 上下文窗口 | API 配额` ### Git 状态指示器 @@ -236,14 +252,27 @@ ccline --patch ~/.local/share/fnm/node-versions/v24.4.1/installation/lib/node_mo 基于转录文件分析的令牌使用百分比,包含上下文限制跟踪。 +### API 配额显示 +智能监控 API 使用情况: + +- **额度显示**: 显示套餐名称和已用/总额度 (例如 `Pro $0.06/$20.25`) +- **自动检测**: 自动检测正确的 API 端点 +- **零配置**: 只需提供 API 密钥,其他都是自动的 + +支持多种 API 密钥来源: + +- 环境变量: `C88_API_KEY`, `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` +- Claude Code settings.json +- 本地 API 密钥文件: `~/.claude/api_key` + ## 配置 CCometixLine 支持通过 TOML 文件和交互式 TUI 进行完整配置: -- **配置文件**: `~/.claude/ccline/config.toml` -- **交互式 TUI**: `ccline --config` 实时编辑配置并预览效果 -- **主题文件**: `~/.claude/ccline/themes/*.toml` 自定义主题文件 -- **自动初始化**: `ccline --init` 创建默认配置 +- **配置文件**: `~/.claude/ccline-88cc/config.toml` +- **交互式 TUI**: `ccline-88cc --config` 实时编辑配置并预览效果 +- **主题文件**: `~/.claude/ccline-88cc/themes/*.toml` 自定义主题文件 +- **自动初始化**: `ccline-88cc --init` 创建默认配置 ### 可用段落 @@ -290,10 +319,16 @@ cargo build --release 欢迎贡献!请随时提交 issue 或 pull request。 +## 相关项目 + +- [tweakcc](https://github.com/Piebald-AI/tweakcc) - 自定义 Claude Code 主题、思考动词等的命令行工具。 +- [CCometixLine](https://github.com/Haleclipse/CCometixLine) - 原始基于 Rust 的高性能 Claude Code 状态栏工具(上游项目)。 +- [ccline-packycc](https://github.com/ding113/ccline-packycc) - 另一个基于 Rust 的高性能 Claude Code 状态栏工具。 + ## 许可证 本项目采用 [MIT 许可证](LICENSE)。 ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=Haleclipse/CCometixLine&type=Date)](https://star-history.com/#Haleclipse/CCometixLine&Date) \ No newline at end of file +[![Star History Chart](https://api.star-history.com/svg?repos=byebye-code/ccline-88cc&type=Date)](https://star-history.com/#byebye-code/ccline-88cc&Date) \ No newline at end of file diff --git a/assets/img1.png b/assets/img1.png index 8887d92..375e657 100644 Binary files a/assets/img1.png and b/assets/img1.png differ diff --git a/npm/main/README.md b/npm/main/README.md index 49db2a7..7f9161d 100644 --- a/npm/main/README.md +++ b/npm/main/README.md @@ -1,11 +1,11 @@ -# @cometix/ccline +# @byebyecode/ccline-88cc -CCometixLine - High-performance Claude Code StatusLine tool +ccline-88cc - High-performance Claude Code StatusLine tool ## Installation ```bash -npm install -g @cometix/ccline +npm install -g @byebyecode/ccline-88cc ``` ## Features @@ -14,17 +14,18 @@ npm install -g @cometix/ccline - 🌍 **Cross-platform**: Works on Windows, macOS, and Linux - 📦 **Easy installation**: One command via npm - 🔄 **Auto-update**: Built-in update notifications +- 📊 **API quota monitoring**: Monitor your API quota usage - 🎨 **Beautiful**: Nerd Font icons and colors ## Usage -After installation, ccline is automatically configured for Claude Code at `~/.claude/ccline/ccline`. +After installation, ccline-88cc is automatically configured for Claude Code at `~/.claude/ccline/ccline-88cc`. You can also use it directly: ```bash -ccline --help -ccline --version +ccline-88cc --help +ccline-88cc --version ``` ## For Users in China @@ -32,11 +33,11 @@ ccline --version Use npm mirror for faster installation: ```bash -npm install -g @cometix/ccline --registry https://registry.npmmirror.com +npm install -g @hobeeliu/ccline-88cc --registry https://registry.npmmirror.com ``` ## More Information -- GitHub: https://github.com/Haleclipse/CCometixLine -- Issues: https://github.com/Haleclipse/CCometixLine/issues +- GitHub: https://github.com/byebye-code/ccline-88cc +- Issues: https://github.com/byebye-code/ccline-88cc/issues - License: MIT \ No newline at end of file diff --git a/npm/main/bin/ccline.js b/npm/main/bin/ccline-88cc.js similarity index 74% rename from npm/main/bin/ccline.js rename to npm/main/bin/ccline-88cc.js index 2fb4089..893e755 100755 --- a/npm/main/bin/ccline.js +++ b/npm/main/bin/ccline-88cc.js @@ -4,12 +4,12 @@ const path = require('path'); const fs = require('fs'); const os = require('os'); -// 1. Priority: Use ~/.claude/ccline/ccline if exists +// 1. Priority: Use ~/.claude/ccline/ccline-88cc if exists const claudePath = path.join( os.homedir(), '.claude', 'ccline', - process.platform === 'win32' ? 'ccline.exe' : 'ccline' + process.platform === 'win32' ? 'ccline-88cc.exe' : 'ccline-88cc' ); if (fs.existsSync(claudePath)) { @@ -58,29 +58,29 @@ if (platform === 'linux') { } const packageMap = { - 'darwin-x64': '@cometix/ccline-darwin-x64', - 'darwin-arm64': '@cometix/ccline-darwin-arm64', - 'linux-x64': '@cometix/ccline-linux-x64', - 'linux-x64-musl': '@cometix/ccline-linux-x64-musl', - 'win32-x64': '@cometix/ccline-win32-x64', - 'win32-ia32': '@cometix/ccline-win32-x64', // Use 64-bit for 32-bit systems + 'darwin-x64': '@byebyecode/ccline-88cc-darwin-x64', + 'darwin-arm64': '@byebyecode/ccline-88cc-darwin-arm64', + 'linux-x64': '@byebyecode/ccline-88cc-linux-x64', + 'linux-x64-musl': '@byebyecode/ccline-88cc-linux-x64-musl', + 'win32-x64': '@byebyecode/ccline-88cc-win32-x64', + 'win32-ia32': '@byebyecode/ccline-88cc-win32-x64', // Use 64-bit for 32-bit systems }; const packageName = packageMap[platformKey]; if (!packageName) { console.error(`Error: Unsupported platform ${platformKey}`); console.error('Supported platforms: darwin (x64/arm64), linux (x64), win32 (x64)'); - console.error('Please visit https://github.com/Haleclipse/CCometixLine for manual installation'); + console.error('Please visit https://github.com/byebye-code/ccline-88cc for manual installation'); process.exit(1); } -const binaryName = platform === 'win32' ? 'ccline.exe' : 'ccline'; +const binaryName = platform === 'win32' ? 'ccline-88cc.exe' : 'ccline-88cc'; const binaryPath = path.join(__dirname, '..', 'node_modules', packageName, binaryName); if (!fs.existsSync(binaryPath)) { console.error(`Error: Binary not found at ${binaryPath}`); console.error('This might indicate a failed installation or unsupported platform.'); - console.error('Please try reinstalling: npm install -g @cometix/ccline'); + console.error('Please try reinstalling: npm install -g @byebyecode/ccline-88cc'); console.error(`Expected package: ${packageName}`); process.exit(1); } diff --git a/npm/main/package.json b/npm/main/package.json index 3bc7d61..731887c 100644 --- a/npm/main/package.json +++ b/npm/main/package.json @@ -1,26 +1,26 @@ { - "name": "@cometix/ccline", + "name": "@byebyecode/ccline-88cc", "version": "0.0.0", - "description": "CCometixLine - High-performance Claude Code StatusLine tool", + "description": "ccline-88cc - High-performance Claude Code StatusLine tool with API quota monitoring", "bin": { - "ccline": "./bin/ccline.js" + "ccline-88cc": "./bin/ccline-88cc.js" }, "scripts": { "postinstall": "node scripts/postinstall.js" }, "optionalDependencies": { - "@cometix/ccline-darwin-x64": "0.0.0", - "@cometix/ccline-darwin-arm64": "0.0.0", - "@cometix/ccline-linux-x64": "0.0.0", - "@cometix/ccline-linux-x64-musl": "0.0.0", - "@cometix/ccline-win32-x64": "0.0.0" + "@byebyecode/ccline-88cc-darwin-x64": "0.0.0", + "@byebyecode/ccline-88cc-darwin-arm64": "0.0.0", + "@byebyecode/ccline-88cc-linux-x64": "0.0.0", + "@byebyecode/ccline-88cc-linux-x64-musl": "0.0.0", + "@byebyecode/ccline-88cc-win32-x64": "0.0.0" }, "repository": { "type": "git", - "url": "https://github.com/Haleclipse/CCometixLine" + "url": "https://github.com/byebye-code/ccline-88cc" }, "keywords": ["claude", "statusline", "claude-code", "rust", "cli"], - "author": "Haleclipse", + "author": "Hobee Liu", "license": "MIT", "engines": { "node": ">=14.0.0" diff --git a/npm/main/scripts/postinstall.js b/npm/main/scripts/postinstall.js index 639058b..1784ee0 100644 --- a/npm/main/scripts/postinstall.js +++ b/npm/main/scripts/postinstall.js @@ -53,12 +53,12 @@ try { } const packageMap = { - 'darwin-x64': '@cometix/ccline-darwin-x64', - 'darwin-arm64': '@cometix/ccline-darwin-arm64', - 'linux-x64': '@cometix/ccline-linux-x64', - 'linux-x64-musl': '@cometix/ccline-linux-x64-musl', - 'win32-x64': '@cometix/ccline-win32-x64', - 'win32-ia32': '@cometix/ccline-win32-x64', // Use 64-bit for 32-bit + 'darwin-x64': '@byebyecode/ccline-88cc-darwin-x64', + 'darwin-arm64': '@byebyecode/ccline-88cc-darwin-arm64', + 'linux-x64': '@byebyecode/ccline-88cc-linux-x64', + 'linux-x64-musl': '@byebyecode/ccline-88cc-linux-x64-musl', + 'win32-x64': '@byebyecode/ccline-88cc-win32-x64', + 'win32-ia32': '@byebyecode/ccline-88cc-win32-x64', // Use 64-bit for 32-bit }; const packageName = packageMap[platformKey]; @@ -69,7 +69,7 @@ try { process.exit(0); } - const binaryName = platform === 'win32' ? 'ccline.exe' : 'ccline'; + const binaryName = platform === 'win32' ? 'ccline-88cc.exe' : 'ccline-88cc'; const targetPath = path.join(claudeDir, binaryName); // Multiple path search strategies for different package managers @@ -123,7 +123,7 @@ try { if (!sourcePath) { if (!silent) { console.log('Binary package not installed, skipping Claude Code setup'); - console.log('The global ccline command will still work via npm'); + console.log('The global ccline-88cc command will still work via npm'); } process.exit(0); } @@ -148,13 +148,13 @@ try { if (!silent) { console.log('✨ CCometixLine is ready for Claude Code!'); console.log(`📍 Location: ${targetPath}`); - console.log('🎉 You can now use: ccline --help'); + console.log('🎉 You can now use: ccline-88cc --help'); } } catch (error) { // Silent failure - don't break installation if (!silent) { console.log('Note: Could not auto-configure for Claude Code'); - console.log('The global ccline command will still work.'); - console.log('You can manually copy ccline to ~/.claude/ccline/ if needed'); + console.log('The global ccline-88cc command will still work.'); + console.log('You can manually copy ccline-88cc to ~/.claude/ccline/ if needed'); } } \ No newline at end of file diff --git a/npm/platforms/darwin-arm64/package.json b/npm/platforms/darwin-arm64/package.json index e6ca5ef..f934b2c 100644 --- a/npm/platforms/darwin-arm64/package.json +++ b/npm/platforms/darwin-arm64/package.json @@ -1,14 +1,14 @@ { - "name": "@cometix/ccline-darwin-arm64", + "name": "@byebyecode/ccline-88cc-darwin-arm64", "version": "0.0.0", - "description": "macOS ARM64 binary for CCometixLine", - "files": ["ccline"], + "description": "macOS ARM64 binary for ccline-88cc", + "files": ["ccline-88cc"], "os": ["darwin"], "cpu": ["arm64"], "repository": { "type": "git", - "url": "https://github.com/Haleclipse/CCometixLine" + "url": "https://github.com/byebye-code/ccline-88cc" }, - "author": "Haleclipse", + "author": "Hobee Liu", "license": "MIT" } \ No newline at end of file diff --git a/npm/platforms/darwin-x64/package.json b/npm/platforms/darwin-x64/package.json index df166fa..8a287bd 100644 --- a/npm/platforms/darwin-x64/package.json +++ b/npm/platforms/darwin-x64/package.json @@ -1,14 +1,14 @@ { - "name": "@cometix/ccline-darwin-x64", + "name": "@byebyecode/ccline-88cc-darwin-x64", "version": "0.0.0", - "description": "macOS x64 binary for CCometixLine", - "files": ["ccline"], + "description": "macOS x64 binary for ccline-88cc", + "files": ["ccline-88cc"], "os": ["darwin"], "cpu": ["x64"], "repository": { "type": "git", - "url": "https://github.com/Haleclipse/CCometixLine" + "url": "https://github.com/byebye-code/ccline-88cc" }, - "author": "Haleclipse", + "author": "Hobee Liu", "license": "MIT" } \ No newline at end of file diff --git a/npm/platforms/linux-x64-musl/package.json b/npm/platforms/linux-x64-musl/package.json index 17bc698..f8a657e 100644 --- a/npm/platforms/linux-x64-musl/package.json +++ b/npm/platforms/linux-x64-musl/package.json @@ -1,14 +1,14 @@ { - "name": "@cometix/ccline-linux-x64-musl", + "name": "@byebyecode/ccline-88cc-linux-x64-musl", "version": "0.0.0", - "description": "Linux x64 static binary for CCometixLine (musl libc)", - "files": ["ccline"], + "description": "Linux x64 static binary for ccline-88cc (musl libc)", + "files": ["ccline-88cc"], "os": ["linux"], "cpu": ["x64"], "repository": { "type": "git", - "url": "https://github.com/Haleclipse/CCometixLine" + "url": "https://github.com/byebye-code/ccline-88cc" }, - "author": "Haleclipse", + "author": "Hobee Liu", "license": "MIT" } \ No newline at end of file diff --git a/npm/platforms/linux-x64/package.json b/npm/platforms/linux-x64/package.json index 578e9c8..415b9b7 100644 --- a/npm/platforms/linux-x64/package.json +++ b/npm/platforms/linux-x64/package.json @@ -1,14 +1,14 @@ { - "name": "@cometix/ccline-linux-x64", + "name": "@byebyecode/ccline-88cc-linux-x64", "version": "0.0.0", - "description": "Linux x64 binary for CCometixLine", - "files": ["ccline"], + "description": "Linux x64 binary for ccline-88cc", + "files": ["ccline-88cc"], "os": ["linux"], "cpu": ["x64"], "repository": { "type": "git", - "url": "https://github.com/Haleclipse/CCometixLine" + "url": "https://github.com/byebye-code/ccline-88cc" }, - "author": "Haleclipse", + "author": "Hobee Liu", "license": "MIT" } \ No newline at end of file diff --git a/npm/platforms/win32-x64/package.json b/npm/platforms/win32-x64/package.json index 91568de..e268bbe 100644 --- a/npm/platforms/win32-x64/package.json +++ b/npm/platforms/win32-x64/package.json @@ -1,14 +1,14 @@ { - "name": "@cometix/ccline-win32-x64", + "name": "@byebyecode/ccline-88cc-win32-x64", "version": "0.0.0", - "description": "Windows x64 binary for CCometixLine", - "files": ["ccline.exe"], + "description": "Windows x64 binary for ccline-88cc", + "files": ["ccline-88cc.exe"], "os": ["win32"], "cpu": ["x64"], "repository": { "type": "git", - "url": "https://github.com/Haleclipse/CCometixLine" + "url": "https://github.com/byebye-code/ccline-88cc" }, - "author": "Haleclipse", + "author": "Hobee Liu", "license": "MIT" } \ No newline at end of file diff --git a/npm/scripts/prepare-packages.js b/npm/scripts/prepare-packages.js index 591bdf6..f15e38f 100755 --- a/npm/scripts/prepare-packages.js +++ b/npm/scripts/prepare-packages.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); -const version = process.env.GITHUB_REF?.replace('refs/tags/v', '') || process.argv[2]; +let version = process.env.GITHUB_REF?.replace('refs/tags/v', '') || process.argv[2]; if (!version) { console.error('Error: Version not provided'); console.error('Usage: GITHUB_REF=refs/tags/v1.0.0 node prepare-packages.js'); @@ -10,6 +10,14 @@ if (!version) { process.exit(1); } +// Remove -88code or -yescode suffix if present (for branch-specific tags) +// Keep other suffixes like -beta, -alpha, -rc for pre-release versions +const originalVersion = version; +version = version.replace(/-(88code|yescode)$/, ''); +if (originalVersion !== version) { + console.log(`📝 Tag suffix removed: ${originalVersion} → ${version}`); +} + console.log(`🚀 Preparing packages for version ${version}`); // Define platform structures @@ -42,7 +50,7 @@ platforms.forEach(platform => { JSON.stringify(packageJson, null, 2) + '\n' ); - console.log(`✓ Prepared @cometix/ccline-${platform} v${version}`); + console.log(`✓ Prepared @byebyecode/ccline-88cc-${platform} v${version}`); }); // Prepare main package @@ -61,7 +69,7 @@ mainPackageJson.version = version; // Update optionalDependencies versions if (mainPackageJson.optionalDependencies) { Object.keys(mainPackageJson.optionalDependencies).forEach(dep => { - if (dep.startsWith('@cometix/ccline-')) { + if (dep.startsWith('@byebyecode/ccline-88cc-')) { mainPackageJson.optionalDependencies[dep] = version; } }); @@ -72,7 +80,7 @@ fs.writeFileSync( JSON.stringify(mainPackageJson, null, 2) + '\n' ); -console.log(`✓ Prepared @cometix/ccline v${version}`); +console.log(`✓ Prepared @byebyecode/ccline-88cc v${version}`); console.log(`\n🎉 All packages prepared for version ${version}`); console.log('\nNext steps:'); console.log('1. Copy binaries to platform directories'); diff --git a/src/config/types.rs b/src/config/types.rs index e5a78dc..2c038e4 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -73,6 +73,7 @@ pub enum SegmentId { Session, OutputStyle, Update, + Quota, } // Legacy compatibility structure diff --git a/src/core/segments/git.rs b/src/core/segments/git.rs index 9e7b73b..6bc9c84 100644 --- a/src/core/segments/git.rs +++ b/src/core/segments/git.rs @@ -66,7 +66,7 @@ impl GitSegment { fn is_git_repository(&self, working_dir: &str) -> bool { Command::new("git") - .args(["rev-parse", "--git-dir"]) + .args(["--no-optional-locks", "rev-parse", "--git-dir"]) .current_dir(working_dir) .output() .map(|output| output.status.success()) @@ -75,7 +75,7 @@ impl GitSegment { fn get_branch(&self, working_dir: &str) -> Option { if let Ok(output) = Command::new("git") - .args(["branch", "--show-current"]) + .args(["--no-optional-locks", "branch", "--show-current"]) .current_dir(working_dir) .output() { @@ -88,7 +88,7 @@ impl GitSegment { } if let Ok(output) = Command::new("git") - .args(["symbolic-ref", "--short", "HEAD"]) + .args(["--no-optional-locks", "symbolic-ref", "--short", "HEAD"]) .current_dir(working_dir) .output() { @@ -105,7 +105,7 @@ impl GitSegment { fn get_status(&self, working_dir: &str) -> GitStatus { let output = Command::new("git") - .args(["status", "--porcelain"]) + .args(["--no-optional-locks", "status", "--porcelain"]) .current_dir(working_dir) .output(); @@ -138,7 +138,7 @@ impl GitSegment { fn get_commit_count(&self, working_dir: &str, range: &str) -> u32 { let output = Command::new("git") - .args(["rev-list", "--count", range]) + .args(["--no-optional-locks", "rev-list", "--count", range]) .current_dir(working_dir) .output(); @@ -153,7 +153,7 @@ impl GitSegment { fn get_sha(&self, working_dir: &str) -> Option { let output = Command::new("git") - .args(["rev-parse", "--short=7", "HEAD"]) + .args(["--no-optional-locks", "rev-parse", "--short=7", "HEAD"]) .current_dir(working_dir) .output() .ok()?; diff --git a/src/core/segments/mod.rs b/src/core/segments/mod.rs index ff036a9..76d8a08 100644 --- a/src/core/segments/mod.rs +++ b/src/core/segments/mod.rs @@ -4,6 +4,7 @@ pub mod directory; pub mod git; pub mod model; pub mod output_style; +pub mod quota; pub mod session; pub mod update; pub mod usage; @@ -31,6 +32,7 @@ pub use directory::DirectorySegment; pub use git::GitSegment; pub use model::ModelSegment; pub use output_style::OutputStyleSegment; +pub use quota::QuotaSegment; pub use session::SessionSegment; pub use update::UpdateSegment; pub use usage::UsageSegment; diff --git a/src/core/segments/quota.rs b/src/core/segments/quota.rs new file mode 100644 index 0000000..2e87923 --- /dev/null +++ b/src/core/segments/quota.rs @@ -0,0 +1,481 @@ +use super::{Segment, SegmentData}; +use crate::config::{InputData, SegmentId}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::env; +use std::fs; +use std::hash::{Hash, Hasher}; +use std::path::PathBuf; +use std::time::{Duration, SystemTime}; + +// API 通用包装响应结构 +#[derive(Debug, Deserialize)] +struct ApiWrapper { + code: i32, + msg: String, + ok: bool, + data: T, +} + +// Usage API 响应数据结构 +#[derive(Debug, Deserialize)] +struct C88ApiResponse { + #[serde(rename = "creditLimit")] + credit_limit: f64, + #[serde(rename = "currentCredits")] + current_credits: f64, + #[serde(rename = "subscriptionName")] + subscription_name: Option, + #[serde(rename = "subscriptionId")] + subscription_id: Option, +} + +// 订阅信息结构 +#[derive(Debug, Deserialize)] +struct SubscriptionResponse { + id: u32, + #[serde(rename = "resetTimes")] + reset_times: u32, + #[serde(rename = "autoResetWhenZero")] + auto_reset_when_zero: bool, +} + +// 端点配置 +#[derive(Debug, Clone)] +struct EndpointConfig { + url: String, + name: String, +} + +// 端点缓存 +#[allow(dead_code)] +#[derive(Debug, Clone, Serialize, Deserialize)] +struct EndpointCache { + api_key_hash: u64, + successful_endpoint: String, + last_success_time: SystemTime, + success_count: u32, +} + +// 智能端点检测器 +struct SmartEndpointDetector { + endpoints: Vec, +} + +impl SmartEndpointDetector { + fn new() -> Self { + let endpoints = vec![EndpointConfig { + url: "https://www.88code.org/api/usage".to_string(), + name: "main".to_string(), + }]; + + Self { endpoints } + } + + #[allow(dead_code)] + fn get_cache_file_path() -> PathBuf { + if let Some(home) = dirs::home_dir() { + home.join(".claude") + .join("ccline") + .join("endpoint_cache.json") + } else { + PathBuf::from("endpoint_cache.json") + } + } + + #[allow(dead_code)] + fn hash_api_key(api_key: &str) -> u64 { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + api_key.hash(&mut hasher); + hasher.finish() + } + + fn try_endpoint(&self, endpoint: &EndpointConfig, api_key: &str, model: &str) -> Option { + let debug = env::var("C88_DEBUG").is_ok(); + + // 构建带 model 参数的 URL + let url_with_model = format!("{}?model={}", endpoint.url, model); + + if debug { + eprintln!("[DEBUG] Trying endpoint: {}", url_with_model); + } + + let start_time = SystemTime::now(); + let bearer_token = format!("Bearer {}", api_key); + let result = ureq::post(&url_with_model) + .set("accept", "*/*") + .set("content-type", "application/json") + .set("Authorization", &bearer_token) + .timeout(Duration::from_secs(5)) + .call(); + + match result { + Ok(response) => { + if response.status() == 200 { + let elapsed = start_time.elapsed().unwrap_or(Duration::from_secs(0)); + if debug { + eprintln!( + "[DEBUG] Success: {} in {}ms", + endpoint.name, + elapsed.as_millis() + ); + } + + // 解析包装后的响应 + response + .into_json::>() + .ok() + .map(|wrapper| wrapper.data) + } else { + if debug { + eprintln!( + "[DEBUG] Failed: {} status {}", + endpoint.name, + response.status() + ); + } + None + } + } + Err(e) => { + if debug { + eprintln!("[DEBUG] Error: {} - {}", endpoint.name, e); + } + None + } + } + } + + fn detect_endpoint(&mut self, api_key: &str, model: &str) -> Option<(String, C88ApiResponse)> { + // 尝试所有端点 + let endpoints_clone = self.endpoints.clone(); + for endpoint in &endpoints_clone { + if let Some(response) = self.try_endpoint(endpoint, api_key, model) { + return Some((endpoint.url.clone(), response)); + } + } + + None + } + + fn detect_endpoint_static(api_key: &str, model: &str) -> Option<(String, C88ApiResponse)> { + let mut detector = SmartEndpointDetector::new(); + detector.detect_endpoint(api_key, model) + } +} + +#[derive(Default)] +pub struct QuotaSegment; + +impl QuotaSegment { + pub fn new() -> Self { + Self + } + + fn load_api_key(&self) -> Option { + // 优先级:环境变量 > Claude Code settings.json > api_key 文件 + + // 1. 环境变量 + if let Ok(key) = env::var("C88_API_KEY") { + return Some(key); + } + + if let Ok(key) = env::var("ANTHROPIC_API_KEY") { + return Some(key); + } + + if let Ok(key) = env::var("ANTHROPIC_AUTH_TOKEN") { + return Some(key); + } + + // 2. Claude Code settings.json + if let Some(key) = self.load_from_settings() { + return Some(key); + } + + // 3. api_key 文件 + if let Some(home) = dirs::home_dir() { + let api_key_path = home.join(".claude").join("api_key"); + if let Ok(key) = fs::read_to_string(api_key_path) { + return Some(key.trim().to_string()); + } + } + + None + } + + fn load_from_settings(&self) -> Option { + if let Some(home) = dirs::home_dir() { + let settings_path = home.join(".claude").join("settings.json"); + if let Ok(content) = fs::read_to_string(settings_path) { + if let Ok(settings) = serde_json::from_str::(&content) { + if let Some(env) = settings.get("env") { + if let Some(token) = env.get("ANTHROPIC_AUTH_TOKEN") { + if let Some(token_str) = token.as_str() { + return Some(token_str.to_string()); + } + } + if let Some(key) = env.get("ANTHROPIC_API_KEY") { + if let Some(key_str) = key.as_str() { + return Some(key_str.to_string()); + } + } + } + } + } + } + None + } + + fn load_model(&self) -> String { + // 优先级:环境变量 > settings.json > 默认值 + + // 1. 环境变量 ANTHROPIC_MODEL + if let Ok(model) = env::var("ANTHROPIC_MODEL") { + return model; + } + + // 2. settings.json(顶层 model 字段) + if let Some(home) = dirs::home_dir() { + let settings_path = home.join(".claude").join("settings.json"); + if let Ok(content) = fs::read_to_string(settings_path) { + if let Ok(settings) = serde_json::from_str::(&content) { + if let Some(model) = settings.get("model") { + if let Some(model_str) = model.as_str() { + return model_str.to_string(); + } + } + } + } + } + + // 3. 默认值:使用 claude-sonnet-4-5-20251022 + "claude-sonnet-4-5-20251022".to_string() + } + + fn format_quota(&self, subscription_name: Option<&str>, used: f64, total: f64) -> String { + if let Some(name) = subscription_name { + format!("{} ${:.2}/${:.2}", name, used, total) + } else { + format!("${:.2}/${:.2}", used, total) + } + } + + fn calculate_used(&self, response: &C88ApiResponse) -> f64 { + response.credit_limit - response.current_credits + } + + fn fetch_subscription_info(&self, api_key: &str, subscription_id: u32, model: &str) -> Option { + let url = format!("https://www.88code.org/api/subscription?model={}", model); + let bearer_token = format!("Bearer {}", api_key); + + let result = ureq::post(&url) + .set("accept", "*/*") + .set("content-type", "application/json") + .set("Authorization", &bearer_token) + .timeout(Duration::from_secs(5)) + .call(); + + match result { + Ok(response) if response.status() == 200 => { + if let Ok(wrapper) = response.into_json::>>() { + // 从包装结构中提取数据并查找匹配的订阅ID + wrapper.data.into_iter().find(|sub| sub.id == subscription_id) + } else { + None + } + } + _ => None, + } + } + + fn format_reset_info(&self, reset_times: u32, auto_reset_when_zero: bool, user_auto_reset_enabled: bool) -> String { + let status = if user_auto_reset_enabled { + // 用户在TUI中启用了Auto Reset + "[reset on auto]" + } else if auto_reset_when_zero { + // API返回的auto_reset_when_zero为true + "[reset on zero]" + } else { + "[reset off]" + }; + format!("↻ {} {}", reset_times, status) + } + + /// 检查当前是否在重置时间窗口内 + /// - 18:55-18:59:必须剩余2次或更多重置机会才进行重置 + /// - 23:55-23:59:只要有重置次数(>=1)就进行重置 + fn is_in_reset_window(&self, reset_times: u32) -> bool { + use chrono::{Local, Timelike}; + + let now = Local::now(); + let hour = now.hour(); + let minute = now.minute(); + + // 18:55 - 18:59:需要2次或更多重置机会 + if hour == 18 && minute >= 55 && minute <= 59 { + return reset_times >= 2; + } + + // 23:55 - 23:59:只要有重置次数就可以 + if hour == 23 && minute >= 55 && minute <= 59 { + return reset_times >= 1; + } + + false + } + + /// 执行重置操作(调用API) + /// 返回重置是否成功 + fn perform_reset(&self, api_key: &str, subscription_id: u32) -> bool { + let url = format!("https://www.88code.org/api/reset-credits/{}", subscription_id); + let bearer_token = format!("Bearer {}", api_key); + let debug = env::var("C88_DEBUG").is_ok(); + + if debug { + eprintln!("[DEBUG] Attempting to reset credits for subscription {}", subscription_id); + } + + let result = ureq::post(&url) + .set("accept", "*/*") + .set("content-type", "application/json") + .set("Authorization", &bearer_token) + .timeout(Duration::from_secs(5)) + .call(); + + match result { + Ok(response) => { + if response.status() == 200 { + if debug { + eprintln!("[DEBUG] Reset successful for subscription {}", subscription_id); + } + true + } else { + if debug { + eprintln!("[DEBUG] Reset failed with status {}", response.status()); + } + false + } + } + Err(e) => { + if debug { + eprintln!("[DEBUG] Reset error: {}", e); + } + false + } + } + } + + /// 检查并执行自动重置(如果需要) + fn check_and_auto_reset( + &self, + api_key: &str, + subscription_id: u32, + reset_times: u32, + auto_reset_enabled: bool, + ) -> bool { + if !auto_reset_enabled { + return false; + } + + if reset_times == 0 { + return false; + } + + if !self.is_in_reset_window(reset_times) { + return false; + } + + // 在重置窗口内,执行重置 + self.perform_reset(api_key, subscription_id) + } +} + +impl Segment for QuotaSegment { + fn collect(&self, _input: &InputData) -> Option { + #[cfg(not(feature = "quota"))] + { + return None; + } + + #[cfg(feature = "quota")] + { + let api_key = self.load_api_key()?; + let model = self.load_model(); + + // 加载配置获取auto_reset_enabled选项 + let auto_reset_enabled = if let Ok(config) = crate::config::Config::load() { + config + .segments + .iter() + .find(|s| s.id == SegmentId::Quota) + .and_then(|sc| sc.options.get("auto_reset_enabled")) + .and_then(|v| v.as_bool()) + .unwrap_or(false) + } else { + false + }; + + // 使用静态方法进行端点检测,传递 model 参数 + if let Some((endpoint_url, response)) = + SmartEndpointDetector::detect_endpoint_static(&api_key, &model) + { + let used = self.calculate_used(&response); + let total = response.credit_limit; + let quota_display = self.format_quota(response.subscription_name.as_deref(), used, total); + + // 获取重置次数信息 + let reset_info = if let Some(sub_id) = response.subscription_id { + if let Some(sub_info) = self.fetch_subscription_info(&api_key, sub_id, &model) { + // 检查并执行自动重置(内部会进行所有必要的检查) + let _ = self.check_and_auto_reset( + &api_key, + sub_id, + sub_info.reset_times, + auto_reset_enabled, + ); + + self.format_reset_info( + sub_info.reset_times, + sub_info.auto_reset_when_zero, + auto_reset_enabled + ) + } else { + String::new() + } + } else { + String::new() + }; + + let mut metadata = HashMap::new(); + metadata.insert("used".to_string(), used.to_string()); + metadata.insert("total".to_string(), total.to_string()); + metadata.insert("remain".to_string(), response.current_credits.to_string()); + metadata.insert("endpoint_used".to_string(), endpoint_url); + if let Some(name) = &response.subscription_name { + metadata.insert("subscription_name".to_string(), name.clone()); + } + + Some(SegmentData { + primary: quota_display, + secondary: reset_info, + metadata, + }) + } else { + // 所有端点都失败 + let mut metadata = HashMap::new(); + metadata.insert("status".to_string(), "offline".to_string()); + + Some(SegmentData { + primary: "Offline".to_string(), + secondary: String::new(), + metadata, + }) + } + } + } + + fn id(&self) -> SegmentId { + SegmentId::Quota + } +} diff --git a/src/core/segments/usage.rs b/src/core/segments/usage.rs index 94fc27d..135ad47 100644 --- a/src/core/segments/usage.rs +++ b/src/core/segments/usage.rs @@ -1,7 +1,7 @@ use super::{Segment, SegmentData}; use crate::config::{InputData, SegmentId}; use crate::utils::credentials; -use chrono::{DateTime, Datelike, Timelike, Utc}; +use chrono::{DateTime, Datelike, Duration, Local, Timelike, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -50,7 +50,16 @@ impl UsageSegment { fn format_reset_time(reset_time_str: Option<&str>) -> String { if let Some(time_str) = reset_time_str { if let Ok(dt) = DateTime::parse_from_rfc3339(time_str) { - return format!("{}-{}-{}", dt.month(), dt.day(), dt.hour()); + let mut local_dt = dt.with_timezone(&Local); + if local_dt.minute() > 45 { + local_dt += Duration::hours(1); + } + return format!( + "{}-{}-{}", + local_dt.month(), + local_dt.day(), + local_dt.hour() + ); } } "?".to_string() diff --git a/src/core/statusline.rs b/src/core/statusline.rs index bd4e581..841d4c5 100644 --- a/src/core/statusline.rs +++ b/src/core/statusline.rs @@ -463,6 +463,11 @@ pub fn collect_all_segments( let mut results = Vec::new(); for segment_config in &config.segments { + // Skip disabled segments to avoid unnecessary API requests + if !segment_config.enabled { + continue; + } + let segment_data = match segment_config.id { crate::config::SegmentId::Model => { let segment = ModelSegment::new(); @@ -505,6 +510,10 @@ pub fn collect_all_segments( let segment = UpdateSegment::new(); segment.collect(input) } + crate::config::SegmentId::Quota => { + let segment = QuotaSegment::new(); + segment.collect(input) + } }; if let Some(data) = segment_data { diff --git a/src/main.rs b/src/main.rs index 66706fb..dbfe679 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ -use ccometixline::cli::Cli; -use ccometixline::config::{Config, InputData}; -use ccometixline::core::{collect_all_segments, StatusLineGenerator}; +use ccometixline_88cc::cli::Cli; +use ccometixline_88cc::config::{Config, InputData}; +use ccometixline_88cc::core::{collect_all_segments, StatusLineGenerator}; use std::io::{self, IsTerminal}; fn main() -> Result<(), Box> { @@ -17,7 +17,7 @@ fn main() -> Result<(), Box> { // Apply theme override if provided if let Some(theme) = cli.theme { - config = ccometixline::ui::themes::ThemePresets::get_theme(&theme); + config = ccometixline_88cc::ui::themes::ThemePresets::get_theme(&theme); } config.print()?; @@ -34,7 +34,7 @@ fn main() -> Result<(), Box> { if cli.config { #[cfg(feature = "tui")] { - ccometixline::ui::run_configurator()?; + ccometixline_88cc::ui::run_configurator()?; } #[cfg(not(feature = "tui"))] { @@ -58,7 +58,7 @@ fn main() -> Result<(), Box> { // Handle Claude Code patcher if let Some(claude_path) = cli.patch { - use ccometixline::utils::ClaudeCodePatcher; + use ccometixline_88cc::utils::ClaudeCodePatcher; println!("🔧 Claude Code Context Warning Disabler"); println!("Target file: {}", claude_path); @@ -101,7 +101,7 @@ fn main() -> Result<(), Box> { // Apply theme override if provided if let Some(theme) = cli.theme { - config = ccometixline::ui::themes::ThemePresets::get_theme(&theme); + config = ccometixline_88cc::ui::themes::ThemePresets::get_theme(&theme); } // Check if stdin has data @@ -109,19 +109,19 @@ fn main() -> Result<(), Box> { // No input data available, show main menu #[cfg(feature = "tui")] { - use ccometixline::ui::{MainMenu, MenuResult}; + use ccometixline_88cc::ui::{MainMenu, MenuResult}; if let Some(result) = MainMenu::run()? { match result { MenuResult::LaunchConfigurator => { - ccometixline::ui::run_configurator()?; + ccometixline_88cc::ui::run_configurator()?; } MenuResult::InitConfig => { - ccometixline::config::Config::init()?; + ccometixline_88cc::config::Config::init()?; println!("Configuration initialized successfully!"); } MenuResult::CheckConfig => { - let config = ccometixline::config::Config::load()?; + let config = ccometixline_88cc::config::Config::load()?; config.check()?; println!("Configuration is valid!"); } diff --git a/src/ui/app.rs b/src/ui/app.rs index 0cca753..78ba40b 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -503,6 +503,7 @@ impl App { SegmentId::Session => "Session", SegmentId::OutputStyle => "Output Style", SegmentId::Update => "Update", + SegmentId::Quota => "Quota", }; let is_enabled = segment.enabled; self.status_message = Some(format!( @@ -530,6 +531,7 @@ impl App { SegmentId::Session => "Session", SegmentId::OutputStyle => "Output Style", SegmentId::Update => "Update", + SegmentId::Quota => "Quota", }; let is_enabled = segment.enabled; self.status_message = Some(format!( @@ -560,9 +562,36 @@ impl App { } } FieldSelection::Options => { - // TODO: Implement options editor - self.status_message = - Some("Options editor not implemented yet".to_string()); + // Handle segment-specific options + if let Some(segment) = self.config.segments.get_mut(self.selected_segment) { + match segment.id { + SegmentId::Quota => { + // Toggle auto_reset_enabled for Quota segment + let current_value = segment + .options + .get("auto_reset_enabled") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + segment.options.insert( + "auto_reset_enabled".to_string(), + serde_json::Value::Bool(!current_value), + ); + self.status_message = Some(format!( + "Auto Reset {}", + if !current_value { + "enabled" + } else { + "disabled" + } + )); + self.preview.update_preview(&self.config); + } + _ => { + self.status_message = + Some("No options available for this segment".to_string()); + } + } + } } } } diff --git a/src/ui/components/preview.rs b/src/ui/components/preview.rs index 53c5d66..31abb15 100644 --- a/src/ui/components/preview.rs +++ b/src/ui/components/preview.rs @@ -183,6 +183,22 @@ impl PreviewComponent { map }, }, + SegmentId::Quota => SegmentData { + primary: "Pro $0.06/$20.25".to_string(), + secondary: "".to_string(), + metadata: { + let mut map = HashMap::new(); + map.insert("used".to_string(), "0.0635".to_string()); + map.insert("total".to_string(), "20.25".to_string()); + map.insert("remain".to_string(), "20.1865".to_string()); + map.insert("subscription_name".to_string(), "Pro".to_string()); + map.insert( + "endpoint_used".to_string(), + "https://www.88code.org/api/usage".to_string(), + ); + map + }, + }, }; segments_data.push((segment_config.clone(), mock_data)); diff --git a/src/ui/components/segment_list.rs b/src/ui/components/segment_list.rs index 832834b..13f6a89 100644 --- a/src/ui/components/segment_list.rs +++ b/src/ui/components/segment_list.rs @@ -57,6 +57,7 @@ impl SegmentListComponent { SegmentId::Session => "Session", SegmentId::OutputStyle => "Output Style", SegmentId::Update => "Update", + SegmentId::Quota => "Quota", }; if is_selected { diff --git a/src/ui/components/settings.rs b/src/ui/components/settings.rs index aa65acb..96fb841 100644 --- a/src/ui/components/settings.rs +++ b/src/ui/components/settings.rs @@ -36,6 +36,7 @@ impl SettingsComponent { SegmentId::Session => "Session", SegmentId::OutputStyle => "Output Style", SegmentId::Update => "Update", + SegmentId::Quota => "Quota", }; let current_icon = match config.style.mode { StyleMode::Plain => &segment.icon.plain, @@ -268,10 +269,25 @@ impl SettingsComponent { ), create_field_line( FieldSelection::Options, - vec![Span::raw(format!( - "└─ Options: {} items", - segment.options.len() - ))], + { + // Show auto_reset_enabled status for Quota segment + if segment.id == SegmentId::Quota { + let auto_reset_enabled = segment + .options + .get("auto_reset_enabled") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + vec![Span::raw(format!( + "└─ Auto Reset: {}", + if auto_reset_enabled { "[✓]" } else { "[ ]" } + ))] + } else { + vec![Span::raw(format!( + "└─ Options: {} items", + segment.options.len() + ))] + } + }, ), ]; let text = Text::from(lines); diff --git a/src/ui/themes/presets.rs b/src/ui/themes/presets.rs index 0a51dab..f4742a2 100644 --- a/src/ui/themes/presets.rs +++ b/src/ui/themes/presets.rs @@ -137,6 +137,7 @@ impl ThemePresets { theme_cometix::cost_segment(), theme_cometix::session_segment(), theme_cometix::output_style_segment(), + theme_cometix::quota_segment(), ], theme: "cometix".to_string(), } @@ -157,6 +158,7 @@ impl ThemePresets { theme_default::cost_segment(), theme_default::session_segment(), theme_default::output_style_segment(), + theme_default::quota_segment(), ], theme: "default".to_string(), } @@ -177,6 +179,7 @@ impl ThemePresets { theme_minimal::cost_segment(), theme_minimal::session_segment(), theme_minimal::output_style_segment(), + theme_minimal::quota_segment(), ], theme: "minimal".to_string(), } @@ -197,6 +200,7 @@ impl ThemePresets { theme_gruvbox::cost_segment(), theme_gruvbox::session_segment(), theme_gruvbox::output_style_segment(), + theme_gruvbox::quota_segment(), ], theme: "gruvbox".to_string(), } @@ -217,6 +221,7 @@ impl ThemePresets { theme_nord::cost_segment(), theme_nord::session_segment(), theme_nord::output_style_segment(), + theme_nord::quota_segment(), ], theme: "nord".to_string(), } @@ -237,6 +242,7 @@ impl ThemePresets { theme_powerline_dark::cost_segment(), theme_powerline_dark::session_segment(), theme_powerline_dark::output_style_segment(), + theme_powerline_dark::quota_segment(), ], theme: "powerline-dark".to_string(), } @@ -257,6 +263,7 @@ impl ThemePresets { theme_powerline_light::cost_segment(), theme_powerline_light::session_segment(), theme_powerline_light::output_style_segment(), + theme_powerline_light::quota_segment(), ], theme: "powerline-light".to_string(), } @@ -277,6 +284,7 @@ impl ThemePresets { theme_powerline_rose_pine::cost_segment(), theme_powerline_rose_pine::session_segment(), theme_powerline_rose_pine::output_style_segment(), + theme_powerline_rose_pine::quota_segment(), ], theme: "powerline-rose-pine".to_string(), } @@ -297,8 +305,126 @@ impl ThemePresets { theme_powerline_tokyo_night::cost_segment(), theme_powerline_tokyo_night::session_segment(), theme_powerline_tokyo_night::output_style_segment(), + theme_powerline_tokyo_night::quota_segment(), ], theme: "powerline-tokyo-night".to_string(), } } + + /// Create default theme configuration file with minimal template + pub fn create_default_theme_file(theme_name: &str) -> Result<(), Box> { + let themes_dir = Self::get_themes_path(); + let theme_path = themes_dir.join(format!("{}.toml", theme_name)); + + // Create themes directory if it doesn't exist + std::fs::create_dir_all(&themes_dir)?; + + // Create a minimal template config + let template_config = Self::get_default(); + let mut theme_config = template_config; + theme_config.theme = theme_name.to_string(); + + let toml_content = toml::to_string_pretty(&theme_config)?; + + // Add comments and examples to the template + let template_content = format!( + "# CCometixLine Theme Configuration: {}\n\ + # This file defines a custom theme for CCometixLine\n\ + # File location: ~/.claude/ccline/themes/{}.toml\n\ + # You can modify colors, icons, and styles below\n\ + \n\ + {}\n", + theme_name, + theme_name, + toml_content.trim() + ); + + std::fs::write(&theme_path, template_content)?; + Ok(()) + } + + /// Check if a theme exists (built-in or custom) + pub fn theme_exists(theme_name: &str) -> bool { + // Check built-in themes + let built_in_themes = [ + "cometix", + "default", + "minimal", + "gruvbox", + "nord", + "powerline-dark", + "powerline-light", + "powerline-rose-pine", + "powerline-tokyo-night", + ]; + + if built_in_themes.contains(&theme_name) { + return true; + } + + // Check custom themes + let themes_dir = Self::get_themes_path(); + let theme_path = themes_dir.join(format!("{}.toml", theme_name)); + theme_path.exists() + } + + /// Delete a custom theme file + pub fn delete_theme(theme_name: &str) -> Result<(), Box> { + // Don't allow deleting built-in themes + let built_in_themes = [ + "cometix", + "default", + "minimal", + "gruvbox", + "nord", + "powerline-dark", + "powerline-light", + "powerline-rose-pine", + "powerline-tokyo-night", + ]; + + if built_in_themes.contains(&theme_name) { + return Err(format!("Cannot delete built-in theme: {}", theme_name).into()); + } + + let themes_dir = Self::get_themes_path(); + let theme_path = themes_dir.join(format!("{}.toml", theme_name)); + + if theme_path.exists() { + std::fs::remove_file(theme_path)?; + } + + Ok(()) + } + + /// Copy an existing theme to create a new one + pub fn copy_theme( + source_theme: &str, + new_theme: &str, + ) -> Result<(), Box> { + let source_config = Self::get_theme(source_theme); + Self::save_theme(new_theme, &source_config)?; + Ok(()) + } + + /// Get theme type (built-in or custom) + pub fn get_theme_type(theme_name: &str) -> &'static str { + let built_in_themes = [ + "cometix", + "default", + "minimal", + "gruvbox", + "nord", + "powerline-dark", + "powerline-light", + "powerline-rose-pine", + "powerline-tokyo-night", + ]; + + if built_in_themes.contains(&theme_name) { + "built-in" + } else { + "custom" + } + } } diff --git a/src/ui/themes/theme_cometix.rs b/src/ui/themes/theme_cometix.rs index dfcd1e6..e8c6cc8 100644 --- a/src/ui/themes/theme_cometix.rs +++ b/src/ui/themes/theme_cometix.rs @@ -162,3 +162,25 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Color16 { c16: 11 }), // Yellow + text: Some(AnsiColor::Color16 { c16: 11 }), + background: None, + }, + styles: TextStyleConfig { text_bold: true }, + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_default.rs b/src/ui/themes/theme_default.rs index 20b21ca..027923f 100644 --- a/src/ui/themes/theme_default.rs +++ b/src/ui/themes/theme_default.rs @@ -162,3 +162,25 @@ pub fn output_style_segment() -> SegmentConfig { options: HashMap::new(), } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Color16 { c16: 11 }), // Yellow + text: Some(AnsiColor::Color16 { c16: 11 }), + background: None, + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_gruvbox.rs b/src/ui/themes/theme_gruvbox.rs index 6b071e1..461e7c2 100644 --- a/src/ui/themes/theme_gruvbox.rs +++ b/src/ui/themes/theme_gruvbox.rs @@ -162,3 +162,25 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Color256 { c256: 214 }), // Gruvbox yellow + text: Some(AnsiColor::Color256 { c256: 214 }), + background: None, + }, + styles: TextStyleConfig { text_bold: true }, + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_minimal.rs b/src/ui/themes/theme_minimal.rs index 0c1cdd6..5306a00 100644 --- a/src/ui/themes/theme_minimal.rs +++ b/src/ui/themes/theme_minimal.rs @@ -162,3 +162,25 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "$".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Color16 { c16: 8 }), + text: Some(AnsiColor::Color16 { c16: 8 }), + background: None, + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_nord.rs b/src/ui/themes/theme_nord.rs index 4811aab..1bc2f6a 100644 --- a/src/ui/themes/theme_nord.rs +++ b/src/ui/themes/theme_nord.rs @@ -246,3 +246,37 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Rgb { + r: 208, + g: 135, + b: 112, + }), + text: Some(AnsiColor::Rgb { + r: 208, + g: 135, + b: 112, + }), + background: Some(AnsiColor::Rgb { + r: 76, + g: 86, + b: 106, + }), + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_powerline_dark.rs b/src/ui/themes/theme_powerline_dark.rs index d003e07..04e7e08 100644 --- a/src/ui/themes/theme_powerline_dark.rs +++ b/src/ui/themes/theme_powerline_dark.rs @@ -246,3 +246,37 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Rgb { + r: 255, + g: 255, + b: 255, + }), + text: Some(AnsiColor::Rgb { + r: 255, + g: 255, + b: 255, + }), + background: Some(AnsiColor::Rgb { + r: 85, + g: 85, + b: 85, + }), + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_powerline_light.rs b/src/ui/themes/theme_powerline_light.rs index c747340..2776a4a 100644 --- a/src/ui/themes/theme_powerline_light.rs +++ b/src/ui/themes/theme_powerline_light.rs @@ -238,3 +238,37 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Rgb { + r: 255, + g: 255, + b: 255, + }), + text: Some(AnsiColor::Rgb { + r: 255, + g: 255, + b: 255, + }), + background: Some(AnsiColor::Rgb { + r: 76, + g: 175, + b: 80, + }), + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_powerline_rose_pine.rs b/src/ui/themes/theme_powerline_rose_pine.rs index f0519b2..70890a8 100644 --- a/src/ui/themes/theme_powerline_rose_pine.rs +++ b/src/ui/themes/theme_powerline_rose_pine.rs @@ -246,3 +246,37 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Rgb { + r: 235, + g: 188, + b: 186, + }), + text: Some(AnsiColor::Rgb { + r: 235, + g: 188, + b: 186, + }), + background: Some(AnsiColor::Rgb { + r: 49, + g: 46, + b: 65, + }), + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +} diff --git a/src/ui/themes/theme_powerline_tokyo_night.rs b/src/ui/themes/theme_powerline_tokyo_night.rs index c7e449b..21419e6 100644 --- a/src/ui/themes/theme_powerline_tokyo_night.rs +++ b/src/ui/themes/theme_powerline_tokyo_night.rs @@ -246,3 +246,37 @@ pub fn usage_segment() -> SegmentConfig { }, } } + +pub fn quota_segment() -> SegmentConfig { + SegmentConfig { + id: SegmentId::Quota, + enabled: true, + icon: IconConfig { + plain: "💰".to_string(), + nerd_font: "\u{f155}".to_string(), + }, + colors: ColorConfig { + icon: Some(AnsiColor::Rgb { + r: 247, + g: 118, + b: 142, + }), + text: Some(AnsiColor::Rgb { + r: 247, + g: 118, + b: 142, + }), + background: Some(AnsiColor::Rgb { + r: 36, + g: 40, + b: 59, + }), + }, + styles: TextStyleConfig::default(), + options: { + let mut opts = HashMap::new(); + opts.insert("auto_reset_enabled".to_string(), serde_json::Value::Bool(false)); + opts + }, + } +}