Full Code of charmbracelet/lipgloss for AI

main cd93a9f5d2e3 cached
300 files
553.6 KB
186.4k tokens
876 symbols
1 requests
Download .txt
Showing preview only (624K chars total). Download the full file or copy to clipboard to get everything.
Repository: charmbracelet/lipgloss
Branch: main
Commit: cd93a9f5d2e3
Files: 300
Total size: 553.6 KB

Directory structure:
gitextract_jquxj2jk/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── build.yml
│       ├── coverage.yml
│       ├── dependabot-sync.yml
│       ├── lint-sync.yml
│       ├── lint.yml
│       └── release.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── LICENSE
├── README.md
├── Taskfile.yaml
├── UPGRADE_GUIDE_V2.md
├── align.go
├── align_test.go
├── ansi_unix.go
├── ansi_windows.go
├── blending.go
├── blending_test.go
├── borders.go
├── borders_test.go
├── canvas.go
├── canvas_test.go
├── color.go
├── color_test.go
├── compat/
│   ├── color.go
│   └── doc.go
├── examples/
│   ├── blending/
│   │   ├── border-blend-rotation/
│   │   │   └── bubbletea/
│   │   │       └── main.go
│   │   ├── linear-1d/
│   │   │   ├── bubbletea/
│   │   │   │   └── main.go
│   │   │   └── standalone/
│   │   │       └── main.go
│   │   └── linear-2d/
│   │       ├── bubbletea/
│   │       │   └── main.go
│   │       └── standalone/
│   │           └── main.go
│   ├── brightness/
│   │   └── main.go
│   ├── canvas/
│   │   └── main.go
│   ├── color/
│   │   ├── bubbletea/
│   │   │   └── main.go
│   │   └── standalone/
│   │       └── main.go
│   ├── compat/
│   │   ├── bubbletea/
│   │   │   └── main.go
│   │   └── standalone/
│   │       └── main.go
│   ├── go.mod
│   ├── go.sum
│   ├── layout/
│   │   └── main.go
│   ├── list/
│   │   ├── duckduckgoose/
│   │   │   └── main.go
│   │   ├── glow/
│   │   │   └── main.go
│   │   ├── grocery/
│   │   │   └── main.go
│   │   ├── roman/
│   │   │   └── main.go
│   │   ├── simple/
│   │   │   └── main.go
│   │   └── sublist/
│   │       └── main.go
│   ├── ssh/
│   │   └── main.go
│   ├── table/
│   │   ├── ansi/
│   │   │   └── main.go
│   │   ├── chess/
│   │   │   └── main.go
│   │   ├── demo.tape
│   │   ├── languages/
│   │   │   └── main.go
│   │   ├── mindy/
│   │   │   └── main.go
│   │   └── pokemon/
│   │       └── main.go
│   └── tree/
│       ├── background/
│       │   └── main.go
│       ├── files/
│       │   └── main.go
│       ├── makeup/
│       │   └── main.go
│       ├── rounded/
│       │   └── main.go
│       ├── selection/
│       │   └── main.go
│       ├── simple/
│       │   └── main.go
│       ├── styles/
│       │   └── main.go
│       └── toggle/
│           └── main.go
├── get.go
├── go.mod
├── go.sum
├── join.go
├── join_test.go
├── layer.go
├── lipgloss.go
├── list/
│   ├── enumerator.go
│   ├── list.go
│   ├── list_test.go
│   └── testdata/
│       ├── TestComplexSublist.golden
│       ├── TestEnumerators/
│       │   ├── alphabet.golden
│       │   ├── arabic.golden
│       │   ├── asterisk.golden
│       │   ├── bullet.golden
│       │   ├── dash.golden
│       │   └── roman.golden
│       ├── TestEnumeratorsAlign.golden
│       ├── TestEnumeratorsTransform/
│       │   ├── alphabet_lower.golden
│       │   ├── arabic).golden
│       │   ├── bullet_is_dash.golden
│       │   └── roman_within_().golden
│       ├── TestList.golden
│       ├── TestListIntegers.golden
│       ├── TestListItems.golden
│       ├── TestMultiline.golden
│       ├── TestSubListItems2.golden
│       ├── TestSublist.golden
│       └── TestSublistItems.golden
├── position.go
├── query.go
├── ranges.go
├── ranges_test.go
├── runes.go
├── runes_test.go
├── set.go
├── size.go
├── size_test.go
├── style.go
├── style_test.go
├── table/
│   ├── resizing.go
│   ├── rows.go
│   ├── table.go
│   ├── table_test.go
│   ├── testdata/
│   │   ├── TestBorderColumnsWithExtraRows.golden
│   │   ├── TestBorderStyles/
│   │   │   ├── ASCIIBorder.golden
│   │   │   ├── BlockBorder.golden
│   │   │   ├── HiddenBorder.golden
│   │   │   ├── MarkdownBorder.golden
│   │   │   ├── NormalBorder.golden
│   │   │   ├── RoundedBorder.golden
│   │   │   └── ThickBorder.golden
│   │   ├── TestBorderedCells.golden
│   │   ├── TestCarriageReturn.golden
│   │   ├── TestContentWrapping/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongRowContentNoWrap.golden
│   │   │   ├── LongRowContentNoWrapCustomMargins.golden
│   │   │   ├── LongRowContentNoWrapNoMargins.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestContentWrapping_ColumnWidth/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestContentWrapping_WithHeight/
│   │   │   └── LongHeaderContentLongAndShortRows/
│   │   │       ├── HeightOf05.golden
│   │   │       ├── HeightOf15.golden
│   │   │       ├── HeightOf25.golden
│   │   │       └── HeightOf35.golden
│   │   ├── TestContentWrapping_WithMargins/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestContentWrapping_WithPadding/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestExtraPaddingHeading.golden
│   │   ├── TestExtraPaddingHeadingLong.golden
│   │   ├── TestFilter.golden
│   │   ├── TestFilterInverse.golden
│   │   ├── TestInnerBordersOnly.golden
│   │   ├── TestMoreCellsThanHeaders.golden
│   │   ├── TestMoreCellsThanHeadersExtra.golden
│   │   ├── TestNoFinalEmptyRowWhenOverflow.golden
│   │   ├── TestStyleFunc/
│   │   │   ├── MarginAndPaddingSet.golden
│   │   │   └── RightAlignedTextWithMargins.golden
│   │   ├── TestTable.golden
│   │   ├── TestTableANSI.golden
│   │   ├── TestTableBorder.golden
│   │   ├── TestTableEmpty.golden
│   │   ├── TestTableExample.golden
│   │   ├── TestTableHeightExact.golden
│   │   ├── TestTableHeightExtra.golden
│   │   ├── TestTableHeightShrink/
│   │   │   ├── NoBorderRow/
│   │   │   │   ├── HeightOf01.golden
│   │   │   │   ├── HeightOf02.golden
│   │   │   │   ├── HeightOf03.golden
│   │   │   │   ├── HeightOf04.golden
│   │   │   │   ├── HeightOf05.golden
│   │   │   │   ├── HeightOf06.golden
│   │   │   │   ├── HeightOf07.golden
│   │   │   │   ├── HeightOf08.golden
│   │   │   │   └── HeightOf09.golden
│   │   │   ├── NoBorderRowPadding/
│   │   │   │   ├── HeightOf01.golden
│   │   │   │   ├── HeightOf02.golden
│   │   │   │   ├── HeightOf03.golden
│   │   │   │   ├── HeightOf04.golden
│   │   │   │   ├── HeightOf05.golden
│   │   │   │   ├── HeightOf06.golden
│   │   │   │   ├── HeightOf07.golden
│   │   │   │   ├── HeightOf08.golden
│   │   │   │   ├── HeightOf09.golden
│   │   │   │   ├── HeightOf10.golden
│   │   │   │   ├── HeightOf11.golden
│   │   │   │   ├── HeightOf12.golden
│   │   │   │   ├── HeightOf13.golden
│   │   │   │   ├── HeightOf14.golden
│   │   │   │   ├── HeightOf15.golden
│   │   │   │   ├── HeightOf16.golden
│   │   │   │   ├── HeightOf17.golden
│   │   │   │   ├── HeightOf18.golden
│   │   │   │   ├── HeightOf19.golden
│   │   │   │   ├── HeightOf20.golden
│   │   │   │   └── HeightOf21.golden
│   │   │   ├── WithBorderRow/
│   │   │   │   ├── HeightOf01.golden
│   │   │   │   ├── HeightOf02.golden
│   │   │   │   ├── HeightOf03.golden
│   │   │   │   ├── HeightOf04.golden
│   │   │   │   ├── HeightOf05.golden
│   │   │   │   ├── HeightOf06.golden
│   │   │   │   ├── HeightOf07.golden
│   │   │   │   ├── HeightOf08.golden
│   │   │   │   ├── HeightOf09.golden
│   │   │   │   ├── HeightOf10.golden
│   │   │   │   ├── HeightOf11.golden
│   │   │   │   ├── HeightOf12.golden
│   │   │   │   └── HeightOf13.golden
│   │   │   └── WithBorderRowPadding/
│   │   │       ├── HeightOf01.golden
│   │   │       ├── HeightOf02.golden
│   │   │       ├── HeightOf03.golden
│   │   │       ├── HeightOf04.golden
│   │   │       ├── HeightOf05.golden
│   │   │       ├── HeightOf06.golden
│   │   │       ├── HeightOf07.golden
│   │   │       ├── HeightOf08.golden
│   │   │       ├── HeightOf09.golden
│   │   │       ├── HeightOf10.golden
│   │   │       ├── HeightOf11.golden
│   │   │       ├── HeightOf12.golden
│   │   │       ├── HeightOf13.golden
│   │   │       ├── HeightOf14.golden
│   │   │       ├── HeightOf15.golden
│   │   │       ├── HeightOf16.golden
│   │   │       ├── HeightOf17.golden
│   │   │       ├── HeightOf18.golden
│   │   │       ├── HeightOf19.golden
│   │   │       ├── HeightOf20.golden
│   │   │       ├── HeightOf21.golden
│   │   │       ├── HeightOf22.golden
│   │   │       ├── HeightOf23.golden
│   │   │       ├── HeightOf24.golden
│   │   │       └── HeightOf25.golden
│   │   ├── TestTableHeightWithYOffset.golden
│   │   ├── TestTableHeights.golden
│   │   ├── TestTableMarginAndRightAlignment.golden
│   │   ├── TestTableMultiLineRowSeparator.golden
│   │   ├── TestTableNoColumnSeparators.golden
│   │   ├── TestTableNoColumnSeparatorsWithHeaders.golden
│   │   ├── TestTableNoHeaders.golden
│   │   ├── TestTableNoStyleFunc.golden
│   │   ├── TestTableOverFlowNoWrap.golden
│   │   ├── TestTableRowSeparators/
│   │   │   ├── no_overflow.golden
│   │   │   └── with_overflow.golden
│   │   ├── TestTableRowSeparators.golden
│   │   ├── TestTableSetRows.golden
│   │   ├── TestTableShrinkWithYOffset/
│   │   │   ├── NoHeaders.golden
│   │   │   ├── WithBorderRow.golden
│   │   │   └── WithHeaders.golden
│   │   ├── TestTableUnsetBorders.golden
│   │   ├── TestTableUnsetHeaderSeparator.golden
│   │   ├── TestTableUnsetHeaderSeparatorWithBorder.golden
│   │   ├── TestTableWidthExpand.golden
│   │   ├── TestTableWidthShrink/
│   │   │   ├── DefaultBorders.golden
│   │   │   ├── NoBorders.golden
│   │   │   └── OutlineBordersOnly.golden
│   │   ├── TestTableWidthSmartCrop.golden
│   │   ├── TestTableWidthSmartCropExtensive.golden
│   │   ├── TestTableWidthSmartCropTiny.golden
│   │   ├── TestTableWidths.golden
│   │   ├── TestTableWithBackground.golden
│   │   ├── TestTableYOffset.golden
│   │   ├── TestWrapPreStyledContent.golden
│   │   └── TestWrapStyleFuncContent.golden
│   └── util.go
├── terminal.go
├── tree/
│   ├── children.go
│   ├── enumerator.go
│   ├── example_test.go
│   ├── renderer.go
│   ├── testdata/
│   │   ├── TestAddItemWithAndWithoutRoot/
│   │   │   ├── with_root.golden
│   │   │   └── without_root.golden
│   │   ├── TestEmbedListWithinTree.golden
│   │   ├── TestFilter.golden
│   │   ├── TestMultilinePrefix.golden
│   │   ├── TestMultilinePrefixInception.golden
│   │   ├── TestMultilinePrefixSubtree.golden
│   │   ├── TestRootStyle.golden
│   │   ├── TestTree/
│   │   │   ├── after.golden
│   │   │   └── before.golden
│   │   ├── TestTreeAddTwoSubTreesWithoutName.golden
│   │   ├── TestTreeAllHidden.golden
│   │   ├── TestTreeCustom.golden
│   │   ├── TestTreeHidden.golden
│   │   ├── TestTreeLastNodeIsSubTree.golden
│   │   ├── TestTreeMixedEnumeratorSize.golden
│   │   ├── TestTreeMultilineNode.golden
│   │   ├── TestTreeNil.golden
│   │   ├── TestTreeRoot.golden
│   │   ├── TestTreeStartsWithSubtree.golden
│   │   ├── TestTreeStyleAt.golden
│   │   ├── TestTreeStyleNilFuncs.golden
│   │   ├── TestTreeSubTreeWithCustomEnumerator.golden
│   │   ├── TestTreeTable.golden
│   │   └── TestTypes.golden
│   ├── tree.go
│   └── tree_test.go
├── unset.go
├── whitespace.go
├── whitespace_test.go
├── wrap.go
└── writer.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# https://editorconfig.org/

root = true

[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.go]
indent_style = tab
indent_size = 8

[*.golden]
insert_final_newline = false
trim_trailing_whitespace = false


================================================
FILE: .gitattributes
================================================
*.golden linguist-generated=true -text


================================================
FILE: .github/CODEOWNERS
================================================
*  @meowgorithm @aymanbagabas


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**Setup**
Please complete the following information along with version numbers, if applicable.
 - OS [e.g. Ubuntu, macOS]
 - Shell [e.g. zsh, fish]
 - Terminal Emulator [e.g. kitty, iterm]
 - Terminal Multiplexer [e.g. tmux]
 - Locale [e.g. en_US.UTF-8, zh_CN.UTF-8, etc.]

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Source Code**
Please include source code if needed to reproduce the behavior. 

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
Add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: Discord
  url: https://charm.sh/discord
  about: Chat on our Discord.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/dependabot.yml
================================================
version: 2

updates:
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "05:00"
      timezone: "America/New_York"
    labels:
      - "dependencies"
    commit-message:
      prefix: "chore"
      include: "scope"
    groups:
      all:
        patterns:
          - "*"
    ignore:
      - dependency-name: github.com/charmbracelet/bubbletea/v2
        versions:
          - v2.0.0-beta1

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "05:00"
      timezone: "America/New_York"
    labels:
      - "dependencies"
    commit-message:
      prefix: "chore"
      include: "scope"
    groups:
      all:
        patterns:
          - "*"

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "05:00"
      timezone: "America/New_York"
    labels:
      - "dependencies"
    commit-message:
      prefix: "chore"
      include: "scope"
    groups:
      all:
        patterns:
          - "*"

  - package-ecosystem: "gomod"
    directory: "/example"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "05:00"
      timezone: "America/New_York"
    labels:
      - "dependencies"
    commit-message:
      prefix: "chore"
      include: "scope"
    groups:
      all:
        patterns:
          - "*"


================================================
FILE: .github/workflows/build.yml
================================================
name: build

on:
  push:
    branches:
      - "master"
  pull_request:

jobs:
  build:
    uses: charmbracelet/meta/.github/workflows/build.yml@main
    secrets:
      gh_pat: ${{ secrets.PERSONAL_ACCESS_TOKEN }}


================================================
FILE: .github/workflows/coverage.yml
================================================
name: coverage
on: [push, pull_request]

jobs:
  coverage:
    strategy:
      matrix:
        go-version: [^1]
        os: [ubuntu-latest]
    runs-on: ${{ matrix.os }}
    env:
      GO111MODULE: "on"
    steps:
      - name: Install Go
        uses: actions/setup-go@v6
        with:
          go-version: ${{ matrix.go-version }}

      - name: Checkout code
        uses: actions/checkout@v6

      - run: |
          git config --global url."https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/charmbracelet".insteadOf "https://github.com/charmbracelet"
          git config --global url."https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/charmcli".insteadOf "https://github.com/charmcli"

      - name: Coverage
        env:
          COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          go test -race -covermode atomic -coverprofile=profile.cov ./...
          go install github.com/mattn/goveralls@latest
          goveralls -coverprofile=profile.cov -service=github


================================================
FILE: .github/workflows/dependabot-sync.yml
================================================
name: dependabot-sync
on:
  schedule:
    - cron: "0 0 * * 0" # every Sunday at midnight
  workflow_dispatch: # allows manual triggering

permissions:
  contents: write
  pull-requests: write

jobs:
  dependabot-sync:
    uses: charmbracelet/meta/.github/workflows/dependabot-sync.yml@main
    with:
      repo_name: ${{ github.event.repository.name }}
    secrets:
      gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}


================================================
FILE: .github/workflows/lint-sync.yml
================================================
name: lint-sync
on:
  schedule:
    # every Sunday at midnight
    - cron: "0 0 * * 0"
  workflow_dispatch: # allows manual triggering

permissions:
  contents: write
  pull-requests: write

jobs:
  lint:
    uses: charmbracelet/meta/.github/workflows/lint-sync.yml@main


================================================
FILE: .github/workflows/lint.yml
================================================
name: lint
on:
  push:
  pull_request:

jobs:
  lint:
    uses: charmbracelet/meta/.github/workflows/lint.yml@main
    with:
      golangci_path: .golangci.yml
      golangci_version: v2.9
      timeout: 10m


================================================
FILE: .github/workflows/release.yml
================================================
name: goreleaser

on:
  push:
    tags:
      - v*.*.*

concurrency:
  group: goreleaser
  cancel-in-progress: true

jobs:
  goreleaser:
    uses: charmbracelet/meta/.github/workflows/goreleaser.yml@main
    secrets:
      docker_username: ${{ secrets.DOCKERHUB_USERNAME }}
      docker_token: ${{ secrets.DOCKERHUB_TOKEN }}
      gh_pat: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
      goreleaser_key: ${{ secrets.GORELEASER_KEY }}
      twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_KEY }}
      twitter_consumer_secret: ${{ secrets.TWITTER_CONSUMER_SECRET }}
      twitter_access_token: ${{ secrets.TWITTER_ACCESS_TOKEN }}
      twitter_access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
      mastodon_client_id: ${{ secrets.MASTODON_CLIENT_ID }}
      mastodon_client_secret: ${{ secrets.MASTODON_CLIENT_SECRET }}
      mastodon_access_token: ${{ secrets.MASTODON_ACCESS_TOKEN }}
      discord_webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
      discord_webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json


================================================
FILE: .gitignore
================================================
ssh_example_ed25519*
/tmp
**/.crush/**


================================================
FILE: .golangci.yml
================================================
version: "2"
run:
  tests: false
linters:
  enable:
    - bodyclose
    - exhaustive
    - goconst
    - godot
    - gomoddirectives
    - goprintffuncname
    - gosec
    - misspell
    - nakedret
    - nestif
    - nilerr
    - noctx
    - nolintlint
    - prealloc
    - revive
    - rowserrcheck
    - sqlclosecheck
    - tparallel
    - unconvert
    - unparam
    - whitespace
    - wrapcheck
  exclusions:
    rules:
      - text: '(slog|log)\.\w+'
        linters:
          - noctx
    generated: lax
    presets:
      - common-false-positives
  settings:
    exhaustive:
      default-signifies-exhaustive: true
issues:
  max-issues-per-linter: 0
  max-same-issues: 0
formatters:
  enable:
    - gofumpt
    - goimports
  exclusions:
    generated: lax


================================================
FILE: .goreleaser.yml
================================================
includes:
  - from_url:
      url: charmbracelet/meta/main/goreleaser-lib.yaml
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json



================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021-2026 Charmbracelet, Inc.

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.


================================================
FILE: README.md
================================================
# Lip Gloss

<p >
    <img src="https://github.com/user-attachments/assets/d13bbe1a-d2b2-4d18-9302-419a0bc3f579" width="350"><br>
    <a href="https://github.com/charmbracelet/lipgloss/releases"><img src="https://img.shields.io/github/release/charmbracelet/lipgloss.svg" alt="Latest Release"></a>
    <a href="https://pkg.go.dev/charm.land/lipgloss/v2?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
    <a href="https://github.com/charmbracelet/lipgloss/actions"><img src="https://github.com/charmbracelet/lipgloss/workflows/build/badge.svg" alt="Build Status"></a>
</p>

<p>Style definitions for nice terminal layouts. Built with TUIs in mind.</p>

![Lip Gloss example](https://github.com/user-attachments/assets/92560e60-d70e-4ce0-b39e-a60bb933356b)

Lip Gloss takes an expressive, declarative approach to terminal rendering.
Users familiar with CSS will feel at home with Lip Gloss.

```go
import "charm.land/lipgloss/v2"

var style = lipgloss.NewStyle().
    Bold(true).
    Foreground(lipgloss.Color("#FAFAFA")).
    Background(lipgloss.Color("#7D56F4")).
    PaddingTop(2).
    PaddingLeft(4).
    Width(22)

lipgloss.Println(style.Render("Hello, kitty"))
```

## Installation

```bash
go get charm.land/lipgloss/v2
```

> [!TIP]
>
> Upgrading from v1? Check out the [upgrade guide](./UPGRADE_GUIDE_V2.md), or
> point your LLM at it and let it go to town.

## Colors

Lip Gloss supports the following color profiles:

### ANSI 16 colors (4-bit)

```go
lipgloss.Color("5")  // magenta
lipgloss.Color("9")  // red
lipgloss.Color("12") // light blue
```

### ANSI 256 Colors (8-bit)

```go
lipgloss.Color("86")  // aqua
lipgloss.Color("201") // hot pink
lipgloss.Color("202") // orange
```

### True Color (16,777,216 colors; 24-bit)

```go
lipgloss.Color("#0000FF") // good ol' 100% blue
lipgloss.Color("#04B575") // a green
lipgloss.Color("#3C3C3C") // a dark gray
```

...as well as a 1-bit ASCII profile, which is black and white only.

There are also named constants for the 16 standard ANSI colors:

```go
lipgloss.Black
lipgloss.Red
lipgloss.Green
lipgloss.Yellow
lipgloss.Blue
lipgloss.Magenta
lipgloss.Cyan
lipgloss.White
lipgloss.BrightBlack
lipgloss.BrightRed
lipgloss.BrightGreen
lipgloss.BrightYellow
lipgloss.BrightBlue
lipgloss.BrightMagenta
lipgloss.BrightCyan
lipgloss.BrightWhite
```

### Automatically Downsampling Colors

Some users don't have Truecolor terminals. Other times, output might not
support color at all (for example, in logs). Lip Gloss was designed to handle
this gracefully by automatically downsampling colors to the best available
profile.

If you're using Lip Gloss with Bubble Tea, there’s nothing to do. If you're
using Lip Gloss standalone, just use `lipgloss.Println` or `lipgloss.Sprint`
(and their variants).

For more, see [advanced color usage](#advanced-color-usage).

### Color Utilities

Lip Gloss ships with a handful of handy tools for working with colors:

```go
c := lipgloss.Color("#EB4268")      // Sriracha sauce color
dark := lipgloss.Darken(c, 0.5)     // dark Sriracha sauce
light := lipgloss.Lighten(c, 0.35)  // light Sriracha sauce
green := lipgloss.Complementary(c)  // greenish Sriracha sauce
withAlpha := lipgloss.Alpha(c, 0.2) // watered down Sriracha sauce
```

### Advanced Color Tooling

Lip Gloss also supports color blending, automatically choosing light or dark
variants of colors at runtime, and a lot more. For details, see [Advanced Color
Usage](#advanced-color-usage) and [the docs][docs].

## Inline Formatting

Lip Gloss supports the usual ANSI text formatting options:

```go
var style = lipgloss.NewStyle().
    Bold(true).
    Italic(true).
    Faint(true).
    Blink(true).
    Strikethrough(true).
    Underline(true).
    Reverse(true)
```

### Underline Styles

Beyond simple on/off, underlines support multiple styles and custom colors:

```go
s := lipgloss.NewStyle().
    UnderlineStyle(lipgloss.UnderlineCurly).
    UnderlineColor(lipgloss.Color("#FF0000"))
```

Available styles: `UnderlineNone`, `UnderlineSingle`, `UnderlineDouble`,
`UnderlineCurly`, `UnderlineDotted`, `UnderlineDashed`.

### Hyperlinks

Styles can render clickable hyperlinks in supporting terminals:

```go
s := lipgloss.NewStyle().
    Foreground(lipgloss.Color("#7B2FBE")).
    Hyperlink("https://charm.land")

lipgloss.Println(s.Render("Visit Charm"))
```

In unsupported terminals this will degrade gracefully and hyperlinks will
simply not render.

## Block-Level Formatting

Lip Gloss also supports rules for block-level formatting:

```go
// Padding
var style = lipgloss.NewStyle().
    PaddingTop(2).
    PaddingRight(4).
    PaddingBottom(2).
    PaddingLeft(4)

// Margins
var style = lipgloss.NewStyle().
    MarginTop(2).
    MarginRight(4).
    MarginBottom(2).
    MarginLeft(4)
```

There is also shorthand syntax for margins and padding, which follows the same
format as CSS:

```go
// 2 cells on all sides
lipgloss.NewStyle().Padding(2)

// 2 cells on the top and bottom, 4 cells on the left and right
lipgloss.NewStyle().Margin(2, 4)

// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
lipgloss.NewStyle().Padding(1, 4, 2)

// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
// the bottom, and 1 on the left
lipgloss.NewStyle().Margin(2, 4, 3, 1)
```

You can also customize the characters used for padding and margin fill:

```go
s := lipgloss.NewStyle().
    Padding(1, 2).
    PaddingChar('·').
    Margin(1, 2).
    MarginChar('░')
```

## Aligning Text

You can align paragraphs of text to the left, right, or center.

```go
var style = lipgloss.NewStyle().
    Width(24).
    Align(lipgloss.Left).  // align it left
    Align(lipgloss.Right). // no wait, align it right
    Align(lipgloss.Center) // just kidding, align it in the center
```

## Width and Height

Setting a minimum width and height is simple and straightforward.

```go
var style = lipgloss.NewStyle().
    SetString("What’s for lunch?").
    Width(24).
    Height(32).
    Foreground(lipgloss.Color("63"))
```

## Borders

Adding borders is easy:

```go
// Add a purple, rectangular border
var style = lipgloss.NewStyle().
    BorderStyle(lipgloss.NormalBorder()).
    BorderForeground(lipgloss.Color("63"))

// Set a rounded, yellow-on-purple border to the top and left
var anotherStyle = lipgloss.NewStyle().
    BorderStyle(lipgloss.RoundedBorder()).
    BorderForeground(lipgloss.Color("228")).
    BorderBackground(lipgloss.Color("63")).
    BorderTop(true).
    BorderLeft(true)

// Make your own border
var myCuteBorder = lipgloss.Border{
    Top:         "._.:*:",
    Bottom:      "._.:*:",
    Left:        "|*",
    Right:       "|*",
    TopLeft:     "*",
    TopRight:    "*",
    BottomLeft:  "*",
    BottomRight: "*",
}
```

There are also shorthand functions for defining borders, which follow a similar
pattern to the margin and padding shorthand functions.

```go
// Add a thick border to the top and bottom
lipgloss.NewStyle().
    Border(lipgloss.ThickBorder(), true, false)

// Add a double border to the top and left sides. Rules are set clockwise
// from top.
lipgloss.NewStyle().
    Border(lipgloss.DoubleBorder(), true, false, false, true)
```

You can also pass multiple colors to a border for a gradient effect:

```go
s := lipgloss.NewStyle().
    Border(lipgloss.RoundedBorder()).
    BorderForegroundBlend(lipgloss.Color("#FF0000"), lipgloss.Color("#0000FF"))
```

For more on borders see [the docs](https://pkg.go.dev/charm.land/lipgloss/v2#Border).

## Copying Styles

Just use assignment:

```go
style := lipgloss.NewStyle().Foreground(lipgloss.Color("219"))

copiedStyle := style // this is a true copy

wildStyle := style.Blink(true) // this is also true copy, with blink added
```

Since `Style` is a pure value type, assigning a style to another effectively
creates a new copy of the style without mutating the original.

## Inheritance

Styles can inherit rules from other styles. When inheriting, only unset rules
on the receiver are inherited.

```go
var styleA = lipgloss.NewStyle().
    Foreground(lipgloss.Color("229")).
    Background(lipgloss.Color("63"))

// Only the background color will be inherited here, because the foreground
// color will have been already set:
var styleB = lipgloss.NewStyle().
    Foreground(lipgloss.Color("201")).
    Inherit(styleA)
```

## Unsetting Rules

All rules can be unset:

```go
var style = lipgloss.NewStyle().
    Bold(true).                        // make it bold
    UnsetBold().                       // jk don't make it bold
    Background(lipgloss.Color("227")). // yellow background
    UnsetBackground()                  // never mind
```

When a rule is unset, it won’t be inherited or copied.

## Enforcing Rules

Sometimes, such as when developing a component, you want to make sure style
definitions respect their intended purpose in the UI. This is where `Inline`
and `MaxWidth`, and `MaxHeight` come in:

```go
// Force rendering onto a single line, ignoring margins, padding, and borders.
someStyle.Inline(true).Render("yadda yadda")

// Also limit rendering to five cells
someStyle.Inline(true).MaxWidth(5).Render("yadda yadda")

// Limit rendering to a 5x5 cell block
someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda")
```

## Tabs

The tab character (`\t`) is rendered differently in different terminals (often
as 8 spaces, sometimes 4). Because of this inconsistency, Lip Gloss converts
tabs to 4 spaces at render time. This behavior can be changed on a per-style
basis, however:

```go
style := lipgloss.NewStyle() // tabs will render as 4 spaces, the default
style = style.TabWidth(2)    // render tabs as 2 spaces
style = style.TabWidth(0)    // remove tabs entirely
style = style.TabWidth(lipgloss.NoTabConversion) // leave tabs intact
```

## Wrapping

The `Wrap` function wraps text while preserving ANSI styles and hyperlinks
across line boundaries:

```go
wrapped := lipgloss.Wrap(styledText, 40, " ")
```

## Rendering

Generally, you just call the `Render(string...)` method on a `lipgloss.Style`:

```go
style := lipgloss.NewStyle().Bold(true).SetString("Hello,")
lipgloss.Println(style.Render("kitty.")) // Hello, kitty.
lipgloss.Println(style.Render("puppy.")) // Hello, puppy.
```

But you could also use the Stringer interface:

```go
var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true)
lipgloss.Println(style) // 你好,猫咪。
```

## Utilities

In addition to pure styling, Lip Gloss also ships with some utilities to help
assemble your layouts.

### Compositing

<p><img width="350" alt="xx" src="https://github.com/user-attachments/assets/1921bac6-2408-436a-9d9e-7930fe4c6ec9" /></p>

Lip Gloss includes a powerful, cell-based compositor for rendering layered
content:

```go
// Create some layers.
a := lipgloss.NewLayer(pickles).X(4).Y(2).Z(1)
b := lipgloss.NewLayer(bitterMelon).X(22).Y(1)
c := lipgloss.NewLayer(sriracha).X(11).Y(7)

// Composite 'em and render.
output := compositor.Compose(a, b, c).Render()
```

For a more thorough example, see [the canvas
example](./examples/canvas/main.go). For reference, including how to detect
mouse clicks on layers, see [the docs][docs].

### Joining Paragraphs

Horizontally and vertically joining paragraphs is a cinch.

```go
// Horizontally join three paragraphs along their bottom edges
lipgloss.JoinHorizontal(lipgloss.Bottom, paragraphA, paragraphB, paragraphC)

// Vertically join two paragraphs along their center axes
lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB)

// Horizontally join three paragraphs, with the shorter ones aligning 20%
// from the top of the tallest
lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC)
```

### Measuring Width and Height

Sometimes you’ll want to know the width and height of text blocks when building
your layouts.

```go
// Render a block of text.
var style = lipgloss.NewStyle().
    Width(40).
    Padding(2)
var block string = style.Render(someLongString)

// Get the actual, physical dimensions of the text block.
width := lipgloss.Width(block)
height := lipgloss.Height(block)

// Here's a shorthand function.
w, h := lipgloss.Size(block)
```

### Blending Colors

You can blend colors in one or two dimensions for gradient effects:

```go
// 1-dimentinoal gradient
colors := lipgloss.Blend1D(10, lipgloss.Color("#FF0000"), lipgloss.Color("#0000FF"))

// 2-dimensional gradient with rotation
colors := lipgloss.Blend2D(80, 24, 45.0, color1, color2, color3)
```

### Placing Text in Whitespace

Sometimes you’ll simply want to place a block of text in whitespace. This is
a lightweight alternative to compositing.

```go
// Center a paragraph horizontally in a space 80 cells wide. The height of
// the block returned will be as tall as the input paragraph.
block := lipgloss.PlaceHorizontal(80, lipgloss.Center, fancyStyledParagraph)

// Place a paragraph at the bottom of a space 30 cells tall. The width of
// the text block returned will be as wide as the input paragraph.
block := lipgloss.PlaceVertical(30, lipgloss.Bottom, fancyStyledParagraph)

// Place a paragraph in the bottom right corner of a 30x80 cell space.
block := lipgloss.Place(30, 80, lipgloss.Right, lipgloss.Bottom, fancyStyledParagraph)
```

You can also style the whitespace. For details, see [the docs][docs].

## Rendering Tables

Lip Gloss ships with a table rendering sub-package.

```go
import "charm.land/lipgloss/v2/table"
```

Define some rows of data.

```go
rows := [][]string{
    {"Chinese", "您好", "你好"},
    {"Japanese", "こんにちは", "やあ"},
    {"Arabic", "أهلين", "أهلا"},
    {"Russian", "Здравствуйте", "Привет"},
    {"Spanish", "Hola", "¿Qué tal?"},
}
```

Use the table package to style and render the table.

```go
var (
    purple    = lipgloss.Color("99")
    gray      = lipgloss.Color("245")
    lightGray = lipgloss.Color("241")

    headerStyle  = lipgloss.NewStyle().Foreground(purple).Bold(true).Align(lipgloss.Center)
    cellStyle    = lipgloss.NewStyle().Padding(0, 1).Width(14)
    oddRowStyle  = cellStyle.Foreground(gray)
    evenRowStyle = cellStyle.Foreground(lightGray)
)

t := table.New().
    Border(lipgloss.NormalBorder()).
    BorderStyle(lipgloss.NewStyle().Foreground(purple)).
    StyleFunc(func(row, col int) lipgloss.Style {
        switch {
        case row == table.HeaderRow:
            return headerStyle
        case row%2 == 0:
            return evenRowStyle
        default:
            return oddRowStyle
        }
    }).
    Headers("LANGUAGE", "FORMAL", "INFORMAL").
    Rows(rows...)

// You can also add tables row-by-row
t.Row("English", "You look absolutely fabulous.", "How's it going?")
```

Print the table.

```go
lipgloss.Println(t)
```

![Table Example](https://github.com/charmbracelet/lipgloss/assets/42545625/6e4b70c4-f494-45da-a467-bdd27df30d5d)

### Table Borders

There are helpers to generate tables in markdown or ASCII style:

#### Markdown Table

```go
table.New().Border(lipgloss.MarkdownBorder()).BorderTop(false).BorderBottom(false)
```

```
| LANGUAGE |    FORMAL    | INFORMAL  |
|----------|--------------|-----------|
| Chinese  | Nǐn hǎo      | Nǐ hǎo    |
| French   | Bonjour      | Salut     |
| Russian  | Zdravstvuyte | Privet    |
| Spanish  | Hola         | ¿Qué tal? |
```

#### ASCII Table

```go
table.New().Border(lipgloss.ASCIIBorder())
```

```
+----------+--------------+-----------+
| LANGUAGE |    FORMAL    | INFORMAL  |
+----------+--------------+-----------+
| Chinese  | Nǐn hǎo      | Nǐ hǎo    |
| French   | Bonjour      | Salut     |
| Russian  | Zdravstvuyte | Privet    |
| Spanish  | Hola         | ¿Qué tal? |
+----------+--------------+-----------+
```

For more on tables see [the docs][docs] and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table).

## Rendering Lists

Lip Gloss ships with a list rendering sub-package.

```go
import "charm.land/lipgloss/v2/list"
```

Define a new list.

```go
l := list.New("A", "B", "C")
```

Print the list.

```go
lipgloss.Println(l)

// • A
// • B
// • C
```

Lists have the ability to nest.

```go
l := list.New(
    "A", list.New("Artichoke"),
    "B", list.New("Baking Flour", "Bananas", "Barley", "Bean Sprouts"),
    "C", list.New("Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst"),
    "D", list.New("Dill", "Dragonfruit", "Dried Shrimp"),
    "E", list.New("Eggs"),
    "F", list.New("Fish Cake", "Furikake"),
    "J", list.New("Jicama"),
    "K", list.New("Kohlrabi"),
    "L", list.New("Leeks", "Lentils", "Licorice Root"),
)
```

Print the list.

```go
lipgloss.Println(l)
```

<p align="center">
<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/0dc9f440-0748-4151-a3b0-7dcf29dfcdb0">
</p>

Lists can be customized via their enumeration function as well as using
`lipgloss.Style`s.

```go
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)

l := list.New(
    "Glossier",
    "Claire's Boutique",
    "Nyx",
    "Mac",
    "Milk",
    ).
    Enumerator(list.Roman).
    EnumeratorStyle(enumeratorStyle).
    ItemStyle(itemStyle)
```

Print the list.

<p align="center">
<img width="600" alt="List example" src="https://github.com/charmbracelet/lipgloss/assets/42545625/360494f1-57fb-4e13-bc19-0006efe01561">
</p>

In addition to the predefined enumerators (`Arabic`, `Alphabet`, `Roman`, `Bullet`, `Tree`),
you may also define your own custom enumerator:

```go
l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck")

func DuckDuckGooseEnumerator(l list.Items, i int) string {
    if l.At(i).Value() == "Goose" {
        return "Honk →"
    }
    return ""
}

l = l.Enumerator(DuckDuckGooseEnumerator)
```

Print the list:

<p align="center">
<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/157aaf30-140d-4948-9bb4-dfba46e5b87e">
</p>

If you need, you can also build lists incrementally:

```go
l := list.New()

for i := 0; i < repeat; i++ {
    l.Item("Lip Gloss")
}
```

## Rendering Trees

Lip Gloss ships with a tree rendering sub-package.

```go
import "charm.land/lipgloss/v2/tree"
```

Define a new tree.

```go
t := tree.Root(".").
    Child("A", "B", "C")
```

Print the tree.

```go
lipgloss.Println(t)

// .
// ├── A
// ├── B
// └── C
```

Trees have the ability to nest.

```go
t := tree.Root(".").
    Child("macOS").
    Child(
        tree.New().
            Root("Linux").
            Child("NixOS").
            Child("Arch Linux (btw)").
            Child("Void Linux"),
        ).
    Child(
        tree.New().
            Root("BSD").
            Child("FreeBSD").
            Child("OpenBSD"),
    )
```

Print the tree.

```go
lipgloss.Println(t)
```

<p align="center">
<img width="663" alt="Tree Example (simple)" src="https://github.com/user-attachments/assets/5ef14eb8-a5d4-4f94-8834-e15d1e714f89">
</p>

Trees can be customized via their enumeration function as well as using
`lipgloss.Style`s.

```go
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1)
rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35"))
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212"))

t := tree.
    Root("⁜ Makeup").
    Child(
        "Glossier",
        "Fenty Beauty",
        tree.New().Child(
            "Gloss Bomb Universal Lip Luminizer",
            "Hot Cheeks Velour Blushlighter",
        ),
        "Nyx",
        "Mac",
        "Milk",
    ).
    Enumerator(tree.RoundedEnumerator).
    EnumeratorStyle(enumeratorStyle).
    RootStyle(rootStyle).
    ItemStyle(itemStyle)
```

Print the tree.

<p align="center">
<img width="663" alt="Tree Example (makeup)" src="https://github.com/user-attachments/assets/06d12d87-744a-4c89-bd98-45de9094a97e">
</p>

The predefined enumerators for trees are `DefaultEnumerator` and `RoundedEnumerator`.

If you need, you can also build trees incrementally:

```go
t := tree.New()

for i := 0; i < repeat; i++ {
    t.Child("Lip Gloss")
}
```

## Advanced Color Usage

One of the most powerful features of Lip Gloss is the ability to render
different colors at runtime depending on the user's terminal and environment,
allowing you to present the best possible user experience.

This section shows you how to do exactly that.

<details>
<summary>Migrating from v1?</summary>

The `compat` package provides `AdaptiveColor`, `CompleteColor`, and
`CompleteAdaptiveColor` for a quicker migration from v1. These work by
looking at `stdin` and `stdout` on a global basis:

```go
import "charm.land/lipgloss/v2/compat"

color := compat.AdaptiveColor{
    Light: lipgloss.Color("#f1f1f1"),
    Dark:  lipgloss.Color("#cccccc"),
}
```

Note that we don't recommend this for new code as it removes the purity from
Lip Gloss, computationally speaking, as it removes transparency around when
I/O happens, which could cause Lip Gloss to compete for resources (like stdin)
with other tools.

</details>

### Adaptive Colors

You can render different colors at runtime depending on whether the terminal
has a light or dark background:

```go
hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
lightDark := lipgloss.LightDark(hasDarkBG)

myColor := lightDark(lipgloss.Color("#D7FFAE"), lipgloss.Color("#D75FEE"))
```

#### With Bubble Tea

In Bubble Tea, request the background color, listen for a
`BackgroundColorMsg`, and respond accordingly:

```go
func (m model) Init() tea.Cmd {
    // First, send a Cmd to request the terminal background color.
    return tea.RequestBackgroundColor
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.BackgroundColorMsg:
        // Great, we have the background color. Now we can set up our styles
        // against the color.
        m.styles = newStyles(msg.IsDark())
        return m, nil
    }
}

func newStyles(bgIsDark bool) styles {
    // A little ternary function that will return the appropriate color
    // based on the background color.
    lightDark := lipgloss.LightDark(bgIsDark)

    return styles{
        myHotStyle: lipgloss.NewStyle().Foreground(lightDark(
            lipgloss.Color("#f1f1f1"),
            lipgloss.Color("#333333"),
        )),
    }
}
```

#### Standalone

If you’re not using Bubble Tea you can perform the query manually:

```go
// What's the background color?
hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stderr)

// A helper function that will return the appropriate color based on the
// background.
lightDark := lipgloss.LightDark(hasDarkBG)

// A couple colors with light and dark variants.
thisColor := lightDark(lipgloss.Color("#C5ADF9"), lipgloss.Color("#864EFF"))
thatColor := lightDark(lipgloss.Color("#37CD96"), lipgloss.Color("#22C78A"))

a := lipgloss.NewStyle().Foreground(thisColor).Render("this")
b := lipgloss.NewStyle().Foreground(thatColor).Render("that")

// Render the appropriate colors at runtime:
lipgloss.Fprintf(os.Stderr, "my fave colors are %s and %s", a, b)
```

### Complete Colors

In some cases where you may want to specify exact values for each color profile
(ANSI 16, ANSI 156, and TrueColor). For these cases, use the `Complete` helper:

```go
// You'll need the colorprofile package.
import "github.com/charmbracelet/colorprofile"

// Get the color profile.
profile := colorprofile.Detect(os.Stdout, os.Environ())

// Create a function for rendering the appropriate color based on the profile.
var completeColor := lipgloss.Complete(profile)

// Now we'll choose the appropriate color at runtime.
myColor := completeColor(ansiColor, ansi256Color, trueColor)
```

### Color Downsampling

One of the best things about Lip Gloss is that it can automatically downsample
colors to the best available profile, stripping colors (and ANSI) entirely when
output is not a TTY.

If you’re using Lip Gloss with Bubble Tea there’s nothing to do here:
downsampling is built into Bubble Tea v2. If you’re not using Bubble Tea, use
the Lip Gloss writer functions, which are a drop-in replacement for the `fmt`
package:

```go
s := lipgloss.NewStyle()
    .Foreground(lipgloss.Color("#EB4268"))
    .Render("Hello!")

// Downsample if needed and print to stdout.
lipgloss.Println(s)

// Render to a variable.
downsampled := lipgloss.Sprint(s)

// Print to stderr.
lipgloss.Fprint(os.Stderr, s)
```

The full set: `Print`, `Println`, `Printf`, `Fprint`, `Fprintln`, `Fprintf`,
`Sprint`, `Sprintln`, `Sprintf`.

Need more control? Check out
[Colorprofile](https://github.com/charmbracelet/colorprofile), which Lip Gloss
uses under the hood.

## What about [Bubble Tea][tea]?

Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea
companion. It was designed to make assembling terminal user interface views as
simple and fun as possible so that you can focus on building your application
instead of concerning yourself with low-level layout details.

In simple terms, you can use Lip Gloss to help build your Bubble Tea views.

[tea]: https://github.com/charmbracelet/bubbletea

## Rendering Markdown

For a more document-centric rendering solution with support for things like
lists, tables, and syntax-highlighted code have a look at [Glamour][glamour],
the stylesheet-based Markdown renderer.

[glamour]: https://github.com/charmbracelet/glamour

## Contributing

See [contributing][contribute].

[contribute]: https://github.com/charmbracelet/lipgloss/contribute

## Feedback

We’d love to hear your thoughts on this project. Feel free to drop us a note!

- [Discord](https://charm.land/chat)
- [Matrix](https://charm.land/matrix)

## License

[MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE)

---

Part of [Charm](https://charm.land).

<a href="https://charm.land/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-banner-next.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source

[docs]: https://pkg.go.dev/charm.land/lipgloss/v2?tab=doc


================================================
FILE: Taskfile.yaml
================================================
# https://taskfile.dev

version: "3"

tasks:
  lint:
    desc: Run base linters
    cmds:
      - golangci-lint run

  test:
    desc: Run tests
    cmds:
      - go test ./... {{.CLI_ARGS}}

  test:table:
    desc: Run table tests
    cmds:
      - go test ./table {{.CLI_ARGS}}

  test:tree:
    desc: Run tree tests
    cmds:
      - go test ./tree {{.CLI_ARGS}}


================================================
FILE: UPGRADE_GUIDE_V2.md
================================================
# Lip Gloss v2 Upgrade Guide

This guide covers migrating from Lip Gloss v1 (`github.com/charmbracelet/lipgloss`)
to Lip Gloss v2 (`charm.land/lipgloss/v2`). It is written for both humans and
LLMs performing automated migrations.

---

## Table of Contents

1. [Quick Start](#quick-start)
2. [Module Path](#module-path)
3. [Color System](#color-system)
4. [Renderer Removal](#renderer-removal)
5. [Printing and Color Downsampling](#printing-and-color-downsampling)
6. [Background Detection and Adaptive Colors](#background-detection-and-adaptive-colors)
7. [Whitespace Options](#whitespace-options)
8. [Underline](#underline)
9. [Style API Changes](#style-api-changes)
10. [Tree Subpackage](#tree-subpackage)
11. [Removed APIs](#removed-apis)
12. [Quick Reference Table](#quick-reference-table)

---

## Quick Start

For the fastest possible upgrade, do these two things:

### 1. Use the `compat` package for adaptive/complete colors

```go
import "charm.land/lipgloss/v2/compat"

// v1
color := lipgloss.AdaptiveColor{Light: "#f1f1f1", Dark: "#cccccc"}

// v2
color := compat.AdaptiveColor{Light: lipgloss.Color("#f1f1f1"), Dark: lipgloss.Color("#cccccc")}
```

The `compat` package reads `stdin`/`stdout` globally, just like v1. To
customize:

```go
import (
    "charm.land/lipgloss/v2/compat"
    "github.com/charmbracelet/colorprofile"
)

func init() {
    compat.HasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stderr)
    compat.Profile = colorprofile.Detect(os.Stderr, os.Environ())
}
```

### 2. Use Lip Gloss writers for output

```go
// v1
fmt.Println(s)

// v2
lipgloss.Println(s)
```

This ensures colors are automatically downsampled. If you're using Bubble Tea
v2, this step is unnecessary — Bubble Tea handles it for you.

**That's the quick path.** Read on for the full migration details.

---

## Module Path

The import path has changed.

```go
// v1
import "github.com/charmbracelet/lipgloss"

// v2
import "charm.land/lipgloss/v2"
```

**Install:**

```bash
go get charm.land/lipgloss/v2
```

All subpackages follow the same pattern:

```go
// v1
import "github.com/charmbracelet/lipgloss/table"
import "github.com/charmbracelet/lipgloss/tree"
import "github.com/charmbracelet/lipgloss/list"

// v2
import "charm.land/lipgloss/v2/table"
import "charm.land/lipgloss/v2/tree"
import "charm.land/lipgloss/v2/list"
```

**Search-and-replace pattern:**

```
github.com/charmbracelet/lipgloss → charm.land/lipgloss/v2
```

---

## Color System

This is the most significant API change.

### `Color` is now a function, not a type

```go
// v1 — Color is a string type
var c lipgloss.Color = "21"
var c lipgloss.Color = "#ff00ff"

// v2 — Color is a function returning color.Color
var c color.Color = lipgloss.Color("21")
var c color.Color = lipgloss.Color("#ff00ff")
```

The return type is `image/color.Color` (from the standard library).

### `TerminalColor` interface is removed

All methods that accepted `lipgloss.TerminalColor` now accept
`image/color.Color`:

```go
// v1
func (s Style) Foreground(c TerminalColor) Style
func (s Style) Background(c TerminalColor) Style
func (s Style) BorderForeground(c ...TerminalColor) Style

// v2
func (s Style) Foreground(c color.Color) Style
func (s Style) Background(c color.Color) Style
func (s Style) BorderForeground(c ...color.Color) Style
```

**Migration:** Replace every `lipgloss.TerminalColor` with `color.Color` and
add `import "image/color"`.

### `ANSIColor` is now an alias

```go
// v1 — custom uint type
type ANSIColor uint

// v2 — alias for ansi.IndexedColor
type ANSIColor = ansi.IndexedColor
```

v2 also exports named constants for the 16 basic ANSI colors:

```go
lipgloss.Black, lipgloss.Red, lipgloss.Green, lipgloss.Yellow,
lipgloss.Blue, lipgloss.Magenta, lipgloss.Cyan, lipgloss.White,
lipgloss.BrightBlack, lipgloss.BrightRed, lipgloss.BrightGreen,
lipgloss.BrightYellow, lipgloss.BrightBlue, lipgloss.BrightMagenta,
lipgloss.BrightCyan, lipgloss.BrightWhite
```

### `AdaptiveColor`, `CompleteColor`, `CompleteAdaptiveColor`

These types have been moved out of the root package. Use the `compat` package
for a drop-in replacement, or use the new `LightDark` and `Complete` helpers
for explicit control:

```go
// v1
color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"}

// v2 — using compat (quick path)
color := compat.AdaptiveColor{
    Light: lipgloss.Color("#0000ff"),
    Dark:  lipgloss.Color("#000099"),
}

// v2 — using LightDark (recommended)
hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
lightDark := lipgloss.LightDark(hasDark)
color := lightDark(lipgloss.Color("#0000ff"), lipgloss.Color("#000099"))
```

```go
// v1
color := lipgloss.CompleteColor{TrueColor: "#ff00ff", ANSI256: "200", ANSI: "5"}

// v2 — using compat
color := compat.CompleteColor{
    TrueColor: lipgloss.Color("#ff00ff"),
    ANSI256:   lipgloss.Color("200"),
    ANSI:      lipgloss.Color("5"),
}

// v2 — using Complete (recommended)
profile := colorprofile.Detect(os.Stdout, os.Environ())
complete := lipgloss.Complete(profile)
color := complete(lipgloss.Color("5"), lipgloss.Color("200"), lipgloss.Color("#ff00ff"))
```

Note that `compat.AdaptiveColor` and friends take `color.Color` values for
their fields, not strings.

---

## Renderer Removal

The `Renderer` type and all associated functions are removed. In v1, every
`Style` carried a `*Renderer` pointer and the package maintained a global
default renderer.

```go
// v1 — these no longer exist
lipgloss.DefaultRenderer()
lipgloss.SetDefaultRenderer(r)
lipgloss.NewRenderer(w, opts...)
lipgloss.ColorProfile()
lipgloss.SetColorProfile(p)
renderer.NewStyle()
```

**In v2, `Style` is a plain value type.** There is no renderer. Color
downsampling is handled at the output layer (see next section).

**Migration:**

- Replace `lipgloss.DefaultRenderer().NewStyle()` with `lipgloss.NewStyle()`.
- Replace `renderer.NewStyle()` with `lipgloss.NewStyle()`.
- Remove any `*Renderer` fields from your types.
- Remove calls to `SetColorProfile` — use `colorprofile.Detect` at the output
  layer instead.

---

## Printing and Color Downsampling

In v1, color downsampling happened inside `Style.Render()` via the renderer. In
v2, `Render()` always emits full-fidelity ANSI. Downsampling happens when you
print.

### Standalone Usage

Use the Lip Gloss writer functions:

```go
s := someStyle.Render("Hello!")

// Print to stdout with automatic downsampling
lipgloss.Println(s)

// Print to stderr
lipgloss.Fprintln(os.Stderr, s)

// Render to a string (downsampled for stdout's profile)
str := lipgloss.Sprint(s)
```

The default writer targets `stdout`. To customize:

```go
lipgloss.Writer = colorprofile.NewWriter(os.Stderr, os.Environ())
```

### With Bubble Tea

No changes needed. Bubble Tea v2 handles downsampling internally.

---

## Background Detection and Adaptive Colors

### Standalone

v1 detected the background color automatically via the global renderer. v2
requires explicit queries:

```go
// v1
hasDark := lipgloss.HasDarkBackground()

// v2 — specify the input and output
hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
```

Then use `LightDark` to pick colors:

```go
lightDark := lipgloss.LightDark(hasDark)
fg := lightDark(lipgloss.Color("#333333"), lipgloss.Color("#f1f1f1"))

s := lipgloss.NewStyle().Foreground(fg)
```

### With Bubble Tea

Request the background color in `Init` and listen for the response:

```go
func (m model) Init() tea.Cmd {
    return tea.RequestBackgroundColor
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.BackgroundColorMsg:
        m.styles = newStyles(msg.IsDark())
    }
    // ...
}

func newStyles(bgIsDark bool) styles {
    lightDark := lipgloss.LightDark(bgIsDark)
    return styles{
        title: lipgloss.NewStyle().Foreground(lightDark(
            lipgloss.Color("#333333"),
            lipgloss.Color("#f1f1f1"),
        )),
    }
}
```

---

## Whitespace Options

The separate foreground/background whitespace options have been replaced by a
single style option:

```go
// v1
lipgloss.Place(width, height, hPos, vPos, str,
    lipgloss.WithWhitespaceForeground(lipgloss.Color("#333")),
    lipgloss.WithWhitespaceBackground(lipgloss.Color("#000")),
)

// v2
lipgloss.Place(width, height, hPos, vPos, str,
    lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().
        Foreground(lipgloss.Color("#333")).
        Background(lipgloss.Color("#000")),
    ),
)
```

---

## Underline

`Underline(bool)` still works for basic on/off. v2 adds fine-grained control:

```go
// v1
s := lipgloss.NewStyle().Underline(true)

// v2 — still works
s := lipgloss.NewStyle().Underline(true)

// v2 — new: specific styles
s := lipgloss.NewStyle().UnderlineStyle(lipgloss.UnderlineCurly)

// v2 — new: colored underlines
s := lipgloss.NewStyle().
    UnderlineStyle(lipgloss.UnderlineSingle).
    UnderlineColor(lipgloss.Color("#FF0000"))
```

Internally, `Underline(true)` is equivalent to `UnderlineStyle(UnderlineSingle)`
and `Underline(false)` is equivalent to `UnderlineStyle(UnderlineNone)`.

---

## Style API Changes

### `NewStyle()` is no longer tied to a Renderer

```go
// v1
s := lipgloss.NewStyle()         // uses global renderer
s := renderer.NewStyle()          // uses specific renderer

// v2
s := lipgloss.NewStyle()         // pure value, no renderer
```

### Color getters return `color.Color`

```go
// v1
fg := s.GetForeground() // returns TerminalColor

// v2
fg := s.GetForeground() // returns color.Color
```

### New style methods

| Method | Description |
|---|---|
| `UnderlineStyle(Underline)` | Set underline style (single, double, curly, etc.) |
| `UnderlineColor(color.Color)` | Set underline color |
| `PaddingChar(rune)` | Set the character used for padding fill |
| `MarginChar(rune)` | Set the character used for margin fill |
| `Hyperlink(link, params...)` | Set a clickable hyperlink |
| `BorderForegroundBlend(...color.Color)` | Apply gradient colors to borders |
| `BorderForegroundBlendOffset(int)` | Set the offset for border gradient |

Each has a corresponding `Get*`, `Unset*`, and where applicable `Get*`
accessor.

---

## Tree Subpackage

The import path changes and there are new styling options:

```go
// v1
import "github.com/charmbracelet/lipgloss/tree"

// v2
import "charm.land/lipgloss/v2/tree"
```

New methods:

- `IndenterStyle(lipgloss.Style)` — set a static style for tree indentation.
- `IndenterStyleFunc(func(Children, int) lipgloss.Style)` — conditionally style
  indentation.
- `Width(int)` — set tree width for padding.

---

## Removed APIs

The following types and functions no longer exist in v2. This table shows each
removed symbol and its replacement.

| v1 Symbol | v2 Replacement |
|---|---|
| `type Renderer` | Removed entirely |
| `DefaultRenderer()` | Not needed |
| `SetDefaultRenderer(r)` | Not needed |
| `NewRenderer(w, opts...)` | Not needed |
| `ColorProfile()` | `colorprofile.Detect(w, env)` |
| `SetColorProfile(p)` | Set `lipgloss.Writer.Profile` |
| `HasDarkBackground()` (no args) | `lipgloss.HasDarkBackground(in, out)` |
| `SetHasDarkBackground(b)` | Not needed — pass bool to `LightDark` |
| `type TerminalColor` | `image/color.Color` |
| `type Color string` | `func Color(string) color.Color` |
| `type ANSIColor uint` | `type ANSIColor = ansi.IndexedColor` |
| `type AdaptiveColor` | `compat.AdaptiveColor` or `LightDark` |
| `type CompleteColor` | `compat.CompleteColor` or `Complete` |
| `type CompleteAdaptiveColor` | `compat.CompleteAdaptiveColor` |
| `WithWhitespaceForeground(c)` | `WithWhitespaceStyle(s)` |
| `WithWhitespaceBackground(c)` | `WithWhitespaceStyle(s)` |
| `renderer.NewStyle()` | `lipgloss.NewStyle()` |

---

## Quick Reference Table

A side-by-side summary for common patterns:

| Task | v1 | v2 |
|---|---|---|
| Import | `"github.com/charmbracelet/lipgloss"` | `"charm.land/lipgloss/v2"` |
| Create style | `lipgloss.NewStyle()` | `lipgloss.NewStyle()` |
| Hex color | `lipgloss.Color("#ff00ff")` | `lipgloss.Color("#ff00ff")` |
| ANSI color | `lipgloss.Color("5")` | `lipgloss.Color("5")` or `lipgloss.Magenta` |
| Adaptive color | `lipgloss.AdaptiveColor{Light: "#fff", Dark: "#000"}` | `compat.AdaptiveColor{Light: lipgloss.Color("#fff"), Dark: lipgloss.Color("#000")}` |
| Set foreground | `s.Foreground(lipgloss.Color("5"))` | `s.Foreground(lipgloss.Color("5"))` |
| Print with downsampling | `fmt.Println(s.Render("hi"))` | `lipgloss.Println(s.Render("hi"))` |
| Detect dark bg | `lipgloss.HasDarkBackground()` | `lipgloss.HasDarkBackground(os.Stdin, os.Stdout)` |
| Light/dark color | `lipgloss.AdaptiveColor{...}` | `lipgloss.LightDark(isDark)(light, dark)` |
| Whitespace styling | `WithWhitespaceForeground(c)` | `WithWhitespaceStyle(lipgloss.NewStyle().Foreground(c))` |
| Underline | `s.Underline(true)` | `s.Underline(true)` or `s.UnderlineStyle(lipgloss.UnderlineCurly)` |

---

## Feedback

Questions, issues, or feedback:

- [Discord](https://charm.land/discord)
- [Matrix](https://charm.land/matrix)
- [Email](mailto:vt100@charm.land)

---

Part of [Charm](https://charm.land).

<a href="https://charm.land/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة


================================================
FILE: align.go
================================================
package lipgloss

import (
	"strings"

	"github.com/charmbracelet/x/ansi"
)

// Perform text alignment. If the string is multi-lined, we also make all lines
// the same width by padding them with spaces. If a style is passed, use that
// to style the spaces added.
func alignTextHorizontal(str string, pos Position, width int, style *ansi.Style) string {
	lines, widestLine := getLines(str)
	var b strings.Builder

	for i, l := range lines {
		lineWidth := ansi.StringWidth(l)

		shortAmount := widestLine - lineWidth                // difference from the widest line
		shortAmount += max(0, width-(shortAmount+lineWidth)) // difference from the total width, if set

		if shortAmount > 0 {
			switch pos {
			case Right:
				s := strings.Repeat(" ", shortAmount)
				if style != nil {
					s = style.Styled(s)
				}
				l = s + l
			case Center:
				// Note: remainder goes on the right.
				left := shortAmount / 2       //nolint:mnd
				right := left + shortAmount%2 //nolint:mnd

				leftSpaces := strings.Repeat(" ", left)
				rightSpaces := strings.Repeat(" ", right)

				if style != nil {
					leftSpaces = style.Styled(leftSpaces)
					rightSpaces = style.Styled(rightSpaces)
				}
				l = leftSpaces + l + rightSpaces
			default: // Left
				s := strings.Repeat(" ", shortAmount)
				if style != nil {
					s = style.Styled(s)
				}
				l += s
			}
		}

		b.WriteString(l)
		if i < len(lines)-1 {
			b.WriteRune('\n')
		}
	}

	return b.String()
}

func alignTextVertical(str string, pos Position, height int, _ *ansi.Style) string {
	strHeight := strings.Count(str, "\n") + 1
	if height < strHeight {
		return str
	}

	switch pos {
	case Top:
		return str + strings.Repeat("\n", height-strHeight)
	case Center:
		topPadding, bottomPadding := (height-strHeight)/2, (height-strHeight)/2 //nolint:mnd
		if strHeight+topPadding+bottomPadding > height {
			topPadding--
		} else if strHeight+topPadding+bottomPadding < height {
			bottomPadding++
		}
		return strings.Repeat("\n", topPadding) + str + strings.Repeat("\n", bottomPadding)
	case Bottom:
		return strings.Repeat("\n", height-strHeight) + str
	}
	return str
}


================================================
FILE: align_test.go
================================================
package lipgloss

import "testing"

func TestAlignTextVertical(t *testing.T) {
	tests := []struct {
		str    string
		pos    Position
		height int
		want   string
	}{
		{str: "Foo", pos: Top, height: 2, want: "Foo\n"},
		{str: "Foo", pos: Center, height: 5, want: "\n\nFoo\n\n"},
		{str: "Foo", pos: Bottom, height: 5, want: "\n\n\n\nFoo"},

		{str: "Foo\nBar", pos: Bottom, height: 5, want: "\n\n\nFoo\nBar"},
		{str: "Foo\nBar", pos: Center, height: 5, want: "\nFoo\nBar\n\n"},
		{str: "Foo\nBar", pos: Top, height: 5, want: "Foo\nBar\n\n\n"},

		{str: "Foo\nBar\nBaz", pos: Bottom, height: 5, want: "\n\nFoo\nBar\nBaz"},
		{str: "Foo\nBar\nBaz", pos: Center, height: 5, want: "\nFoo\nBar\nBaz\n"},

		{str: "Foo\nBar\nBaz", pos: Bottom, height: 3, want: "Foo\nBar\nBaz"},
		{str: "Foo\nBar\nBaz", pos: Center, height: 3, want: "Foo\nBar\nBaz"},
		{str: "Foo\nBar\nBaz", pos: Top, height: 3, want: "Foo\nBar\nBaz"},

		{str: "Foo\n\n\n\nBar", pos: Bottom, height: 5, want: "Foo\n\n\n\nBar"},
		{str: "Foo\n\n\n\nBar", pos: Center, height: 5, want: "Foo\n\n\n\nBar"},
		{str: "Foo\n\n\n\nBar", pos: Top, height: 5, want: "Foo\n\n\n\nBar"},

		{str: "Foo\nBar\nBaz", pos: Center, height: 9, want: "\n\n\nFoo\nBar\nBaz\n\n\n"},
		{str: "Foo\nBar\nBaz", pos: Center, height: 10, want: "\n\n\nFoo\nBar\nBaz\n\n\n\n"},
	}

	for _, test := range tests {
		got := alignTextVertical(test.str, test.pos, test.height, nil)
		if got != test.want {
			t.Errorf("alignTextVertical(%q, %v, %d) = %q, want %q", test.str, test.pos, test.height, got, test.want)
		}
	}
}


================================================
FILE: ansi_unix.go
================================================
//go:build !windows

package lipgloss

import "os"

// EnableLegacyWindowsANSI is only needed on Windows.
func EnableLegacyWindowsANSI(*os.File) {}


================================================
FILE: ansi_windows.go
================================================
//go:build windows

package lipgloss

import (
	"os"

	"golang.org/x/sys/windows"
)

// EnableLegacyWindowsANSI enables support for ANSI color sequences in the
// Windows default console (cmd.exe and the PowerShell application). Note that
// this only works with Windows 10 and greater. Also note that Windows Terminal
// supports colors by default.
func EnableLegacyWindowsANSI(f *os.File) {
	var mode uint32
	handle := windows.Handle(f.Fd())
	err := windows.GetConsoleMode(handle, &mode)
	if err != nil {
		return
	}

	// See https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
	if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
		vtpmode := mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
		if err := windows.SetConsoleMode(handle, vtpmode); err != nil {
			return
		}
	}
}


================================================
FILE: blending.go
================================================
package lipgloss

import (
	"image/color"
	"math"
	"slices"

	"github.com/lucasb-eyer/go-colorful"
)

// Blend1D blends a series of colors together in one linear dimension using multiple
// stops, into the provided number of steps. Uses the "CIE L*, a*, b*" (CIELAB) color-space.
//
// Note that if any of the provided colors are completely transparent, we will
// assume that the alpha value was lost in conversion from RGB -> RGBA, and we
// will set the alpha to opaque, as it's not possible to blend something completely
// transparent.
func Blend1D(steps int, stops ...color.Color) []color.Color {
	if steps < 0 {
		steps = 0
	}

	if steps <= len(stops) {
		return stops[:steps]
	}

	// Ensure they didn't provide any nil colors.
	stops = slices.DeleteFunc(stops, func(c color.Color) bool {
		return c == nil
	})

	if len(stops) == 0 {
		return nil // We can't safely fallback.
	}

	// If they only provided one valid color (or some nil colors), we will just return
	// an array of that color, for the amount of steps they requested.
	if len(stops) == 1 {
		singleColor := stops[0]
		result := make([]color.Color, steps)
		for i := range result {
			result[i] = singleColor
		}
		return result
	}

	blended := make([]color.Color, steps)

	// Convert stops to colorful.Color once
	cstops := make([]colorful.Color, len(stops))
	for i, k := range stops {
		cstops[i], _ = colorful.MakeColor(ensureNotTransparent(k))
	}

	numSegments := len(cstops) - 1
	defaultSize := steps / numSegments
	remainingSteps := steps % numSegments

	resultIndex := 0
	for i := range numSegments {
		from := cstops[i]
		to := cstops[i+1]

		// Calculate segment size.
		segmentSize := defaultSize
		if i < remainingSteps {
			segmentSize++
		}

		divisor := float64(segmentSize - 1)

		// Generate colors for this segment.
		for j := 0; j < segmentSize; j++ {
			var blendingFactor float64
			if segmentSize > 1 {
				blendingFactor = float64(j) / divisor
			}
			blended[resultIndex] = from.BlendLab(to, blendingFactor).Clamped()
			resultIndex++
		}
	}

	return blended
}

// Blend2D blends a series of colors together in two linear dimensions using
// multiple stops, into the provided width/height. Uses the "CIE L*, a*, b*" (CIELAB)
// color-space. The angle parameter controls the rotation of the gradient (0-360°),
// where 0° is left-to-right, 45° is bottom-left to top-right (diagonal). The function
// returns colors in a 1D row-major order ([row1, row2, row3, ...]).
//
// Example of how to iterate over the result:
//
//	gradient := colors.Blend2D(width, height, 180, color1, color2, color3, ...)
//	gradientContent := strings.Builder{}
//	for y := range height {
//		for x := range width {
//			index := y*width + x
//			gradientContent.WriteString(
//				lipgloss.NewStyle().
//					Background(gradient[index]).
//					Render(" "),
//			)
//		}
//		if y < height-1 { // End of row.
//			gradientContent.WriteString("\n")
//		}
//	}
//
// Note that if any of the provided colors are completely transparent, we will
// assume that the alpha value was lost in conversion from RGB -> RGBA, and we
// will set the alpha to opaque, as it's not possible to blend something completely
// transparent.
func Blend2D(width, height int, angle float64, stops ...color.Color) []color.Color {
	if width < 1 {
		width = 1
	}
	if height < 1 {
		height = 1
	}

	// Normalize angle to 0-360.
	angle = math.Mod(angle, 360)
	if angle < 0 {
		angle += 360
	}

	// Ensure they didn't provide any nil colors.
	stops = slices.DeleteFunc(stops, func(c color.Color) bool {
		return c == nil
	})

	if len(stops) == 0 {
		return nil // We can't safely fallback.
	}

	// If they only provided one valid color (or some nil colors), we will just return
	// an array of that color, for the amount of pixels they requested.
	if len(stops) == 1 {
		singleColor := stops[0]
		result := make([]color.Color, width*height)
		for i := range result {
			result[i] = singleColor
		}
		return result
	}

	// For 2D blending, we'll create a gradient along the diagonal and then sample
	// from it based on the angle. We'll use the maximum dimension to ensure we have
	// enough resolution for the gradient.
	diagonalGradient := Blend1D(max(width, height), stops...)

	result := make([]color.Color, width*height)

	// Calculate center point for rotation.
	centerX := float64(width-1) / 2.0
	centerY := float64(height-1) / 2.0

	angleRad := angle * math.Pi / 180.0 // -> radians.

	// Pre-calculate sin and cos.
	cosAngle := math.Cos(angleRad)
	sinAngle := math.Sin(angleRad)

	// Calculate diagonal length for proper gradient mapping.
	diagonalLength := math.Sqrt(float64(width*width + height*height))

	// Pre-calculate gradient length for index calculation.
	gradientLen := float64(len(diagonalGradient) - 1)

	for y := range height {
		// Calculate the distance from center along the gradient direction.
		dy := float64(y) - centerY

		for x := 0; x < width; x++ {
			// Calculate the distance from center along the gradient direction.
			dx := float64(x) - centerX

			rotX := dx*cosAngle - dy*sinAngle // Rotate the point by the angle.

			// Map the rotated position to the gradient. Normalize to 0-1 range based on
			// the diagonal length.
			gradientPos := clamp((rotX+diagonalLength/2.0)/diagonalLength, 0, 1)

			// Calculate the index in the gradient.
			gradientIndex := int(gradientPos * gradientLen)
			if gradientIndex >= len(diagonalGradient) {
				gradientIndex = len(diagonalGradient) - 1
			}

			result[y*width+x] = diagonalGradient[gradientIndex] // -> row-major order.
		}
	}

	return result
}


================================================
FILE: blending_test.go
================================================
package lipgloss

import (
	"image/color"
	"testing"
)

func TestBlend1D(t *testing.T) {
	tests := []struct {
		name     string
		steps    int
		stops    []color.Color
		expected []color.Color
	}{
		{
			name:  "2-colors-10-steps",
			steps: 10,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
				&color.RGBA{R: 246, G: 0, B: 45, A: 255},
				&color.RGBA{R: 235, G: 0, B: 73, A: 255},
				&color.RGBA{R: 223, G: 0, B: 99, A: 255},
				&color.RGBA{R: 210, G: 0, B: 124, A: 255},
				&color.RGBA{R: 193, G: 0, B: 149, A: 255},
				&color.RGBA{R: 173, G: 0, B: 175, A: 255},
				&color.RGBA{R: 147, G: 0, B: 201, A: 255},
				&color.RGBA{R: 109, G: 0, B: 228, A: 255},
				&color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
		},
		{
			name:  "3-colors-4-steps",
			steps: 4,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 255, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
				&color.RGBA{R: 0, G: 255, B: 0, A: 255},
				&color.RGBA{R: 0, G: 255, B: 0, A: 255},
				&color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
		},
		{
			name:  "black-to-white-5-steps",
			steps: 5,
			stops: []color.Color{
				color.RGBA{R: 0, G: 0, B: 0, A: 255},
				color.RGBA{R: 255, G: 255, B: 255, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 0, G: 0, B: 0, A: 255},
				&color.RGBA{R: 59, G: 59, B: 59, A: 255},
				&color.RGBA{R: 119, G: 119, B: 119, A: 255},
				&color.RGBA{R: 185, G: 185, B: 185, A: 255},
				&color.RGBA{R: 255, G: 255, B: 255, A: 255},
			},
		},
		{
			name:  "4-colors-6-steps",
			steps: 6,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 255, G: 255, B: 0, A: 255},
				color.RGBA{R: 0, G: 255, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
				&color.RGBA{R: 255, G: 255, B: 0, A: 255},
				&color.RGBA{R: 255, G: 255, B: 0, A: 255},
				&color.RGBA{R: 0, G: 255, B: 0, A: 255},
				&color.RGBA{R: 0, G: 255, B: 0, A: 255},
				&color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
		},
		{
			name:  "2-steps-5-stops",
			steps: 2,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 255, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
				color.RGBA{R: 255, G: 255, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 0, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
				&color.RGBA{R: 0, G: 255, B: 0, A: 255},
			},
		},
		{
			name:  "3-steps-1-stop",
			steps: 3,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
			},
		},
		{
			name:  "1-step-2-stops",
			steps: 1,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expected: []color.Color{
				&color.RGBA{R: 255, G: 0, B: 0, A: 255},
			},
		},
		{
			name:     "0-steps-0-stops",
			steps:    0,
			stops:    []color.Color{},
			expected: []color.Color{},
		},
		{
			name:  "0-steps-1-stop",
			steps: 0,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
			},
			expected: []color.Color{},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			got := Blend1D(tt.steps, tt.stops...)

			if len(got) != len(tt.expected) {
				t.Errorf("Blend() = %v length, want %v length", len(got), len(tt.expected))
			}

			for i := range tt.expected {
				expectColorMatches(t, got[i], tt.expected[i])
			}
		})
	}
}

func TestBlend2D(t *testing.T) {
	tests := []struct {
		name           string
		width, height  int
		angle          float64
		stops          []color.Color
		expectedLength int
	}{
		{
			name:   "2x2-red-to-blue-0deg",
			width:  2,
			height: 2,
			angle:  0,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 4,
		},
		{
			name:   "3x2-red-to-blue-90deg",
			width:  3,
			height: 2,
			angle:  90,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 6,
		},
		{
			name:   "2x3-red-to-blue-180deg",
			width:  2,
			height: 3,
			angle:  180,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 6,
		},
		{
			name:   "2x2-red-to-blue-270deg",
			width:  2,
			height: 2,
			angle:  270,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 4,
		},
		{
			name:   "1x1-single-color",
			width:  1,
			height: 1,
			angle:  0,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
			},
			expectedLength: 1,
		},
		{
			name:   "3-colors-2x2-0deg",
			width:  2,
			height: 2,
			angle:  0,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 255, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 4,
		},
		{
			name:   "invalid-dimensions-fallback",
			width:  0,
			height: -1,
			angle:  0,
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
			},
			expectedLength: 1,
		},
		{
			name:   "angle-normalization-450",
			width:  2,
			height: 2,
			angle:  450, // Should normalize to 90
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 4,
		},
		{
			name:   "negative-angle-normalization",
			width:  2,
			height: 2,
			angle:  -90, // Should normalize to 270
			stops: []color.Color{
				color.RGBA{R: 255, G: 0, B: 0, A: 255},
				color.RGBA{R: 0, G: 0, B: 255, A: 255},
			},
			expectedLength: 4,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			got := Blend2D(tt.width, tt.height, tt.angle, tt.stops...)

			if len(got) != tt.expectedLength {
				t.Errorf("Blend2D() = %v length, want %v length", len(got), tt.expectedLength)
			}

			// Verify row-major order by checking that the width matches.
			if tt.width > 0 && tt.height > 0 {
				expectedTotal := max(tt.width, 1) * max(tt.height, 1)
				if len(got) != expectedTotal {
					t.Errorf("Blend2D() total pixels = %v, want %v", len(got), expectedTotal)
				}
			}

			// Verify that we have valid colors (not nil).
			for i, color := range got {
				if color == nil {
					t.Errorf("Blend2D() color at index %d is nil", i)
				}
			}

			// For single color tests, verify all colors are the same.
			if len(tt.stops) == 1 && len(got) > 0 {
				firstColor := got[0]
				for _, color := range got {
					expectColorMatches(t, color, firstColor)
				}
			}
		})
	}
}

func TestBlend2DEdgeCases(t *testing.T) {
	t.Run("nil-stops", func(t *testing.T) {
		t.Parallel()
		got := Blend2D(2, 2, 0, nil, nil)
		if got != nil {
			t.Errorf("Blend2D() with nil stops = %v, want nil", got)
		}
	})

	t.Run("empty-stops", func(t *testing.T) {
		t.Parallel()
		got := Blend2D(2, 2, 0)
		if got != nil {
			t.Errorf("Blend2D() with empty stops = %v, want nil", got)
		}
	})

	t.Run("nil-color-in-stops", func(t *testing.T) {
		t.Parallel()
		got := Blend2D(2, 2, 0, color.RGBA{R: 255, G: 0, B: 0, A: 255}, nil, color.RGBA{R: 0, G: 0, B: 255, A: 255})
		if len(got) != 4 {
			t.Errorf("Blend2D() with nil color in stops = %v length, want 4", len(got))
		}
		// Should still work with the non-nil colors and produce valid colors
		for i, color := range got {
			if color == nil {
				t.Errorf("Blend2D() color at index %d is nil", i)
			}
		}
	})
}

func BenchmarkBlend1D(b *testing.B) {
	stops := []color.Color{
		hex("#FF0000"), // Red
		hex("#00FF00"), // Green
		hex("#0000FF"), // Blue
		hex("#FFFF00"), // Yellow
		hex("#FF00FF"), // Magenta
	}

	for b.Loop() {
		Blend1D(100, stops...)
	}
}

func BenchmarkBlend2D(b *testing.B) {
	stops := []color.Color{
		hex("#FF0000"), // Red
		hex("#00FF00"), // Green
		hex("#0000FF"), // Blue
		hex("#FFFF00"), // Yellow
		hex("#FF00FF"), // Magenta
	}

	for b.Loop() {
		Blend2D(100, 50, 45, stops...)
	}
}


================================================
FILE: borders.go
================================================
package lipgloss

import (
	"image/color"
	"slices"
	"strings"
	"unicode/utf8"

	"github.com/charmbracelet/x/ansi"
	"github.com/clipperhouse/displaywidth"
	"github.com/rivo/uniseg"
)

// Border contains a series of values which comprise the various parts of a
// border.
type Border struct {
	Top          string
	Bottom       string
	Left         string
	Right        string
	TopLeft      string
	TopRight     string
	BottomLeft   string
	BottomRight  string
	MiddleLeft   string
	MiddleRight  string
	Middle       string
	MiddleTop    string
	MiddleBottom string
}

// GetTopSize returns the width of the top border. If borders contain runes of
// varying widths, the widest rune is returned. If no border exists on the top
// edge, 0 is returned.
func (b Border) GetTopSize() int {
	return getBorderEdgeWidth(b.TopLeft, b.Top, b.TopRight)
}

// GetRightSize returns the width of the right border. If borders contain
// runes of varying widths, the widest rune is returned. If no border exists on
// the right edge, 0 is returned.
func (b Border) GetRightSize() int {
	return getBorderEdgeWidth(b.TopRight, b.Right, b.BottomRight)
}

// GetBottomSize returns the width of the bottom border. If borders contain
// runes of varying widths, the widest rune is returned. If no border exists on
// the bottom edge, 0 is returned.
func (b Border) GetBottomSize() int {
	return getBorderEdgeWidth(b.BottomLeft, b.Bottom, b.BottomRight)
}

// GetLeftSize returns the width of the left border. If borders contain runes
// of varying widths, the widest rune is returned. If no border exists on the
// left edge, 0 is returned.
func (b Border) GetLeftSize() int {
	return getBorderEdgeWidth(b.TopLeft, b.Left, b.BottomLeft)
}

func getBorderEdgeWidth(borderParts ...string) (maxWidth int) {
	for _, piece := range borderParts {
		maxWidth = max(maxWidth, maxRuneWidth(piece))
	}
	return maxWidth
}

var (
	noBorder = Border{}

	normalBorder = Border{
		Top:          "─",
		Bottom:       "─",
		Left:         "│",
		Right:        "│",
		TopLeft:      "┌",
		TopRight:     "┐",
		BottomLeft:   "└",
		BottomRight:  "┘",
		MiddleLeft:   "├",
		MiddleRight:  "┤",
		Middle:       "┼",
		MiddleTop:    "┬",
		MiddleBottom: "┴",
	}

	roundedBorder = Border{
		Top:          "─",
		Bottom:       "─",
		Left:         "│",
		Right:        "│",
		TopLeft:      "╭",
		TopRight:     "╮",
		BottomLeft:   "╰",
		BottomRight:  "╯",
		MiddleLeft:   "├",
		MiddleRight:  "┤",
		Middle:       "┼",
		MiddleTop:    "┬",
		MiddleBottom: "┴",
	}

	blockBorder = Border{
		Top:          "█",
		Bottom:       "█",
		Left:         "█",
		Right:        "█",
		TopLeft:      "█",
		TopRight:     "█",
		BottomLeft:   "█",
		BottomRight:  "█",
		MiddleLeft:   "█",
		MiddleRight:  "█",
		Middle:       "█",
		MiddleTop:    "█",
		MiddleBottom: "█",
	}

	outerHalfBlockBorder = Border{
		Top:         "▀",
		Bottom:      "▄",
		Left:        "▌",
		Right:       "▐",
		TopLeft:     "▛",
		TopRight:    "▜",
		BottomLeft:  "▙",
		BottomRight: "▟",
	}

	innerHalfBlockBorder = Border{
		Top:         "▄",
		Bottom:      "▀",
		Left:        "▐",
		Right:       "▌",
		TopLeft:     "▗",
		TopRight:    "▖",
		BottomLeft:  "▝",
		BottomRight: "▘",
	}

	thickBorder = Border{
		Top:          "━",
		Bottom:       "━",
		Left:         "┃",
		Right:        "┃",
		TopLeft:      "┏",
		TopRight:     "┓",
		BottomLeft:   "┗",
		BottomRight:  "┛",
		MiddleLeft:   "┣",
		MiddleRight:  "┫",
		Middle:       "╋",
		MiddleTop:    "┳",
		MiddleBottom: "┻",
	}

	doubleBorder = Border{
		Top:          "═",
		Bottom:       "═",
		Left:         "║",
		Right:        "║",
		TopLeft:      "╔",
		TopRight:     "╗",
		BottomLeft:   "╚",
		BottomRight:  "╝",
		MiddleLeft:   "╠",
		MiddleRight:  "╣",
		Middle:       "╬",
		MiddleTop:    "╦",
		MiddleBottom: "╩",
	}

	hiddenBorder = Border{
		Top:          " ",
		Bottom:       " ",
		Left:         " ",
		Right:        " ",
		TopLeft:      " ",
		TopRight:     " ",
		BottomLeft:   " ",
		BottomRight:  " ",
		MiddleLeft:   " ",
		MiddleRight:  " ",
		Middle:       " ",
		MiddleTop:    " ",
		MiddleBottom: " ",
	}

	markdownBorder = Border{
		Top:          "-",
		Bottom:       "-",
		Left:         "|",
		Right:        "|",
		TopLeft:      "|",
		TopRight:     "|",
		BottomLeft:   "|",
		BottomRight:  "|",
		MiddleLeft:   "|",
		MiddleRight:  "|",
		Middle:       "|",
		MiddleTop:    "|",
		MiddleBottom: "|",
	}

	asciiBorder = Border{
		Top:          "-",
		Bottom:       "-",
		Left:         "|",
		Right:        "|",
		TopLeft:      "+",
		TopRight:     "+",
		BottomLeft:   "+",
		BottomRight:  "+",
		MiddleLeft:   "+",
		MiddleRight:  "+",
		Middle:       "+",
		MiddleTop:    "+",
		MiddleBottom: "+",
	}
)

// NormalBorder returns a standard-type border with a normal weight and 90
// degree corners.
func NormalBorder() Border {
	return normalBorder
}

// RoundedBorder returns a border with rounded corners.
func RoundedBorder() Border {
	return roundedBorder
}

// BlockBorder returns a border that takes the whole block.
func BlockBorder() Border {
	return blockBorder
}

// OuterHalfBlockBorder returns a half-block border that sits outside the frame.
func OuterHalfBlockBorder() Border {
	return outerHalfBlockBorder
}

// InnerHalfBlockBorder returns a half-block border that sits inside the frame.
func InnerHalfBlockBorder() Border {
	return innerHalfBlockBorder
}

// ThickBorder returns a border that's thicker than the one returned by
// NormalBorder.
func ThickBorder() Border {
	return thickBorder
}

// DoubleBorder returns a border comprised of two thin strokes.
func DoubleBorder() Border {
	return doubleBorder
}

// HiddenBorder returns a border that renders as a series of single-cell
// spaces. It's useful for cases when you want to remove a standard border but
// maintain layout positioning. This said, you can still apply a background
// color to a hidden border.
func HiddenBorder() Border {
	return hiddenBorder
}

// MarkdownBorder return a table border in markdown style.
//
// Make sure to disable top and bottom border for the best result. This will
// ensure that the output is valid markdown.
//
//	table.New().Border(lipgloss.MarkdownBorder()).BorderTop(false).BorderBottom(false)
func MarkdownBorder() Border {
	return markdownBorder
}

// ASCIIBorder returns a table border with ASCII characters.
func ASCIIBorder() Border {
	return asciiBorder
}

type borderBlend struct {
	topGradient    []color.Color
	rightGradient  []color.Color
	bottomGradient []color.Color
	leftGradient   []color.Color
}

func (s Style) borderBlend(width, height int, colors ...color.Color) *borderBlend {
	gradient := Blend1D(
		(height+width+2)*2,
		colors...,
	)

	// Rotate array forward or reverse based on the offset if provided.
	if r := -s.getAsInt(borderForegroundBlendOffsetKey); r != 0 {
		n := len(gradient)
		r %= n
		if r < 0 {
			r += n
		}
		slices.Reverse(gradient[:r])
		slices.Reverse(gradient[r:])
		slices.Reverse(gradient)
	}

	offset := 0
	getFromOffset := func(size int) (s []color.Color) {
		s = gradient[offset : offset+size]
		offset += size
		return s
	}

	blend := &borderBlend{
		topGradient:    getFromOffset(width + 2),
		rightGradient:  getFromOffset(height),
		bottomGradient: getFromOffset(width + 2),
		leftGradient:   getFromOffset(height),
	}

	// bottom and left gradients are reversed because they are drawn in reverse order.
	slices.Reverse(blend.bottomGradient)
	slices.Reverse(blend.leftGradient)

	return blend
}

func (s Style) applyBorder(str string) string {
	var (
		border    = s.getBorderStyle()
		hasTop    = s.getAsBool(borderTopKey, false)
		hasRight  = s.getAsBool(borderRightKey, false)
		hasBottom = s.getAsBool(borderBottomKey, false)
		hasLeft   = s.getAsBool(borderLeftKey, false)
	)

	// If a border is set and no sides have been specifically turned on or off
	// render borders on all sides.
	if s.isBorderStyleSetWithoutSides() {
		hasTop = true
		hasRight = true
		hasBottom = true
		hasLeft = true
	}

	// If no border is set or all borders are been disabled, abort.
	if border == noBorder || (!hasTop && !hasRight && !hasBottom && !hasLeft) {
		return str
	}

	lines, width := getLines(str)

	if hasLeft {
		if border.Left == "" {
			border.Left = " "
		}
		width += maxRuneWidth(border.Left)
	}

	if hasRight {
		if border.Right == "" {
			border.Right = " "
		}
		width += maxRuneWidth(border.Right)
	}

	// If corners should be rendered but are set with the empty string, fill them
	// with a single space.
	if hasTop && hasLeft && border.TopLeft == "" {
		border.TopLeft = " "
	}
	if hasTop && hasRight && border.TopRight == "" {
		border.TopRight = " "
	}
	if hasBottom && hasLeft && border.BottomLeft == "" {
		border.BottomLeft = " "
	}
	if hasBottom && hasRight && border.BottomRight == "" {
		border.BottomRight = " "
	}

	// Figure out which corners we should actually be using based on which
	// sides are set to show.
	if hasTop {
		switch {
		case !hasLeft && !hasRight:
			border.TopLeft = ""
			border.TopRight = ""
		case !hasLeft:
			border.TopLeft = ""
		case !hasRight:
			border.TopRight = ""
		}
	}
	if hasBottom {
		switch {
		case !hasLeft && !hasRight:
			border.BottomLeft = ""
			border.BottomRight = ""
		case !hasLeft:
			border.BottomLeft = ""
		case !hasRight:
			border.BottomRight = ""
		}
	}

	// For now, limit corners to one rune.
	border.TopLeft = getFirstRuneAsString(border.TopLeft)
	border.TopRight = getFirstRuneAsString(border.TopRight)
	border.BottomRight = getFirstRuneAsString(border.BottomRight)
	border.BottomLeft = getFirstRuneAsString(border.BottomLeft)

	var topFG, rightFG, bottomFG, leftFG color.Color
	var (
		blendFG  = s.getAsColors(borderForegroundBlendKey)
		topBG    = s.getAsColor(borderTopBackgroundKey)
		rightBG  = s.getAsColor(borderRightBackgroundKey)
		bottomBG = s.getAsColor(borderBottomBackgroundKey)
		leftBG   = s.getAsColor(borderLeftBackgroundKey)
	)

	var blend *borderBlend
	if len(blendFG) > 0 {
		blend = s.borderBlend(width, len(lines), blendFG...)
	} else {
		topFG = s.getAsColor(borderTopForegroundKey)
		rightFG = s.getAsColor(borderRightForegroundKey)
		bottomFG = s.getAsColor(borderBottomForegroundKey)
		leftFG = s.getAsColor(borderLeftForegroundKey)
	}

	var out strings.Builder

	// Render top
	if hasTop {
		top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width)
		if blend != nil {
			out.WriteString(s.styleBorderBlend(top, blend.topGradient, topBG))
		} else {
			out.WriteString(s.styleBorder(top, topFG, topBG))
		}
		out.WriteRune('\n')
	}

	leftRunes := []rune(border.Left)
	leftIndex := 0

	rightRunes := []rune(border.Right)
	rightIndex := 0

	// Render sides
	var r string
	for i, l := range lines {
		if hasLeft {
			r = string(leftRunes[leftIndex])
			leftIndex++
			if leftIndex >= len(leftRunes) {
				leftIndex = 0
			}
			if blend != nil {
				out.WriteString(s.styleBorder(r, blend.leftGradient[i], leftBG))
			} else {
				out.WriteString(s.styleBorder(r, leftFG, leftBG))
			}
		}
		out.WriteString(l)
		if hasRight {
			r = string(rightRunes[rightIndex])
			rightIndex++
			if rightIndex >= len(rightRunes) {
				rightIndex = 0
			}
			if blend != nil {
				out.WriteString(s.styleBorder(r, blend.rightGradient[i], rightBG))
			} else {
				out.WriteString(s.styleBorder(r, rightFG, rightBG))
			}
		}
		if i < len(lines)-1 {
			out.WriteRune('\n')
		}
	}

	// Render bottom
	if hasBottom {
		bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width)
		out.WriteRune('\n')
		if blend != nil {
			out.WriteString(s.styleBorderBlend(bottom, blend.bottomGradient, bottomBG))
		} else {
			out.WriteString(s.styleBorder(bottom, bottomFG, bottomBG))
		}
	}

	return out.String()
}

// Render the horizontal (top or bottom) portion of a border.
func renderHorizontalEdge(left, middle, right string, width int) string {
	if middle == "" {
		middle = " "
	}

	leftWidth := ansi.StringWidth(left)
	rightWidth := ansi.StringWidth(right)

	runes := []rune(middle)
	j := 0

	out := strings.Builder{}
	out.WriteString(left)

	for i := 0; i < width-leftWidth-rightWidth; {
		r := runes[j]
		out.WriteRune(r)
		i += ansi.StringWidth(string(r))
		j++
		if j >= len(runes) {
			j = 0
		}
	}

	out.WriteString(right)
	return out.String()
}

// styleBorder applies foreground and background styling to a border.
func (s Style) styleBorder(border string, fg, bg color.Color) string {
	if fg == noColor && bg == noColor {
		return border
	}
	var style ansi.Style
	if fg != noColor {
		style = style.ForegroundColor(fg)
	}
	if bg != noColor {
		style = style.BackgroundColor(bg)
	}
	return style.Styled(border)
}

// styleBorderBlend applies foreground and background styling to a border, using blending.
func (s Style) styleBorderBlend(border string, fg []color.Color, bg color.Color) string {
	var out strings.Builder
	var style ansi.Style
	var i int

	gr := uniseg.NewGraphemes(border)
	for gr.Next() {
		style = style[:0]
		if fg[i] != noColor {
			style = style.ForegroundColor(fg[i])
		}
		if bg != noColor {
			style = style.BackgroundColor(bg)
		}
		_, _ = out.WriteString(style.String())
		_, _ = out.Write(gr.Bytes())
		i++
	}
	_, _ = out.WriteString(ansi.ResetStyle)
	return out.String()
}

func maxRuneWidth(str string) int {
	switch len(str) {
	case 0:
		return 0
	case 1:
		return displaywidth.String(str)
	}

	var width int

	g := displaywidth.StringGraphemes(str)
	for g.Next() {
		width = max(width, g.Width())
	}
	return width
}

func getFirstRuneAsString(str string) string {
	if str == "" {
		return str
	}
	_, size := utf8.DecodeRuneInString(str)
	return str[:size]
}


================================================
FILE: borders_test.go
================================================
package lipgloss

import (
	"testing"

	"github.com/rivo/uniseg"
)

func BenchmarkBorderRendering(b *testing.B) {
	dimensions := []struct {
		name   string
		width  int
		height int
	}{
		{"10x5", 10, 5},
		{"20x10", 20, 10},
		{"40x20", 40, 15},
		{"80x40", 80, 20},
		{"120x60", 120, 25},
		{"160x80", 160, 30},
	}

	for _, dim := range dimensions {
		b.Run(dim.name, func(b *testing.B) {
			style := NewStyle().
				Border(RoundedBorder(), true).
				Foreground(Color("#ffffff")).
				Background(Color("#000000")).
				Width(dim.width).
				Height(dim.height)

			b.ResetTimer()
			for b.Loop() {
				_ = style.Render("")
			}
		})
	}
}

func BenchmarkBorderBlend(b *testing.B) {
	dimensions := []struct {
		name   string
		width  int
		height int
	}{
		{"10x5", 10, 5},
		{"20x10", 20, 10},
		{"40x20", 40, 15},
		{"80x40", 80, 20},
		{"120x60", 120, 25},
		{"160x80", 160, 30},
	}

	for _, dim := range dimensions {
		b.Run(dim.name, func(b *testing.B) {
			style := NewStyle().
				Border(RoundedBorder(), true).
				BorderForegroundBlend(
					Color("#00FA68"),
					Color("#9900FF"),
					Color("#ED5353"),
				).
				Width(dim.width).
				Height(dim.height)

			b.ResetTimer()
			for b.Loop() {
				_ = style.Render("")
			}
		})
	}
}

func BenchmarkBorderRenderingNoColors(b *testing.B) {
	dimensions := []struct {
		name   string
		width  int
		height int
	}{
		{"10x5", 10, 5},
		{"20x10", 20, 10},
		{"40x20", 40, 15},
		{"80x40", 80, 20},
		{"120x60", 120, 25},
		{"160x80", 160, 30},
	}

	for _, dim := range dimensions {
		b.Run(dim.name, func(b *testing.B) {
			style := NewStyle().
				Border(RoundedBorder(), true).
				Width(dim.width).
				Height(dim.height)

			b.ResetTimer()
			for b.Loop() {
				_ = style.Render("")
			}
		})
	}
}

// Old implementation using rune slice conversion
func getFirstRuneAsStringOld(str string) string {
	if str == "" {
		return str
	}
	r := []rune(str)
	return string(r[0])
}

func TestGetFirstRuneAsString(t *testing.T) {
	tests := []struct {
		name  string
		input string
		want  string
	}{
		{"Empty", "", ""},
		{"SingleASCII", "A", "A"},
		{"SingleUnicode", "世", "世"},
		{"ASCIIString", "Hello", "H"},
		{"UnicodeString", "你好世界", "你"},
		{"MixedASCIIFirst", "Hello世界", "H"},
		{"MixedUnicodeFirst", "世界Hello", "世"},
		{"Emoji", "😀Happy", "😀"},
		{"MultiByteFirst", "ñoño", "ñ"},
		{"LongString", "The quick brown fox jumps over the lazy dog", "T"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := getFirstRuneAsString(tt.input)
			if got != tt.want {
				t.Errorf("getFirstRuneAsString(%q) = %q, want %q", tt.input, got, tt.want)
			}

			// Verify new implementation matches old implementation
			old := getFirstRuneAsStringOld(tt.input)
			if got != old {
				t.Errorf("getFirstRuneAsString(%q) = %q, but old implementation returns %q", tt.input, got, old)
			}
		})
	}
}

func BenchmarkGetFirstRuneAsString(b *testing.B) {
	testCases := []struct {
		name string
		str  string
	}{
		{"ASCII", "Hello, World!"},
		{"Unicode", "你好世界"},
		{"Single", "A"},
		{"Empty", ""},
	}

	b.Run("Old", func(b *testing.B) {
		for _, tc := range testCases {
			b.Run(tc.name, func(b *testing.B) {
				b.ReportAllocs()
				for i := 0; i < b.N; i++ {
					_ = getFirstRuneAsStringOld(tc.str)
				}
			})
		}
	})

	b.Run("New", func(b *testing.B) {
		for _, tc := range testCases {
			b.Run(tc.name, func(b *testing.B) {
				b.ReportAllocs()
				for i := 0; i < b.N; i++ {
					_ = getFirstRuneAsString(tc.str)
				}
			})
		}
	})
}

func BenchmarkMaxRuneWidth(b *testing.B) {
	testCases := []struct {
		name string
		str  string
	}{
		{"Blank", " "},
		{"ASCII", "+"},
		{"Markdown", "|"},
		{"Normal", "├"},
		{"Rounded", "╭"},
		{"Block", "█"},
		{"Emoji", "😀"},
	}
	for _, tc := range testCases {
		b.Run(tc.name, func(b *testing.B) {
			b.Run("Before", func(b *testing.B) {
				b.ReportAllocs()
				for b.Loop() {
					_ = maxRuneWidthOld(tc.str)
				}
			})
			b.Run("After", func(b *testing.B) {
				b.ReportAllocs()
				for b.Loop() {
					_ = maxRuneWidth(tc.str)
				}
			})
		})
	}
}

func maxRuneWidthOld(str string) int {
	var width int

	state := -1
	for len(str) > 0 {
		var w int
		_, str, w, state = uniseg.FirstGraphemeClusterInString(str, state)
		if w > width {
			width = w
		}
	}

	return width
}


================================================
FILE: canvas.go
================================================
package lipgloss

import (
	uv "github.com/charmbracelet/ultraviolet"
	"github.com/charmbracelet/x/ansi"
)

// Canvas is a cell-buffer that can be used to compose and draw [uv.Drawable]s
// like [Layer]s.
//
// Composed drawables are drawn onto the canvas in the order they were
// composed, meaning later drawables will appear "on top" of earlier ones.
//
// A canvas can read, modify, and render its cell contents.
//
// It implements [uv.Screen] and [uv.Drawable].
type Canvas struct {
	scr uv.ScreenBuffer
}

var _ uv.Screen = (*Canvas)(nil)

// NewCanvas creates a new [Canvas] with the given size.
func NewCanvas(width, height int) *Canvas {
	c := new(Canvas)
	c.scr = uv.NewScreenBuffer(width, height)
	c.scr.Method = ansi.GraphemeWidth
	return c
}

// Resize resizes the canvas to the given width and height.
func (c *Canvas) Resize(width, height int) {
	c.scr.Resize(width, height)
}

// Clear clears the canvas.
func (c *Canvas) Clear() {
	c.scr.Clear()
}

// Bounds implements [uv.Screen].
func (c *Canvas) Bounds() uv.Rectangle {
	return c.scr.Bounds()
}

// Width returns the width of the canvas.
func (c *Canvas) Width() int {
	return c.scr.Width()
}

// Height returns the height of the canvas.
func (c *Canvas) Height() int {
	return c.scr.Height()
}

// CellAt implements [uv.Screen].
func (c *Canvas) CellAt(x int, y int) *uv.Cell {
	return c.scr.CellAt(x, y)
}

// SetCell implements [uv.Screen].
func (c *Canvas) SetCell(x int, y int, cell *uv.Cell) {
	c.scr.SetCell(x, y, cell)
}

// WidthMethod implements [uv.Screen].
func (c *Canvas) WidthMethod() uv.WidthMethod {
	return c.scr.WidthMethod()
}

// Compose composes a [Layer] or any [uv.Drawable] onto the [Canvas].
func (c *Canvas) Compose(drawer uv.Drawable) *Canvas {
	drawer.Draw(c, c.Bounds())
	return c
}

// Draw draws the [Canvas] onto the given [uv.Screen] within the specified
// area.
//
// It implements [uv.Drawable].
func (c *Canvas) Draw(scr uv.Screen, area uv.Rectangle) {
	c.scr.Draw(scr, area)
}

// Render renders the canvas into a styled string.
func (c *Canvas) Render() string {
	return c.scr.Render()
}


================================================
FILE: canvas_test.go
================================================
package lipgloss

import (
	"strings"
	"testing"
)

func TestCanvasRender(t *testing.T) {
	c := NewCanvas(5, 3)

	// Fill the canvas with dots
	for y := 0; y < c.Height(); y++ {
		for x := 0; x < c.Width(); x++ {
			cell := c.CellAt(x, y)
			cell.Content = "."
		}
	}

	// Draw a rectangle
	for y := 1; y < 2; y++ {
		for x := 1; x < 4; x++ {
			cell := c.CellAt(x, y)
			cell.Content = "#"
		}
	}

	expected := strings.Join([]string{
		".....",
		".###.",
		".....",
	}, "\n")

	if rendered := c.Render(); rendered != expected {
		t.Errorf("expected:\n%q\ngot:\n%q", expected, rendered)
	}
}

func TestCanvasRenderWithTrailingSpaces(t *testing.T) {
	c := NewCanvas(5, 2)

	// Fill the canvas with spaces and some trailing spaces
	for y := 0; y < c.Height(); y++ {
		for x := 0; x < c.Width(); x++ {
			cell := c.CellAt(x, y)
			if x < 3 {
				cell.Content = "A"
			} else {
				cell.Content = " "
			}
		}
	}

	expected := strings.Join([]string{
		"AAA",
		"AAA",
	}, "\n")

	if rendered := c.Render(); rendered != expected {
		t.Errorf("expected:\n%q\ngot:\n%q", expected, rendered)
	}
}


================================================
FILE: color.go
================================================
package lipgloss

import (
	"cmp"
	"errors"
	"image/color"
	"strconv"
	"strings"

	"github.com/charmbracelet/colorprofile"
	"github.com/charmbracelet/x/ansi"
	"github.com/lucasb-eyer/go-colorful"
)

func clamp[T cmp.Ordered](v, low, high T) T {
	if high < low {
		high, low = low, high
	}
	return min(high, max(low, v))
}

// 4-bit color constants.
const (
	Black ansi.BasicColor = iota
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	White

	BrightBlack
	BrightRed
	BrightGreen
	BrightYellow
	BrightBlue
	BrightMagenta
	BrightCyan
	BrightWhite
)

var noColor = NoColor{}

// NoColor is used to specify the absence of color styling. When this is active
// foreground colors will be rendered with the terminal's default text color,
// and background colors will not be drawn at all.
//
// Example usage:
//
//	var style = someStyle.Background(lipgloss.NoColor{})
type NoColor struct{}

// RGBA returns the RGBA value of this color. Because we have to return
// something, despite this color being the absence of color, we're returning
// black with 100% opacity.
//
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
func (n NoColor) RGBA() (r, g, b, a uint32) {
	return 0x0, 0x0, 0x0, 0xFFFF //nolint:mnd
}

// Color specifies a color by hex or ANSI256 value. For example:
//
//	ansiColor := lipgloss.Color("1") // The same as lipgloss.Red
//	ansi256Color := lipgloss.Color("21")
//	hexColor := lipgloss.Color("#0000ff")
func Color(s string) color.Color {
	if strings.HasPrefix(s, "#") {
		c, err := parseHex(s)
		if err != nil {
			return noColor
		}
		return c
	}

	i, err := strconv.Atoi(s)
	if err != nil {
		return noColor
	}

	if i < 0 {
		// Only positive numbers
		i = -i
	}

	if i < 16 {
		return ansi.BasicColor(i) //nolint:gosec
	} else if i < 256 {
		return ANSIColor(i) //nolint:gosec
	}

	r, g, b := uint8((i>>16)&0xff), uint8(i>>8&0xff), uint8(i&0xff) //nolint:gosec
	return color.RGBA{R: r, G: g, B: b, A: 0xff}
}

var errInvalidFormat = errors.New("invalid hex format") // pre-allocated.

// parseHex parses a hex color string and returns a color.RGBA. The string can be
// in the format #RRGGBB or #RGB. This is a more performant implementation of
// [colorful.Hex].
func parseHex(s string) (c color.RGBA, err error) {
	c.A = 0xff

	if len(s) == 0 || s[0] != '#' {
		return c, errInvalidFormat
	}

	hexToByte := func(b byte) byte {
		switch {
		case b >= '0' && b <= '9':
			return b - '0'
		case b >= 'a' && b <= 'f':
			return b - 'a' + 10
		case b >= 'A' && b <= 'F':
			return b - 'A' + 10
		}
		err = errInvalidFormat
		return 0
	}

	switch len(s) {
	case 7:
		c.R = hexToByte(s[1])<<4 + hexToByte(s[2])
		c.G = hexToByte(s[3])<<4 + hexToByte(s[4])
		c.B = hexToByte(s[5])<<4 + hexToByte(s[6])
	case 4:
		c.R = hexToByte(s[1]) * 17
		c.G = hexToByte(s[2]) * 17
		c.B = hexToByte(s[3]) * 17
	default:
		err = errInvalidFormat
	}
	return c, err
}

// RGBColor is a color specified by red, green, and blue values.
type RGBColor struct {
	R uint8
	G uint8
	B uint8
}

// RGBA returns the RGBA value of this color. This satisfies the Go Color
// interface.
func (c RGBColor) RGBA() (r, g, b, a uint32) {
	const shift = 8
	r |= uint32(c.R) << shift
	g |= uint32(c.G) << shift
	b |= uint32(c.B) << shift
	a = 0xFFFF
	return
}

// ANSIColor is a color specified by an ANSI256 color value.
//
// Example usage:
//
//	colorA := lipgloss.ANSIColor(8)
//	colorB := lipgloss.ANSIColor(134)
type ANSIColor = ansi.IndexedColor

// LightDarkFunc is a function that returns a color based on whether the
// terminal has a light or dark background. You can create one of these with
// [LightDark].
//
// Example:
//
//	lightDark := lipgloss.LightDark(hasDarkBackground)
//	red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff")
//	myHotColor := lightDark(red, blue)
//
// For more info see [LightDark].
type LightDarkFunc func(light, dark color.Color) color.Color

// LightDark is a simple helper type that can be used to choose the appropriate
// color based on whether the terminal has a light or dark background.
//
//	lightDark := lipgloss.LightDark(hasDarkBackground)
//	red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff")
//	myHotColor := lightDark(red, blue)
//
// In practice, there are slightly different workflows between Bubble Tea and
// Lip Gloss standalone.
//
// In Bubble Tea, listen for tea.BackgroundColorMsg, which automatically
// flows through Update on start. This message will be received whenever the
// background color changes:
//
//	case tea.BackgroundColorMsg:
//	    m.hasDarkBackground = msg.IsDark()
//
// Later, when you're rendering use:
//
//	lightDark := lipgloss.LightDark(m.hasDarkBackground)
//	red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff")
//	myHotColor := lightDark(red, blue)
//
// In standalone Lip Gloss, the workflow is simpler:
//
//	hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
//	lightDark := lipgloss.LightDark(hasDarkBG)
//	red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff")
//	myHotColor := lightDark(red, blue)
func LightDark(isDark bool) LightDarkFunc {
	return func(light, dark color.Color) color.Color {
		if isDark {
			return dark
		}
		return light
	}
}

// isDarkColor returns whether the given color is dark (based on the luminance
// portion of the color as interpreted as HSL).
//
// Example usage:
//
//	color := lipgloss.Color("#0000ff")
//	if lipgloss.isDarkColor(color) {
//		fmt.Println("It's dark! I love darkness!")
//	} else {
//		fmt.Println("It's light! Cover your eyes!")
//	}
func isDarkColor(c color.Color) bool {
	col, ok := colorful.MakeColor(c)
	if !ok {
		return true
	}

	_, _, l := col.Hsl()
	return l < 0.5 //nolint:mnd
}

// CompleteFunc is a function that returns the appropriate color based on the
// given color profile.
//
// Example usage:
//
//	p := colorprofile.Detect(os.Stderr, os.Environ())
//	complete := lipgloss.Complete(p)
//	color := complete(
//		lipgloss.Color(1), // ANSI
//		lipgloss.Color(124), // ANSI256
//		lipgloss.Color("#ff34ac"), // TrueColor
//	)
//	fmt.Println("Ooh, pretty color: ", color)
//
// For more info see [Complete].
type CompleteFunc func(ansi, ansi256, truecolor color.Color) color.Color

// Complete returns a function that will return the appropriate color based on
// the given color profile.
//
// Example usage:
//
//	p := colorprofile.Detect(os.Stderr, os.Environ())
//	complete := lipgloss.Complete(p)
//	color := complete(
//	    lipgloss.Color(1), // ANSI
//	    lipgloss.Color(124), // ANSI256
//	    lipgloss.Color("#ff34ac"), // TrueColor
//	)
//	fmt.Println("Ooh, pretty color: ", color)
func Complete(p colorprofile.Profile) CompleteFunc {
	return func(ansi, ansi256, truecolor color.Color) color.Color {
		switch p { //nolint:exhaustive
		case colorprofile.ANSI:
			return ansi
		case colorprofile.ANSI256:
			return ansi256
		case colorprofile.TrueColor:
			return truecolor
		}
		return noColor
	}
}

// ensureNotTransparent ensures that the alpha value of a color is not 0, and if
// it is, we will set it to 1. This is useful for when we are converting from
// RGB -> RGBA, and the alpha value is lost in the conversion for gradient purposes.
func ensureNotTransparent(c color.Color) color.Color {
	_, _, _, a := c.RGBA()
	if a == 0 {
		return Alpha(c, 1)
	}
	return c
}

// Alpha adjusts the alpha value of a color using a 0-1 (clamped) float scale
// 0 = transparent, 1 = opaque.
func Alpha(c color.Color, alpha float64) color.Color {
	if c == nil {
		return nil
	}

	r, g, b, _ := c.RGBA()
	return color.RGBA{
		R: uint8(min(255, float64(r>>8))),
		G: uint8(min(255, float64(g>>8))),
		B: uint8(min(255, float64(b>>8))),
		A: uint8(clamp(alpha, 0, 1) * 255),
	}
}

// Complementary returns the complementary color (180° away on color wheel) of
// the given color. This is useful for creating a contrasting color.
func Complementary(c color.Color) color.Color {
	if c == nil {
		return nil
	}

	// Offset hue by 180°.
	cf, _ := colorful.MakeColor(ensureNotTransparent(c))

	h, s, v := cf.Hsv()
	h += 180
	if h >= 360 {
		h -= 360
	} else if h < 0 {
		h += 360
	}

	return colorful.Hsv(h, s, v).Clamped()
}

// Darken takes a color and makes it darker by a specific percentage (0-1, clamped).
func Darken(c color.Color, percent float64) color.Color {
	if c == nil {
		return nil
	}

	mult := 1.0 - clamp(percent, 0, 1)

	r, g, b, a := c.RGBA()
	return color.RGBA{
		R: uint8(float64(r>>8) * mult),
		G: uint8(float64(g>>8) * mult),
		B: uint8(float64(b>>8) * mult),
		A: uint8(min(255, float64(a>>8))),
	}
}

// Lighten makes a color lighter by a specific percentage (0-1, clamped).
func Lighten(c color.Color, percent float64) color.Color {
	if c == nil {
		return nil
	}

	add := 255 * (clamp(percent, 0, 1))

	r, g, b, a := c.RGBA()
	return color.RGBA{
		R: uint8(min(255, float64(r>>8)+add)),
		G: uint8(min(255, float64(g>>8)+add)),
		B: uint8(min(255, float64(b>>8)+add)),
		A: uint8(min(255, float64(a>>8))),
	}
}


================================================
FILE: color_test.go
================================================
package lipgloss

import (
	"fmt"
	"image/color"
	"testing"
)

// hex converts a color to a hex string or panics if invalid.
func hex(hex string) color.Color {
	cf, err := parseHex(hex)
	if err != nil {
		panic(err)
	}
	return cf
}

func expectColorMatches(t *testing.T, got, want color.Color) {
	t.Helper()

	if (got == nil) != (want == nil) {
		t.Errorf("expectColorMatches() = %s, want %s", rgbaString(t, got), rgbaString(t, want))
	}

	if got == nil {
		return
	}

	gr, gg, gb, ga := got.RGBA()
	wr, wg, wb, wa := want.RGBA()

	gru, ggu, gbu, gau := uint8(gr>>8), uint8(gg>>8), uint8(gb>>8), uint8(ga>>8)
	wru, wgu, wbu, wau := uint8(wr>>8), uint8(wg>>8), uint8(wb>>8), uint8(wa>>8)

	if gru != wru || ggu != wgu || gbu != wbu || gau != wau {
		t.Errorf("expectColorMatches() = %s, want %s", rgbaString(t, got), rgbaString(t, want))
	}
}

func rgbaString(t *testing.T, c color.Color) string {
	t.Helper()

	if c == nil {
		return "nil"
	}

	r, g, b, a := c.RGBA()
	return fmt.Sprintf("rgba(%d,%d,%d,%d)", uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8))
}

func TestHexToColor(t *testing.T) {
	t.Parallel()

	tt := []struct {
		input    string
		expected uint
	}{
		{
			"#FF0000",
			0xFF0000,
		},
		{
			"#00F",
			0x0000FF,
		},
		{
			"#6B50FF",
			0x6B50FF,
		},
		{
			"invalid color",
			0x0,
		},
	}

	for i, tc := range tt {
		r, g, b, _ := Color(tc.input).RGBA()
		o := uint(r>>8)<<16 + uint(g>>8)<<8 + uint(b>>8)
		if o != tc.expected {
			t.Errorf("expected %X, got %X (test #%d)", tc.expected, o, i+1)
		}
	}
}

func TestRGBA(t *testing.T) {
	tt := []struct {
		input    string
		expected uint
	}{
		// lipgloss.Color
		{
			"#FF0000",
			0xFF0000,
		},
		{
			"9",
			0xFF0000,
		},
		{
			"21",
			0x0000FF,
		},
		{
			"16711680", // #FF0000
			0xFF0000,
		},
	}

	for i, tc := range tt {
		r, g, b, _ := Color(tc.input).RGBA()
		o := uint(r/256)<<16 + uint(g/256)<<8 + uint(b/256)

		if o != tc.expected {
			t.Errorf("expected %X, got %X (test #%d)", tc.expected, o, i+1)
		}
	}
}

func TestParseHex(t *testing.T) {
	tests := []struct {
		name        string
		input       string
		expected    color.Color
		expectError bool
	}{
		{name: "valid-6-red", input: "#FF0000", expected: hex("#FF0000")},
		{name: "valid-6-green", input: "#00FF00", expected: hex("#00FF00")},
		{name: "valid-6-blue", input: "#0000FF", expected: hex("#0000FF")},
		{name: "valid-6-white", input: "#FFFFFF", expected: hex("#FFFFFF")},
		{name: "valid-6-black", input: "#000000", expected: hex("#000000")},
		{name: "valid-6-gray", input: "#808080", expected: hex("#808080")},
		{name: "valid-3-red", input: "#F00", expected: hex("#FF0000")},
		{name: "valid-3-green", input: "#0F0", expected: hex("#00FF00")},
		{name: "valid-3-blue", input: "#00F", expected: hex("#0000FF")},
		{name: "valid-3-white", input: "#FFF", expected: hex("#FFFFFF")},
		{name: "valid-3-black", input: "#000", expected: hex("#000000")},
		{name: "valid-6-lowercase", input: "#ff0000", expected: hex("#FF0000")},
		{name: "valid-6-mixed-case", input: "#Ff0000", expected: hex("#FF0000")},
		{name: "valid-3-lowercase", input: "#f00", expected: hex("#FF0000")},
		{name: "missing-hash-prefix", input: "FF0000", expectError: true},
		{name: "empty-string", input: "", expectError: true},
		{name: "only-hash", input: "#", expectError: true},
		{name: "too-short-3", input: "#F0", expectError: true},
		{name: "too-long-6", input: "#FF00000", expectError: true},
		{name: "invalid-char", input: "#FG0000", expectError: true},
		{name: "invalid-char-3", input: "#FG0", expectError: true},
		{name: "invalid-char-lowercase", input: "#fg0000", expectError: true},
		{name: "invalid-char-mixed", input: "#Fg0000", expectError: true},
		{name: "wrong-len-5", input: "#FF000", expectError: true},
		{name: "wrong-len-8", input: "#FF000000", expectError: true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			result, err := parseHex(tt.input)

			if tt.expectError {
				if err == nil {
					t.Errorf("FromHex() expected error but got none for input %q", tt.input)
				}
				return
			}

			if err != nil {
				t.Errorf("FromHex() unexpected error for input %q: %v", tt.input, err)
				return
			}

			expectColorMatches(t, result, tt.expected)
		})
	}
}

func TestAlpha(t *testing.T) {
	tests := []struct {
		name     string
		color    color.Color
		alpha    float64
		expected color.Color
	}{
		{
			name:     "alpha-full-opacity",
			color:    color.RGBA{R: 255, G: 0, B: 0, A: 255},
			alpha:    1.0,
			expected: color.RGBA{R: 255, G: 0, B: 0, A: 255},
		},
		{
			name:     "alpha-half-opacity",
			color:    color.RGBA{R: 0, G: 255, B: 0, A: 255},
			alpha:    0.5,
			expected: color.RGBA{R: 0, G: 255, B: 0, A: 127},
		},
		{
			name:     "alpha-quarter-opacity",
			color:    color.RGBA{R: 0, G: 0, B: 255, A: 255},
			alpha:    0.25,
			expected: color.RGBA{R: 0, G: 0, B: 255, A: 63},
		},
		{
			name:     "alpha-zero-opacity",
			color:    color.RGBA{R: 255, G: 255, B: 255, A: 255},
			alpha:    0.0,
			expected: color.RGBA{R: 255, G: 255, B: 255, A: 0},
		},
		{
			name:     "alpha-clamp-above-max",
			color:    color.RGBA{R: 255, G: 0, B: 255, A: 255},
			alpha:    1.5,
			expected: color.RGBA{R: 255, G: 0, B: 255, A: 255},
		},
		{
			name:     "alpha-clamp-below-min",
			color:    color.RGBA{R: 255, G: 255, B: 0, A: 255},
			alpha:    -0.5,
			expected: color.RGBA{R: 255, G: 255, B: 0, A: 0},
		},
		{
			name:     "alpha-complex-color",
			color:    color.RGBA{R: 18, G: 52, B: 86, A: 255},
			alpha:    0.75,
			expected: color.RGBA{R: 18, G: 52, B: 86, A: 191},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			expectColorMatches(t, Alpha(tt.color, tt.alpha), tt.expected)
		})
	}
}

func TestComplementary(t *testing.T) {
	tests := []struct {
		name     string
		color    color.Color
		expected color.Color
	}{
		{name: "red", color: hex("#FF0000"), expected: hex("#00FFFF")},
		{name: "green", color: hex("#00FF00"), expected: hex("#FF00FF")},
		{name: "blue", color: hex("#0000FF"), expected: hex("#FFFF00")},
		{name: "yellow", color: hex("#FFFF00"), expected: hex("#0000FF")},
		{name: "cyan", color: hex("#00FFFF"), expected: hex("#FF0000")},
		{name: "magenta", color: hex("#FF00FF"), expected: hex("#00FF00")},
		// Black has no hue to complement
		{name: "black", color: hex("#000000"), expected: hex("#000000")},
		// White has no hue to complement
		{name: "white", color: hex("#FFFFFF"), expected: hex("#FFFFFF")},
		// Gray has no hue to complement
		{name: "gray", color: hex("#808080"), expected: hex("#808080")},
		{name: "orange", color: hex("#FF8000"), expected: hex("#007FFF")},
		{name: "purple", color: hex("#8000FF"), expected: hex("#7FFF00")},
		{name: "nil-color", color: nil, expected: nil},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			expectColorMatches(t, Complementary(tt.color), tt.expected)
		})
	}
}

func TestDarken(t *testing.T) {
	tests := []struct {
		name     string
		color    color.Color
		percent  float64
		expected color.Color
	}{
		{name: "darken-white-50-percent", color: hex("#FFFFFF"), percent: 0.5, expected: hex("#7F7F7F")},
		{name: "darken-red-25-percent", color: hex("#FF0000"), percent: 0.25, expected: hex("#BF0000")},
		{name: "darken-blue-75-percent", color: hex("#0000FF"), percent: 0.75, expected: hex("#00003F")},
		{name: "darken-black-10-percent", color: hex("#000000"), percent: 0.1, expected: hex("#000000")},
		{name: "darken-with-clamp-min", color: hex("#FFFFFF"), percent: 0, expected: hex("#FFFFFF")},
		{name: "darken-with-clamp-max", color: hex("#FFFFFF"), percent: 1, expected: hex("#000000")},
		{name: "darken-nil-color", color: nil, percent: 0.5, expected: nil},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			expectColorMatches(t, Darken(tt.color, tt.percent), tt.expected)
		})
	}
}

func TestLighten(t *testing.T) {
	tests := []struct {
		name     string
		color    color.Color
		percent  float64
		expected color.Color
	}{
		{name: "lighten-black-50-percent", color: hex("#000000"), percent: 0.5, expected: hex("#7F7F7F")},
		{name: "lighten-red-25-percent", color: hex("#800000"), percent: 0.25, expected: hex("#BF3F3F")},
		{name: "lighten-blue-75-percent", color: hex("#000080"), percent: 0.75, expected: hex("#BFBFFF")},
		{name: "lighten-white-10-percent", color: hex("#FFFFFF"), percent: 0.1, expected: hex("#FFFFFF")},
		{name: "lighten-with-clamp-min", color: hex("#000000"), percent: 0, expected: hex("#000000")},
		{name: "lighten-with-clamp-max", color: hex("#000000"), percent: 1, expected: hex("#FFFFFF")},
		{name: "lighten-nil-color", color: nil, percent: 0.5, expected: nil},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			expectColorMatches(t, Lighten(tt.color, tt.percent), tt.expected)
		})
	}
}


================================================
FILE: compat/color.go
================================================
package compat

import (
	"image/color"
	"os"

	"charm.land/lipgloss/v2"
	"github.com/charmbracelet/colorprofile"
)

var (
	// HasDarkBackground is true if the terminal has a dark background.
	HasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)

	// Profile is the color profile of the terminal.
	Profile = colorprofile.Detect(os.Stdout, os.Environ())
)

// AdaptiveColor provides color options for light and dark backgrounds. The
// appropriate color will be returned at runtime based on the darkness of the
// terminal background color.
//
// Example usage:
//
//	color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"}
type AdaptiveColor struct {
	Light color.Color
	Dark  color.Color
}

// RGBA returns the RGBA value of this color. This satisfies the Go Color
// interface.
func (c AdaptiveColor) RGBA() (uint32, uint32, uint32, uint32) {
	if HasDarkBackground {
		return c.Dark.RGBA()
	}
	return c.Light.RGBA()
}

// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
// profiles. Automatic color degradation will not be performed.
type CompleteColor struct {
	TrueColor color.Color
	ANSI256   color.Color
	ANSI      color.Color
}

// RGBA returns the RGBA value of this color. This satisfies the Go Color
// interface.
func (c CompleteColor) RGBA() (uint32, uint32, uint32, uint32) {
	switch Profile { //nolint:exhaustive
	case colorprofile.TrueColor:
		return c.TrueColor.RGBA()
	case colorprofile.ANSI256:
		return c.ANSI256.RGBA()
	case colorprofile.ANSI:
		return c.ANSI.RGBA()
	}
	return lipgloss.NoColor{}.RGBA()
}

// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color
// profiles, with separate options for light and dark backgrounds. Automatic
// color degradation will not be performed.
type CompleteAdaptiveColor struct {
	Light CompleteColor
	Dark  CompleteColor
}

// RGBA returns the RGBA value of this color. This satisfies the Go Color
// interface.
func (c CompleteAdaptiveColor) RGBA() (uint32, uint32, uint32, uint32) {
	if HasDarkBackground {
		return c.Dark.RGBA()
	}
	return c.Light.RGBA()
}


================================================
FILE: compat/doc.go
================================================
// Package compat is a compatibility layer for Lip Gloss that provides a way to
// deal with the hassle of setting up a writer. It's impure because it uses
// global variables, is not thread-safe, and only works with the default
// standard I/O streams.
//
// In case you want [os.Stderr] to be used as the default writer, you can set
// both [Writer] and [HasDarkBackground] to use [os.Stderr] with
// the following code:
//
//	import (
//		"os"
//
//		"github.com/charmbracelet/colorprofile"
//		"charm.land/lipgloss/v2/impure"
//	)
//
//	func init() {
//		impure.Writer = colorprofile.NewWriter(os.Stderr, os.Environ())
//		impure.HasDarkBackground, _ = lipgloss.HasDarkBackground(os.Stdin, os.Stderr)
//	}
package compat


================================================
FILE: examples/blending/border-blend-rotation/bubbletea/main.go
================================================
package main

import (
	"fmt"
	"os"
	"time"

	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
)

const (
	borderRotationFPS   = 15
	borderRotationSteps = 5
)

type borderRotationTickMsg struct {
	Value int
}

func borderRotationTick(current int) tea.Cmd {
	return tea.Tick(time.Second/time.Duration(borderRotationFPS), func(_ time.Time) tea.Msg {
		return borderRotationTickMsg{Value: current + borderRotationSteps}
	})
}

type model struct {
	borderRotation int
}

func (m model) Init() tea.Cmd {
	return borderRotationTick(0)
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyPressMsg:
		switch msg.String() {
		case "q", "esc", "ctrl+c":
			return m, tea.Quit
		}
	case borderRotationTickMsg:
		m.borderRotation = msg.Value
		return m, borderRotationTick(msg.Value)
	}

	return m, nil
}

func (m model) View() tea.View {
	v := tea.NewView(lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForegroundBlend(
			lipgloss.Color("#00FA68"),
			lipgloss.Color("#9900FF"),
			lipgloss.Color("#ED5353"),
			lipgloss.Color("#9900FF"),
			lipgloss.Color("#00FA68"),
		).
		BorderForegroundBlendOffset(m.borderRotation).
		Width(60).
		Height(15).
		Render("Hello, world!"))
	v.AltScreen = true
	return v
}

func main() {
	_, err := tea.NewProgram(model{}).Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Uh oh: %v", err)
		os.Exit(1)
	}
}


================================================
FILE: examples/blending/linear-1d/bubbletea/main.go
================================================
// This example demonstrates how to use the colors.Blend1D function to create
// beautiful color gradients in a Bubble Tea application.
package main

import (
	"fmt"
	"image/color"
	"os"
	"strings"

	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
)

var gradients = []gradientData{
	{
		name: "Sunset",
		stops: []color.Color{
			lipgloss.Color("#FF6B6B"), // Coral
			lipgloss.Color("#FFB74D"), // Orange
			lipgloss.Color("#FFDFBA"), // Peach
		},
	},
	{
		name: "Ocean",
		stops: []color.Color{
			lipgloss.Color("#0077B6"), // Deep Blue
			lipgloss.Color("#48CAE4"), // Sky Blue
			lipgloss.Color("#ADE8F4"), // Light Blue
		},
	},
	{
		name: "Forest",
		stops: []color.Color{
			lipgloss.Color("#228B22"), // Forest Green
			lipgloss.Color("#90EE90"), // Light Green
			lipgloss.Color("#FFFFE0"), // Cream
		},
	},
	{
		name: "Purple Dream",
		stops: []color.Color{
			lipgloss.Color("#9370DB"), // Medium Purple
			lipgloss.Color("#DDA0DD"), // Plum
			lipgloss.Color("#FFB6C1"), // Light Pink
		},
	},
	{
		name: "Fire",
		stops: []color.Color{
			lipgloss.Color("#FF0000"), // Red
			lipgloss.Color("#FFA500"), // Orange
			lipgloss.Color("#FFFF00"), // Yellow
		},
	},
}

type gradientData struct {
	name  string
	stops []color.Color
}

// Style definitions.
type styles struct {
	// UI styles.
	title        lipgloss.Style
	gradientName lipgloss.Style
	info         lipgloss.Style
}

func newStyles(dark bool) (s *styles) {
	s = &styles{}

	lightDark := lipgloss.LightDark(dark)

	s.title = lipgloss.NewStyle().
		Bold(true).
		Foreground(lightDark(lipgloss.Color("#2D3748"), lipgloss.Color("#E2E8F0"))).
		MarginBottom(1).
		Align(lipgloss.Center)

	s.gradientName = lipgloss.NewStyle().
		Bold(true).
		Foreground(lightDark(lipgloss.Color("#4A5568"), lipgloss.Color("#CBD5E0"))).
		PaddingRight(1)

	s.info = lipgloss.NewStyle().
		Foreground(lightDark(lipgloss.Color("#718096"), lipgloss.Color("#A0AEC0"))).
		Italic(true)

	return s
}

type model struct {
	width  int
	height int
	styles *styles
}

func (m model) Init() tea.Cmd {
	return tea.RequestBackgroundColor
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		m.width = msg.Width
		m.height = msg.Height
		return m, nil
	case tea.BackgroundColorMsg:
		m.styles = newStyles(msg.IsDark())
		return m, nil

	case tea.KeyPressMsg:
		switch msg.String() {
		case "q", "esc", "ctrl+c":
			return m, tea.Quit
		}
	}

	return m, nil
}

func (m model) View() tea.View {
	var maxTitleWidth int

	for _, gradient := range gradients {
		maxTitleWidth = max(maxTitleWidth, lipgloss.Width(m.styles.gradientName.Render(gradient.name)))
	}

	var content strings.Builder

	content.WriteString(m.styles.title.Render("Color Gradient Examples with Blend1D"))
	content.WriteString("\n\n")

	var title string

	for _, gradient := range gradients {
		title = m.styles.gradientName.Width(maxTitleWidth).Render(gradient.name)
		content.WriteString(title)

		blendedColors := lipgloss.Blend1D(m.width-maxTitleWidth, gradient.stops...)

		for _, c := range blendedColors {
			content.WriteString(lipgloss.NewStyle().Background(c).Foreground(c).Render("█"))
		}

		content.WriteString("\n")
	}

	content.WriteString("\n")
	content.WriteString(m.styles.info.Render("Press Q to exit"))

	cursor := &tea.Cursor{}
	cursor.X = 0
	cursor.Y = 0

	v := tea.NewView(content.String())
	v.Cursor = cursor
	v.AltScreen = true

	return v
}

func main() {
	_, err := tea.NewProgram(model{styles: newStyles(true)}).Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Uh oh: %v", err)
		os.Exit(1)
	}
}


================================================
FILE: examples/blending/linear-1d/standalone/main.go
================================================
// This example demonstrates how to use the colors.Blend1D function to create
// beautiful color gradients in a standalone Lip Gloss application.
package main

import (
	"image/color"
	"os"
	"strings"

	"charm.land/lipgloss/v2"
)

var gradients = [][]color.Color{
	{
		lipgloss.Color("#FF6B6B"), // Coral
		lipgloss.Color("#FFB74D"), // Orange
		lipgloss.Color("#FFDFBA"), // Peach
	},
	{
		lipgloss.Color("#0077B6"), // Deep Blue
		lipgloss.Color("#48CAE4"), // Sky Blue
		lipgloss.Color("#ADE8F4"), // Light Blue
	},
	{
		lipgloss.Color("#228B22"), // Forest Green
		lipgloss.Color("#90EE90"), // Light Green
		lipgloss.Color("#FFFFE0"), // Cream
	},
	{
		lipgloss.Color("#9370DB"), // Medium Purple
		lipgloss.Color("#DDA0DD"), // Plum
		lipgloss.Color("#FFB6C1"), // Light Pink
	},
	{
		lipgloss.Color("#9900FF"), // Purple
		lipgloss.Color("#00FA68"), // Lime
		lipgloss.Color("#ED5353"), // Red
	},
}

func main() {
	hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
	lightDark := lipgloss.LightDark(hasDarkBG)

	// Create styles.
	titleStyle := lipgloss.NewStyle().
		Bold(true).
		Foreground(lightDark(lipgloss.Color("#2D3748"), lipgloss.Color("#E2E8F0"))).
		MarginBottom(1).
		Align(lipgloss.Center)

	gradientStyle := lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(lightDark(lipgloss.Color("#718096"), lipgloss.Color("#A0AEC0")))

	var content strings.Builder

	content.WriteString(titleStyle.Render("Color Gradient Examples with Blend1D"))
	content.WriteString("\n")

	for _, gradient := range gradients {
		blendedColors := lipgloss.Blend1D(40, gradient...)

		var gradientBar strings.Builder
		for _, c := range blendedColors {
			blockStyle := lipgloss.NewStyle().Foreground(c)
			gradientBar.WriteString(blockStyle.Render("█"))
		}

		content.WriteString(gradientStyle.Render(gradientBar.String()))
		content.WriteString("\n")
	}

	lipgloss.Println(content.String())
}


================================================
FILE: examples/blending/linear-2d/bubbletea/main.go
================================================
// This example demonstrates how to use the colors.Blend2D function to create
// beautiful 2D color gradients in a Bubble Tea application.
package main

import (
	"cmp"
	"fmt"
	"image/color"
	"math"
	"os"
	"strings"

	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
	"github.com/charmbracelet/x/exp/charmtone"
)

var gradients = [][]color.Color{
	{
		lipgloss.Color("#FF6B6B"), // Coral
		lipgloss.Color("#FFB74D"), // Orange
		lipgloss.Color("#FFDFBA"), // Peach
	},
	{
		lipgloss.Color("#0077B6"), // Deep Blue
		lipgloss.Color("#48CAE4"), // Sky Blue
		lipgloss.Color("#ADE8F4"), // Light Blue
	},
	{
		lipgloss.Color("#228B22"), // Forest Green
		lipgloss.Color("#90EE90"), // Light Green
		lipgloss.Color("#FFFFE0"), // Cream
	},
	{
		lipgloss.Color("#9370DB"), // Medium Purple
		lipgloss.Color("#DDA0DD"), // Plum
		lipgloss.Color("#FFB6C1"), // Light Pink
	},
	{
		lipgloss.Color("#9900FF"), // Purple
		lipgloss.Color("#00FA68"), // Lime
		lipgloss.Color("#ED5353"), // Red
	},
}

func main() {
	m := model{
		boxWidth:         20,
		boxHeight:        10,
		angle:            45,
		selectedGradient: 0,

		infoStyle: lipgloss.NewStyle().
			Foreground(lipgloss.Color("#888888")).
			MarginTop(1),
		controlsStyle: lipgloss.NewStyle().
			Foreground(lipgloss.Color("#666666")).
			MarginTop(1),
		gradientBoxStyle: lipgloss.NewStyle().
			Border(lipgloss.RoundedBorder()).
			BorderForegroundBlend(
				charmtone.Cherry,
				charmtone.Charple,
				charmtone.Guac,
				charmtone.Charple,
				charmtone.Sriracha,
			),
	}
	p := tea.NewProgram(m)
	if _, err := p.Run(); err != nil {
		fmt.Printf("Alas, there's been an error: %v", err)
		os.Exit(1)
	}
}

type model struct {
	// UI state.
	windowWidth      int
	windowHeight     int
	boxWidth         int
	boxHeight        int
	angle            float64
	selectedGradient int

	// UI styles.
	infoStyle        lipgloss.Style
	controlsStyle    lipgloss.Style
	gradientBoxStyle lipgloss.Style
	gradients        []color.Color
}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		m.windowWidth = msg.Width
		m.windowHeight = msg.Height
		m.updateGradient()
	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c", "esc":
			return m, tea.Quit
		case "a":
			m.angle = math.Mod(m.angle+15, 360)
			m.updateGradient()
		case "d":
			m.angle = math.Mod(m.angle-15+360, 360)
			m.updateGradient()
		case "left":
			m.boxWidth -= 2
			m.updateGradient()
		case "right":
			m.boxWidth += 2
			m.updateGradient()
		case "up":
			m.boxHeight--
			m.updateGradient()
		case "down":
			m.boxHeight++
			m.updateGradient()
		case "1", "2", "3", "4", "5", "6", "7", "8", "9":
			m.selectedGradient = max(0, min(int(msg.String()[0]-'1'), len(gradients)-1))
			m.updateGradient()
		}
	case tea.MouseClickMsg:
		switch msg.Mouse().Button {
		case tea.MouseLeft:
			m.boxWidth = msg.Mouse().X
			m.boxHeight = msg.Mouse().Y
			m.updateGradient()
		}
	}
	return m, nil
}

func (m *model) updateGradient() {
	m.boxWidth = clamp(m.boxWidth, 5, m.windowWidth-m.gradientBoxStyle.GetHorizontalFrameSize())
	m.boxHeight = clamp(m.boxHeight, 3, m.windowHeight-m.gradientBoxStyle.GetVerticalFrameSize()-m.infoStyle.GetVerticalFrameSize()-m.controlsStyle.GetVerticalFrameSize()-2)

	// Since gradients that might be large can take up more memory, only generate gradients when
	// the box size (potentially) changes. If you have much smaller gradients, this is less of
	// an issue.
	m.gradients = lipgloss.Blend2D(m.boxWidth, m.boxHeight, m.angle, gradients[m.selectedGradient]...)
}

func (m model) View() tea.View {
	var v tea.View
	v.AltScreen = true
	v.MouseMode = tea.MouseModeCellMotion

	if len(m.gradients) == 0 || m.windowWidth == 0 || m.windowHeight == 0 {
		return v // Wait until we generate the initial gradient/get window size.
	}

	// Build the gradient content.
	gradientContent := strings.Builder{}
	for y := range m.boxHeight { // Uses 1D row-major order.
		for x := range m.boxWidth {
			index := y*m.boxWidth + x
			gradientContent.WriteString(
				lipgloss.NewStyle().
					Background(m.gradients[index]).
					Render(" "),
			)
		}
		if y < m.boxHeight-1 { // End of row.
			gradientContent.WriteString("\n")
		}
	}

	gradient := m.gradientBoxStyle.Render(gradientContent.String())

	info := m.infoStyle.Width(m.windowWidth).Render(fmt.Sprintf(
		"Size: %dx%d | Angle: %.1f° | Colors: %d",
		m.boxWidth,
		m.boxHeight,
		m.angle,
		len(gradients[m.selectedGradient]),
	))

	controls := m.controlsStyle.Width(m.windowWidth).Render(fmt.Sprintf(
		"Controls: a/d (angle) | ←→ (width) | ↑↓ (height) | 1-%d (color scheme) | mouse click",
		len(gradients),
	))

	content := lipgloss.NewStyle().
		Width(m.windowWidth).
		Height(m.windowHeight).
		Render(lipgloss.JoinVertical(
			lipgloss.Top,
			lipgloss.NewStyle().
				Width(m.windowWidth).
				Height(m.windowHeight-lipgloss.Height(info)-lipgloss.Height(controls)).
				Render(gradient),
			info,
			controls,
		))
	v.SetContent(content)
	return v
}

func clamp[T cmp.Ordered](v, low, high T) T {
	return min(high, max(low, v))
}


================================================
FILE: examples/blending/linear-2d/standalone/main.go
================================================
// This example demonstrates how to use the colors.Blend2D function to create
// beautiful 2D color gradients in a standalone Lip Gloss application.
package main

import (
	"fmt"
	"image/color"
	"os"
	"strings"

	"charm.land/lipgloss/v2"
)

func main() {
	hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
	lightDark := lipgloss.LightDark(hasDarkBG)

	gradients := []struct {
		name  string
		stops []color.Color
		angle float64
	}{
		{
			name: "Sunset Diagonal",
			stops: []color.Color{
				lipgloss.Color("#FF6B6B"), // Coral
				lipgloss.Color("#FFB74D"), // Orange
				lipgloss.Color("#FFDFBA"), // Peach
			},
			angle: 45,
		},
		{
			name: "Ocean Wave",
			stops: []color.Color{
				lipgloss.Color("#0077B6"), // Deep Blue
				lipgloss.Color("#48CAE4"), // Sky Blue
				lipgloss.Color("#ADE8F4"), // Light Blue
			},
			angle: 90,
		},
		{
			name: "Forest Mist",
			stops: []color.Color{
				lipgloss.Color("#228B22"), // Forest Green
				lipgloss.Color("#90EE90"), // Light Green
				lipgloss.Color("#FFFFE0"), // Cream
			},
			angle: 135,
		},
		{
			name: "Purple Dream",
			stops: []color.Color{
				lipgloss.Color("#9370DB"), // Medium Purple
				lipgloss.Color("#DDA0DD"), // Plum
				lipgloss.Color("#FFB6C1"), // Light Pink
			},
			angle: 180,
		},
		{
			name: "Fire Gradient",
			stops: []color.Color{
				lipgloss.Color("#FF0000"), // Red
				lipgloss.Color("#FFA500"), // Orange
				lipgloss.Color("#FFFF00"), // Yellow
			},
			angle: 225,
		},
	}

	// Create styles.
	titleStyle := lipgloss.NewStyle().
		Bold(true).
		Foreground(lightDark(lipgloss.Color("#2D3748"), lipgloss.Color("#E2E8F0"))).
		MarginBottom(1).
		Align(lipgloss.Center)

	gradientStyle := lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(lightDark(lipgloss.Color("#718096"), lipgloss.Color("#A0AEC0"))).
		MarginBottom(1)

	gradientNameStyle := lipgloss.NewStyle().
		Bold(true).
		Foreground(lightDark(lipgloss.Color("#4A5568"), lipgloss.Color("#CBD5E0"))).
		MarginBottom(1)

	var content strings.Builder

	content.WriteString(titleStyle.Render("2D Color Gradient Examples with Blend2D"))
	content.WriteString("\n\n")

	for _, gradient := range gradients {
		// Generate the gradient using Blend2D.
		width, height := 30, 12
		blendedColors := lipgloss.Blend2D(width, height, gradient.angle, gradient.stops...)

		// Create the gradient box using individual character styling.
		var gradientBox strings.Builder
		for y := range height { // Uses 1D row-major order.
			for x := range width {
				index := y*width + x
				gradientBox.WriteString(
					lipgloss.NewStyle().
						Foreground(blendedColors[index]).
						Render("█"),
				)
			}
			if y < height-1 { // End of row.
				gradientBox.WriteString("\n")
			}
		}

		content.WriteString(gradientNameStyle.Render(fmt.Sprintf("%s (Angle: %.0f°)", gradient.name, gradient.angle)))
		content.WriteString("\n")
		content.WriteString(gradientStyle.Render(gradientBox.String()))
		content.WriteString("\n")
	}

	lipgloss.Println(content.String())
}


================================================
FILE: examples/brightness/main.go
================================================
// This example demonstrates how to use the colors.Lighten and colors.Darken functions
// to create progressive brightness variations in a standalone Lip Gloss application.
package main

import (
	"image/color"
	"os"
	"strings"

	"charm.land/lipgloss/v2"
)

func main() {
	hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
	lightDark := lipgloss.LightDark(hasDarkBG)

	// Base colors to demonstrate lightening and darkening.
	baseColors := map[string]color.Color{
		"Red":   lipgloss.Color("#FF0000"),
		"Blue":  lipgloss.Color("#0066FF"),
		"Green": lipgloss.Color("#00FF00"),
		"Gray":  lipgloss.Color("#808080"),
	}

	// Percentage to lighten/darken by.
	percentage := 0.05 // 5%

	// Number of steps to generate.
	steps := 20

	colorNameStyle := lipgloss.NewStyle().
		Bold(true).
		Foreground(lightDark(lipgloss.Color("#2D3748"), lipgloss.Color("#E2E8F0")))

	var content strings.Builder

	for name, baseColor := range baseColors {
		content.WriteString(colorNameStyle.Render(name))
		content.WriteString("\n")

		// Create lightened variations.
		var lightenedBox strings.Builder
		lightenedBox.WriteString("Lightened: ")
		for i := range steps {
			lightenedBox.WriteString(
				lipgloss.NewStyle().
					Foreground(lipgloss.Lighten(baseColor, percentage*(float64(i)+1))).
					Render("██"),
			)
		}
		content.WriteString(lightenedBox.String())
		content.WriteString("\n")

		// Create darkened variations.
		var darkenedBox strings.Builder
		darkenedBox.WriteString("Darkened:  ")
		for i := range steps {
			darkenedBox.WriteString(
				lipgloss.NewStyle().
					Foreground(lipgloss.Darken(baseColor, percentage*(float64(i)+1))).
					Render("██"),
			)
		}
		content.WriteString(darkenedBox.String())
		content.WriteString("\n\n")
	}

	lipgloss.Println(content.String())
}


================================================
FILE: examples/canvas/main.go
================================================
package main

import (
	"image/color"
	"os"
	"strings"

	"charm.land/lipgloss/v2"
	"github.com/charmbracelet/x/exp/charmtone"
)

// newField fills a rectangular area with a given character in a given color.
func newField(rows, cols int, color color.Color) string {
	fieldSetyle := lipgloss.NewStyle().Foreground(color)
	fieldBuilder := strings.Builder{}
	for i := range rows {
		for range cols {
			fieldBuilder.WriteString("/")
		}
		if i < rows-1 {
			fieldBuilder.WriteString("\n")
		}
	}
	return fieldSetyle.Render(fieldBuilder.String())
}

// newCard creates a little card with rounded borders and a text label.
func newCard(darkMode bool, text string) string {
	lightDark := lipgloss.LightDark(darkMode)

	return lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForegroundBlend(
			charmtone.Cherry,
			charmtone.Charple,
			charmtone.Guac,
			charmtone.Charple,
			charmtone.Sriracha,
		).
		Foreground(lightDark(charmtone.Iron, charmtone.Butter)).
		Height(9).
		Width(16).
		PaddingTop(3).
		Align(lipgloss.Center).
		Render(text)
}

func main() {
	darkMode := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
	lightDark := lipgloss.LightDark(darkMode)

	// A few text blocks.
	lighterField := newField(17, 43, lightDark(charmtone.Smoke, charmtone.Pepper))
	darkerField := newField(17, 43, lightDark(charmtone.Squid, charmtone.Charcoal))

	// A few layers. Layers are created from strings (or blocks of text).
	pickles := lipgloss.NewLayer(newCard(darkMode, "Pickles"))
	melon := lipgloss.NewLayer(newCard(darkMode, "Bitter Melon"))
	sriracha := lipgloss.NewLayer(newCard(darkMode, "Sriracha"))

	// Let's create our layers.
	layers := []*lipgloss.Layer{
		// Layers can have X, Y, and Z offsets. By default, X, Y, and
		// Z are all 0.
		lipgloss.NewLayer(lighterField).X(5).Y(2),

		// Layers can be nested.
		lipgloss.NewLayer(darkerField).AddLayers(
			pickles.X(4).Y(2).Z(1), // the Z index places this layer above the others
			melon.X(22).Y(1),
			sriracha.X(11).Y(7),
		),
	}

	// A compositor takes multiple layers and composites them together into
	// a single output.
	comp := lipgloss.NewCompositor(layers...)

	lipgloss.Println(comp.Render())
}


================================================
FILE: examples/color/bubbletea/main.go
================================================
package main

import (
	"fmt"
	"os"

	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
)

// Style definitions.
type styles struct {
	frame,
	paragraph,
	text,
	keyword,
	activeButton,
	inactiveButton lipgloss.Style
}

// Styles are initialized based on the background color of the terminal.
func newStyles(backgroundIsDark bool) (s *styles) {
	s = new(styles)

	// Create a new helper function for choosing either a light or dark color
	// based on the detected background color.
	lightDark := lipgloss.LightDark(backgroundIsDark)

	// Define some styles. adaptive.Color() can be used to choose the
	// appropriate light or dark color based on the detected background color.
	s.frame = lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(lightDark(
			lipgloss.Color("#C5ADF9"),
			lipgloss.Color("#864EFF"))).
		Padding(1, 3).
		Margin(1, 3)
	s.paragraph = lipgloss.NewStyle().
		Width(40).
		MarginBottom(1).
		Align(lipgloss.Center)
	s.text = lipgloss.NewStyle().
		Foreground(lightDark(
			lipgloss.Color("#696969"),
			lipgloss.Color("#bdbdbd")))
	s.keyword = lipgloss.NewStyle().
		Foreground(lightDark(
			lipgloss.Color("#37CD96"),
			lipgloss.Color("#22C78A"))).
		Bold(true)

	s.activeButton = lipgloss.NewStyle().
		Padding(0, 3).
		Background(lipgloss.Color("#FF6AD2")).
		Foreground(lipgloss.Color("#FFFCC2"))
	s.inactiveButton = s.activeButton.
		Background(lightDark(
			lipgloss.Color("#988F95"),
			lipgloss.Color("#978692"))).
		Foreground(lightDark(
			lipgloss.Color("#FDFCE3"),
			lipgloss.Color("#FBFAE7")))
	return s
}

type model struct {
	styles  *styles
	yes     bool
	chosen  bool
	aborted bool
}

func (m model) Init() tea.Cmd {
	// Query for the background color on start.
	m.yes = true
	return tea.RequestBackgroundColor
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {

	// Bubble Tea automatically detects the background color on start. We
	// listen for the response here, then initialize our styles accordingly.
	case tea.BackgroundColorMsg:
		m.styles = newStyles(msg.IsDark())
		return m, nil

	case tea.KeyPressMsg:
		switch msg.String() {
		case "q", "esc", "ctrl+c":
			m.aborted = true
			return m, tea.Quit
		case "enter":
			m.chosen = true
			return m, tea.Quit
		case "left", "right", "h", "l":
			m.yes = !m.yes
		case "y":
			m.yes = true
			m.chosen = true
			return m, tea.Quit
		case "n":
			m.yes = false
			m.chosen = true
			return m, tea.Quit
		}
	}

	return m, nil
}

func (m model) View() tea.View {
	var v tea.View
	if m.styles == nil {
		// We haven't received tea.BackgroundColorMsg yet. Don't worry, it'll
		// be here in a flash.
		return v
	}
	if m.chosen || m.aborted {
		// We're about to exit, so wipe the UI.
		return v
	}

	var (
		s = m.styles
		y = "Yes"
		n = "No"
	)

	if m.yes {
		y = s.activeButton.Render(y)
		n = s.inactiveButton.Render(n)
	} else {
		y = s.inactiveButton.Render(y)
		n = s.activeButton.Render(n)
	}

	content := s.frame.Render(
		lipgloss.JoinVertical(lipgloss.Center,
			s.paragraph.Render(
				s.text.Render("Are you sure you want to eat that ")+
					s.keyword.Render("moderatly ripe")+
					s.text.Render(" banana?"),
			),
			y+"  "+n,
		),
	)
	v.SetContent(content)
	return v
}

func main() {
	m, err := tea.NewProgram(model{}).Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Uh oh: %v", err)
		os.Exit(1)
	}

	if m := m.(model); m.chosen {
		if m.yes {
			fmt.Println("Are you sure? It's not ripe yet.")
		} else {
			fmt.Println("Well, alright. It was probably good, though.")
		}
	}
}


================================================
FILE: examples/color/standalone/main.go
================================================
// This example illustrates how to detect the terminal's background color and
// choose either light or dark colors accordingly when using Lip Gloss in a.
// standalone fashion, i.e. independent of Bubble Tea.
//
// For an example of how to do this in a Bubble Tea program, see the
// 'bubbletea' example.
package main

import (
	"os"

	"charm.land/lipgloss/v2"
)

func main() {
	// Query for the background color. We only need to do this once, and only
	// when using Lip Gloss standalone.
	//
	// In Bubble Tea listen for tea.BackgroundColorMsg in your Update.
	hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)

	// Create a new helper function for choosing either a light or dark color
	// based on the detected background color.
	lightDark := lipgloss.LightDark(hasDarkBG)

	// Define some styles. adaptive.Color() can be used to choose the
	// appropriate light or dark color based on the detected background color.
	frameStyle := lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(lightDark(lipgloss.Color("#C5ADF9"), lipgloss.Color("#864EFF"))).
		Padding(1, 3).
		Margin(1, 3)
	paragraphStyle := lipgloss.NewStyle().
		Width(40).
		MarginBottom(1).
		Align(lipgloss.Center)
	textStyle := lipgloss.NewStyle().
		Foreground(lightDark(lipgloss.Color("#696969"), lipgloss.Color("#bdbdbd")))
	keywordStyle := lipgloss.NewStyle().
		Foreground(lightDark(lipgloss.Color("#37CD96"), lipgloss.Color("#22C78A"))).
		Bold(true)

	activeButton := lipgloss.NewStyle().
		Padding(0, 3).
		Background(lipgloss.Color("#FF6AD2")).
		Foreground(lipgloss.Color("#FFFCC2"))
	inactiveButton := activeButton.
		Background(lightDark(lipgloss.Color("#988F95"), lipgloss.Color("#978692"))).
		Foreground(lightDark(lipgloss.Color("#FDFCE3"), lipgloss.Color("#FBFAE7")))

	// Build layout.
	text := paragraphStyle.Render(
		textStyle.Render("Are you sure you want to eat that ") +
			keywordStyle.Render("moderatly ripe") +
			textStyle.Render(" banana?"),
	)
	buttons := activeButton.Render("Yes") + "  " + inactiveButton.Render("No")
	block := frameStyle.Render(
		lipgloss.JoinVertical(lipgloss.Center, text, buttons),
	)

	// Print the block to stdout. It's important to use Lip Gloss's print
	// functions to ensure that colors are downsampled correctly. If output
	// isn't a TTY (i.e. we're logging to a file) colors will be stripped
	// entirely.
	//
	// Note that in Bubble Tea downsampling happens automatically.
	lipgloss.Println(block)
}


================================================
FILE: examples/compat/bubbletea/main.go
================================================
package main

import (
	"fmt"
	"os"

	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/compat"
)

var (
	frameColor      = compat.AdaptiveColor{Light: lipgloss.Color("#C5ADF9"), Dark: lipgloss.Color("#864EFF")}
	textColor       = compat.AdaptiveColor{Light: lipgloss.Color("#696969"), Dark: lipgloss.Color("#bdbdbd")}
	keywordColor    = compat.AdaptiveColor{Light: lipgloss.Color("#37CD96"), Dark: lipgloss.Color("#22C78A")}
	inactiveBgColor = compat.AdaptiveColor{Light: lipgloss.Color("#988F95"), Dark: lipgloss.Color("#978692")}
	inactiveFgColor = compat.AdaptiveColor{Light: lipgloss.Color("#FDFCE3"), Dark: lipgloss.Color("#FBFAE7")}
)

// Style definitions.
type styles struct {
	frame,
	paragraph,
	text,
	keyword,
	activeButton,
	inactiveButton lipgloss.Style
}

// Styles are initialized based on the background color of the terminal.
func newStyles() (s styles) {
	// Define some styles. adaptive.Color() can be used to choose the
	// appropriate light or dark color based on the detected background color.
	s.frame = lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(frameColor).
		Padding(1, 3).
		Margin(1, 3)
	s.paragraph = lipgloss.NewStyle().
		Width(40).
		MarginBottom(1).
		Align(lipgloss.Center)
	s.text = lipgloss.NewStyle().
		Foreground(textColor)
	s.keyword = lipgloss.NewStyle().
		Foreground(keywordColor).
		Bold(true)

	s.activeButton = lipgloss.NewStyle().
		Padding(0, 3).
		Background(lipgloss.Color("#FF6AD2")).
		Foreground(lipgloss.Color("#FFFCC2"))
	s.inactiveButton = s.activeButton.
		Background(inactiveBgColor).
		Foreground(inactiveFgColor)
	return s
}

type model struct {
	styles  styles
	yes     bool
	chosen  bool
	aborted bool
}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyPressMsg:
		switch msg.String() {
		case "q", "esc", "ctrl+c":
			m.aborted = true
			return m, tea.Quit
		case "enter":
			m.chosen = true
			return m, tea.Quit
		case "left", "right", "h", "l":
			m.yes = !m.yes
		case "y":
			m.yes = true
			m.chosen = true
			return m, tea.Quit
		case "n":
			m.yes = false
			m.chosen = true
			return m, tea.Quit
		}
	}

	return m, nil
}

func (m model) View() tea.View {
	var v tea.View
	if m.chosen || m.aborted {
		// We're about to exit, so wipe the UI.
		return v
	}

	var (
		s = m.styles
		y = "Yes"
		n = "No"
	)

	if m.yes {
		y = s.activeButton.Render(y)
		n = s.inactiveButton.Render(n)
	} else {
		y = s.inactiveButton.Render(y)
		n = s.activeButton.Render(n)
	}

	content := s.frame.Render(
		lipgloss.JoinVertical(lipgloss.Center,
			s.paragraph.Render(
				s.text.Render("Are you sure you want to eat that ")+
					s.keyword.Render("moderatly ripe")+
					s.text.Render(" banana?"),
			),
			y+"  "+n,
		),
	)
	v.SetContent(content)
	return v
}

func main() {
	initialModel := model{
		yes:    true,
		styles: newStyles(),
	}
	m, err := tea.NewProgram(initialModel).Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Uh oh: %v", err)
		os.Exit(1)
	}

	if m := m.(model); m.chosen {
		if m.yes {
			fmt.Println("Are you sure? It's not ripe yet.")
		} else {
			fmt.Println("Well, alright. It was probably good, though.")
		}
	}
}


================================================
FILE: examples/compat/standalone/main.go
================================================
// This example illustrates how to detect the terminal's background color and
// choose either light or dark colors accordingly when using Lip Gloss in a.
// standalone fashion, i.e. independent of Bubble Tea.
//
// For an example of how to do this in a Bubble Tea program, see the
// 'bubbletea' example.
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/compat"
)

var (
	frameColor      = compat.AdaptiveColor{Light: lipgloss.Color("#C5ADF9"), Dark: lipgloss.Color("#864EFF")}
	textColor       = compat.AdaptiveColor{Light: lipgloss.Color("#696969"), Dark: lipgloss.Color("#bdbdbd")}
	keywordColor    = compat.AdaptiveColor{Light: lipgloss.Color("#37CD96"), Dark: lipgloss.Color("#22C78A")}
	inactiveBgColor = compat.AdaptiveColor{Light: lipgloss.Color("#988F95"), Dark: lipgloss.Color("#978692")}
	inactiveFgColor = compat.AdaptiveColor{Light: lipgloss.Color("#FDFCE3"), Dark: lipgloss.Color("#FBFAE7")}
)

func main() {
	// Define some styles. adaptive.Color() can be used to choose the
	// appropriate light or dark color based on the detected background color.
	frameStyle := lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(frameColor).
		Padding(1, 3).
		Margin(1, 3)
	paragraphStyle := lipgloss.NewStyle().
		Width(40).
		MarginBottom(1).
		Align(lipgloss.Center)
	textStyle := lipgloss.NewStyle().
		Foreground(textColor)
	keywordStyle := lipgloss.NewStyle().
		Foreground(keywordColor).
		Bold(true)

	activeButton := lipgloss.NewStyle().
		Padding(0, 3).
		Background(lipgloss.Color("#FF6AD2")).
		Foreground(lipgloss.Color("#FFFCC2"))
	inactiveButton := activeButton.
		Background(inactiveBgColor).
		Foreground(inactiveFgColor)

	// Build layout.
	text := paragraphStyle.Render(
		textStyle.Render("Are you sure you want to eat that ") +
			keywordStyle.Render("moderatly ripe") +
			textStyle.Render(" banana?"),
	)
	buttons := activeButton.Render("Yes") + "  " + inactiveButton.Render("No")
	block := frameStyle.Render(
		lipgloss.JoinVertical(lipgloss.Center, text, buttons),
	)

	// Print the block to stdout. It's important to use Lip Gloss's print
	// functions to ensure that colors are downsampled correctly. If output
	// isn't a TTY (i.e. we're logging to a file) colors will be stripped
	// entirely.
	//
	// Note that in Bubble Tea downsampling happens automatically.
	lipgloss.Println(block)
}


================================================
FILE: examples/go.mod
================================================
module examples

go 1.24.3

toolchain go1.24.4

replace charm.land/lipgloss/v2 => ../

require (
	charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251201184111-551c60ee5a5c
	charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106192539-4b304240aab7
	github.com/charmbracelet/colorprofile v0.4.2
	github.com/charmbracelet/ssh v0.0.0-20241211182756-4fe22b0f1b7c
	github.com/charmbracelet/wish/v2 v2.0.0-20251106193208-3cd15da8229f
	github.com/charmbracelet/x/exp/charmtone v0.0.0-20250627134340-c144409e381c
	github.com/charmbracelet/x/term v0.2.2
	github.com/rivo/uniseg v0.4.7
)

require (
	github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
	github.com/charmbracelet/keygen v0.5.1 // indirect
	github.com/charmbracelet/log/v2 v2.0.0-20251106192421-eb64aaa963a0 // indirect
	github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 // indirect
	github.com/charmbracelet/x/ansi v0.11.6 // indirect
	github.com/charmbracelet/x/conpty v0.1.0 // indirect
	github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 // indirect
	github.com/charmbracelet/x/termios v0.1.1 // indirect
	github.com/charmbracelet/x/windows v0.2.2 // indirect
	github.com/clipperhouse/displaywidth v0.9.0 // indirect
	github.com/clipperhouse/stringish v0.1.1 // indirect
	github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
	github.com/creack/pty v1.1.21 // indirect
	github.com/go-logfmt/logfmt v0.6.0 // indirect
	github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
	github.com/mattn/go-runewidth v0.0.19 // indirect
	github.com/muesli/cancelreader v0.2.2 // indirect
	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
	golang.org/x/crypto v0.36.0 // indirect
	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
	golang.org/x/sync v0.18.0 // indirect
	golang.org/x/sys v0.41.0 // indirect
)

// replace with log v2
replace github.com/charmbracelet/log => github.com/charmbracelet/log v0.4.1-0.20241010222913-47ce960d4847


================================================
FILE: examples/go.sum
================================================
charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251201184111-551c60ee5a5c h1:Jn9nugUf2ddyARHsA79zsWl7szy7dV7HpBj645Sp6DU=
charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251201184111-551c60ee5a5c/go.mod h1:BLGnNsQA++rg5IEiTVeLix8AKte880DQWjc8Afs3Nw8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY=
github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8=
github.com/charmbracelet/keygen v0.5.1 h1:zBkkYPtmKDVTw+cwUyY6ZwGDhRxXkEp0Oxs9sqMLqxI=
github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw=
github.com/charmbracelet/log/v2 v2.0.0-20251106192421-eb64aaa963a0 h1:lxHzxsHd4P7o7+5D5OcEItYkQ1xY3ovNg8Dc5ftd3rI=
github.com/charmbracelet/log/v2 v2.0.0-20251106192421-eb64aaa963a0/go.mod h1:Q7oMtlboDPnnrYiJDXNwdWmJblOmuOnycPKczlVju6I=
github.com/charmbracelet/ssh v0.0.0-20241211182756-4fe22b0f1b7c h1:treQxMBdI2PaD4eOYfFux8stfCkUxhuUxaqGcxKqVpI=
github.com/charmbracelet/ssh v0.0.0-20241211182756-4fe22b0f1b7c/go.mod h1:CY1xbl2z+ZeBmNWItKZyxx0zgDgnhmR57+DTsHOobJ4=
github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 h1:OqDqxQZliC7C8adA7KjelW3OjtAxREfeHkNcd66wpeI=
github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318/go.mod h1:Y6kE2GzHfkyQQVCSL9r2hwokSrIlHGzZG+71+wDYSZI=
github.com/charmbracelet/wish/v2 v2.0.0-20251106193208-3cd15da8229f h1:yR3ru/zfVX4cnyhs5GPL1dxArAtxL/IZzJ9/mt1IoeI=
github.com/charmbracelet/wish/v2 v2.0.0-20251106193208-3cd15da8229f/go.mod h1:YW+dfIwHgy7eKZqSffA+Fx9EEW2YyXKUGkAdijsvpGI=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250627134340-c144409e381c h1:2GELBLPgfSbHU53bsQhR9XIgNuVZ6w+Rz8RWV5Lq+A4=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250627134340-c144409e381c/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: examples/layout/main.go
================================================
package main

// This example demonstrates various Lip Gloss style and layout features.

import (
	"fmt"
	"image/color"
	"os"
	"strings"

	"charm.land/lipgloss/v2"
	"github.com/charmbracelet/x/term"
	"github.com/rivo/uniseg"
)

const (
	// In real life situations we'd adjust the document to fit the width we've
	// detected. In the case of this example we're hardcoding the width, and
	// later using the detected width only to truncate in order to avoid jaggy
	// wrapping.
	width = 96

	// How wide to render various columns in the layout.
	columnWidth = 30
)

var (
	// Whether the detected background color is dark. We detect this at app start.
	hasDarkBG bool

	// A helper function for choosing either a light or dark color based on the
	// detected background color. We create this at app start.
	lightDark lipgloss.LightDarkFunc
)

func main() {
	// Detect the background color.
	hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)

	// Create a new helper function for choosing either a light or dark color
	// based on the detected background color.
	lightDark = lipgloss.LightDark(hasDarkBG)

	// Style definitions.
	var (

		// General.

		subtle    = lightDark(lipgloss.Color("#D9DCCF"), lipgloss.Color("#383838"))
		highlight = lightDark(lipgloss.Color("#874BFD"), lipgloss.Color("#7D56F4"))
		special   = lightDark(lipgloss.Color("#43BF6D"), lipgloss.Color("#73F59F"))

		divider = lipgloss.NewStyle().
			SetString("•").
			Padding(0, 1).
			Foreground(subtle).
			String()

		url = lipgloss.NewStyle().Foreground(special).Render

		// Tabs.

		activeTabBorder = lipgloss.Border{
			Top:         "─",
			Bottom:      " ",
			Left:        "│",
			Right:       "│",
			TopLeft:     "╭",
			TopRight:    "╮",
			BottomLeft:  "┘",
			BottomRight: "└",
		}

		tabBorder = lipgloss.Border{
			Top:         "─",
			Bottom:      "─",
			Left:        "│",
			Right:       "│",
			TopLeft:     "╭",
			TopRight:    "╮",
			BottomLeft:  "┴",
			BottomRight: "┴",
		}

		tab = lipgloss.NewStyle().
			Border(tabBorder, true).
			BorderForeground(highlight).
			Padding(0, 1)

		activeTab = tab.Border(activeTabBorder, true)

		tabGap = tab.
			BorderTop(false).
			BorderLeft(false).
			BorderRight(false)

		// Title.

		titleStyle = lipgloss.NewStyle().
				MarginLeft(1).
				MarginRight(5).
				Padding(0, 1).
				Italic(true).
				Foreground(lipgloss.Color("#FFF7DB")).
				SetString("Lip Gloss")

		descStyle = lipgloss.NewStyle().MarginTop(1)

		infoStyle = lipgloss.NewStyle().
				BorderStyle(lipgloss.NormalBorder()).
				BorderTop(true).
				BorderForeground(subtle)

		// Dialog.

		dialogBoxStyle = lipgloss.NewStyle().
				Border(lipgloss.RoundedBorder()).
				BorderForeground(lipgloss.Color("#874BFD")).
				Padding(1, 0).
				BorderTop(true).
				BorderLeft(true).
				BorderRight(true).
				BorderBottom(true)

		buttonStyle = lipgloss.NewStyle().
				Foreground(lipgloss.Color("#FFF7DB")).
				Background(lipgloss.Color("#888B7E")).
				Padding(0, 3).
				MarginTop(1)

		activeButtonStyle = buttonStyle.
					Foreground(lipgloss.Color("#FFF7DB")).
					Background(lipgloss.Color("#F25D94")).
					MarginRight(2).
					Underline(true)

		// List.

		list = lipgloss.NewStyle().
			Border(lipgloss.NormalBorder(), false, true, false, false).
			BorderForeground(subtle).
			MarginRight(1).
			Height(8).
			Width(width / 3)

		listHeader = lipgloss.NewStyle().
				BorderStyle(lipgloss.NormalBorder()).
				BorderBottom(true).
				BorderForeground(subtle).
				MarginRight(2).
				Render

		listItem = lipgloss.NewStyle().PaddingLeft(2).Render

		checkMark = lipgloss.NewStyle().SetString("✓").
				Foreground(special).
				PaddingRight(1).
				String()

		listDone = func(s string) string {
			return checkMark + lipgloss.NewStyle().
				Strikethrough(true).
				Foreground(lightDark(lipgloss.Color("#969B86"), lipgloss.Color("#696969"))).
				Render(s)
		}

		// Paragraphs/History.

		historyStyle = lipgloss.NewStyle().
				Align(lipgloss.Left).
				Foreground(lipgloss.Color("#FAFAFA")).
				Background(highlight).
				Margin(1, 3, 0, 0).
				Padding(1, 2).
				Height(19).
				Width(columnWidth)

		// Status Bar.

		statusNugget = lipgloss.NewStyle().
				Foreground(lipgloss.Color("#FFFDF5")).
				Padding(0, 1)

		statusBarStyle = lipgloss.NewStyle().
				Foreground(lightDark(lipgloss.Color("#343433"), lipgloss.Color("#C1C6B2"))).
				Background(lightDark(lipgloss.Color("#D9DCCF"), lipgloss.Color("#353533")))

		statusStyle = lipgloss.NewStyle().
				Inherit(statusBarStyle).
				Foreground(lipgloss.Color("#FFFDF5")).
				Background(lipgloss.Color("#FF5F87")).
				Padding(0, 1).
				MarginRight(1)

		encodingStyle = statusNugget.
				Background(lipgloss.Color("#A550DF")).
				Align(lipgloss.Right)

		statusText = lipgloss.NewStyle().Inherit(statusBarStyle)

		fishCakeStyle = statusNugget.Background(lipgloss.Color("#6124DF"))

		// Floating thing.

		floatingStyle = lipgloss.NewStyle().
				Italic(true).
				Foreground(lipgloss.Color("#FFF7DB")).
				Background(lipgloss.Color("#F25D94")).
				Padding(1, 6).
				Align(lipgloss.Center)

		// Page.

		docStyle = lipgloss.NewStyle().Padding(1, 2, 1, 2)
	)

	physicalWidth, _, _ := term.GetSize(os.Stdout.Fd())
	doc := strings.Builder{}

	// Tabs.
	{
		row := lipgloss.JoinHorizontal(
			lipgloss.Top,
			activeTab.Render("Lip Gloss"),
			tab.Render("Blush"),
			tab.Render("Eye Shadow"),
			tab.Render("Mascara"),
			tab.Render("Foundation"),
		)
		gap := tabGap.Render(strings.Repeat(" ", max(0, width-lipgloss.Width(row)-2)))
		row = lipgloss.JoinHorizontal(lipgloss.Bottom, row, gap)
		doc.WriteString(row + "\n\n")
	}

	// Title.
	{
		var (
			colors = colorGrid(1, 5)
			title  strings.Builder
		)

		for i, v := range colors {
			const offset = 2
			fmt.Fprint(&title, titleStyle.MarginLeft(i*offset).Background(v[0]))
			if i < len(colors)-1 {
				title.WriteRune('\n')
			}
		}

		desc := lipgloss.JoinVertical(lipgloss.Left,
			descStyle.Render("Style Definitions for Nice Terminal Layouts"),
			infoStyle.Render("From Charm"+divider+url("https://github.com/charmbracelet/lipgloss")),
		)

		row := lipgloss.JoinHorizontal(lipgloss.Top, title.String(), desc)
		doc.WriteString(row + "\n\n")
	}

	// Dialog.
	{
		okButton := activeButtonStyle.Render("Yes")
		cancelButton := buttonStyle.Render("Maybe")

		grad := applyGradient(
			lipgloss.NewStyle(),
			"Are you sure you want to eat marmalade?",
			lipgloss.Color("#EDFF82"),
			lipgloss.Color("#F25D94"),
		)

		question := lipgloss.NewStyle().
			Width(50).
			Align(lipgloss.Center).
			Render(grad)

		buttons := lipgloss.JoinHorizontal(lipgloss.Top, okButton, cancelButton)
		ui := lipgloss.JoinVertical(lipgloss.Center, question, buttons)

		dialog := lipgloss.Place(width, 9,
			lipgloss.Center, lipgloss.Center,
			dialogBoxStyle.Render(ui),
			lipgloss.WithWhitespaceChars("猫咪"),
			lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Foreground(subtle)),
		)

		doc.WriteString(dialog + "\n\n")
	}

	// Color grid.
	colors := func() string {
		colors := colorGrid(14, 8)

		b := strings.Builder{}
		for _, x := range colors {
			for _, y := range x {
				s := lipgloss.NewStyle().SetString("  ").Background(y)
				b.WriteString(s.String())
			}
			b.WriteRune('\n')
		}

		return b.String()
	}()

	lists := lipgloss.JoinHorizontal(lipgloss.Top,
		list.Render(
			lipgloss.JoinVertical(lipgloss.Left,
				listHeader("Citrus Fruits to Try"),
				listDone("Grapefruit"),
				listDone("Yuzu"),
				listItem("Citron"),
				listItem("Kumquat"),
				listItem("Pomelo"),
			),
		),
		list.Render(
			lipgloss.JoinVertical(lipgloss.Left,
				listHeader("Actual Lip Gloss Vendors"),
				listItem("Glossier"),
				listItem("Claire‘s Boutique"),
				listDone("Nyx"),
				listItem("Mac"),
				listDone("Milk"),
			),
		),
	)

	doc.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, lists, lipgloss.NewStyle().MarginLeft(1).Render(colors)))

	// Marmalade history.
	{
		const (
			historyA = "The Romans learned from the Greeks that quinces slowly cooked with honey would “set” when cool. The Apicius gives a recipe for preserving whole quinces, stems and leaves attached, in a bath of honey diluted with defrutum: Roman marmalade. Preserves of quince and lemon appear (along with rose, apple, plum and pear) in the Book of ceremonies of the Byzantine Emperor Constantine VII Porphyrogennetos."
			historyB = "Medieval quince preserves, which went by the French name cotignac, produced in a clear version and a fruit pulp version, began to lose their medieval seasoning of spices in the 16th century. In the 17th century, La Varenne provided recipes for both thick and clear cotignac."
			historyC = "In 1524, Henry VIII, King of England, received a “box of marmalade” from Mr. Hull of Exeter. This was probably marmelada, a solid quince paste from Portugal, still made and sold in southern Europe today. It became a favourite treat of Anne Boleyn and her ladies in waiting."
		)

		doc.WriteString(lipgloss.JoinHorizontal(
			lipgloss.Top,
			historyStyle.Align(lipgloss.Right).Render(historyA),
			historyStyle.Align(lipgloss.Center).Render(historyB),
			historyStyle.MarginRight(0).Render(historyC),
		))

		doc.WriteString("\n\n")
	}

	// Status bar.
	{
		w := lipgloss.Width

		lightDarkState := "Light"
		if hasDarkBG {
			lightDarkState = "Dark"
		}

		statusKey := statusStyle.Render("STATUS")
		encoding := encodingStyle.Render("UTF-8")
		fishCake := fishCakeStyle.Render("🍥 Fish Cake")
		statusVal := statusText.
			Width(width - w(statusKey) - w(encoding) - w(fishCake)).
			Render("Ravishingly " + lightDarkState + "!")

		bar := lipgloss.JoinHorizontal(lipgloss.Top,
			statusKey,
			statusVal,
			encoding,
			fishCake,
		)

		doc.WriteString(statusBarStyle.Width(width).Render(bar))
	}

	if physicalWidth > 0 {
		docStyle = docStyle.MaxWidth(physicalWidth)
	}

	// Render the document.
	document := docStyle.Render(doc.String())

	// Surprise! Composite some bonus content on top of the document.
	modal := floatingStyle.Render("Now with Compositing!")
	layers := []*lipgloss.Layer{
		lipgloss.NewLayer(document),
		lipgloss.NewLayer(modal).X(58).Y(44),
	}

	comp := lipgloss.NewCompositor(layers...)

	// Okay, let's print it. We use a special Lipgloss writer to downsample
	// colors to the terminal's color palette. And, if output's not a TTY, we
	// will remove color entirely.
	lipgloss.Println(comp.Render())
}

// colorGrid blends colors from 4 corner quadrants, into a box region.
func colorGrid(xSteps, ySteps int) [][]color.Color {
	leftColors := lipgloss.Blend1D(ySteps, lipgloss.Color("#F25D94"), lipgloss.Color("#643AFF"))
	rightColors := lipgloss.Blend1D(ySteps, lipgloss.Color("#EDFF82"), lipgloss.Color("#14F9D5"))

	grid := make([][]color.Color, ySteps)
	for y := range ySteps {
		rowColors := lipgloss.Blend1D(xSteps, leftColors[y], rightColors[y])
		grid[y] = make([]color.Color, xSteps)
		for x := range xSteps {
			grid[y][x] = rowColors[x]
		}
	}
	return grid
}

// applyGradient applies a gradient to the given string.
func applyGradient(base lipgloss.Style, input string, from, to color.Color) string {
	// We want to get the graphemes of the input string, which is the number of
	// characters as a human would see them.
	//
	// We definitely don't want to use len(), because that returns the
	// bytes. The rune count would get us closer but there are times, like with
	// emojis, where the rune count is greater than the number of actual
	// characters.
	g := uniseg.NewGraphemes(input)
	var chars []string
	for g.Next() {
		chars = append(chars, g.Str())
	}

	gradient := lipgloss.Blend1D(len(chars), from, to)
	var output strings.Builder
	for i, char := range chars {
		output.WriteString(base.Foreground(gradient[i]).Render(char))
	}
	return output.String()
}


================================================
FILE: examples/list/duckduckgoose/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/list"
)

func duckDuckGooseEnumerator(items list.Items, i int) string {
	if items.At(i).Value() == "Goose" {
		return "Honk →"
	}
	return " "
}

func main() {
	enumStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#00d787")).MarginRight(1)
	itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("255"))

	l := list.New("Duck", "Duck", "Duck", "Goose", "Duck").
		ItemStyle(itemStyle).
		EnumeratorStyle(enumStyle).
		Enumerator(duckDuckGooseEnumerator)

	lipgloss.Println(l)
}


================================================
FILE: examples/list/glow/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/list"
)

type Document struct {
	Name string
	Time string
}

var faint = lipgloss.NewStyle().Faint(true)

func (d Document) String() string {
	return d.Name + "\n" +
		faint.Render(d.Time)
}

var docs = []Document{
	{"README.md", "2 minutes ago"},
	{"Example.md", "1 hour ago"},
	{"secrets.md", "1 week ago"},
}

const selected = 1

func main() {
	baseStyle := lipgloss.NewStyle().
		MarginBottom(1).
		MarginLeft(1)
	dimColor := lipgloss.Color("250")
	hightlightColor := lipgloss.Color("#EE6FF8")

	l := list.New().
		Enumerator(func(_ list.Items, i int) string {
			if i == selected {
				return "│\n│"
			}
			return " "
		}).
		ItemStyleFunc(func(_ list.Items, i int) lipgloss.Style {
			st := baseStyle
			if selected == i {
				return st.Foreground(hightlightColor)
			}
			return st.Foreground(dimColor)
		}).
		EnumeratorStyleFunc(func(_ list.Items, i int) lipgloss.Style {
			if selected == i {
				return lipgloss.NewStyle().Foreground(hightlightColor)
			}
			return lipgloss.NewStyle().Foreground(dimColor)
		})

	for _, d := range docs {
		l.Item(d.String())
	}

	lipgloss.Print("\n", l, "\n")
}


================================================
FILE: examples/list/grocery/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/list"
)

var purchased = []string{
	"Bananas",
	"Barley",
	"Cashews",
	"Coconut Milk",
	"Dill",
	"Eggs",
	"Fish Cake",
	"Leeks",
	"Papaya",
}

func groceryEnumerator(items list.Items, i int) string {
	for _, p := range purchased {
		if items.At(i).Value() == p {
			return "✓"
		}
	}
	return "•"
}

var dimEnumStyle = lipgloss.NewStyle().
	Foreground(lipgloss.Color("240")).
	MarginRight(1)

var highlightedEnumStyle = lipgloss.NewStyle().
	Foreground(lipgloss.Color("10")).
	MarginRight(1)

func enumStyleFunc(items list.Items, i int) lipgloss.Style {
	for _, p := range purchased {
		if items.At(i).Value() == p {
			return highlightedEnumStyle
		}
	}
	return dimEnumStyle
}

func itemStyleFunc(items list.Items, i int) lipgloss.Style {
	itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("255"))
	for _, p := range purchased {
		if items.At(i).Value() == p {
			return itemStyle.Strikethrough(true)
		}
	}
	return itemStyle
}

func main() {
	l := list.New(
		"Artichoke",
		"Baking Flour", "Bananas", "Barley", "Bean Sprouts",
		"Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst",
		"Dill", "Dragonfruit", "Dried Shrimp",
		"Eggs",
		"Fish Cake", "Furikake",
		"Jicama",
		"Kohlrabi",
		"Leeks", "Lentils", "Licorice Root",
	).
		Enumerator(groceryEnumerator).
		EnumeratorStyleFunc(enumStyleFunc).
		ItemStyleFunc(itemStyleFunc)

	lipgloss.Println(l)
}


================================================
FILE: examples/list/roman/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/list"
)

func main() {
	enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
	itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("255")).MarginRight(1)

	l := list.New(
		"Glossier",
		"Claire’s Boutique",
		"Nyx",
		"Mac",
		"Milk",
	).
		Enumerator(list.Roman).
		EnumeratorStyle(enumeratorStyle).
		ItemStyle(itemStyle)

	lipgloss.Println(l)
}


================================================
FILE: examples/list/simple/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/list"
)

func main() {
	l := list.New(
		"A",
		"B",
		"C",
		list.New(
			"D",
			"E",
			"F",
		).Enumerator(list.Roman),
		"G",
	)
	lipgloss.Println(l)
}


================================================
FILE: examples/list/sublist/main.go
================================================
package main

import (
	"os"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/list"
	"charm.land/lipgloss/v2/table"
)

func main() {
	hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
	lightDark := lipgloss.LightDark(hasDarkBG)

	purple := lipgloss.NewStyle().
		Foreground(lipgloss.Color("99")).
		MarginRight(1)

	pink := lipgloss.NewStyle().
		Foreground(lipgloss.Color("212")).
		MarginRight(1)

	base := lipgloss.NewStyle().
		MarginBottom(1).
		MarginLeft(1)

	faint := lipgloss.NewStyle().Faint(true)

	dim := lipgloss.Color("250")
	highlight := lipgloss.Color("#EE6FF8")

	special := lightDark(lipgloss.Color("#43BF6D"), lipgloss.Color("#73F59F"))

	checklistEnumStyle := func(items list.Items, index int) lipgloss.Style {
		switch index {
		case 1, 2, 4:
			return lipgloss.NewStyle().
				Foreground(special).
				PaddingRight(1)
		default:
			return lipgloss.NewStyle().PaddingRight(1)
		}
	}

	checklistEnum := func(items list.Items, index int) string {
		switch index {
		case 1, 2, 4:
			return "✓"
		default:
			return "•"
		}
	}

	checklistStyle := func(items list.Items, index int) lipgloss.Style {
		switch index {
		case 1, 2, 4:
			return lipgloss.NewStyle().
				Strikethrough(true).
				Foreground(lightDark(lipgloss.Color("#969B86"), lipgloss.Color("#696969")))
		default:
			return lipgloss.NewStyle()
		}
	}

	gradient := lipgloss.Blend1D(5, lipgloss.Color("#F25D94"), lipgloss.Color("#643AFF"))

	titleStyle := lipgloss.NewStyle().
		Italic(true).
		Foreground(lipgloss.Color("#FFF7DB"))

	lipglossStyleFunc := func(items list.Items, index int) lipgloss.Style {
		if index == items.Length()-1 {
			return titleStyle.Padding(1, 2).Margin(0, 0, 1, 0).MaxWidth(20).Background(gradient[index])
		}
		return titleStyle.Padding(0, 5-index, 0, index+2).MaxWidth(20).Background(gradient[index])
	}

	history := "Medieval quince preserves, which went by the French name cotignac, produced in a clear version and a fruit pulp version, began to lose their medieval seasoning of spices in the 16th century. In the 17th century, La Varenne provided recipes for both thick and clear cotignac."

	l := list.New().
		EnumeratorStyle(purple).
		Item("Lip Gloss").
		Item("Blush").
		Item("Eye Shadow").
		Item("Mascara").
		Item("Foundation").
		Item(
			list.New().
				EnumeratorStyle(pink).
				Item("Citrus Fruits to Try").
				Item(
					list.New().
						ItemStyleFunc(checklistStyle).
						EnumeratorStyleFunc(checklistEnumStyle).
						Enumerator(checklistEnum).
						Item("Grapefruit").
						Item("Yuzu").
						Item("Citron").
						Item("Kumquat").
						Item("Pomelo"),
				).
				Item("Actual Lip Gloss Vendors").
				Item(
					list.New().
						ItemStyleFunc(checklistStyle).
						EnumeratorStyleFunc(checklistEnumStyle).
						Enumerator(checklistEnum).
						Item("Glossier").
						Item("Claire‘s Boutique").
						Item("Nyx").
						Item("Mac").
						Item("Milk").
						Item(
							list.New().
								EnumeratorStyle(purple).
								Enumerator(list.Dash).
								ItemStyleFunc(lipglossStyleFunc).
								Item("Lip Gloss").
								Item("Lip Gloss").
								Item("Lip Gloss").
								Item("Lip Gloss").
								Item(
									list.New().
										EnumeratorStyle(lipgloss.NewStyle().Foreground(gradient[4]).MarginRight(1)).
										Item("\nStyle Definitions for Nice Terminal Layouts\n─────").
										Item("From Charm").
										Item("https://github.com/charmbracelet/lipgloss").
										Item(
											list.New().
												EnumeratorStyle(lipgloss.NewStyle().Foreground(gradient[3]).MarginRight(1)).
												Item("Emperors: Julio-Claudian dynasty").
												Item(
													lipgloss.NewStyle().Padding(1).Render(
														list.New(
															"Augustus",
															"Tiberius",
															"Caligula",
															"Claudius",
															"Nero",
														).Enumerator(list.Roman).String(),
													),
												).
												Item(
													lipgloss.NewStyle().
														Bold(true).
														Foreground(lipgloss.Color("#FAFAFA")).
														Background(lipgloss.Color("#7D56F4")).
														AlignHorizontal(lipgloss.Center).
														AlignVertical(lipgloss.Center).
														Padding(1, 3).
														Margin(0, 1, 1, 1).
														Width(40).
														Render(history),
												).
												Item(
													table.New().
														Width(30).
														BorderStyle(purple.MarginRight(0)).
														StyleFunc(func(row, col int) lipgloss.Style {
															style := lipgloss.NewStyle()

															if col == 0 {
																style = style.Align(lipgloss.Center)
															} else {
																style = style.Align(lipgloss.Right).PaddingRight(2)
															}
															if row == 0 {
																return style.Bold(true).Align(lipgloss.Center).PaddingRight(0)
															}
															return style.Faint(true)
														}).
														Headers("ITEM", "QUANTITY").
														Row("Apple", "6").
														Row("Banana", "10").
														Row("Orange", "2").
														Row("Strawberry", "12"),
												).
												Item("Documents").
												Item(
													list.New().
														Enumerator(func(_ list.Items, i int) string {
															if i == 1 {
																return "│\n│"
															}
															return " "
														}).
														ItemStyleFunc(func(_ list.Items, i int) lipgloss.Style {
															if i == 1 {
																return base.Foreground(highlight)
															}
															return base.Foreground(dim)
														}).
														EnumeratorStyleFunc(func(_ list.Items, i int) lipgloss.Style {
															if i == 1 {
																return lipgloss.NewStyle().Foreground(highlight)
															}
															return lipgloss.NewStyle().Foreground(dim)
														}).
														Item("Foo Document\n" + faint.Render("1 day ago")).
														Item("Bar Document\n" + faint.Render("2 days ago")).
														Item("Baz Document\n" + faint.Render("10 minutes ago")).
														Item("Qux Document\n" + faint.Render("1 month ago")),
												).
												Item("EOF"),
										).
										Item("go get github.com/charmbracelet/lipgloss/list\n"),
								).
								Item("See ya later"),
						),
				).
				Item("List"),
		).
		Item("xoxo, Charm_™")

	lipgloss.Println(l)
}


================================================
FILE: examples/ssh/main.go
================================================
package main

// This example demonstrates how to use a colorprofile to accurately detect
// client terminal color capabilities for Lip Gloss rendering with Wish, a
// package for building custom SSH servers.
//
// For details on wish see: https://github.com/charmbracelet/wish/

import (
	"fmt"
	"log"
	"strings"

	"charm.land/lipgloss/v2"
	"github.com/charmbracelet/colorprofile"
	"github.com/charmbracelet/ssh"
	"github.com/charmbracelet/wish/v2"
)

// Available styles.
type styles struct {
	bold          lipgloss.Style
	faint         lipgloss.Style
	italic        lipgloss.Style
	underline     lipgloss.Style
	strikethrough lipgloss.Style
	red           lipgloss.Style
	green         lipgloss.Style
	yellow        lipgloss.Style
	blue          lipgloss.Style
	magenta       lipgloss.Style
	cyan          lipgloss.Style
	gray          lipgloss.Style
}

// Create new styles.
func makeStyles() styles {
	return styles{
		bold:          lipgloss.NewStyle().SetString("bold").Bold(true),
		faint:         lipgloss.NewStyle().SetString("faint").Faint(true),
		italic:        lipgloss.NewStyle().SetString("italic").Italic(true),
		underline:     lipgloss.NewStyle().SetString("underline").Underline(true),
		strikethrough: lipgloss.NewStyle().SetString("strikethrough").Strikethrough(true),
		red:           lipgloss.NewStyle().SetString("red").Foreground(lipgloss.Color("#E88388")),
		green:         lipgloss.NewStyle().SetString("green").Foreground(lipgloss.Color("#A8CC8C")),
		yellow:        lipgloss.NewStyle().SetString("yellow").Foreground(lipgloss.Color("#DBAB79")),
		blue:          lipgloss.NewStyle().SetString("blue").Foreground(lipgloss.Color("#71BEF2")),
		magenta:       lipgloss.NewStyle().SetString("magenta").Foreground(lipgloss.Color("#D290E4")),
		cyan:          lipgloss.NewStyle().SetString("cyan").Foreground(lipgloss.Color("#66C2CD")),
		gray:          lipgloss.NewStyle().SetString("gray").Foreground(lipgloss.Color("#B9BFCA")),
	}
}

// Handle SSH requests.
func handler(next ssh.Handler) ssh.Handler {
	return func(sess ssh.Session) {
		pty, _, active := sess.Pty()
		if !active {
			next(sess)
			return
		}

		environ := sess.Environ()
		environ = append(environ, fmt.Sprintf("TERM=%s", pty.Term))
		output := colorprofile.NewWriter(pty.Slave, environ)
		width := pty.Window.Width

		// Initialize new styles.
		styles := makeStyles()

		str := strings.Builder{}

		fmt.Fprintf(&str, "\n\nProfile: %s\n%s %s %s %s %s",
			colorprofile.Detect(pty.Slave, environ),
			styles.bold,
			styles.faint,
			styles.italic,
			styles.underline,
			styles.strikethrough,
		)

		fmt.Fprintf(&str, "\n%s %s %s %s %s %s %s",
			styles.red,
			styles.green,
			styles.yellow,
			styles.blue,
			styles.magenta,
			styles.cyan,
			styles.gray,
		)

		fmt.Fprintf(&str, "\n%s %s %s %s %s %s %s\n\n",
			styles.red,
			styles.green,
			styles.yellow,
			styles.blue,
			styles.magenta,
			styles.cyan,
			styles.gray,
		)

		hasDarkBG := lipgloss.HasDarkBackground(pty.Slave, pty.Slave)
		lightDark := lipgloss.LightDark(hasDarkBG)

		fmt.Fprintf(&str, "%s %s\n\n",
			styles.bold.UnsetString().Render("Has dark background?"),
			func() string {
				if hasDarkBG {
					return "Yep."
				}
				return "Nope!"
			}(),
		)

		block := lipgloss.Place(width,
			lipgloss.Height(str.String()), lipgloss.Center, lipgloss.Center, str.String(),
			lipgloss.WithWhitespaceChars("/"),
			lipgloss.WithWhitespaceStyle(
				lipgloss.NewStyle().Foreground(lightDark(
					lipgloss.ANSIColor(250),
					lipgloss.ANSIColor(236),
				))),
		)

		// Render to client.
		output.WriteString(block)

		next(sess)
	}
}

func main() {
	port := 3456
	s, err := wish.NewServer(
		ssh.AllocatePty(),
		wish.WithAddress(fmt.Sprintf(":%d", port)),
		wish.WithHostKeyPath("ssh_example"),
		wish.WithMiddleware(handler),
	)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("SSH server listening on port %d", port)
	log.Printf("To connect from your local machine run: ssh localhost -p %d", port)
	if err := s.ListenAndServe(); err != nil {
		log.Fatal(err)
	}
}


================================================
FILE: examples/table/ansi/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/table"
)

func main() {
	s := lipgloss.NewStyle().Foreground(lipgloss.Color("240")).Render

	t := table.New()
	t.Row("Bubble Tea", s("Milky"))
	t.Row("Milk Tea", s("Also milky"))
	t.Row("Actual milk", s("Milky as well"))
	lipgloss.Println(t.Render())
}


================================================
FILE: examples/table/chess/main.go
================================================
package main

import (
	"strings"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/table"
)

func main() {
	labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("241"))

	board := [][]string{
		{"♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"},
		{"♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"},
		{" ", " ", " ", " ", " ", " ", " ", " "},
		{" ", " ", " ", " ", " ", " ", " ", " "},
		{" ", " ", " ", " ", " ", " ", " ", " "},
		{" ", " ", " ", " ", " ", " ", " ", " "},
		{"♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"},
		{"♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"},
	}

	t := table.New().
		Border(lipgloss.NormalBorder()).
		BorderRow(true).
		BorderColumn(true).
		Rows(board...).
		StyleFunc(func(row, col int) lipgloss.Style {
			return lipgloss.NewStyle().Padding(0, 1)
		})

	ranks := labelStyle.Render(strings.Join([]string{" A", "B", "C", "D", "E", "F", "G", "H  "}, "   "))
	files := labelStyle.Render(strings.Join([]string{" 1", "2", "3", "4", "5", "6", "7", "8 "}, "\n\n "))

	lipgloss.Println(
		lipgloss.JoinVertical(
			lipgloss.Right,
			lipgloss.JoinHorizontal(lipgloss.Center, files, t.Render()),
			ranks,
		) + "\n",
	)
}


================================================
FILE: examples/table/demo.tape
================================================
Output table.gif

Set Height 900
Set Width 1600
Set Padding 80
Set FontSize 42

Hide
Type "go build -o table"
Enter
Ctrl+L
Show

Sleep 0.5s
Type "clear && ./table"
Sleep 0.5s
Enter
Sleep 1s

Screenshot "table.png"

Sleep 1s

Hide
Type "rm table"
Enter
Show

Sleep 1s


================================================
FILE: examples/table/languages/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/table"
)

const (
	purple    = "99"
	gray      = "245"
	lightGray = "241"
)

func main() {
	var (
		// HeaderStyle is the lipgloss style used for the table headers.
		HeaderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(purple)).Bold(true).Align(lipgloss.Center)
		// CellStyle is the base lipgloss style used for the table rows.
		CellStyle = lipgloss.NewStyle().Padding(0, 1).Width(14)
		// OddRowStyle is the lipgloss style used for odd-numbered table rows.
		OddRowStyle = CellStyle.Foreground(lipgloss.Color(gray))
		// EvenRowStyle is the lipgloss style used for even-numbered table rows.
		EvenRowStyle = CellStyle.Foreground(lipgloss.Color(lightGray))
		// BorderStyle is the lipgloss style used for the table border.
		BorderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(purple))
	)

	rows := [][]string{
		{"Chinese", "您好", "你好"},
		{"Japanese", "こんにちは", "やあ"},
		{"Arabic", "أهلين", "أهلا"},
		{"Russian", "Здравствуйте", "Привет"},
		{"Spanish", "Hola", "¿Qué tal?"},
	}

	t := table.New().
		Border(lipgloss.ThickBorder()).
		BorderStyle(BorderStyle).
		StyleFunc(func(row, col int) lipgloss.Style {
			var style lipgloss.Style

			switch {
			case row == table.HeaderRow:
				return HeaderStyle
			case row%2 == 0:
				style = EvenRowStyle
			default:
				style = OddRowStyle
			}

			// Make the second column a little wider.
			if col == 1 {
				style = style.Width(22)
			}

			// Arabic is a right-to-left language, so right align the text.
			if row < len(rows) && rows[row][0] == "Arabic" && col != 0 {
				style = style.Align(lipgloss.Right)
			}

			return style
		}).
		Headers("LANGUAGE", "FORMAL", "INFORMAL").
		Rows(rows...)

	t.Row("English", "You look absolutely fabulous.", "How's it going?")

	lipgloss.Println(t)
}


================================================
FILE: examples/table/mindy/main.go
================================================
package main

import (
	"fmt"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/table"
)

func main() {
	labelStyle := lipgloss.NewStyle().Width(3).Align(lipgloss.Right)
	swatchStyle := lipgloss.NewStyle().Width(6)

	data := [][]string{}
	for i := 0; i < 13; i += 8 {
		data = append(data, makeRow(i, i+5))
	}
	data = append(data, makeEmptyRow())
	for i := 6; i < 15; i += 8 {
		data = append(data, makeRow(i, i+1))
	}
	data = append(data, makeEmptyRow())
	for i := 16; i < 231; i += 6 {
		data = append(data, makeRow(i, i+5))
	}
	data = append(data, makeEmptyRow())
	for i := 232; i < 256; i += 6 {
		data = append(data, makeRow(i, i+5))
	}

	t := table.New().
		Border(lipgloss.HiddenBorder()).
		Rows(data...).
		StyleFunc(func(row, col int) lipgloss.Style {
			color := lipgloss.Color(fmt.Sprint(data[row][col-col%2]))
			switch col % 2 {
			case 0:
				return labelStyle.Foreground(color)
			default:
				return swatchStyle.Background(color)
			}
		})

	lipgloss.Println(t)
}

const rowLength = 12

func makeRow(start, end int) []string {
	var row []string
	for i := start; i <= end; i++ {
		row = append(row, fmt.Sprint(i))
		row = append(row, "")
	}
	for i := len(row); i < rowLength; i++ {
		row = append(row, "")
	}
	return row
}

func makeEmptyRow() []string {
	return makeRow(0, -1)
}


================================================
FILE: examples/table/pokemon/main.go
================================================
package main

import (
	"fmt"
	"image/color"
	"strings"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/table"
)

func main() {
	baseStyle := lipgloss.NewStyle().Padding(0, 1)
	headerStyle := baseStyle.Foreground(lipgloss.Color("252")).Bold(true)
	selectedStyle := baseStyle.Foreground(lipgloss.Color("#01BE85")).Background(lipgloss.Color("#00432F"))
	typeColors := map[string]color.Color{
		"Bug":      lipgloss.Color("#D7FF87"),
		"Electric": lipgloss.Color("#FDFF90"),
		"Fire":     lipgloss.Color("#FF7698"),
		"Flying":   lipgloss.Color("#FF87D7"),
		"Grass":    lipgloss.Color("#75FBAB"),
		"Ground":   lipgloss.Color("#FF875F"),
		"Normal":   lipgloss.Color("#929292"),
		"Poison":   lipgloss.Color("#7D5AFC"),
		"Water":    lipgloss.Color("#00E2C7"),
	}
	dimTypeColors := map[string]color.Color{
		"Bug":      lipgloss.Color("#97AD64"),
		"Electric": lipgloss.Color("#FCFF5F"),
		"Fire":     lipgloss.Color("#BA5F75"),
		"Flying":   lipgloss.Color("#C97AB2"),
		"Grass":    lipgloss.Color("#59B980"),
		"Ground":   lipgloss.Color("#C77252"),
		"Normal":   lipgloss.Color("#727272"),
		"Poison":   lipgloss.Color("#634BD0"),
		"Water":    lipgloss.Color("#439F8E"),
	}

	headers := []string{"#", "Name", "Type 1", "Type 2", "Japanese", "Official Rom."}
	data := [][]string{
		{"1", "Bulbasaur", "Grass", "Poison", "フシギダネ", "Fushigidane"},
		{"2", "Ivysaur", "Grass", "Poison", "フシギソウ", "Fushigisou"},
		{"3", "Venusaur", "Grass", "Poison", "フシギバナ", "Fushigibana"},
		{"4", "Charmander", "Fire", "", "ヒトカゲ", "Hitokage"},
		{"5", "Charmeleon", "Fire", "", "リザード", "Lizardo"},
		{"6", "Charizard", "Fire", "Flying", "リザードン", "Lizardon"},
		{"7", "Squirtle", "Water", "", "ゼニガメ", "Zenigame"},
		{"8", "Wartortle", "Water", "", "カメール", "Kameil"},
		{"9", "Blastoise", "Water", "", "カメックス", "Kamex"},
		{"10", "Caterpie", "Bug", "", "キャタピー", "Caterpie"},
		{"11", "Metapod", "Bug", "", "トランセル", "Trancell"},
		{"12", "Butterfree", "Bug", "Flying", "バタフリー", "Butterfree"},
		{"13", "Weedle", "Bug", "Poison", "ビードル", "Beedle"},
		{"14", "Kakuna", "Bug", "Poison", "コクーン", "Cocoon"},
		{"15", "Beedrill", "Bug", "Poison", "スピアー", "Spear"},
		{"16", "Pidgey", "Normal", "Flying", "ポッポ", "Poppo"},
		{"17", "Pidgeotto", "Normal", "Flying", "ピジョン", "Pigeon"},
		{"18", "Pidgeot", "Normal", "Flying", "ピジョット", "Pigeot"},
		{"19", "Rattata", "Normal", "", "コラッタ", "Koratta"},
		{"20", "Raticate", "Normal", "", "ラッタ", "Ratta"},
		{"21", "Spearow", "Normal", "Flying", "オニスズメ", "Onisuzume"},
		{"22", "Fearow", "Normal", "Flying", "オニドリル", "Onidrill"},
		{"23", "Ekans", "Poison", "", "アーボ", "Arbo"},
		{"24", "Arbok", "Poison", "", "アーボック", "Arbok"},
		{"25", "Pikachu", "Electric", "", "ピカチュウ", "Pikachu"},
		{"26", "Raichu", "Electric", "", "ライチュウ", "Raichu"},
		{"27", "Sandshrew", "Ground", "", "サンド", "Sand"},
		{"28", "Sandslash", "Ground", "", "サンドパン", "Sandpan"},
	}

	CapitalizeHeaders := func(data []string) []string {
		for i := range data {
			data[i] = strings.ToUpper(data[i])
		}
		return data
	}

	t := table.New().
		Border(lipgloss.NormalBorder()).
		BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("238"))).
		Headers(CapitalizeHeaders(headers)...).
		Width(80).
		Rows(data...).
		StyleFunc(func(row, col int) lipgloss.Style {
			if row == table.HeaderRow {
				return headerStyle
			}

			if data[row][1] == "Pikachu" {
				return selectedStyle
			}

			even := row%2 == 0

			switch col {
			case 2, 3: // Type 1 + 2
				c := typeColors
				if even {
					c = dimTypeColors
				}

				color := c[fmt.Sprint(data[row][col])]
				return baseStyle.Foreground(color)
			}

			if even {
				return baseStyle.Foreground(lipgloss.Color("245"))
			}
			return baseStyle.Foreground(lipgloss.Color("252"))
		})

	lipgloss.Println(t)
}


================================================
FILE: examples/tree/background/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

func main() {
	darkBg := lipgloss.NewStyle().
		Background(lipgloss.Color("0")).
		Padding(0, 1)

	headerItemStyle := lipgloss.NewStyle().
		Background(lipgloss.Color("#ee6ff8")).
		Foreground(lipgloss.Color("#ecfe65")).
		Bold(true).
		Padding(0, 1)

	itemStyle := headerItemStyle.Background(lipgloss.Color("0"))

	t := tree.Root("# Table of Contents").
		RootStyle(itemStyle).
		ItemStyle(itemStyle).
		EnumeratorStyle(darkBg).
		IndenterStyle(darkBg).
		Child(
			tree.Root("## Chapter 1").
				Child("Chapter 1.1").
				Child("Chapter 1.2"),
		).
		Child(
			tree.Root("## Chapter 2").
				Child("Chapter 2.1").
				Child("Chapter 2.2"),
		)

	lipgloss.Println(t)
}


================================================
FILE: examples/tree/files/main.go
================================================
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

func addBranches(root *tree.Tree, path string) error {
	items, err := os.ReadDir(path)
	if err != nil {
		return err
	}

	for _, item := range items {
		if item.IsDir() {
			// It's a directory.

			// Skip directories that start with a dot.
			if strings.HasPrefix(item.Name(), ".") {
				continue
			}

			treeBranch := tree.Root(item.Name())
			root.Child(treeBranch)

			// Recurse.
			branchPath := filepath.Join(path, item.Name())
			if err := addBranches(treeBranch, branchPath); err != nil {
				return err
			}
		} else {
			// It's a file.

			// Skip files that start with a dot.
			if strings.HasPrefix(item.Name(), ".") {
				continue
			}

			root.Child(item.Name())
		}
	}

	return nil
}

func main() {
	enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240")).PaddingRight(1)
	itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).Bold(true).PaddingRight(1)

	pwd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error getting current working directory: %v\n", err)
		os.Exit(1)
	}

	t := tree.Root(pwd).
		IndenterStyle(enumeratorStyle).
		EnumeratorStyle(enumeratorStyle).
		RootStyle(itemStyle).
		ItemStyle(itemStyle)

	if err := addBranches(t, "."); err != nil {
		fmt.Fprintf(os.Stderr, "Error building tree: %v\n", err)
		os.Exit(1)
	}

	lipgloss.Println(t)
}


================================================
FILE: examples/tree/makeup/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

func main() {
	enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1)
	rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35"))
	itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212"))

	t := tree.
		Root("⁜ Makeup").
		Child(
			"Glossier",
			"Fenty Beauty",
			tree.New().Child(
				"Gloss Bomb Universal Lip Luminizer",
				"Hot Cheeks Velour Blushlighter",
			),
			"Nyx",
			"Mac",
			"Milk",
		).
		Enumerator(tree.RoundedEnumerator).
		EnumeratorStyle(enumeratorStyle).
		IndenterStyle(enumeratorStyle).
		RootStyle(rootStyle).
		ItemStyle(itemStyle)

	lipgloss.Println(t)
}


================================================
FILE: examples/tree/rounded/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

func main() {
	itemStyle := lipgloss.NewStyle().MarginRight(1)
	enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("8")).MarginRight(1)

	t := tree.Root("Groceries").
		Child(
			tree.Root("Fruits").
				Child(
					"Blood Orange",
					"Papaya",
					"Dragonfruit",
					"Yuzu",
				),
			tree.Root("Items").
				Child(
					"Cat Food",
					"Nutella",
					"Powdered Sugar",
				),
			tree.Root("Veggies").
				Child(
					"Leek",
					"Artichoke",
				),
		).ItemStyle(itemStyle).
		EnumeratorStyle(enumeratorStyle).
		Enumerator(tree.RoundedEnumerator).
		IndenterStyle(enumeratorStyle)

	lipgloss.Println(t)
}


================================================
FILE: examples/tree/selection/main.go
================================================
package main

import (
	"fmt"
	"path"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

const selected = "/Users/bash/.config/doom-emacs"

type styles struct {
	base,
	container,
	dir,
	selected,
	dimmed,
	toggle lipgloss.Style
}

func defaultStyles() styles {
	var s styles
	s.base = lipgloss.NewStyle()
	s.container = s.base.
		Margin(1, 2).
		Padding(1, 0)
	s.dir = s.base.
		Inline(true)
	s.toggle = s.base.
		Foreground(lipgloss.Color("5")).
		PaddingRight(1)
	s.selected = s.base.
		Background(lipgloss.Color("8")).
		Foreground(lipgloss.Color("207")).
		Bold(true)
	s.dimmed = s.base.
		Foreground(lipgloss.Color("241"))
	return s
}

type dir struct {
	name   string
	open   bool
	styles styles
}

func (d dir) String() string {
	t := d.styles.toggle.PaddingLeft(1).Render
	n := d.styles.dir.Render
	if d.open {
		return t("▼") + n(d.name)
	}
	return t("▶") + n(d.name)
}

// file implements the Node interface.
type file struct {
	name   string
	styles styles
}

func (s file) String() string {
	return path.Base(s.name)
}

func (s file) Hidden() bool {
	return false
}

func (s file) Children() tree.Children {
	return tree.NodeChildren(nil)
}

func (s file) Value() string {
	return s.String()
}

func (s file) SetValue(val any) {
	return
}

func (s file) SetHidden(val bool) {
	return
}

func isItemSelected(children tree.Children, index int) bool {
	child := children.At(index)
	if file, ok := child.(file); ok && file.name == selected {
		return true
	}

	return false
}

func itemStyle(children tree.Children, index int) lipgloss.Style {
	s := defaultStyles()
	if isItemSelected(children, index) {
		return s.selected
	}

	return s.base
}

func indenterStyle(children tree.Children, index int) lipgloss.Style {
	s := defaultStyles()
	if isItemSelected(children, index) {
		return s.dimmed.Background(s.selected.GetBackground())
	}

	return s.dimmed
}

func main() {
	s := defaultStyles()

	t := tree.Root(dir{"~/charm", true, s}).
		Child(
			dir{"ayman", false, s},
			tree.Root(dir{"bash", true, s}).
				Child(
					file{"/Users/bash/.config/doom-emacs", s},
				),
			tree.Root(dir{"carlos", true, s}).
				Child(
					tree.Root(dir{"emotes", true, s}).
						Child(
							file{"/home/caarlos0/Pictures/chefkiss.png", s},
							file{"/home/caarlos0/Pictures/kekw.png", s},
						),
				),
			dir{"maas", false, s},
		).
		Width(30).
		Indenter(Indenter).
		Enumerator(Enumerator).
		EnumeratorStyleFunc(indenterStyle).
		IndenterStyleFunc(indenterStyle).
		ItemStyleFunc(itemStyle)

	fmt.Println(s.container.Render(t.String()))
}

func Enumerator(children tree.Children, index int) string {
	return " │ "
}

func Indenter(children tree.Children, index int) string {
	return " │ "
}


================================================
FILE: examples/tree/simple/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

func main() {
	t := tree.Root(".").
		Child("macOS").
		Child(
			tree.New().
				Root("Linux").
				Child("NixOS").
				Child("Arch Linux (btw)").
				Child("Void Linux"),
		).
		Child(
			tree.New().
				Root("BSD").
				Child("FreeBSD").
				Child("OpenBSD"),
		)

	lipgloss.Println(t)
}


================================================
FILE: examples/tree/styles/main.go
================================================
package main

import (
	"fmt"

	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

func main() {
	purple := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
	pink := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)

	t := tree.New().
		Child(
			"Glossier",
			"Claire’s Boutique",
			tree.Root("Nyx").
				Child("Lip Gloss", "Foundation").
				EnumeratorStyle(pink).
				IndenterStyle(purple),
			"Mac",
			"Milk",
		).
		EnumeratorStyle(purple).
		IndenterStyle(purple)
	fmt.Println(t)
}


================================================
FILE: examples/tree/toggle/main.go
================================================
package main

import (
	"charm.land/lipgloss/v2"
	"charm.land/lipgloss/v2/tree"
)

type styles struct {
	base,
	block,
	pink,
	dir,
	toggle,
	file lipgloss.Style
}

func defaultStyles() styles {
	var s styles
	s.base = lipgloss.NewStyle().
		Background(lipgloss.Color("57")).
		Foreground(lipgloss.Color("225"))
	s.block = s.base.
		Padding(1, 3).
		Margin(1, 3).
		Width(40)
	s.pink = s.base.
		Foreground(lipgloss.Color("212")).
		PaddingRight(1)
	s.di
Download .txt
gitextract_jquxj2jk/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── build.yml
│       ├── coverage.yml
│       ├── dependabot-sync.yml
│       ├── lint-sync.yml
│       ├── lint.yml
│       └── release.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── LICENSE
├── README.md
├── Taskfile.yaml
├── UPGRADE_GUIDE_V2.md
├── align.go
├── align_test.go
├── ansi_unix.go
├── ansi_windows.go
├── blending.go
├── blending_test.go
├── borders.go
├── borders_test.go
├── canvas.go
├── canvas_test.go
├── color.go
├── color_test.go
├── compat/
│   ├── color.go
│   └── doc.go
├── examples/
│   ├── blending/
│   │   ├── border-blend-rotation/
│   │   │   └── bubbletea/
│   │   │       └── main.go
│   │   ├── linear-1d/
│   │   │   ├── bubbletea/
│   │   │   │   └── main.go
│   │   │   └── standalone/
│   │   │       └── main.go
│   │   └── linear-2d/
│   │       ├── bubbletea/
│   │       │   └── main.go
│   │       └── standalone/
│   │           └── main.go
│   ├── brightness/
│   │   └── main.go
│   ├── canvas/
│   │   └── main.go
│   ├── color/
│   │   ├── bubbletea/
│   │   │   └── main.go
│   │   └── standalone/
│   │       └── main.go
│   ├── compat/
│   │   ├── bubbletea/
│   │   │   └── main.go
│   │   └── standalone/
│   │       └── main.go
│   ├── go.mod
│   ├── go.sum
│   ├── layout/
│   │   └── main.go
│   ├── list/
│   │   ├── duckduckgoose/
│   │   │   └── main.go
│   │   ├── glow/
│   │   │   └── main.go
│   │   ├── grocery/
│   │   │   └── main.go
│   │   ├── roman/
│   │   │   └── main.go
│   │   ├── simple/
│   │   │   └── main.go
│   │   └── sublist/
│   │       └── main.go
│   ├── ssh/
│   │   └── main.go
│   ├── table/
│   │   ├── ansi/
│   │   │   └── main.go
│   │   ├── chess/
│   │   │   └── main.go
│   │   ├── demo.tape
│   │   ├── languages/
│   │   │   └── main.go
│   │   ├── mindy/
│   │   │   └── main.go
│   │   └── pokemon/
│   │       └── main.go
│   └── tree/
│       ├── background/
│       │   └── main.go
│       ├── files/
│       │   └── main.go
│       ├── makeup/
│       │   └── main.go
│       ├── rounded/
│       │   └── main.go
│       ├── selection/
│       │   └── main.go
│       ├── simple/
│       │   └── main.go
│       ├── styles/
│       │   └── main.go
│       └── toggle/
│           └── main.go
├── get.go
├── go.mod
├── go.sum
├── join.go
├── join_test.go
├── layer.go
├── lipgloss.go
├── list/
│   ├── enumerator.go
│   ├── list.go
│   ├── list_test.go
│   └── testdata/
│       ├── TestComplexSublist.golden
│       ├── TestEnumerators/
│       │   ├── alphabet.golden
│       │   ├── arabic.golden
│       │   ├── asterisk.golden
│       │   ├── bullet.golden
│       │   ├── dash.golden
│       │   └── roman.golden
│       ├── TestEnumeratorsAlign.golden
│       ├── TestEnumeratorsTransform/
│       │   ├── alphabet_lower.golden
│       │   ├── arabic).golden
│       │   ├── bullet_is_dash.golden
│       │   └── roman_within_().golden
│       ├── TestList.golden
│       ├── TestListIntegers.golden
│       ├── TestListItems.golden
│       ├── TestMultiline.golden
│       ├── TestSubListItems2.golden
│       ├── TestSublist.golden
│       └── TestSublistItems.golden
├── position.go
├── query.go
├── ranges.go
├── ranges_test.go
├── runes.go
├── runes_test.go
├── set.go
├── size.go
├── size_test.go
├── style.go
├── style_test.go
├── table/
│   ├── resizing.go
│   ├── rows.go
│   ├── table.go
│   ├── table_test.go
│   ├── testdata/
│   │   ├── TestBorderColumnsWithExtraRows.golden
│   │   ├── TestBorderStyles/
│   │   │   ├── ASCIIBorder.golden
│   │   │   ├── BlockBorder.golden
│   │   │   ├── HiddenBorder.golden
│   │   │   ├── MarkdownBorder.golden
│   │   │   ├── NormalBorder.golden
│   │   │   ├── RoundedBorder.golden
│   │   │   └── ThickBorder.golden
│   │   ├── TestBorderedCells.golden
│   │   ├── TestCarriageReturn.golden
│   │   ├── TestContentWrapping/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongRowContentNoWrap.golden
│   │   │   ├── LongRowContentNoWrapCustomMargins.golden
│   │   │   ├── LongRowContentNoWrapNoMargins.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestContentWrapping_ColumnWidth/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestContentWrapping_WithHeight/
│   │   │   └── LongHeaderContentLongAndShortRows/
│   │   │       ├── HeightOf05.golden
│   │   │       ├── HeightOf15.golden
│   │   │       ├── HeightOf25.golden
│   │   │       └── HeightOf35.golden
│   │   ├── TestContentWrapping_WithMargins/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestContentWrapping_WithPadding/
│   │   │   ├── LongHeaderContentLongAndShortRows.golden
│   │   │   ├── LongRowContent.golden
│   │   │   ├── LongTextDifferentLanguages.golden
│   │   │   └── MissingRowContent.golden
│   │   ├── TestExtraPaddingHeading.golden
│   │   ├── TestExtraPaddingHeadingLong.golden
│   │   ├── TestFilter.golden
│   │   ├── TestFilterInverse.golden
│   │   ├── TestInnerBordersOnly.golden
│   │   ├── TestMoreCellsThanHeaders.golden
│   │   ├── TestMoreCellsThanHeadersExtra.golden
│   │   ├── TestNoFinalEmptyRowWhenOverflow.golden
│   │   ├── TestStyleFunc/
│   │   │   ├── MarginAndPaddingSet.golden
│   │   │   └── RightAlignedTextWithMargins.golden
│   │   ├── TestTable.golden
│   │   ├── TestTableANSI.golden
│   │   ├── TestTableBorder.golden
│   │   ├── TestTableEmpty.golden
│   │   ├── TestTableExample.golden
│   │   ├── TestTableHeightExact.golden
│   │   ├── TestTableHeightExtra.golden
│   │   ├── TestTableHeightShrink/
│   │   │   ├── NoBorderRow/
│   │   │   │   ├── HeightOf01.golden
│   │   │   │   ├── HeightOf02.golden
│   │   │   │   ├── HeightOf03.golden
│   │   │   │   ├── HeightOf04.golden
│   │   │   │   ├── HeightOf05.golden
│   │   │   │   ├── HeightOf06.golden
│   │   │   │   ├── HeightOf07.golden
│   │   │   │   ├── HeightOf08.golden
│   │   │   │   └── HeightOf09.golden
│   │   │   ├── NoBorderRowPadding/
│   │   │   │   ├── HeightOf01.golden
│   │   │   │   ├── HeightOf02.golden
│   │   │   │   ├── HeightOf03.golden
│   │   │   │   ├── HeightOf04.golden
│   │   │   │   ├── HeightOf05.golden
│   │   │   │   ├── HeightOf06.golden
│   │   │   │   ├── HeightOf07.golden
│   │   │   │   ├── HeightOf08.golden
│   │   │   │   ├── HeightOf09.golden
│   │   │   │   ├── HeightOf10.golden
│   │   │   │   ├── HeightOf11.golden
│   │   │   │   ├── HeightOf12.golden
│   │   │   │   ├── HeightOf13.golden
│   │   │   │   ├── HeightOf14.golden
│   │   │   │   ├── HeightOf15.golden
│   │   │   │   ├── HeightOf16.golden
│   │   │   │   ├── HeightOf17.golden
│   │   │   │   ├── HeightOf18.golden
│   │   │   │   ├── HeightOf19.golden
│   │   │   │   ├── HeightOf20.golden
│   │   │   │   └── HeightOf21.golden
│   │   │   ├── WithBorderRow/
│   │   │   │   ├── HeightOf01.golden
│   │   │   │   ├── HeightOf02.golden
│   │   │   │   ├── HeightOf03.golden
│   │   │   │   ├── HeightOf04.golden
│   │   │   │   ├── HeightOf05.golden
│   │   │   │   ├── HeightOf06.golden
│   │   │   │   ├── HeightOf07.golden
│   │   │   │   ├── HeightOf08.golden
│   │   │   │   ├── HeightOf09.golden
│   │   │   │   ├── HeightOf10.golden
│   │   │   │   ├── HeightOf11.golden
│   │   │   │   ├── HeightOf12.golden
│   │   │   │   └── HeightOf13.golden
│   │   │   └── WithBorderRowPadding/
│   │   │       ├── HeightOf01.golden
│   │   │       ├── HeightOf02.golden
│   │   │       ├── HeightOf03.golden
│   │   │       ├── HeightOf04.golden
│   │   │       ├── HeightOf05.golden
│   │   │       ├── HeightOf06.golden
│   │   │       ├── HeightOf07.golden
│   │   │       ├── HeightOf08.golden
│   │   │       ├── HeightOf09.golden
│   │   │       ├── HeightOf10.golden
│   │   │       ├── HeightOf11.golden
│   │   │       ├── HeightOf12.golden
│   │   │       ├── HeightOf13.golden
│   │   │       ├── HeightOf14.golden
│   │   │       ├── HeightOf15.golden
│   │   │       ├── HeightOf16.golden
│   │   │       ├── HeightOf17.golden
│   │   │       ├── HeightOf18.golden
│   │   │       ├── HeightOf19.golden
│   │   │       ├── HeightOf20.golden
│   │   │       ├── HeightOf21.golden
│   │   │       ├── HeightOf22.golden
│   │   │       ├── HeightOf23.golden
│   │   │       ├── HeightOf24.golden
│   │   │       └── HeightOf25.golden
│   │   ├── TestTableHeightWithYOffset.golden
│   │   ├── TestTableHeights.golden
│   │   ├── TestTableMarginAndRightAlignment.golden
│   │   ├── TestTableMultiLineRowSeparator.golden
│   │   ├── TestTableNoColumnSeparators.golden
│   │   ├── TestTableNoColumnSeparatorsWithHeaders.golden
│   │   ├── TestTableNoHeaders.golden
│   │   ├── TestTableNoStyleFunc.golden
│   │   ├── TestTableOverFlowNoWrap.golden
│   │   ├── TestTableRowSeparators/
│   │   │   ├── no_overflow.golden
│   │   │   └── with_overflow.golden
│   │   ├── TestTableRowSeparators.golden
│   │   ├── TestTableSetRows.golden
│   │   ├── TestTableShrinkWithYOffset/
│   │   │   ├── NoHeaders.golden
│   │   │   ├── WithBorderRow.golden
│   │   │   └── WithHeaders.golden
│   │   ├── TestTableUnsetBorders.golden
│   │   ├── TestTableUnsetHeaderSeparator.golden
│   │   ├── TestTableUnsetHeaderSeparatorWithBorder.golden
│   │   ├── TestTableWidthExpand.golden
│   │   ├── TestTableWidthShrink/
│   │   │   ├── DefaultBorders.golden
│   │   │   ├── NoBorders.golden
│   │   │   └── OutlineBordersOnly.golden
│   │   ├── TestTableWidthSmartCrop.golden
│   │   ├── TestTableWidthSmartCropExtensive.golden
│   │   ├── TestTableWidthSmartCropTiny.golden
│   │   ├── TestTableWidths.golden
│   │   ├── TestTableWithBackground.golden
│   │   ├── TestTableYOffset.golden
│   │   ├── TestWrapPreStyledContent.golden
│   │   └── TestWrapStyleFuncContent.golden
│   └── util.go
├── terminal.go
├── tree/
│   ├── children.go
│   ├── enumerator.go
│   ├── example_test.go
│   ├── renderer.go
│   ├── testdata/
│   │   ├── TestAddItemWithAndWithoutRoot/
│   │   │   ├── with_root.golden
│   │   │   └── without_root.golden
│   │   ├── TestEmbedListWithinTree.golden
│   │   ├── TestFilter.golden
│   │   ├── TestMultilinePrefix.golden
│   │   ├── TestMultilinePrefixInception.golden
│   │   ├── TestMultilinePrefixSubtree.golden
│   │   ├── TestRootStyle.golden
│   │   ├── TestTree/
│   │   │   ├── after.golden
│   │   │   └── before.golden
│   │   ├── TestTreeAddTwoSubTreesWithoutName.golden
│   │   ├── TestTreeAllHidden.golden
│   │   ├── TestTreeCustom.golden
│   │   ├── TestTreeHidden.golden
│   │   ├── TestTreeLastNodeIsSubTree.golden
│   │   ├── TestTreeMixedEnumeratorSize.golden
│   │   ├── TestTreeMultilineNode.golden
│   │   ├── TestTreeNil.golden
│   │   ├── TestTreeRoot.golden
│   │   ├── TestTreeStartsWithSubtree.golden
│   │   ├── TestTreeStyleAt.golden
│   │   ├── TestTreeStyleNilFuncs.golden
│   │   ├── TestTreeSubTreeWithCustomEnumerator.golden
│   │   ├── TestTreeTable.golden
│   │   └── TestTypes.golden
│   ├── tree.go
│   └── tree_test.go
├── unset.go
├── whitespace.go
├── whitespace_test.go
├── wrap.go
└── writer.go
Download .txt
SYMBOL INDEX (876 symbols across 80 files)

FILE: align.go
  function alignTextHorizontal (line 12) | func alignTextHorizontal(str string, pos Position, width int, style *ans...
  function alignTextVertical (line 61) | func alignTextVertical(str string, pos Position, height int, _ *ansi.Sty...

FILE: align_test.go
  function TestAlignTextVertical (line 5) | func TestAlignTextVertical(t *testing.T) {

FILE: ansi_unix.go
  function EnableLegacyWindowsANSI (line 8) | func EnableLegacyWindowsANSI(*os.File) {}

FILE: ansi_windows.go
  function EnableLegacyWindowsANSI (line 15) | func EnableLegacyWindowsANSI(f *os.File) {

FILE: blending.go
  function Blend1D (line 18) | func Blend1D(steps int, stops ...color.Color) []color.Color {
  function Blend2D (line 114) | func Blend2D(width, height int, angle float64, stops ...color.Color) []c...

FILE: blending_test.go
  function TestBlend1D (line 8) | func TestBlend1D(t *testing.T) {
  function TestBlend2D (line 153) | func TestBlend2D(t *testing.T) {
  function TestBlend2DEdgeCases (line 296) | func TestBlend2DEdgeCases(t *testing.T) {
  function BenchmarkBlend1D (line 328) | func BenchmarkBlend1D(b *testing.B) {
  function BenchmarkBlend2D (line 342) | func BenchmarkBlend2D(b *testing.B) {

FILE: borders.go
  type Border (line 16) | type Border struct
    method GetTopSize (line 35) | func (b Border) GetTopSize() int {
    method GetRightSize (line 42) | func (b Border) GetRightSize() int {
    method GetBottomSize (line 49) | func (b Border) GetBottomSize() int {
    method GetLeftSize (line 56) | func (b Border) GetLeftSize() int {
  function getBorderEdgeWidth (line 60) | func getBorderEdgeWidth(borderParts ...string) (maxWidth int) {
  function NormalBorder (line 223) | func NormalBorder() Border {
  function RoundedBorder (line 228) | func RoundedBorder() Border {
  function BlockBorder (line 233) | func BlockBorder() Border {
  function OuterHalfBlockBorder (line 238) | func OuterHalfBlockBorder() Border {
  function InnerHalfBlockBorder (line 243) | func InnerHalfBlockBorder() Border {
  function ThickBorder (line 249) | func ThickBorder() Border {
  function DoubleBorder (line 254) | func DoubleBorder() Border {
  function HiddenBorder (line 262) | func HiddenBorder() Border {
  function MarkdownBorder (line 272) | func MarkdownBorder() Border {
  function ASCIIBorder (line 277) | func ASCIIBorder() Border {
  type borderBlend (line 281) | type borderBlend struct
  method borderBlend (line 288) | func (s Style) borderBlend(width, height int, colors ...color.Color) *bo...
  method applyBorder (line 327) | func (s Style) applyBorder(str string) string {
  function renderHorizontalEdge (line 498) | func renderHorizontalEdge(left, middle, right string, width int) string {
  method styleBorder (line 527) | func (s Style) styleBorder(border string, fg, bg color.Color) string {
  method styleBorderBlend (line 542) | func (s Style) styleBorderBlend(border string, fg []color.Color, bg colo...
  function maxRuneWidth (line 564) | func maxRuneWidth(str string) int {
  function getFirstRuneAsString (line 581) | func getFirstRuneAsString(str string) string {

FILE: borders_test.go
  function BenchmarkBorderRendering (line 9) | func BenchmarkBorderRendering(b *testing.B) {
  function BenchmarkBorderBlend (line 40) | func BenchmarkBorderBlend(b *testing.B) {
  function BenchmarkBorderRenderingNoColors (line 74) | func BenchmarkBorderRenderingNoColors(b *testing.B) {
  function getFirstRuneAsStringOld (line 104) | func getFirstRuneAsStringOld(str string) string {
  function TestGetFirstRuneAsString (line 112) | func TestGetFirstRuneAsString(t *testing.T) {
  function BenchmarkGetFirstRuneAsString (line 146) | func BenchmarkGetFirstRuneAsString(b *testing.B) {
  function BenchmarkMaxRuneWidth (line 180) | func BenchmarkMaxRuneWidth(b *testing.B) {
  function maxRuneWidthOld (line 211) | func maxRuneWidthOld(str string) int {

FILE: canvas.go
  type Canvas (line 17) | type Canvas struct
    method Resize (line 32) | func (c *Canvas) Resize(width, height int) {
    method Clear (line 37) | func (c *Canvas) Clear() {
    method Bounds (line 42) | func (c *Canvas) Bounds() uv.Rectangle {
    method Width (line 47) | func (c *Canvas) Width() int {
    method Height (line 52) | func (c *Canvas) Height() int {
    method CellAt (line 57) | func (c *Canvas) CellAt(x int, y int) *uv.Cell {
    method SetCell (line 62) | func (c *Canvas) SetCell(x int, y int, cell *uv.Cell) {
    method WidthMethod (line 67) | func (c *Canvas) WidthMethod() uv.WidthMethod {
    method Compose (line 72) | func (c *Canvas) Compose(drawer uv.Drawable) *Canvas {
    method Draw (line 81) | func (c *Canvas) Draw(scr uv.Screen, area uv.Rectangle) {
    method Render (line 86) | func (c *Canvas) Render() string {
  function NewCanvas (line 24) | func NewCanvas(width, height int) *Canvas {

FILE: canvas_test.go
  function TestCanvasRender (line 8) | func TestCanvasRender(t *testing.T) {
  function TestCanvasRenderWithTrailingSpaces (line 38) | func TestCanvasRenderWithTrailingSpaces(t *testing.T) {

FILE: color.go
  function clamp (line 15) | func clamp[T cmp.Ordered](v, low, high T) T {
  constant Black (line 24) | Black ansi.BasicColor = iota
  constant Red (line 25) | Red
  constant Green (line 26) | Green
  constant Yellow (line 27) | Yellow
  constant Blue (line 28) | Blue
  constant Magenta (line 29) | Magenta
  constant Cyan (line 30) | Cyan
  constant White (line 31) | White
  constant BrightBlack (line 33) | BrightBlack
  constant BrightRed (line 34) | BrightRed
  constant BrightGreen (line 35) | BrightGreen
  constant BrightYellow (line 36) | BrightYellow
  constant BrightBlue (line 37) | BrightBlue
  constant BrightMagenta (line 38) | BrightMagenta
  constant BrightCyan (line 39) | BrightCyan
  constant BrightWhite (line 40) | BrightWhite
  type NoColor (line 52) | type NoColor struct
    method RGBA (line 59) | func (n NoColor) RGBA() (r, g, b, a uint32) {
  function Color (line 68) | func Color(s string) color.Color {
  function parseHex (line 102) | func parseHex(s string) (c color.RGBA, err error) {
  type RGBColor (line 138) | type RGBColor struct
    method RGBA (line 146) | func (c RGBColor) RGBA() (r, g, b, a uint32) {
  type LightDarkFunc (line 174) | type LightDarkFunc
  function LightDark (line 205) | func LightDark(isDark bool) LightDarkFunc {
  function isDarkColor (line 225) | func isDarkColor(c color.Color) bool {
  type CompleteFunc (line 250) | type CompleteFunc
  function Complete (line 265) | func Complete(p colorprofile.Profile) CompleteFunc {
  function ensureNotTransparent (line 282) | func ensureNotTransparent(c color.Color) color.Color {
  function Alpha (line 292) | func Alpha(c color.Color, alpha float64) color.Color {
  function Complementary (line 308) | func Complementary(c color.Color) color.Color {
  function Darken (line 328) | func Darken(c color.Color, percent float64) color.Color {
  function Lighten (line 345) | func Lighten(c color.Color, percent float64) color.Color {

FILE: color_test.go
  function hex (line 10) | func hex(hex string) color.Color {
  function expectColorMatches (line 18) | func expectColorMatches(t *testing.T, got, want color.Color) {
  function rgbaString (line 40) | func rgbaString(t *testing.T, c color.Color) string {
  function TestHexToColor (line 51) | func TestHexToColor(t *testing.T) {
  function TestRGBA (line 85) | func TestRGBA(t *testing.T) {
  function TestParseHex (line 119) | func TestParseHex(t *testing.T) {
  function TestAlpha (line 176) | func TestAlpha(t *testing.T) {
  function TestComplementary (line 235) | func TestComplementary(t *testing.T) {
  function TestDarken (line 266) | func TestDarken(t *testing.T) {
  function TestLighten (line 290) | func TestLighten(t *testing.T) {

FILE: compat/color.go
  type AdaptiveColor (line 26) | type AdaptiveColor struct
    method RGBA (line 33) | func (c AdaptiveColor) RGBA() (uint32, uint32, uint32, uint32) {
  type CompleteColor (line 42) | type CompleteColor struct
    method RGBA (line 50) | func (c CompleteColor) RGBA() (uint32, uint32, uint32, uint32) {
  type CompleteAdaptiveColor (line 65) | type CompleteAdaptiveColor struct
    method RGBA (line 72) | func (c CompleteAdaptiveColor) RGBA() (uint32, uint32, uint32, uint32) {

FILE: examples/blending/border-blend-rotation/bubbletea/main.go
  constant borderRotationFPS (line 13) | borderRotationFPS   = 15
  constant borderRotationSteps (line 14) | borderRotationSteps = 5
  type borderRotationTickMsg (line 17) | type borderRotationTickMsg struct
  function borderRotationTick (line 21) | func borderRotationTick(current int) tea.Cmd {
  type model (line 27) | type model struct
    method Init (line 31) | func (m model) Init() tea.Cmd {
    method Update (line 35) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 50) | func (m model) View() tea.View {
  function main (line 68) | func main() {

FILE: examples/blending/linear-1d/bubbletea/main.go
  type gradientData (line 58) | type gradientData struct
  type styles (line 64) | type styles struct
  function newStyles (line 71) | func newStyles(dark bool) (s *styles) {
  type model (line 94) | type model struct
    method Init (line 100) | func (m model) Init() tea.Cmd {
    method Update (line 104) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 124) | func (m model) View() tea.View {
  function main (line 165) | func main() {

FILE: examples/blending/linear-1d/standalone/main.go
  function main (line 41) | func main() {

FILE: examples/blending/linear-2d/bubbletea/main.go
  function main (line 46) | func main() {
  type model (line 76) | type model struct
    method Init (line 92) | func (m model) Init() tea.Cmd {
    method Update (line 96) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method updateGradient (line 139) | func (m *model) updateGradient() {
    method View (line 149) | func (m model) View() tea.View {
  function clamp (line 205) | func clamp[T cmp.Ordered](v, low, high T) T {

FILE: examples/blending/linear-2d/standalone/main.go
  function main (line 14) | func main() {

FILE: examples/brightness/main.go
  function main (line 13) | func main() {

FILE: examples/canvas/main.go
  function newField (line 13) | func newField(rows, cols int, color color.Color) string {
  function newCard (line 28) | func newCard(darkMode bool, text string) string {
  function main (line 48) | func main() {

FILE: examples/color/bubbletea/main.go
  type styles (line 12) | type styles struct
  function newStyles (line 22) | func newStyles(backgroundIsDark bool) (s *styles) {
  type model (line 66) | type model struct
    method Init (line 73) | func (m model) Init() tea.Cmd {
    method Update (line 79) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 112) | func (m model) View() tea.View {
  function main (line 152) | func main() {

FILE: examples/color/standalone/main.go
  function main (line 15) | func main() {

FILE: examples/compat/bubbletea/main.go
  type styles (line 21) | type styles struct
  function newStyles (line 31) | func newStyles() (s styles) {
  type model (line 59) | type model struct
    method Init (line 66) | func (m model) Init() tea.Cmd {
    method Update (line 70) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 96) | func (m model) View() tea.View {
  function main (line 131) | func main() {

FILE: examples/compat/standalone/main.go
  function main (line 22) | func main() {

FILE: examples/layout/main.go
  constant width (line 21) | width = 96
  constant columnWidth (line 24) | columnWidth = 30
  function main (line 36) | func main() {
  function colorGrid (line 397) | func colorGrid(xSteps, ySteps int) [][]color.Color {
  function applyGradient (line 413) | func applyGradient(base lipgloss.Style, input string, from, to color.Col...

FILE: examples/list/duckduckgoose/main.go
  function duckDuckGooseEnumerator (line 8) | func duckDuckGooseEnumerator(items list.Items, i int) string {
  function main (line 15) | func main() {

FILE: examples/list/glow/main.go
  type Document (line 8) | type Document struct
    method String (line 15) | func (d Document) String() string {
  constant selected (line 26) | selected = 1
  function main (line 28) | func main() {

FILE: examples/list/grocery/main.go
  function groceryEnumerator (line 20) | func groceryEnumerator(items list.Items, i int) string {
  function enumStyleFunc (line 37) | func enumStyleFunc(items list.Items, i int) lipgloss.Style {
  function itemStyleFunc (line 46) | func itemStyleFunc(items list.Items, i int) lipgloss.Style {
  function main (line 56) | func main() {

FILE: examples/list/roman/main.go
  function main (line 8) | func main() {

FILE: examples/list/simple/main.go
  function main (line 8) | func main() {

FILE: examples/list/sublist/main.go
  function main (line 11) | func main() {

FILE: examples/ssh/main.go
  type styles (line 21) | type styles struct
  function makeStyles (line 37) | func makeStyles() styles {
  function handler (line 55) | func handler(next ssh.Handler) ssh.Handler {
  function main (line 132) | func main() {

FILE: examples/table/ansi/main.go
  function main (line 8) | func main() {

FILE: examples/table/chess/main.go
  function main (line 10) | func main() {

FILE: examples/table/languages/main.go
  constant purple (line 9) | purple    = "99"
  constant gray (line 10) | gray      = "245"
  constant lightGray (line 11) | lightGray = "241"
  function main (line 14) | func main() {

FILE: examples/table/mindy/main.go
  function main (line 10) | func main() {
  constant rowLength (line 47) | rowLength = 12
  function makeRow (line 49) | func makeRow(start, end int) []string {
  function makeEmptyRow (line 61) | func makeEmptyRow() []string {

FILE: examples/table/pokemon/main.go
  function main (line 12) | func main() {

FILE: examples/tree/background/main.go
  function main (line 8) | func main() {

FILE: examples/tree/files/main.go
  function addBranches (line 13) | func addBranches(root *tree.Tree, path string) error {
  function main (line 51) | func main() {

FILE: examples/tree/makeup/main.go
  function main (line 8) | func main() {

FILE: examples/tree/rounded/main.go
  function main (line 8) | func main() {

FILE: examples/tree/selection/main.go
  constant selected (line 11) | selected = "/Users/bash/.config/doom-emacs"
  type styles (line 13) | type styles struct
  function defaultStyles (line 22) | func defaultStyles() styles {
  type dir (line 42) | type dir struct
    method String (line 48) | func (d dir) String() string {
  type file (line 58) | type file struct
    method String (line 63) | func (s file) String() string {
    method Hidden (line 67) | func (s file) Hidden() bool {
    method Children (line 71) | func (s file) Children() tree.Children {
    method Value (line 75) | func (s file) Value() string {
    method SetValue (line 79) | func (s file) SetValue(val any) {
    method SetHidden (line 83) | func (s file) SetHidden(val bool) {
  function isItemSelected (line 87) | func isItemSelected(children tree.Children, index int) bool {
  function itemStyle (line 96) | func itemStyle(children tree.Children, index int) lipgloss.Style {
  function indenterStyle (line 105) | func indenterStyle(children tree.Children, index int) lipgloss.Style {
  function main (line 114) | func main() {
  function Enumerator (line 144) | func Enumerator(children tree.Children, index int) string {
  function Indenter (line 148) | func Indenter(children tree.Children, index int) string {

FILE: examples/tree/simple/main.go
  function main (line 8) | func main() {

FILE: examples/tree/styles/main.go
  function main (line 10) | func main() {

FILE: examples/tree/toggle/main.go
  type styles (line 8) | type styles struct
  function defaultStyles (line 17) | func defaultStyles() styles {
  type dir (line 38) | type dir struct
    method String (line 44) | func (d dir) String() string {
  type file (line 53) | type file struct
    method String (line 58) | func (s file) String() string {
  function main (line 62) | func main() {

FILE: get.go
  method GetBold (line 11) | func (s Style) GetBold() bool {
  method GetItalic (line 17) | func (s Style) GetItalic() bool {
  method GetUnderline (line 23) | func (s Style) GetUnderline() bool {
  method GetUnderlineStyle (line 29) | func (s Style) GetUnderlineStyle() Underline {
  method GetUnderlineColor (line 35) | func (s Style) GetUnderlineColor() color.Color {
  method GetStrikethrough (line 41) | func (s Style) GetStrikethrough() bool {
  method GetReverse (line 47) | func (s Style) GetReverse() bool {
  method GetBlink (line 53) | func (s Style) GetBlink() bool {
  method GetFaint (line 59) | func (s Style) GetFaint() bool {
  method GetForeground (line 65) | func (s Style) GetForeground() color.Color {
  method GetBackground (line 71) | func (s Style) GetBackground() color.Color {
  method GetWidth (line 77) | func (s Style) GetWidth() int {
  method GetHeight (line 83) | func (s Style) GetHeight() int {
  method GetAlign (line 89) | func (s Style) GetAlign() Position {
  method GetAlignHorizontal (line 99) | func (s Style) GetAlignHorizontal() Position {
  method GetAlignVertical (line 109) | func (s Style) GetAlignVertical() Position {
  method GetPadding (line 119) | func (s Style) GetPadding() (top, right, bottom, left int) {
  method GetPaddingTop (line 128) | func (s Style) GetPaddingTop() int {
  method GetPaddingRight (line 134) | func (s Style) GetPaddingRight() int {
  method GetPaddingBottom (line 140) | func (s Style) GetPaddingBottom() int {
  method GetPaddingLeft (line 146) | func (s Style) GetPaddingLeft() int {
  method GetPaddingChar (line 152) | func (s Style) GetPaddingChar() rune {
  method GetHorizontalPadding (line 162) | func (s Style) GetHorizontalPadding() int {
  method GetVerticalPadding (line 168) | func (s Style) GetVerticalPadding() int {
  method GetColorWhitespace (line 174) | func (s Style) GetColorWhitespace() bool {
  method GetMargin (line 180) | func (s Style) GetMargin() (top, right, bottom, left int) {
  method GetMarginTop (line 189) | func (s Style) GetMarginTop() int {
  method GetMarginRight (line 195) | func (s Style) GetMarginRight() int {
  method GetMarginBottom (line 201) | func (s Style) GetMarginBottom() int {
  method GetMarginLeft (line 207) | func (s Style) GetMarginLeft() int {
  method GetMarginChar (line 213) | func (s Style) GetMarginChar() rune {
  method GetHorizontalMargins (line 223) | func (s Style) GetHorizontalMargins() int {
  method GetVerticalMargins (line 229) | func (s Style) GetVerticalMargins() int {
  method GetBorder (line 237) | func (s Style) GetBorder() (b Border, top, right, bottom, left bool) {
  method GetBorderStyle (line 247) | func (s Style) GetBorderStyle() Border {
  method GetBorderTop (line 253) | func (s Style) GetBorderTop() bool {
  method GetBorderRight (line 259) | func (s Style) GetBorderRight() bool {
  method GetBorderBottom (line 265) | func (s Style) GetBorderBottom() bool {
  method GetBorderLeft (line 271) | func (s Style) GetBorderLeft() bool {
  method GetBorderTopForeground (line 277) | func (s Style) GetBorderTopForeground() color.Color {
  method GetBorderRightForeground (line 283) | func (s Style) GetBorderRightForeground() color.Color {
  method GetBorderBottomForeground (line 289) | func (s Style) GetBorderBottomForeground() color.Color {
  method GetBorderLeftForeground (line 295) | func (s Style) GetBorderLeftForeground() color.Color {
  method GetBorderForegroundBlend (line 301) | func (s Style) GetBorderForegroundBlend() []color.Color {
  method GetBorderForegroundBlendOffset (line 307) | func (s Style) GetBorderForegroundBlendOffset() int {
  method GetBorderTopBackground (line 313) | func (s Style) GetBorderTopBackground() color.Color {
  method GetBorderRightBackground (line 319) | func (s Style) GetBorderRightBackground() color.Color {
  method GetBorderBottomBackground (line 325) | func (s Style) GetBorderBottomBackground() color.Color {
  method GetBorderLeftBackground (line 331) | func (s Style) GetBorderLeftBackground() color.Color {
  method GetBorderTopWidth (line 340) | func (s Style) GetBorderTopWidth() int {
  method GetBorderTopSize (line 347) | func (s Style) GetBorderTopSize() int {
  method GetBorderLeftSize (line 360) | func (s Style) GetBorderLeftSize() int {
  method GetBorderBottomSize (line 373) | func (s Style) GetBorderBottomSize() int {
  method GetBorderRightSize (line 386) | func (s Style) GetBorderRightSize() int {
  method GetHorizontalBorderSize (line 399) | func (s Style) GetHorizontalBorderSize() int {
  method GetVerticalBorderSize (line 406) | func (s Style) GetVerticalBorderSize() int {
  method GetInline (line 412) | func (s Style) GetInline() bool {
  method GetMaxWidth (line 418) | func (s Style) GetMaxWidth() int {
  method GetMaxHeight (line 424) | func (s Style) GetMaxHeight() int {
  method GetTabWidth (line 430) | func (s Style) GetTabWidth() int {
  method GetUnderlineSpaces (line 436) | func (s Style) GetUnderlineSpaces() bool {
  method GetStrikethroughSpaces (line 442) | func (s Style) GetStrikethroughSpaces() bool {
  method GetHorizontalFrameSize (line 450) | func (s Style) GetHorizontalFrameSize() int {
  method GetVerticalFrameSize (line 458) | func (s Style) GetVerticalFrameSize() int {
  method GetFrameSize (line 464) | func (s Style) GetFrameSize() (x, y int) {
  method GetTransform (line 470) | func (s Style) GetTransform() func(string) string {
  method GetHyperlink (line 476) | func (s Style) GetHyperlink() (link, params string) {
  method isSet (line 487) | func (s Style) isSet(k propKey) bool {
  method getAsRune (line 491) | func (s Style) getAsRune(k propKey) rune {
  method getAsBool (line 504) | func (s Style) getAsBool(k propKey, defaultVal bool) bool {
  method getAsColors (line 511) | func (s Style) getAsColors(k propKey) (colors []color.Color) {
  method getAsColor (line 524) | func (s Style) getAsColor(k propKey) color.Color {
  method getAsInt (line 564) | func (s Style) getAsInt(k propKey) int {
  method getAsPosition (line 601) | func (s Style) getAsPosition(k propKey) Position {
  method getBorderStyle (line 614) | func (s Style) getBorderStyle() Border {
  method getAsTransform (line 621) | func (s Style) getAsTransform(propKey) func(string) string {
  function getLines (line 630) | func getLines(s string) (lines []string, widest int) {
  method isBorderStyleSetWithoutSides (line 648) | func (s Style) isBorderStyleSetWithoutSides() bool {

FILE: join.go
  function JoinHorizontal (line 28) | func JoinHorizontal(pos Position, strs ...string) string {
  function JoinVertical (line 116) | func JoinVertical(pos Position, strs ...string) string {

FILE: join_test.go
  function TestJoinVertical (line 5) | func TestJoinVertical(t *testing.T) {
  function TestJoinHorizontal (line 26) | func TestJoinHorizontal(t *testing.T) {

FILE: layer.go
  type Layer (line 13) | type Layer struct
    method GetContent (line 31) | func (l *Layer) GetContent() string {
    method Width (line 36) | func (l *Layer) Width() int {
    method Height (line 41) | func (l *Layer) Height() int {
    method GetID (line 46) | func (l *Layer) GetID() string {
    method ID (line 51) | func (l *Layer) ID(id string) *Layer {
    method X (line 57) | func (l *Layer) X(x int) *Layer {
    method Y (line 63) | func (l *Layer) Y(y int) *Layer {
    method Z (line 69) | func (l *Layer) Z(z int) *Layer {
    method GetX (line 75) | func (l *Layer) GetX() int {
    method GetY (line 80) | func (l *Layer) GetY() int {
    method GetZ (line 85) | func (l *Layer) GetZ() int {
    method AddLayers (line 90) | func (l *Layer) AddLayers(layers ...*Layer) *Layer {
    method GetLayer (line 105) | func (l *Layer) GetLayer(id string) *Layer {
    method MaxZ (line 121) | func (l *Layer) MaxZ() int {
    method boundsWithOffset (line 133) | func (l *Layer) boundsWithOffset(parentX, parentY int) image.Rectangle {
    method Draw (line 153) | func (l *Layer) Draw(scr uv.Screen, area uv.Rectangle) {
  function NewLayer (line 22) | func NewLayer(content string, layers ...*Layer) *Layer {
  type LayerHit (line 159) | type LayerHit struct
    method Empty (line 166) | func (lh LayerHit) Empty() bool {
    method ID (line 171) | func (lh LayerHit) ID() string {
    method Layer (line 176) | func (lh LayerHit) Layer() *Layer {
    method Bounds (line 181) | func (lh LayerHit) Bounds() image.Rectangle {
  type Compositor (line 188) | type Compositor struct
    method AddLayers (line 218) | func (c *Compositor) AddLayers(layers ...*Layer) *Compositor {
    method flatten (line 225) | func (c *Compositor) flatten() {
    method flattenRecursive (line 245) | func (c *Compositor) flattenRecursive(layer *Layer, parentX, parentY i...
    method Bounds (line 273) | func (c *Compositor) Bounds() image.Rectangle {
    method Draw (line 278) | func (c *Compositor) Draw(scr uv.Screen, area image.Rectangle) {
    method Hit (line 289) | func (c *Compositor) Hit(x, y int) LayerHit {
    method GetLayer (line 307) | func (c *Compositor) GetLayer(id string) *Layer {
    method Refresh (line 316) | func (c *Compositor) Refresh() {
    method Render (line 323) | func (c *Compositor) Render() string {
  type compositeLayer (line 196) | type compositeLayer struct
  function NewCompositor (line 206) | func NewCompositor(layers ...*Layer) *Compositor {

FILE: list/enumerator.go
  type Enumerator (line 26) | type Enumerator
  type Indenter (line 42) | type Indenter
  function Alphabet (line 51) | func Alphabet(_ Items, i int) string {
  constant abcLen (line 61) | abcLen = 26
  function Arabic (line 70) | func Arabic(_ Items, i int) string {
  function Roman (line 81) | func Roman(_ Items, i int) string {
  function Bullet (line 104) | func Bullet(Items, int) string {
  function Asterisk (line 115) | func Asterisk(Items, int) string {
  function Dash (line 126) | func Dash(Items, int) string {

FILE: list/list.go
  type List (line 34) | type List struct
    method Hidden (line 84) | func (l *List) Hidden() bool {
    method Hide (line 90) | func (l *List) Hide(hide bool) *List {
    method Offset (line 106) | func (l *List) Offset(start, end int) *List {
    method Value (line 112) | func (l *List) Value() string {
    method String (line 116) | func (l *List) String() string {
    method EnumeratorStyle (line 124) | func (l *List) EnumeratorStyle(style lipgloss.Style) *List {
    method EnumeratorStyleFunc (line 143) | func (l *List) EnumeratorStyleFunc(f StyleFunc) *List {
    method IndenterStyle (line 154) | func (l *List) IndenterStyle(style lipgloss.Style) *List {
    method IndenterStyleFunc (line 173) | func (l *List) IndenterStyleFunc(f StyleFunc) *List {
    method Indenter (line 201) | func (l *List) Indenter(indenter Indenter) *List {
    method ItemStyle (line 214) | func (l *List) ItemStyle(style lipgloss.Style) *List {
    method ItemStyleFunc (line 233) | func (l *List) ItemStyleFunc(f StyleFunc) *List {
    method Item (line 246) | func (l *List) Item(item any) *List {
    method Items (line 260) | func (l *List) Items(items ...any) *List {
    method Enumerator (line 288) | func (l *List) Enumerator(enumerator Enumerator) *List {
  function New (line 50) | func New(items ...any) *List {
  type Items (line 58) | type Items
  type StyleFunc (line 81) | type StyleFunc

FILE: list/list_test.go
  function TestList (line 18) | func TestList(t *testing.T) {
  function TestListItems (line 27) | func TestListItems(t *testing.T) {
  function TestSublist (line 34) | func TestSublist(t *testing.T) {
  function TestSublistItems (line 44) | func TestSublistItems(t *testing.T) {
  function TestComplexSublist (line 60) | func TestComplexSublist(t *testing.T) {
  function TestMultiline (line 142) | func TestMultiline(t *testing.T) {
  function TestListIntegers (line 151) | func TestListIntegers(t *testing.T) {
  function TestEnumerators (line 160) | func TestEnumerators(t *testing.T) {
  function TestEnumeratorsTransform (line 228) | func TestEnumeratorsTransform(t *testing.T) {
  function TestBullet (line 292) | func TestBullet(t *testing.T) {
  function TestEnumeratorsAlign (line 326) | func TestEnumeratorsAlign(t *testing.T) {
  function TestSubListItems2 (line 336) | func TestSubListItems2(t *testing.T) {
  function assertEqual (line 350) | func assertEqual(tb testing.TB, expected, got string) {
  function trimSpace (line 361) | func trimSpace(s string) string {

FILE: position.go
  type Position (line 19) | type Position
    method value (line 21) | func (p Position) value() float64 {
  constant Top (line 27) | Top    Position = 0.0
  constant Bottom (line 28) | Bottom Position = 1.0
  constant Center (line 29) | Center Position = 0.5
  constant Left (line 30) | Left   Position = 0.0
  constant Right (line 31) | Right  Position = 1.0
  function Place (line 36) | func Place(width, height int, hPos, vPos Position, str string, opts ...W...
  function PlaceHorizontal (line 43) | func PlaceHorizontal(width int, pos Position, str string, opts ...Whites...
  function PlaceVertical (line 90) | func PlaceVertical(height int, pos Position, str string, opts ...Whitesp...

FILE: query.go
  function backgroundColor (line 12) | func backgroundColor(in term.File, out term.File) (color.Color, error) {
  function BackgroundColor (line 34) | func BackgroundColor(in term.File, out term.File) (bg color.Color, err e...
  function HasDarkBackground (line 83) | func HasDarkBackground(in term.File, out term.File) bool {

FILE: ranges.go
  function StyleRanges (line 11) | func StyleRanges(s string, ranges ...Range) string {
  function NewRange (line 39) | func NewRange(start, end int, style Style) Range {
  type Range (line 45) | type Range struct

FILE: ranges_test.go
  function TestStyleRanges (line 7) | func TestStyleRanges(t *testing.T) {

FILE: runes.go
  function StyleRunes (line 10) | func StyleRunes(str string, indices []int, matched, unmatched Style) str...

FILE: runes_test.go
  function TestStyleRunes (line 7) | func TestStyleRunes(t *testing.T) {

FILE: set.go
  method set (line 9) | func (s *Style) set(key propKey, value any) {
  method setFrom (line 111) | func (s *Style) setFrom(key propKey, i Style) {
  function colorOrNil (line 187) | func colorOrNil(c any) color.Color {
  method Bold (line 195) | func (s Style) Bold(v bool) Style {
  method Italic (line 202) | func (s Style) Italic(v bool) Style {
  method Underline (line 210) | func (s Style) Underline(v bool) Style {
  method UnderlineStyle (line 223) | func (s Style) UnderlineStyle(u Underline) Style {
  method UnderlineColor (line 234) | func (s Style) UnderlineColor(c color.Color) Style {
  method Strikethrough (line 242) | func (s Style) Strikethrough(v bool) Style {
  method Reverse (line 248) | func (s Style) Reverse(v bool) Style {
  method Blink (line 254) | func (s Style) Blink(v bool) Style {
  method Faint (line 260) | func (s Style) Faint(v bool) Style {
  method Foreground (line 272) | func (s Style) Foreground(c color.Color) Style {
  method Background (line 278) | func (s Style) Background(c color.Color) Style {
  method Width (line 286) | func (s Style) Width(i int) Style {
  method Height (line 294) | func (s Style) Height(i int) Style {
  method Align (line 305) | func (s Style) Align(p ...Position) Style {
  method AlignHorizontal (line 316) | func (s Style) AlignHorizontal(p Position) Style {
  method AlignVertical (line 322) | func (s Style) AlignVertical(p Position) Style {
  method Padding (line 341) | func (s Style) Padding(i ...int) Style {
  method PaddingLeft (line 355) | func (s Style) PaddingLeft(i int) Style {
  method PaddingRight (line 361) | func (s Style) PaddingRight(i int) Style {
  method PaddingTop (line 367) | func (s Style) PaddingTop(i int) Style {
  method PaddingBottom (line 373) | func (s Style) PaddingBottom(i int) Style {
  method PaddingChar (line 385) | func (s Style) PaddingChar(r rune) Style {
  method ColorWhitespace (line 396) | func (s Style) ColorWhitespace(v bool) Style {
  method Margin (line 415) | func (s Style) Margin(i ...int) Style {
  method MarginLeft (line 429) | func (s Style) MarginLeft(i int) Style {
  method MarginRight (line 435) | func (s Style) MarginRight(i int) Style {
  method MarginTop (line 441) | func (s Style) MarginTop(i int) Style {
  method MarginBottom (line 447) | func (s Style) MarginBottom(i int) Style {
  method MarginBackground (line 455) | func (s Style) MarginBackground(c color.Color) Style {
  method MarginChar (line 462) | func (s Style) MarginChar(r rune) Style {
  method Border (line 490) | func (s Style) Border(b Border, sides ...bool) Style {
  method BorderStyle (line 523) | func (s Style) BorderStyle(b Border) Style {
  method BorderTop (line 529) | func (s Style) BorderTop(v bool) Style {
  method BorderRight (line 535) | func (s Style) BorderRight(v bool) Style {
  method BorderBottom (line 541) | func (s Style) BorderBottom(v bool) Style {
  method BorderLeft (line 547) | func (s Style) BorderLeft(v bool) Style {
  method BorderForeground (line 567) | func (s Style) BorderForeground(c ...color.Color) Style {
  method BorderTopForeground (line 586) | func (s Style) BorderTopForeground(c color.Color) Style {
  method BorderRightForeground (line 593) | func (s Style) BorderRightForeground(c color.Color) Style {
  method BorderBottomForeground (line 600) | func (s Style) BorderBottomForeground(c color.Color) Style {
  method BorderLeftForeground (line 607) | func (s Style) BorderLeftForeground(c color.Color) Style {
  method BorderForegroundBlend (line 628) | func (s Style) BorderForegroundBlend(c ...color.Color) Style {
  method BorderForegroundBlendOffset (line 655) | func (s Style) BorderForegroundBlendOffset(v int) Style {
  method BorderBackground (line 675) | func (s Style) BorderBackground(c ...color.Color) Style {
  method BorderTopBackground (line 694) | func (s Style) BorderTopBackground(c color.Color) Style {
  method BorderRightBackground (line 700) | func (s Style) BorderRightBackground(c color.Color) Style {
  method BorderBottomBackground (line 707) | func (s Style) BorderBottomBackground(c color.Color) Style {
  method BorderLeftBackground (line 714) | func (s Style) BorderLeftBackground(c color.Color) Style {
  method Inline (line 732) | func (s Style) Inline(v bool) Style {
  method MaxWidth (line 750) | func (s Style) MaxWidth(n int) Style {
  method MaxHeight (line 762) | func (s Style) MaxHeight(n int) Style {
  constant NoTabConversion (line 770) | NoTabConversion = -1
  method TabWidth (line 777) | func (s Style) TabWidth(n int) Style {
  method UnderlineSpaces (line 788) | func (s Style) UnderlineSpaces(v bool) Style {
  method StrikethroughSpaces (line 796) | func (s Style) StrikethroughSpaces(v bool) Style {
  method Transform (line 808) | func (s Style) Transform(fn func(string) string) Style {
  method Hyperlink (line 820) | func (s Style) Hyperlink(link string, params ...string) Style {
  function whichSidesInt (line 838) | func whichSidesInt(i ...int) (top, right, bottom, left int, ok bool) {
  function whichSidesBool (line 871) | func whichSidesBool(i ...bool) (top, right, bottom, left bool, ok bool) {
  function whichSidesColor (line 904) | func whichSidesColor(i ...color.Color) (top, right, bottom, left color.C...

FILE: size.go
  function Width (line 15) | func Width(str string) (width int) {
  function Height (line 29) | func Height(str string) int {
  function Size (line 36) | func Size(str string) (width, height int) {

FILE: size_test.go
  function BenchmarkWidthSimple (line 8) | func BenchmarkWidthSimple(b *testing.B) {
  function BenchmarkWidthMultiLine (line 24) | func BenchmarkWidthMultiLine(b *testing.B) {

FILE: style.go
  constant NBSP (line 13) | NBSP            = '\u00A0'
  constant tabWidthDefault (line 14) | tabWidthDefault = 4
  type propKey (line 18) | type propKey
  constant boldKey (line 23) | boldKey propKey = 1 << iota
  constant italicKey (line 24) | italicKey
  constant strikethroughKey (line 25) | strikethroughKey
  constant reverseKey (line 26) | reverseKey
  constant blinkKey (line 27) | blinkKey
  constant faintKey (line 28) | faintKey
  constant underlineSpacesKey (line 29) | underlineSpacesKey
  constant strikethroughSpacesKey (line 30) | strikethroughSpacesKey
  constant colorWhitespaceKey (line 31) | colorWhitespaceKey
  constant underlineKey (line 34) | underlineKey
  constant foregroundKey (line 35) | foregroundKey
  constant backgroundKey (line 36) | backgroundKey
  constant underlineColorKey (line 37) | underlineColorKey
  constant widthKey (line 38) | widthKey
  constant heightKey (line 39) | heightKey
  constant alignHorizontalKey (line 40) | alignHorizontalKey
  constant alignVerticalKey (line 41) | alignVerticalKey
  constant paddingTopKey (line 44) | paddingTopKey
  constant paddingRightKey (line 45) | paddingRightKey
  constant paddingBottomKey (line 46) | paddingBottomKey
  constant paddingLeftKey (line 47) | paddingLeftKey
  constant paddingCharKey (line 48) | paddingCharKey
  constant marginTopKey (line 51) | marginTopKey
  constant marginRightKey (line 52) | marginRightKey
  constant marginBottomKey (line 53) | marginBottomKey
  constant marginLeftKey (line 54) | marginLeftKey
  constant marginBackgroundKey (line 55) | marginBackgroundKey
  constant marginCharKey (line 56) | marginCharKey
  constant borderStyleKey (line 59) | borderStyleKey
  constant borderTopKey (line 62) | borderTopKey
  constant borderRightKey (line 63) | borderRightKey
  constant borderBottomKey (line 64) | borderBottomKey
  constant borderLeftKey (line 65) | borderLeftKey
  constant borderTopForegroundKey (line 68) | borderTopForegroundKey
  constant borderRightForegroundKey (line 69) | borderRightForegroundKey
  constant borderBottomForegroundKey (line 70) | borderBottomForegroundKey
  constant borderLeftForegroundKey (line 71) | borderLeftForegroundKey
  constant borderForegroundBlendKey (line 72) | borderForegroundBlendKey
  constant borderForegroundBlendOffsetKey (line 73) | borderForegroundBlendOffsetKey
  constant borderTopBackgroundKey (line 76) | borderTopBackgroundKey
  constant borderRightBackgroundKey (line 77) | borderRightBackgroundKey
  constant borderBottomBackgroundKey (line 78) | borderBottomBackgroundKey
  constant borderLeftBackgroundKey (line 79) | borderLeftBackgroundKey
  constant inlineKey (line 81) | inlineKey
  constant maxWidthKey (line 82) | maxWidthKey
  constant maxHeightKey (line 83) | maxHeightKey
  constant tabWidthKey (line 84) | tabWidthKey
  constant transformKey (line 86) | transformKey
  constant linkKey (line 89) | linkKey
  constant linkParamsKey (line 90) | linkParamsKey
  type props (line 94) | type props
    method set (line 97) | func (p props) set(k propKey) props {
    method unset (line 102) | func (p props) unset(k propKey) props {
    method has (line 107) | func (p props) has(k propKey) bool {
  constant UnderlineNone (line 121) | UnderlineNone = ansi.UnderlineNone
  constant UnderlineSingle (line 123) | UnderlineSingle = ansi.UnderlineSingle
  constant UnderlineDouble (line 125) | UnderlineDouble = ansi.UnderlineDouble
  constant UnderlineCurly (line 127) | UnderlineCurly = ansi.UnderlineCurly
  constant UnderlineDotted (line 129) | UnderlineDotted = ansi.UnderlineDotted
  constant UnderlineDashed (line 131) | UnderlineDashed = ansi.UnderlineDashed
  function NewStyle (line 137) | func NewStyle() Style {
  type Style (line 142) | type Style struct
    method SetString (line 208) | func (s Style) SetString(strs ...string) Style {
    method Value (line 214) | func (s Style) Value() string {
    method String (line 221) | func (s Style) String() string {
    method Copy (line 229) | func (s Style) Copy() Style {
    method Inherit (line 238) | func (s Style) Inherit(i Style) Style {
    method Render (line 268) | func (s Style) Render(strs ...string) string {
    method maybeConvertTabs (line 528) | func (s Style) maybeConvertTabs(str string) string {
    method applyMargins (line 543) | func (s Style) applyMargins(str string, inline bool) string {
  function joinString (line 199) | func joinString(strs ...string) string {
  function padLeft (line 583) | func padLeft(str string, n int, style *ansi.Style, r rune) string {
  function padRight (line 588) | func padRight(str string, n int, style *ansi.Style, r rune) string {
  function pad (line 598) | func pad(str string, n int, style *ansi.Style, r rune) string {
  function abs (line 631) | func abs(a int) int {

FILE: style_test.go
  function TestUnderline (line 10) | func TestUnderline(t *testing.T) {
  function TestGetUnderlineColor (line 54) | func TestGetUnderlineColor(t *testing.T) {
  function TestStrikethrough (line 64) | func TestStrikethrough(t *testing.T) {
  function TestStyleRender (line 100) | func TestStyleRender(t *testing.T) {
  function TestValueCopy (line 144) | func TestValueCopy(t *testing.T) {
  function TestStyleInherit (line 156) | func TestStyleInherit(t *testing.T) {
  function TestStyleCopy (line 194) | func TestStyleCopy(t *testing.T) {
  function TestStyleUnset (line 234) | func TestStyleUnset(t *testing.T) {
  function TestStyleValue (line 364) | func TestStyleValue(t *testing.T) {
  function TestCustomPaddingChar (line 433) | func TestCustomPaddingChar(t *testing.T) {
  function TestTabConversion (line 438) | func TestTabConversion(t *testing.T) {
  function TestStringTransform (line 449) | func TestStringTransform(t *testing.T) {
  function requireTrue (line 494) | func requireTrue(tb testing.TB, b bool) {
  function requireFalse (line 499) | func requireFalse(tb testing.TB, b bool) {
  function requireEqual (line 504) | func requireEqual(tb testing.TB, a, b any) {
  function requireNotEqual (line 512) | func requireNotEqual(tb testing.TB, a, b any) {
  function TestCarriageReturnInRender (line 520) | func TestCarriageReturnInRender(t *testing.T) {
  function TestWidth (line 533) | func TestWidth(t *testing.T) {
  function TestHeight (line 558) | func TestHeight(t *testing.T) {
  function TestHyperlink (line 583) | func TestHyperlink(t *testing.T) {
  function TestUnsetHyperlink (line 615) | func TestUnsetHyperlink(t *testing.T) {
  function BenchmarkPad (line 647) | func BenchmarkPad(b *testing.B) {
  function BenchmarkStyleRender (line 667) | func BenchmarkStyleRender(b *testing.B) {

FILE: table/resizing.go
  method resize (line 47) | func (t *Table) resize() {
  type resizerColumn (line 111) | type resizerColumn struct
  type resizer (line 122) | type resizer struct
    method optimizedWidths (line 190) | func (r *resizer) optimizedWidths() (colWidths, rowHeights []int) {
    method detectTableWidth (line 198) | func (r *resizer) detectTableWidth() int {
    method expandTableWidth (line 203) | func (r *resizer) expandTableWidth() (colWidths []int) {
    method shrinkTableWidth (line 234) | func (r *resizer) shrinkTableWidth() (colWidths []int) {
    method expandRowHeights (line 311) | func (r *resizer) expandRowHeights(colWidths []int) {
    method defaultRowHeights (line 332) | func (r *resizer) defaultRowHeights() (rowHeights []int) {
    method maxColumnWidths (line 344) | func (r *resizer) maxColumnWidths() []int {
    method columnCount (line 357) | func (r *resizer) columnCount() int {
    method maxCharCount (line 362) | func (r *resizer) maxCharCount() int {
    method maxTotal (line 375) | func (r *resizer) maxTotal() (maxTotal int) {
    method totalHorizontalPadding (line 387) | func (r *resizer) totalHorizontalPadding() (totalHorizontalPadding int) {
    method xPaddingForCol (line 395) | func (r *resizer) xPaddingForCol(j int) int {
    method yPaddingForCell (line 403) | func (r *resizer) yPaddingForCell(i, j int) int {
    method totalHorizontalBorder (line 411) | func (r *resizer) totalHorizontalBorder() int {
    method detectContentHeight (line 416) | func (r *resizer) detectContentHeight(content string, width int) (heig...
    method visibleRowIndexes (line 434) | func (r *resizer) visibleRowIndexes() (firstVisibleRowIndex, lastVisib...
  function newResizer (line 145) | func newResizer(tableWidth, tableHeight int, headers []string, rows [][]...

FILE: table/rows.go
  type Data (line 4) | type Data interface
  type StringData (line 16) | type StringData struct
    method Append (line 34) | func (m *StringData) Append(row []string) {
    method At (line 40) | func (m *StringData) At(row, cell int) string {
    method Columns (line 49) | func (m *StringData) Columns() int {
    method Item (line 54) | func (m *StringData) Item(rows ...string) *StringData {
    method Rows (line 61) | func (m *StringData) Rows() int {
  function NewStringData (line 22) | func NewStringData(rows ...[]string) *StringData {
  type Filter (line 66) | type Filter struct
    method Filter (line 77) | func (m *Filter) Filter(f func(row int) bool) *Filter {
    method At (line 83) | func (m *Filter) At(row, cell int) string {
    method Columns (line 99) | func (m *Filter) Columns() int {
    method Rows (line 104) | func (m *Filter) Rows() int {
  function NewFilter (line 72) | func NewFilter(data Data) *Filter {
  function DataToMatrix (line 117) | func DataToMatrix(data Data) (rows [][]string) {

FILE: table/table.go
  constant HeaderRow (line 13) | HeaderRow int = -1
  type StyleFunc (line 37) | type StyleFunc
  function DefaultStyles (line 40) | func DefaultStyles(_, _ int) lipgloss.Style {
  type Table (line 45) | type Table struct
    method ClearRows (line 96) | func (t *Table) ClearRows() *Table {
    method BaseStyle (line 103) | func (t *Table) BaseStyle(baseStyle lipgloss.Style) *Table {
    method StyleFunc (line 110) | func (t *Table) StyleFunc(style StyleFunc) *Table {
    method style (line 116) | func (t *Table) style(row, col int) lipgloss.Style {
    method Data (line 124) | func (t *Table) Data(data Data) *Table {
    method GetData (line 130) | func (t *Table) GetData() Data {
    method Rows (line 135) | func (t *Table) Rows(rows ...[]string) *Table {
    method Row (line 146) | func (t *Table) Row(row ...string) *Table {
    method Headers (line 155) | func (t *Table) Headers(headers ...string) *Table {
    method GetHeaders (line 161) | func (t *Table) GetHeaders() []string {
    method Border (line 166) | func (t *Table) Border(border lipgloss.Border) *Table {
    method BorderTop (line 172) | func (t *Table) BorderTop(v bool) *Table {
    method BorderBottom (line 178) | func (t *Table) BorderBottom(v bool) *Table {
    method BorderLeft (line 184) | func (t *Table) BorderLeft(v bool) *Table {
    method BorderRight (line 190) | func (t *Table) BorderRight(v bool) *Table {
    method BorderHeader (line 196) | func (t *Table) BorderHeader(v bool) *Table {
    method BorderColumn (line 202) | func (t *Table) BorderColumn(v bool) *Table {
    method BorderRow (line 208) | func (t *Table) BorderRow(v bool) *Table {
    method BorderStyle (line 214) | func (t *Table) BorderStyle(style lipgloss.Style) *Table {
    method GetBorderTop (line 220) | func (t *Table) GetBorderTop() bool {
    method GetBorderBottom (line 225) | func (t *Table) GetBorderBottom() bool {
    method GetBorderLeft (line 230) | func (t *Table) GetBorderLeft() bool {
    method GetBorderRight (line 235) | func (t *Table) GetBorderRight() bool {
    method GetBorderHeader (line 240) | func (t *Table) GetBorderHeader() bool {
    method GetBorderColumn (line 245) | func (t *Table) GetBorderColumn() bool {
    method GetBorderRow (line 250) | func (t *Table) GetBorderRow() bool {
    method Width (line 257) | func (t *Table) Width(w int) *Table {
    method Height (line 263) | func (t *Table) Height(h int) *Table {
    method GetHeight (line 270) | func (t *Table) GetHeight() int {
    method YOffset (line 275) | func (t *Table) YOffset(o int) *Table {
    method GetYOffset (line 281) | func (t *Table) GetYOffset() int {
    method FirstVisibleRowIndex (line 286) | func (t *Table) FirstVisibleRowIndex() int {
    method LastVisibleRowIndex (line 291) | func (t *Table) LastVisibleRowIndex() int {
    method VisibleRows (line 296) | func (t *Table) VisibleRows() int {
    method Wrap (line 306) | func (t *Table) Wrap(w bool) *Table {
    method String (line 312) | func (t *Table) String() string {
    method computeHeight (line 371) | func (t *Table) computeHeight() int {
    method Render (line 379) | func (t *Table) Render() string {
    method constructTopBorder (line 385) | func (t *Table) constructTopBorder() string {
    method constructBottomBorder (line 404) | func (t *Table) constructBottomBorder() string {
    method constructHeaders (line 423) | func (t *Table) constructHeaders() string {
    method constructRow (line 484) | func (t *Table) constructRow(index int, isOverflow bool) string {
    method truncateCell (line 555) | func (t *Table) truncateCell(cell string, rowIndex, colIndex int) stri...
  function New (line 80) | func New() *Table {

FILE: table/table_test.go
  function TestTable (line 23) | func TestTable(t *testing.T) {
  function TestTableWithBackground (line 37) | func TestTableWithBackground(t *testing.T) {
  function TestTableExample (line 55) | func TestTableExample(t *testing.T) {
  function TestTableEmpty (line 89) | func TestTableEmpty(t *testing.T) {
  function TestTableNoStyleFunc (line 98) | func TestTableNoStyleFunc(t *testing.T) {
  function TestTableYOffset (line 112) | func TestTableYOffset(t *testing.T) {
  function TestTableBorder (line 128) | func TestTableBorder(t *testing.T) {
  function TestTableSetRows (line 146) | func TestTableSetRows(t *testing.T) {
  function TestMoreCellsThanHeaders (line 163) | func TestMoreCellsThanHeaders(t *testing.T) {
  function TestMoreCellsThanHeadersExtra (line 180) | func TestMoreCellsThanHeadersExtra(t *testing.T) {
  function TestTableNoHeaders (line 198) | func TestTableNoHeaders(t *testing.T) {
  function TestTableNoColumnSeparators (line 211) | func TestTableNoColumnSeparators(t *testing.T) {
  function TestTableNoColumnSeparatorsWithHeaders (line 225) | func TestTableNoColumnSeparatorsWithHeaders(t *testing.T) {
  function TestInnerBordersOnly (line 240) | func TestInnerBordersOnly(t *testing.T) {
  function TestBorderColumnsWithExtraRows (line 260) | func TestBorderColumnsWithExtraRows(t *testing.T) {
  function TestNew (line 279) | func TestNew(t *testing.T) {
  function TestTableUnsetBorders (line 287) | func TestTableUnsetBorders(t *testing.T) {
  function TestTableUnsetHeaderSeparator (line 309) | func TestTableUnsetHeaderSeparator(t *testing.T) {
  function TestTableUnsetHeaderSeparatorWithBorder (line 332) | func TestTableUnsetHeaderSeparatorWithBorder(t *testing.T) {
  function TestTableRowSeparators (line 351) | func TestTableRowSeparators(t *testing.T) {
  function TestTableHeights (line 370) | func TestTableHeights(t *testing.T) {
  function TestTableMultiLineRowSeparator (line 396) | func TestTableMultiLineRowSeparator(t *testing.T) {
  function TestTableWidthExpand (line 419) | func TestTableWidthExpand(t *testing.T) {
  function TestTableWidthShrink (line 442) | func TestTableWidthShrink(t *testing.T) {
  function TestTableWidthSmartCrop (line 492) | func TestTableWidthSmartCrop(t *testing.T) {
  function TestTableWidthSmartCropExtensive (line 509) | func TestTableWidthSmartCropExtensive(t *testing.T) {
  function TestTableWidthSmartCropTiny (line 530) | func TestTableWidthSmartCropTiny(t *testing.T) {
  function TestTableWidths (line 549) | func TestTableWidths(t *testing.T) {
  function TestFilter (line 571) | func TestFilter(t *testing.T) {
  function TestFilterInverse (line 592) | func TestFilterInverse(t *testing.T) {
  function TestTableANSI (line 613) | func TestTableANSI(t *testing.T) {
  function TestTableHeightExact (line 633) | func TestTableHeightExact(t *testing.T) {
  function TestTableHeightExtra (line 648) | func TestTableHeightExtra(t *testing.T) {
  function TestTableHeightShrink (line 663) | func TestTableHeightShrink(t *testing.T) {
  function TestTableHeightWithYOffset (line 737) | func TestTableHeightWithYOffset(t *testing.T) {
  function TestStyleFunc (line 756) | func TestStyleFunc(t *testing.T) {
  function TestClearRows (line 810) | func TestClearRows(t *testing.T) {
  function TestContentWrapping (line 828) | func TestContentWrapping(t *testing.T) {
  function TestContentWrapping_WithPadding (line 928) | func TestContentWrapping_WithPadding(t *testing.T) {
  function TestContentWrapping_WithMargins (line 990) | func TestContentWrapping_WithMargins(t *testing.T) {
  function TestContentWrapping_WithHeight (line 1045) | func TestContentWrapping_WithHeight(t *testing.T) {
  function TestContentWrapping_ColumnWidth (line 1083) | func TestContentWrapping_ColumnWidth(t *testing.T) {
  function TestTableOverFlowNoWrap (line 1167) | func TestTableOverFlowNoWrap(t *testing.T) {
  function TestCarriageReturn (line 1196) | func TestCarriageReturn(t *testing.T) {
  function TestTableShrinkWithYOffset (line 1208) | func TestTableShrinkWithYOffset(t *testing.T) {
  function TestBorderStyles (line 1343) | func TestBorderStyles(t *testing.T) {
  function TestNoFinalEmptyRowWhenOverflow (line 1381) | func TestNoFinalEmptyRowWhenOverflow(t *testing.T) {
  function TestExtraPaddingHeading (line 1409) | func TestExtraPaddingHeading(t *testing.T) {
  function TestExtraPaddingHeadingLong (line 1426) | func TestExtraPaddingHeadingLong(t *testing.T) {
  function TestBorderedCells (line 1444) | func TestBorderedCells(t *testing.T) {
  function ExampleTable_Wrap (line 1463) | func ExampleTable_Wrap() {
  function TestWrapPreStyledContent (line 1558) | func TestWrapPreStyledContent(t *testing.T) {
  function TestWrapStyleFuncContent (line 1575) | func TestWrapStyleFuncContent(t *testing.T) {

FILE: table/util.go
  function btoi (line 8) | func btoi(b bool) int {
  function bton (line 16) | func bton(b bool, n int) int {
  function sum (line 24) | func sum(n []int) int {
  function median (line 33) | func median(n []int) int {

FILE: terminal.go
  function queryBackgroundColor (line 25) | func queryBackgroundColor(in io.Reader, out io.Writer) (c color.Color, e...
  constant defaultQueryTimeout (line 49) | defaultQueryTimeout = time.Second * 2
  type queryTerminalFilter (line 54) | type queryTerminalFilter
  function queryTerminal (line 63) | func queryTerminal(

FILE: tree/children.go
  type Children (line 6) | type Children interface
  type NodeChildren (line 15) | type NodeChildren
    method Append (line 18) | func (n NodeChildren) Append(child Node) NodeChildren {
    method Remove (line 24) | func (n NodeChildren) Remove(index int) NodeChildren {
    method Length (line 33) | func (n NodeChildren) Length() int {
    method At (line 38) | func (n NodeChildren) At(i int) Node {
  function NewStringData (line 46) | func NewStringData(data ...string) Children {
  type Filter (line 59) | type Filter struct
    method At (line 71) | func (m *Filter) At(index int) Node {
    method Filter (line 86) | func (m *Filter) Filter(f func(index int) bool) *Filter {
    method Length (line 92) | func (m *Filter) Length() int {
  function NewFilter (line 65) | func NewFilter(data Children) *Filter {

FILE: tree/enumerator.go
  type Enumerator (line 15) | type Enumerator
  function DefaultEnumerator (line 23) | func DefaultEnumerator(children Children, index int) string {
  function RoundedEnumerator (line 36) | func RoundedEnumerator(children Children, index int) string {
  type Indenter (line 57) | type Indenter
  function DefaultIndenter (line 69) | func DefaultIndenter(children Children, index int) string {

FILE: tree/example_test.go
  function ExampleLeaf_SetHidden (line 12) | func ExampleLeaf_SetHidden() {
  function ExampleNewLeaf (line 38) | func ExampleNewLeaf() {
  function ExampleLeaf_SetValue (line 68) | func ExampleLeaf_SetValue() {
  function ExampleTree_Hide (line 99) | func ExampleTree_Hide() {
  function ExampleTree_SetHidden (line 123) | func ExampleTree_SetHidden() {

FILE: tree/renderer.go
  type StyleFunc (line 10) | type StyleFunc
  type Style (line 13) | type Style struct
  function newRenderer (line 21) | func newRenderer() *renderer {
  type renderer (line 39) | type renderer struct
    method render (line 47) | func (r *renderer) render(node Node, root bool, prefix string) string {

FILE: tree/tree.go
  type Node (line 36) | type Node interface
  type Leaf (line 46) | type Leaf struct
    method Children (line 60) | func (Leaf) Children() Children {
    method Value (line 65) | func (s Leaf) Value() string {
    method SetValue (line 70) | func (s *Leaf) SetValue(value any) {
    method Hidden (line 82) | func (s Leaf) Hidden() bool {
    method SetHidden (line 87) | func (s *Leaf) SetHidden(hidden bool) { s.hidden = hidden }
    method String (line 91) | func (s Leaf) String() string {
  function NewLeaf (line 52) | func NewLeaf(value any, hidden bool) *Leaf {
  type Tree (line 96) | type Tree struct
    method Hidden (line 107) | func (t *Tree) Hidden() bool {
    method Hide (line 113) | func (t *Tree) Hide(hide bool) *Tree {
    method SetHidden (line 119) | func (t *Tree) SetHidden(hidden bool) { t.Hide(hidden) }
    method Offset (line 122) | func (t *Tree) Offset(start, end int) *Tree {
    method Value (line 143) | func (t *Tree) Value() string {
    method SetValue (line 148) | func (t *Tree) SetValue(value any) {
    method String (line 153) | func (t *Tree) String() string {
    method Child (line 169) | func (t *Tree) Child(children ...any) *Tree {
    method ensureRenderer (line 226) | func (t *Tree) ensureRenderer() *renderer {
    method EnumeratorStyle (line 234) | func (t *Tree) EnumeratorStyle(style lipgloss.Style) *Tree {
    method EnumeratorStyleFunc (line 251) | func (t *Tree) EnumeratorStyleFunc(fn StyleFunc) *Tree {
    method IndenterStyle (line 262) | func (t *Tree) IndenterStyle(style lipgloss.Style) *Tree {
    method IndenterStyleFunc (line 279) | func (t *Tree) IndenterStyleFunc(fn StyleFunc) *Tree {
    method RootStyle (line 288) | func (t *Tree) RootStyle(style lipgloss.Style) *Tree {
    method ItemStyle (line 296) | func (t *Tree) ItemStyle(style lipgloss.Style) *Tree {
    method ItemStyleFunc (line 311) | func (t *Tree) ItemStyleFunc(fn StyleFunc) *Tree {
    method Enumerator (line 325) | func (t *Tree) Enumerator(enum Enumerator) *Tree {
    method Indenter (line 351) | func (t *Tree) Indenter(indenter Indenter) *Tree {
    method Width (line 359) | func (t *Tree) Width(width int) *Tree {
    method Children (line 365) | func (t *Tree) Children() Children {
    method Root (line 386) | func (t *Tree) Root(root any) *Tree {
  function ensureParent (line 207) | func ensureParent(nodes Children, item *Tree) (*Tree, int) {
  function Root (line 380) | func Root(root any) *Tree {
  function New (line 403) | func New() *Tree {

FILE: tree/tree_test.go
  function TestTree (line 14) | func TestTree(t *testing.T) {
  function TestTreeHidden (line 42) | func TestTreeHidden(t *testing.T) {
  function TestTreeAllHidden (line 60) | func TestTreeAllHidden(t *testing.T) {
  function TestTreeRoot (line 80) | func TestTreeRoot(t *testing.T) {
  function TestTreeStartsWithSubtree (line 93) | func TestTreeStartsWithSubtree(t *testing.T) {
  function TestTreeAddTwoSubTreesWithoutName (line 105) | func TestTreeAddTwoSubTreesWithoutName(t *testing.T) {
  function TestTreeLastNodeIsSubTree (line 132) | func TestTreeLastNodeIsSubTree(t *testing.T) {
  function TestTreeNil (line 146) | func TestTreeNil(t *testing.T) {
  function TestTreeCustom (line 163) | func TestTreeCustom(t *testing.T) {
  function TestTreeMultilineNode (line 198) | func TestTreeMultilineNode(t *testing.T) {
  function TestTreeSubTreeWithCustomEnumerator (line 221) | func TestTreeSubTreeWithCustomEnumerator(t *testing.T) {
  function TestTreeMixedEnumeratorSize (line 243) | func TestTreeMixedEnumeratorSize(t *testing.T) {
  function TestTreeStyleNilFuncs (line 267) | func TestTreeStyleNilFuncs(t *testing.T) {
  function TestTreeStyleAt (line 277) | func TestTreeStyleAt(t *testing.T) {
  function TestRootStyle (line 293) | func TestRootStyle(t *testing.T) {
  function TestAt (line 306) | func TestAt(t *testing.T) {
  function TestFilter (line 322) | func TestFilter(t *testing.T) {
  function TestNodeDataRemoveOutOfBounds (line 346) | func TestNodeDataRemoveOutOfBounds(t *testing.T) {
  function TestTreeTable (line 353) | func TestTreeTable(t *testing.T) {
  function TestAddItemWithAndWithoutRoot (line 378) | func TestAddItemWithAndWithoutRoot(t *testing.T) {
  function TestEmbedListWithinTree (line 404) | func TestEmbedListWithinTree(t *testing.T) {
  function TestMultilinePrefix (line 414) | func TestMultilinePrefix(t *testing.T) {
  function TestMultilinePrefixSubtree (line 434) | func TestMultilinePrefixSubtree(t *testing.T) {
  function TestMultilinePrefixInception (line 463) | func TestMultilinePrefixInception(t *testing.T) {
  function TestTypes (line 494) | func TestTypes(t *testing.T) {

FILE: unset.go
  method unset (line 4) | func (s *Style) unset(key propKey) {
  method UnsetBold (line 9) | func (s Style) UnsetBold() Style {
  method UnsetItalic (line 15) | func (s Style) UnsetItalic() Style {
  method UnsetUnderline (line 21) | func (s Style) UnsetUnderline() Style {
  method UnsetStrikethrough (line 26) | func (s Style) UnsetStrikethrough() Style {
  method UnsetReverse (line 32) | func (s Style) UnsetReverse() Style {
  method UnsetBlink (line 38) | func (s Style) UnsetBlink() Style {
  method UnsetFaint (line 44) | func (s Style) UnsetFaint() Style {
  method UnsetForeground (line 50) | func (s Style) UnsetForeground() Style {
  method UnsetBackground (line 56) | func (s Style) UnsetBackground() Style {
  method UnsetWidth (line 62) | func (s Style) UnsetWidth() Style {
  method UnsetHeight (line 68) | func (s Style) UnsetHeight() Style {
  method UnsetAlign (line 74) | func (s Style) UnsetAlign() Style {
  method UnsetAlignHorizontal (line 81) | func (s Style) UnsetAlignHorizontal() Style {
  method UnsetAlignVertical (line 87) | func (s Style) UnsetAlignVertical() Style {
  method UnsetPadding (line 93) | func (s Style) UnsetPadding() Style {
  method UnsetPaddingChar (line 103) | func (s Style) UnsetPaddingChar() Style {
  method UnsetPaddingLeft (line 109) | func (s Style) UnsetPaddingLeft() Style {
  method UnsetPaddingRight (line 115) | func (s Style) UnsetPaddingRight() Style {
  method UnsetPaddingTop (line 121) | func (s Style) UnsetPaddingTop() Style {
  method UnsetPaddingBottom (line 127) | func (s Style) UnsetPaddingBottom() Style {
  method UnsetColorWhitespace (line 133) | func (s Style) UnsetColorWhitespace() Style {
  method UnsetMargins (line 139) | func (s Style) UnsetMargins() Style {
  method UnsetMarginLeft (line 148) | func (s Style) UnsetMarginLeft() Style {
  method UnsetMarginRight (line 154) | func (s Style) UnsetMarginRight() Style {
  method UnsetMarginTop (line 160) | func (s Style) UnsetMarginTop() Style {
  method UnsetMarginBottom (line 166) | func (s Style) UnsetMarginBottom() Style {
  method UnsetMarginBackground (line 174) | func (s Style) UnsetMarginBackground() Style {
  method UnsetBorderStyle (line 180) | func (s Style) UnsetBorderStyle() Style {
  method UnsetBorderTop (line 186) | func (s Style) UnsetBorderTop() Style {
  method UnsetBorderRight (line 192) | func (s Style) UnsetBorderRight() Style {
  method UnsetBorderBottom (line 198) | func (s Style) UnsetBorderBottom() Style {
  method UnsetBorderLeft (line 204) | func (s Style) UnsetBorderLeft() Style {
  method UnsetBorderForeground (line 210) | func (s Style) UnsetBorderForeground() Style {
  method UnsetBorderTopForeground (line 220) | func (s Style) UnsetBorderTopForeground() Style {
  method UnsetBorderRightForeground (line 227) | func (s Style) UnsetBorderRightForeground() Style {
  method UnsetBorderBottomForeground (line 234) | func (s Style) UnsetBorderBottomForeground() Style {
  method UnsetBorderLeftForeground (line 241) | func (s Style) UnsetBorderLeftForeground() Style {
  method UnsetBorderForegroundBlend (line 248) | func (s Style) UnsetBorderForegroundBlend() Style {
  method UnsetBorderForegroundBlendOffset (line 255) | func (s Style) UnsetBorderForegroundBlendOffset() Style {
  method UnsetBorderBackground (line 262) | func (s Style) UnsetBorderBackground() Style {
  method UnsetBorderTopBackgroundColor (line 274) | func (s Style) UnsetBorderTopBackgroundColor() Style {
  method UnsetBorderTopBackground (line 280) | func (s Style) UnsetBorderTopBackground() Style {
  method UnsetBorderRightBackground (line 287) | func (s Style) UnsetBorderRightBackground() Style {
  method UnsetBorderBottomBackground (line 294) | func (s Style) UnsetBorderBottomBackground() Style {
  method UnsetBorderLeftBackground (line 300) | func (s Style) UnsetBorderLeftBackground() Style {
  method UnsetInline (line 306) | func (s Style) UnsetInline() Style {
  method UnsetMaxWidth (line 312) | func (s Style) UnsetMaxWidth() Style {
  method UnsetMaxHeight (line 318) | func (s Style) UnsetMaxHeight() Style {
  method UnsetTabWidth (line 324) | func (s Style) UnsetTabWidth() Style {
  method UnsetUnderlineSpaces (line 330) | func (s Style) UnsetUnderlineSpaces() Style {
  method UnsetStrikethroughSpaces (line 336) | func (s Style) UnsetStrikethroughSpaces() Style {
  method UnsetTransform (line 342) | func (s Style) UnsetTransform() Style {
  method UnsetHyperlink (line 348) | func (s Style) UnsetHyperlink() Style {
  method UnsetString (line 356) | func (s Style) UnsetString() Style {

FILE: whitespace.go
  type whitespace (line 10) | type whitespace struct
    method render (line 25) | func (w whitespace) render(width int) string {
  function newWhitespace (line 16) | func newWhitespace(opts ...WhitespaceOption) *whitespace {
  type WhitespaceOption (line 62) | type WhitespaceOption
  function WithWhitespaceStyle (line 65) | func WithWhitespaceStyle(s Style) WhitespaceOption {
  function WithWhitespaceChars (line 72) | func WithWhitespaceChars(s string) WhitespaceOption {

FILE: whitespace_test.go
  function TestWhitespaceRenderWithTab (line 8) | func TestWhitespaceRenderWithTab(t *testing.T) {
  function TestWhitespaceRenderWithZeroWidthChar (line 27) | func TestWhitespaceRenderWithZeroWidthChar(t *testing.T) {
  function TestWhitespaceRenderNormal (line 45) | func TestWhitespaceRenderNormal(t *testing.T) {

FILE: wrap.go
  function Wrap (line 12) | func Wrap(s string, width int, breakpoints string) string {
  type WrapWriter (line 26) | type WrapWriter struct
    method Style (line 55) | func (w *WrapWriter) Style() uv.Style {
    method Link (line 60) | func (w *WrapWriter) Link() uv.Link {
    method Write (line 65) | func (w *WrapWriter) Write(p []byte) (int, error) {
    method Close (line 95) | func (w *WrapWriter) Close() error {
  function NewWrapWriter (line 34) | func NewWrapWriter(w io.Writer) *WrapWriter {

FILE: writer.go
  function Println (line 26) | func Println(v ...any) (int, error) {
  function Printf (line 40) | func Printf(format string, v ...any) (int, error) {
  function Print (line 53) | func Print(v ...any) (int, error) {
  function Fprint (line 67) | func Fprint(w io.Writer, v ...any) (int, error) {
  function Fprintln (line 81) | func Fprintln(w io.Writer, v ...any) (int, error) {
  function Fprintf (line 95) | func Fprintf(w io.Writer, format string, v ...any) (int, error) {
  function Sprint (line 110) | func Sprint(v ...any) string {
  function Sprintln (line 131) | func Sprintln(v ...any) string {
  function Sprintf (line 152) | func Sprintf(format string, v ...any) string {
Condensed preview — 300 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (643K chars).
[
  {
    "path": ".editorconfig",
    "chars": 276,
    "preview": "# https://editorconfig.org/\n\nroot = true\n\n[*]\ncharset = utf-8\ninsert_final_newline = true\ntrim_trailing_whitespace = tru"
  },
  {
    "path": ".gitattributes",
    "chars": 39,
    "preview": "*.golden linguist-generated=true -text\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 30,
    "preview": "*  @meowgorithm @aymanbagabas\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 876,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 120,
    "preview": "blank_issues_enabled: true\ncontact_links:\n- name: Discord\n  url: https://charm.sh/discord\n  about: Chat on our Discord.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 604,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 1443,
    "preview": "version: 2\n\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n      day:"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 214,
    "preview": "name: build\n\non:\n  push:\n    branches:\n      - \"master\"\n  pull_request:\n\njobs:\n  build:\n    uses: charmbracelet/meta/.gi"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "chars": 1003,
    "preview": "name: coverage\non: [push, pull_request]\n\njobs:\n  coverage:\n    strategy:\n      matrix:\n        go-version: [^1]\n        "
  },
  {
    "path": ".github/workflows/dependabot-sync.yml",
    "chars": 419,
    "preview": "name: dependabot-sync\non:\n  schedule:\n    - cron: \"0 0 * * 0\" # every Sunday at midnight\n  workflow_dispatch: # allows m"
  },
  {
    "path": ".github/workflows/lint-sync.yml",
    "chars": 271,
    "preview": "name: lint-sync\non:\n  schedule:\n    # every Sunday at midnight\n    - cron: \"0 0 * * 0\"\n  workflow_dispatch: # allows man"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 208,
    "preview": "name: lint\non:\n  push:\n  pull_request:\n\njobs:\n  lint:\n    uses: charmbracelet/meta/.github/workflows/lint.yml@main\n    w"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1106,
    "preview": "name: goreleaser\n\non:\n  push:\n    tags:\n      - v*.*.*\n\nconcurrency:\n  group: goreleaser\n  cancel-in-progress: true\n\njob"
  },
  {
    "path": ".gitignore",
    "chars": 39,
    "preview": "ssh_example_ed25519*\n/tmp\n**/.crush/**\n"
  },
  {
    "path": ".golangci.yml",
    "chars": 764,
    "preview": "version: \"2\"\nrun:\n  tests: false\nlinters:\n  enable:\n    - bodyclose\n    - exhaustive\n    - goconst\n    - godot\n    - gom"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 158,
    "preview": "includes:\n  - from_url:\n      url: charmbracelet/meta/main/goreleaser-lib.yaml\n# yaml-language-server: $schema=https://g"
  },
  {
    "path": "LICENSE",
    "chars": 1081,
    "preview": "MIT License\n\nCopyright (c) 2021-2026 Charmbracelet, Inc.\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 25992,
    "preview": "# Lip Gloss\n\n<p >\n    <img src=\"https://github.com/user-attachments/assets/d13bbe1a-d2b2-4d18-9302-419a0bc3f579\" width=\""
  },
  {
    "path": "Taskfile.yaml",
    "chars": 366,
    "preview": "# https://taskfile.dev\n\nversion: \"3\"\n\ntasks:\n  lint:\n    desc: Run base linters\n    cmds:\n      - golangci-lint run\n\n  t"
  },
  {
    "path": "UPGRADE_GUIDE_V2.md",
    "chars": 13291,
    "preview": "# Lip Gloss v2 Upgrade Guide\n\nThis guide covers migrating from Lip Gloss v1 (`github.com/charmbracelet/lipgloss`)\nto Lip"
  },
  {
    "path": "align.go",
    "chars": 2126,
    "preview": "package lipgloss\n\nimport (\n\t\"strings\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// Perform text alignment. If the string is"
  },
  {
    "path": "align_test.go",
    "chars": 1555,
    "preview": "package lipgloss\n\nimport \"testing\"\n\nfunc TestAlignTextVertical(t *testing.T) {\n\ttests := []struct {\n\t\tstr    string\n\t\tpo"
  },
  {
    "path": "ansi_unix.go",
    "chars": 148,
    "preview": "//go:build !windows\n\npackage lipgloss\n\nimport \"os\"\n\n// EnableLegacyWindowsANSI is only needed on Windows.\nfunc EnableLeg"
  },
  {
    "path": "ansi_windows.go",
    "chars": 860,
    "preview": "//go:build windows\n\npackage lipgloss\n\nimport (\n\t\"os\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n// EnableLegacyWindowsANSI enables "
  },
  {
    "path": "blending.go",
    "chars": 5567,
    "preview": "package lipgloss\n\nimport (\n\t\"image/color\"\n\t\"math\"\n\t\"slices\"\n\n\t\"github.com/lucasb-eyer/go-colorful\"\n)\n\n// Blend1D blends "
  },
  {
    "path": "blending_test.go",
    "chars": 8496,
    "preview": "package lipgloss\n\nimport (\n\t\"image/color\"\n\t\"testing\"\n)\n\nfunc TestBlend1D(t *testing.T) {\n\ttests := []struct {\n\t\tname    "
  },
  {
    "path": "borders.go",
    "chars": 13622,
    "preview": "package lipgloss\n\nimport (\n\t\"image/color\"\n\t\"slices\"\n\t\"strings\"\n\t\"unicode/utf8\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n\t\"git"
  },
  {
    "path": "borders_test.go",
    "chars": 4294,
    "preview": "package lipgloss\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rivo/uniseg\"\n)\n\nfunc BenchmarkBorderRendering(b *testing.B) {\n\tdimen"
  },
  {
    "path": "canvas.go",
    "chars": 2100,
    "preview": "package lipgloss\n\nimport (\n\tuv \"github.com/charmbracelet/ultraviolet\"\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// Canvas is"
  },
  {
    "path": "canvas_test.go",
    "chars": 1090,
    "preview": "package lipgloss\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCanvasRender(t *testing.T) {\n\tc := NewCanvas(5, 3)\n\n\t// Fil"
  },
  {
    "path": "color.go",
    "chars": 8938,
    "preview": "package lipgloss\n\nimport (\n\t\"cmp\"\n\t\"errors\"\n\t\"image/color\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/charmbracelet/colorprofil"
  },
  {
    "path": "color_test.go",
    "chars": 8902,
    "preview": "package lipgloss\n\nimport (\n\t\"fmt\"\n\t\"image/color\"\n\t\"testing\"\n)\n\n// hex converts a color to a hex string or panics if inva"
  },
  {
    "path": "compat/color.go",
    "chars": 2106,
    "preview": "package compat\n\nimport (\n\t\"image/color\"\n\t\"os\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"github.com/charmbracelet/colorprofile\"\n)\n\nvar"
  },
  {
    "path": "compat/doc.go",
    "chars": 725,
    "preview": "// Package compat is a compatibility layer for Lip Gloss that provides a way to\n// deal with the hassle of setting up a "
  },
  {
    "path": "examples/blending/border-blend-rotation/bubbletea/main.go",
    "chars": 1408,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\ttea \"charm.land/bubbletea/v2\"\n\t\"charm.land/lipgloss/v2\"\n)\n\nconst (\n\tborder"
  },
  {
    "path": "examples/blending/linear-1d/bubbletea/main.go",
    "chars": 3601,
    "preview": "// This example demonstrates how to use the colors.Blend1D function to create\n// beautiful color gradients in a Bubble T"
  },
  {
    "path": "examples/blending/linear-1d/standalone/main.go",
    "chars": 1925,
    "preview": "// This example demonstrates how to use the colors.Blend1D function to create\n// beautiful color gradients in a standalo"
  },
  {
    "path": "examples/blending/linear-2d/bubbletea/main.go",
    "chars": 5149,
    "preview": "// This example demonstrates how to use the colors.Blend2D function to create\n// beautiful 2D color gradients in a Bubbl"
  },
  {
    "path": "examples/blending/linear-2d/standalone/main.go",
    "chars": 3034,
    "preview": "// This example demonstrates how to use the colors.Blend2D function to create\n// beautiful 2D color gradients in a stand"
  },
  {
    "path": "examples/brightness/main.go",
    "chars": 1795,
    "preview": "// This example demonstrates how to use the colors.Lighten and colors.Darken functions\n// to create progressive brightne"
  },
  {
    "path": "examples/canvas/main.go",
    "chars": 2182,
    "preview": "package main\n\nimport (\n\t\"image/color\"\n\t\"os\"\n\t\"strings\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"github.com/charmbracelet/x/exp/charm"
  },
  {
    "path": "examples/color/bubbletea/main.go",
    "chars": 3559,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\ttea \"charm.land/bubbletea/v2\"\n\t\"charm.land/lipgloss/v2\"\n)\n\n// Style definitions.\nt"
  },
  {
    "path": "examples/color/standalone/main.go",
    "chars": 2468,
    "preview": "// This example illustrates how to detect the terminal's background color and\n// choose either light or dark colors acco"
  },
  {
    "path": "examples/compat/bubbletea/main.go",
    "chars": 3261,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\ttea \"charm.land/bubbletea/v2\"\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/c"
  },
  {
    "path": "examples/compat/standalone/main.go",
    "chars": 2373,
    "preview": "// This example illustrates how to detect the terminal's background color and\n// choose either light or dark colors acco"
  },
  {
    "path": "examples/go.mod",
    "chars": 1963,
    "preview": "module examples\n\ngo 1.24.3\n\ntoolchain go1.24.4\n\nreplace charm.land/lipgloss/v2 => ../\n\nrequire (\n\tcharm.land/bubbletea/v"
  },
  {
    "path": "examples/go.sum",
    "chars": 6968,
    "preview": "charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251201184111-551c60ee5a5c h1:Jn9nugUf2ddyARHsA79zsWl7szy7dV7HpBj645Sp6DU=\ncharm."
  },
  {
    "path": "examples/layout/main.go",
    "chars": 11759,
    "preview": "package main\n\n// This example demonstrates various Lip Gloss style and layout features.\n\nimport (\n\t\"fmt\"\n\t\"image/color\"\n"
  },
  {
    "path": "examples/list/duckduckgoose/main.go",
    "chars": 562,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n)\n\nfunc duckDuckGooseEnumerator(items li"
  },
  {
    "path": "examples/list/glow/main.go",
    "chars": 1183,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n)\n\ntype Document struct {\n\tName string\n\t"
  },
  {
    "path": "examples/list/grocery/main.go",
    "chars": 1459,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n)\n\nvar purchased = []string{\n\t\"Bananas\","
  },
  {
    "path": "examples/list/roman/main.go",
    "chars": 463,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n)\n\nfunc main() {\n\tenumeratorStyle := lip"
  },
  {
    "path": "examples/list/simple/main.go",
    "chars": 231,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n)\n\nfunc main() {\n\tl := list.New(\n\t\t\"A\",\n"
  },
  {
    "path": "examples/list/sublist/main.go",
    "chars": 6398,
    "preview": "package main\n\nimport (\n\t\"os\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n\t\"charm.land/lipgloss/v2/table\"\n)"
  },
  {
    "path": "examples/ssh/main.go",
    "chars": 4044,
    "preview": "package main\n\n// This example demonstrates how to use a colorprofile to accurately detect\n// client terminal color capab"
  },
  {
    "path": "examples/table/ansi/main.go",
    "chars": 327,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/table\"\n)\n\nfunc main() {\n\ts := lipgloss.NewStyl"
  },
  {
    "path": "examples/table/chess/main.go",
    "chars": 1129,
    "preview": "package main\n\nimport (\n\t\"strings\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/table\"\n)\n\nfunc main() {\n\tlabelStyl"
  },
  {
    "path": "examples/table/demo.tape",
    "chars": 267,
    "preview": "Output table.gif\n\nSet Height 900\nSet Width 1600\nSet Padding 80\nSet FontSize 42\n\nHide\nType \"go build -o table\"\nEnter\nCtrl"
  },
  {
    "path": "examples/table/languages/main.go",
    "chars": 1836,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/table\"\n)\n\nconst (\n\tpurple    = \"99\"\n\tgray     "
  },
  {
    "path": "examples/table/mindy/main.go",
    "chars": 1298,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/table\"\n)\n\nfunc main() {\n\tlabelStyle :="
  },
  {
    "path": "examples/table/pokemon/main.go",
    "chars": 3755,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"image/color\"\n\t\"strings\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/table\"\n)\n\nfu"
  },
  {
    "path": "examples/tree/background/main.go",
    "chars": 753,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\nfunc main() {\n\tdarkBg := lipgloss.New"
  },
  {
    "path": "examples/tree/files/main.go",
    "chars": 1449,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree"
  },
  {
    "path": "examples/tree/makeup/main.go",
    "chars": 718,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\nfunc main() {\n\tenumeratorStyle := lip"
  },
  {
    "path": "examples/tree/rounded/main.go",
    "chars": 712,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\nfunc main() {\n\titemStyle := lipgloss."
  },
  {
    "path": "examples/tree/selection/main.go",
    "chars": 2713,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\nconst selected = \"/Us"
  },
  {
    "path": "examples/tree/simple/main.go",
    "chars": 374,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\nfunc main() {\n\tt := tree.Root(\".\").\n\t"
  },
  {
    "path": "examples/tree/styles/main.go",
    "chars": 537,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\nfunc main() {\n\tpurple := lipg"
  },
  {
    "path": "examples/tree/toggle/main.go",
    "chars": 1564,
    "preview": "package main\n\nimport (\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/tree\"\n)\n\ntype styles struct {\n\tbase,\n\tblock,\n\t"
  },
  {
    "path": "get.go",
    "chars": 18753,
    "preview": "package lipgloss\n\nimport (\n\t\"image/color\"\n\t\"strings\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// GetBold returns the style"
  },
  {
    "path": "go.mod",
    "chars": 1046,
    "preview": "module charm.land/lipgloss/v2\n\nretract v2.0.0-beta1 // We add a \".\" after the \"beta\" in the version number.\n\ngo 1.25.0\n\n"
  },
  {
    "path": "go.sum",
    "chars": 3428,
    "preview": "github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=\ngithub.com/aymanbagabas/go-udiff"
  },
  {
    "path": "join.go",
    "chars": 4109,
    "preview": "package lipgloss\n\nimport (\n\t\"math\"\n\t\"strings\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// JoinHorizontal is a utility func"
  },
  {
    "path": "join_test.go",
    "chars": 1099,
    "preview": "package lipgloss\n\nimport \"testing\"\n\nfunc TestJoinVertical(t *testing.T) {\n\ttype test struct {\n\t\tname     string\n\t\tresult"
  },
  {
    "path": "layer.go",
    "chars": 8052,
    "preview": "package lipgloss\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t\"slices\"\n\n\tuv \"github.com/charmbracelet/ultraviolet\"\n)\n\n// Layer represents "
  },
  {
    "path": "lipgloss.go",
    "chars": 119,
    "preview": "// Package lipgloss provides style definitions for nice terminal layouts. Built\n// with TUIs in mind.\npackage lipgloss\n"
  },
  {
    "path": "list/enumerator.go",
    "chars": 2554,
    "preview": "package list\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Enumerator enumerates a list. Given a list of items and the index of the\n"
  },
  {
    "path": "list/list.go",
    "chars": 6850,
    "preview": "// Package list allows you to build lists, as simple or complicated as you need.\n//\n// Simply, define a list with some i"
  },
  {
    "path": "list/list_test.go",
    "chars": 7607,
    "preview": "package list_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"unicode\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"charm.land/lipgloss/v2/list\"\n\t"
  },
  {
    "path": "list/testdata/TestComplexSublist.golden",
    "chars": 2172,
    "preview": "• Foo\n• Bar\n  • foo2\n  • bar2\n• Qux\n   \u001b[38;5;99mI.\u001b[m aaa\n  \u001b[38;5;99mII.\u001b[m bbb\n• Deep\n  \u001b[38;5;212mA.\u001b[m foo\n  \u001b[38;5"
  },
  {
    "path": "list/testdata/TestEnumerators/alphabet.golden",
    "chars": 20,
    "preview": "A. Foo\nB. Bar\nC. Baz"
  },
  {
    "path": "list/testdata/TestEnumerators/arabic.golden",
    "chars": 20,
    "preview": "1. Foo\n2. Bar\n3. Baz"
  },
  {
    "path": "list/testdata/TestEnumerators/asterisk.golden",
    "chars": 17,
    "preview": "* Foo\n* Bar\n* Baz"
  },
  {
    "path": "list/testdata/TestEnumerators/bullet.golden",
    "chars": 17,
    "preview": "• Foo\n• Bar\n• Baz"
  },
  {
    "path": "list/testdata/TestEnumerators/dash.golden",
    "chars": 17,
    "preview": "- Foo\n- Bar\n- Baz"
  },
  {
    "path": "list/testdata/TestEnumerators/roman.golden",
    "chars": 26,
    "preview": "  I. Foo\n II. Bar\nIII. Baz"
  },
  {
    "path": "list/testdata/TestEnumeratorsAlign.golden",
    "chars": 1399,
    "preview": "       I. Foo\n      II. Foo\n     III. Foo\n      IV. Foo\n       V. Foo\n      VI. Foo\n     VII. Foo\n    VIII. Foo\n      IX"
  },
  {
    "path": "list/testdata/TestEnumeratorsTransform/alphabet_lower.golden",
    "chars": 20,
    "preview": "a. Foo\nb. Bar\nc. Baz"
  },
  {
    "path": "list/testdata/TestEnumeratorsTransform/arabic).golden",
    "chars": 20,
    "preview": "1) Foo\n2) Bar\n3) Baz"
  },
  {
    "path": "list/testdata/TestEnumeratorsTransform/bullet_is_dash.golden",
    "chars": 17,
    "preview": "- Foo\n- Bar\n- Baz"
  },
  {
    "path": "list/testdata/TestEnumeratorsTransform/roman_within_().golden",
    "chars": 29,
    "preview": "  (i) Foo\n (ii) Bar\n(iii) Baz"
  },
  {
    "path": "list/testdata/TestList.golden",
    "chars": 17,
    "preview": "• Foo\n• Bar\n• Baz"
  },
  {
    "path": "list/testdata/TestListIntegers.golden",
    "chars": 11,
    "preview": "• 1\n• 2\n• 3"
  },
  {
    "path": "list/testdata/TestListItems.golden",
    "chars": 17,
    "preview": "• Foo\n• Bar\n• Baz"
  },
  {
    "path": "list/testdata/TestMultiline.golden",
    "chars": 57,
    "preview": "• Item1 \n  line 2\n  line 3\n• Item2 \n  line 2\n  line 3\n• 3"
  },
  {
    "path": "list/testdata/TestSubListItems2.golden",
    "chars": 87,
    "preview": "• S\n  • neovim\n  • vscode\n• HI\n  • vim\n  • doom emacs\n• Parent 2\n  • I like fuzzy socks"
  },
  {
    "path": "list/testdata/TestSublist.golden",
    "chars": 52,
    "preview": "• Foo\n• Bar\n    I. Hi\n   II. Hello\n  III. Halo\n• Qux"
  },
  {
    "path": "list/testdata/TestSublistItems.golden",
    "chars": 42,
    "preview": "• A\n• B\n• C\n    I. D\n   II. E\n  III. F\n• G"
  },
  {
    "path": "position.go",
    "chars": 3270,
    "preview": "package lipgloss\n\nimport (\n\t\"math\"\n\t\"strings\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// Position represents a position a"
  },
  {
    "path": "query.go",
    "chars": 2564,
    "preview": "package lipgloss\n\nimport (\n\t\"fmt\"\n\t\"image/color\"\n\t\"os\"\n\t\"runtime\"\n\n\t\"github.com/charmbracelet/x/term\"\n)\n\nfunc background"
  },
  {
    "path": "ranges.go",
    "chars": 1197,
    "preview": "package lipgloss\n\nimport (\n\t\"strings\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// StyleRanges applying styling to ranges i"
  },
  {
    "path": "ranges_test.go",
    "chars": 2590,
    "preview": "package lipgloss\n\nimport (\n\t\"testing\"\n)\n\nfunc TestStyleRanges(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\ti"
  },
  {
    "path": "runes.go",
    "chars": 891,
    "preview": "package lipgloss\n\nimport (\n\t\"strings\"\n)\n\n// StyleRunes apply a given style to runes at the given indices in the string.\n"
  },
  {
    "path": "runes_test.go",
    "chars": 1009,
    "preview": "package lipgloss\n\nimport (\n\t\"testing\"\n)\n\nfunc TestStyleRunes(t *testing.T) {\n\tmatchedStyle := NewStyle().Reverse(true)\n\t"
  },
  {
    "path": "set.go",
    "chars": 26780,
    "preview": "package lipgloss\n\nimport (\n\t\"image/color\"\n\t\"strings\"\n)\n\n// Set a value on the underlying rules map.\nfunc (s *Style) set("
  },
  {
    "path": "size.go",
    "chars": 1118,
    "preview": "package lipgloss\n\nimport (\n\t\"strings\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// Width returns the cell width of characte"
  },
  {
    "path": "size_test.go",
    "chars": 725,
    "preview": "package lipgloss\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc BenchmarkWidthSimple(b *testing.B) {\n\tsimpleStrings := []string"
  },
  {
    "path": "style.go",
    "chars": 14449,
    "preview": "package lipgloss\n\nimport (\n\t\"image/color\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\nconst (\n\t// NBSP "
  },
  {
    "path": "style_test.go",
    "chars": 19330,
    "preview": "package lipgloss\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestUnderline(t *testing.T) {\n\tt.Parallel()\n\n"
  },
  {
    "path": "table/resizing.go",
    "chars": 13003,
    "preview": "package table\n\nimport (\n\t\"math\"\n\t\"strings\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"github.com/charmbracelet/x/ansi\"\n)\n\n// resize re"
  },
  {
    "path": "table/rows.go",
    "chars": 2750,
    "preview": "package table\n\n// Data is the interface that wraps the basic methods of a table model.\ntype Data interface {\n\t// At retu"
  },
  {
    "path": "table/table.go",
    "chars": 14057,
    "preview": "// Package table provides a styled table renderer for terminals.\npackage table\n\nimport (\n\t\"strings\"\n\n\t\"charm.land/lipglo"
  },
  {
    "path": "table/table_test.go",
    "chars": 57335,
    "preview": "package table\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"charm.land/lipgloss/v2\"\n\t\"github.com/charmbracelet/x/exp/golden\""
  },
  {
    "path": "table/testdata/TestBorderColumnsWithExtraRows.golden",
    "chars": 478,
    "preview": "┌───────────────────────────────────────────────────┐\n│ LANGUAGE     FORMAL                               │\n├───────────"
  },
  {
    "path": "table/testdata/TestBorderStyles/ASCIIBorder.golden",
    "chars": 352,
    "preview": "+----------+--------------+-----------+\n| LANGUAGE |    FORMAL    | INFORMAL  |\n+----------+--------------+-----------+\n"
  },
  {
    "path": "table/testdata/TestBorderStyles/BlockBorder.golden",
    "chars": 352,
    "preview": "███████████████████████████████████████\n█ LANGUAGE █    FORMAL    █ INFORMAL  █\n███████████████████████████████████████\n"
  },
  {
    "path": "table/testdata/TestBorderStyles/HiddenBorder.golden",
    "chars": 352,
    "preview": "                                       \n  LANGUAGE      FORMAL      INFORMAL   \n                                       \n"
  },
  {
    "path": "table/testdata/TestBorderStyles/MarkdownBorder.golden",
    "chars": 272,
    "preview": "| LANGUAGE |    FORMAL    | INFORMAL  |\n|----------|--------------|-----------|\n| Chinese  | Nǐn hǎo      | Nǐ hǎo    |\n"
  },
  {
    "path": "table/testdata/TestBorderStyles/NormalBorder.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestBorderStyles/RoundedBorder.golden",
    "chars": 352,
    "preview": "╭──────────┬──────────────┬───────────╮\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestBorderStyles/ThickBorder.golden",
    "chars": 352,
    "preview": "┏━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━┓\n┃ LANGUAGE ┃    FORMAL    ┃ INFORMAL  ┃\n┣━━━━━━━━━━╋━━━━━━━━━━━━━━╋━━━━━━━━━━━┫\n"
  },
  {
    "path": "table/testdata/TestBorderedCells.golden",
    "chars": 854,
    "preview": "┌──────────────────────┬───────────────────┬───────────┐\n│┌────────────────────┐│┌─────────────────┐│┌─────────┐│\n││Name"
  },
  {
    "path": "table/testdata/TestCarriageReturn.golden",
    "chars": 191,
    "preview": "┌──┬────┬──┬──┐\n│a0│b0  │c0│d0│\n│a1│b1.0│c1│d1│\n│  │b1.1│  │  │\n│  │b1.2│  │  │\n│  │b1.3│  │  │\n│  │b1.4│  │  │\n│  │b1.5"
  },
  {
    "path": "table/testdata/TestContentWrapping/LongHeaderContentLongAndShortRows.golden",
    "chars": 890,
    "preview": "┌─────────────┬────────────────────────────────────────────────┬───────────────┐\n│ Destination │ Why are you going on th"
  },
  {
    "path": "table/testdata/TestContentWrapping/LongRowContent.golden",
    "chars": 890,
    "preview": "┌─────────┬────────────────────────────────────────┬──────┬──────────┬─────────┐\n│  Name   │              Description   "
  },
  {
    "path": "table/testdata/TestContentWrapping/LongRowContentNoWrap.golden",
    "chars": 404,
    "preview": "┌─────────┬────────────────────────────────────────┬──────┬──────────┬─────────┐\n│  Name   │              Description   "
  },
  {
    "path": "table/testdata/TestContentWrapping/LongRowContentNoWrapCustomMargins.golden",
    "chars": 404,
    "preview": "┌───────────┬───────────────────────────────────────┬───────┬─────────┬────────┐\n│   Name    │              Description "
  },
  {
    "path": "table/testdata/TestContentWrapping/LongRowContentNoWrapNoMargins.golden",
    "chars": 404,
    "preview": "┌───────┬────────────────────────────────────────────────┬────┬────────┬───────┐\n│ Name  │                  Description "
  },
  {
    "path": "table/testdata/TestContentWrapping/LongTextDifferentLanguages.golden",
    "chars": 4287,
    "preview": "┌──────────────┬───────────────┬───────────────┬───────────────┬───────────────┐\n│    Hello     │     你好      │     مرحب"
  },
  {
    "path": "table/testdata/TestContentWrapping/MissingRowContent.golden",
    "chars": 890,
    "preview": "┌─────────┬────────────────────────────────────────┬──────┬──────────┬─────────┐\n│  Name   │              Description   "
  },
  {
    "path": "table/testdata/TestContentWrapping_ColumnWidth/LongHeaderContentLongAndShortRows.golden",
    "chars": 1052,
    "preview": "┌─────────────────────────────────────────┬──────────────────────────────┬─────┐\n│Destination                           "
  },
  {
    "path": "table/testdata/TestContentWrapping_ColumnWidth/LongRowContent.golden",
    "chars": 1052,
    "preview": "┌─────────────┬──────────────────────────────┬─────┬─────────────┬─────────────┐\n│Name         │Description             "
  },
  {
    "path": "table/testdata/TestContentWrapping_ColumnWidth/LongTextDifferentLanguages.golden",
    "chars": 5988,
    "preview": "┌─────────────┬──────────────────────────────┬─────┬─────────────┬─────────────┐\n│Hello        │你好                      "
  },
  {
    "path": "table/testdata/TestContentWrapping_ColumnWidth/MissingRowContent.golden",
    "chars": 1052,
    "preview": "┌─────────────┬──────────────────────────────┬─────┬─────────────┬─────────────┐\n│Name         │Description             "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithHeight/LongHeaderContentLongAndShortRows/HeightOf05.golden",
    "chars": 304,
    "preview": "┌─────────────┬─────────────────────────────┬──────────────┐\n│ Destination │ Why are you going on this … │ Affordabili… "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithHeight/LongHeaderContentLongAndShortRows/HeightOf15.golden",
    "chars": 914,
    "preview": "┌─────────────┬─────────────────────────────┬──────────────┐\n│ Destination │ Why are you going on this … │ Affordabili… "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithHeight/LongHeaderContentLongAndShortRows/HeightOf25.golden",
    "chars": 1402,
    "preview": "┌─────────────┬─────────────────────────────┬──────────────┐\n│ Destination │ Why are you going on this … │ Affordabili… "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithHeight/LongHeaderContentLongAndShortRows/HeightOf35.golden",
    "chars": 1402,
    "preview": "┌─────────────┬─────────────────────────────┬──────────────┐\n│ Destination │ Why are you going on this … │ Affordabili… "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithMargins/LongHeaderContentLongAndShortRows.golden",
    "chars": 1052,
    "preview": "┌───────────────────┬───────────────────────────────────────┬──────────────────┐\n│    Destination    │    Why are you go"
  },
  {
    "path": "table/testdata/TestContentWrapping_WithMargins/LongRowContent.golden",
    "chars": 1781,
    "preview": "┌───────────────┬────────────────────────┬───────────┬─────────────┬───────────┐\n│    Name       │    Description       "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithMargins/LongTextDifferentLanguages.golden",
    "chars": 8744,
    "preview": "┌──────────────┬───────────────┬───────────────┬───────────────┬───────────────┐\n│    Hello     │    你好       │    مرحبً"
  },
  {
    "path": "table/testdata/TestContentWrapping_WithMargins/MissingRowContent.golden",
    "chars": 1295,
    "preview": "┌───────────────┬────────────────────────────────┬───────────┬────────┬────────┐\n│    Name       │    Description       "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithPadding/LongHeaderContentLongAndShortRows.golden",
    "chars": 890,
    "preview": "┌─────────────┬────────────────────────────────────────────────┬───────────────┐\n│ Destination │ Why are you going on th"
  },
  {
    "path": "table/testdata/TestContentWrapping_WithPadding/LongRowContent.golden",
    "chars": 890,
    "preview": "┌─────────┬────────────────────────────────────────┬──────┬──────────┬─────────┐\n│ Name    │ Description                "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithPadding/LongTextDifferentLanguages.golden",
    "chars": 3639,
    "preview": "┌───────┬────────────────┬─────────────────┬─────────────────┬─────────────────┐\n│ Hello │ 你好           │ مرحبًا        "
  },
  {
    "path": "table/testdata/TestContentWrapping_WithPadding/MissingRowContent.golden",
    "chars": 890,
    "preview": "┌─────────┬────────────────────────────────────────┬──────┬──────────┬─────────┐\n│ Name    │ Description                "
  },
  {
    "path": "table/testdata/TestExtraPaddingHeading.golden",
    "chars": 1448,
    "preview": "┌────────────────────────┬─────────────────────┬─────────────┐\n│                        │                     │         "
  },
  {
    "path": "table/testdata/TestExtraPaddingHeadingLong.golden",
    "chars": 1127,
    "preview": "┌──────────────┬──────────────┬──────────────┐\n│              │              │              │\n│              │          "
  },
  {
    "path": "table/testdata/TestFilter.golden",
    "chars": 312,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestFilterInverse.golden",
    "chars": 169,
    "preview": "┌──────────┬─────────┬──────────┐\n│ LANGUAGE │ FORMAL  │ INFORMAL │\n├──────────┼─────────┼──────────┤\n│ French   │ Bonjo"
  },
  {
    "path": "table/testdata/TestInnerBordersOnly.golden",
    "chars": 410,
    "preview": " LANGUAGE │    FORMAL    │ INFORMAL  \n──────────┼──────────────┼───────────\n Chinese  │ Nǐn hǎo      │ Nǐ hǎo    \n──────"
  },
  {
    "path": "table/testdata/TestMoreCellsThanHeaders.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │           │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestMoreCellsThanHeadersExtra.golden",
    "chars": 514,
    "preview": "┌──────────┬──────────────┬───────────┬────────┬────────┐\n│ LANGUAGE │    FORMAL    │           │        │        │\n├───"
  },
  {
    "path": "table/testdata/TestNoFinalEmptyRowWhenOverflow.golden",
    "chars": 629,
    "preview": "┌────┬────────────┬──────────┬──────────┐\n│Rank│City        │Country   │Population│\n├────┼────────────┼──────────┼──────"
  },
  {
    "path": "table/testdata/TestStyleFunc/MarginAndPaddingSet.golden",
    "chars": 3153,
    "preview": "┌────────────┬────────────────┬─────────────┐\n│  LANGUAGE  │     FORMAL     │  INFORMAL   │\n├────────────┼──────────────"
  },
  {
    "path": "table/testdata/TestStyleFunc/RightAlignedTextWithMargins.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTable.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableANSI.golden",
    "chars": 311,
    "preview": "┌───────────┬────────┬──────┐\n│   Fruit   │ Color  │ \u001b[31mC\u001b[0m\u001b[32mo\u001b[0m\u001b[34md\u001b[0m\u001b[33me\u001b[0m │\n├───────────┼────────┼──"
  },
  {
    "path": "table/testdata/TestTableBorder.golden",
    "chars": 352,
    "preview": "╔══════════╦══════════════╦═══════════╗\n║ LANGUAGE ║    FORMAL    ║ INFORMAL  ║\n╠══════════╬══════════════╬═══════════╣\n"
  },
  {
    "path": "table/testdata/TestTableEmpty.golden",
    "chars": 131,
    "preview": "┌──────────┬────────┬──────────┐\n│ LANGUAGE │ FORMAL │ INFORMAL │\n├──────────┼────────┼──────────┤\n└──────────┴────────┴"
  },
  {
    "path": "table/testdata/TestTableExample.golden",
    "chars": 1140,
    "preview": "\u001b[38;5;99m┌\u001b[m\u001b[38;5;99m──────────\u001b[m\u001b[38;5;99m┬\u001b[m\u001b[38;5;99m───────────────────────────────\u001b[m\u001b[38;5;99m┬\u001b[m\u001b[38;5;99m─"
  },
  {
    "path": "table/testdata/TestTableHeightExact.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightExtra.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf01.golden",
    "chars": 39,
    "preview": "┌──────────┬──────────────┬───────────┐"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf02.golden",
    "chars": 79,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf03.golden",
    "chars": 119,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf04.golden",
    "chars": 159,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf05.golden",
    "chars": 199,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf06.golden",
    "chars": 239,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf07.golden",
    "chars": 279,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf08.golden",
    "chars": 312,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRow/HeightOf09.golden",
    "chars": 352,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf01.golden",
    "chars": 39,
    "preview": "┌──────────┬──────────────┬───────────┐"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf02.golden",
    "chars": 79,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf03.golden",
    "chars": 119,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf04.golden",
    "chars": 159,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf05.golden",
    "chars": 199,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf06.golden",
    "chars": 239,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf07.golden",
    "chars": 279,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf08.golden",
    "chars": 319,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf09.golden",
    "chars": 359,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf10.golden",
    "chars": 359,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf11.golden",
    "chars": 359,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf12.golden",
    "chars": 479,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf13.golden",
    "chars": 479,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf14.golden",
    "chars": 479,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf15.golden",
    "chars": 599,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf16.golden",
    "chars": 599,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf17.golden",
    "chars": 599,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf18.golden",
    "chars": 712,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf19.golden",
    "chars": 712,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf20.golden",
    "chars": 712,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/NoBorderRowPadding/HeightOf21.golden",
    "chars": 832,
    "preview": "┌──────────┬──────────────┬───────────┐\n│          │              │           │\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf01.golden",
    "chars": 39,
    "preview": "┌──────────┬──────────────┬───────────┐"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf02.golden",
    "chars": 79,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf03.golden",
    "chars": 119,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf04.golden",
    "chars": 159,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf05.golden",
    "chars": 199,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf06.golden",
    "chars": 199,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  },
  {
    "path": "table/testdata/TestTableHeightShrink/WithBorderRow/HeightOf07.golden",
    "chars": 279,
    "preview": "┌──────────┬──────────────┬───────────┐\n│ LANGUAGE │    FORMAL    │ INFORMAL  │\n├──────────┼──────────────┼───────────┤\n"
  }
]

// ... and 100 more files (download for full content)

About this extraction

This page contains the full source code of the charmbracelet/lipgloss GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 300 files (553.6 KB), approximately 186.4k tokens, and a symbol index with 876 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!