{
  "generated_at": "2026-02-25T18:26:22+00:00",
  "snippets": {
    "py:painted._components.spinner:SpinnerState#definition": {
      "id": "py:painted._components.spinner:SpinnerState#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 38,
        "path": "src/painted/_components/spinner.py",
        "start_line": 28
      },
      "source": "@dataclass(frozen=True)\nclass SpinnerState:\n    \"\"\"Immutable spinner state tracking current frame.\"\"\"\n\n    frame: int = 0\n    frames: SpinnerFrames = DOTS\n\n    def tick(self) -> SpinnerState:\n        \"\"\"Advance to the next frame, wrapping around.\"\"\"\n        cursor = Cursor(index=self.frame, count=len(self.frames.frames), mode=CursorMode.WRAP).next()\n        return replace(self, frame=cursor.index)\n",
      "title": "SpinnerState"
    },
    "py:painted._components.spinner:spinner#signature": {
      "id": "py:painted._components.spinner:spinner#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 46,
        "path": "src/painted/_components/spinner.py",
        "start_line": 41
      },
      "source": "def spinner(\n    state: SpinnerState,\n    *,\n    style: Style | None = None,\n    icons: \"IconSet | None\" = None,\n) -> Block:\n",
      "title": "spinner"
    },
    "py:painted.app:Surface#signature": {
      "id": "py:painted.app:Surface#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 20,
        "path": "src/painted/app.py",
        "start_line": 20
      },
      "source": "class Surface:\n",
      "title": "Surface"
    },
    "py:painted.app:Surface.run#signature": {
      "id": "py:painted.app:Surface.run#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 57,
        "path": "src/painted/app.py",
        "start_line": 57
      },
      "source": "    async def run(self) -> None:\n",
      "title": "Surface.run"
    },
    "py:painted.block:Block.empty#signature": {
      "id": "py:painted.block:Block.empty#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 110,
        "path": "src/painted/block.py",
        "start_line": 109
      },
      "source": "    @staticmethod\n    def empty(width: int, height: int, style: Style = Style(), *, id: str | None = None) -> Block:\n",
      "title": "Block.empty"
    },
    "py:painted.block:Block.paint#signature": {
      "id": "py:painted.block:Block.paint#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 116,
        "path": "src/painted/block.py",
        "start_line": 116
      },
      "source": "    def paint(self, buffer: Buffer | BufferView, x: int = 0, y: int = 0) -> None:\n",
      "title": "Block.paint"
    },
    "py:painted.block:Block.text#signature": {
      "id": "py:painted.block:Block.text#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 67,
        "path": "src/painted/block.py",
        "start_line": 65
      },
      "source": "    @staticmethod\n    def text(content: str, style: Style, *, width: int | None = None,\n             wrap: Wrap = Wrap.NONE, id: str | None = None) -> Block:\n",
      "title": "Block.text"
    },
    "py:painted.buffer:Buffer.fill#signature": {
      "id": "py:painted.buffer:Buffer.fill#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 92,
        "path": "src/painted/buffer.py",
        "start_line": 92
      },
      "source": "    def fill(self, x: int, y: int, w: int, h: int, char: str, style: Style) -> None:\n",
      "title": "Buffer.fill"
    },
    "py:painted.buffer:Buffer.put#signature": {
      "id": "py:painted.buffer:Buffer.put#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 49,
        "path": "src/painted/buffer.py",
        "start_line": 49
      },
      "source": "    def put(self, x: int, y: int, char: str, style: Style) -> None:\n",
      "title": "Buffer.put"
    },
    "py:painted.buffer:Buffer.put_text#signature": {
      "id": "py:painted.buffer:Buffer.put_text#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 66,
        "path": "src/painted/buffer.py",
        "start_line": 66
      },
      "source": "    def put_text(self, x: int, y: int, text: str, style: Style) -> None:\n",
      "title": "Buffer.put_text"
    },
    "py:painted.buffer:Buffer.region#signature": {
      "id": "py:painted.buffer:Buffer.region#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 103,
        "path": "src/painted/buffer.py",
        "start_line": 103
      },
      "source": "    def region(self, x: int, y: int, w: int, h: int) -> BufferView:\n",
      "title": "Buffer.region"
    },
    "py:painted.cell:Cell#definition": {
      "id": "py:painted.cell:Cell#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 60,
        "path": "src/painted/cell.py",
        "start_line": 51
      },
      "source": "@dataclass(frozen=True)\nclass Cell:\n    \"\"\"Atomic display unit: a single character with style.\"\"\"\n\n    char: str\n    style: Style\n\n    def __post_init__(self):\n        if len(self.char) != 1:\n            raise ValueError(f\"Cell char must be a single character, got {self.char!r}\")\n",
      "title": "Cell"
    },
    "py:painted.cell:Style#definition": {
      "id": "py:painted.cell:Style#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 48,
        "path": "src/painted/cell.py",
        "start_line": 26
      },
      "source": "@dataclass(frozen=True)\nclass Style:\n    \"\"\"Immutable text style with color and attribute flags.\"\"\"\n\n    fg: Color = None\n    bg: Color = None\n    bold: bool = False\n    italic: bool = False\n    underline: bool = False\n    reverse: bool = False\n    dim: bool = False\n\n    def merge(self, other: Style) -> Style:\n        \"\"\"Combine styles. `other` overrides non-None/non-False fields.\"\"\"\n        return Style(\n            fg=other.fg if other.fg is not None else self.fg,\n            bg=other.bg if other.bg is not None else self.bg,\n            bold=other.bold or self.bold,\n            italic=other.italic or self.italic,\n            underline=other.underline or self.underline,\n            reverse=other.reverse or self.reverse,\n            dim=other.dim or self.dim,\n        )\n",
      "title": "Style"
    },
    "py:painted.compose:join_horizontal#signature": {
      "id": "py:painted.compose:join_horizontal#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 20,
        "path": "src/painted/compose.py",
        "start_line": 19
      },
      "source": "def join_horizontal(*blocks: Block, gap: int = 0,\n                    align: Align = Align.START) -> Block:\n",
      "title": "join_horizontal"
    },
    "py:painted.compose:join_vertical#signature": {
      "id": "py:painted.compose:join_vertical#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 67,
        "path": "src/painted/compose.py",
        "start_line": 66
      },
      "source": "def join_vertical(*blocks: Block, gap: int = 0,\n                  align: Align = Align.START) -> Block:\n",
      "title": "join_vertical"
    },
    "py:painted.focus:Focus#definition": {
      "id": "py:painted.focus:Focus#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 34,
        "path": "src/painted/focus.py",
        "start_line": 9
      },
      "source": "@dataclass(frozen=True, slots=True)\nclass Focus:\n    \"\"\"Immutable focus state primitive.\n\n    Tracks what's focused and whether the focused component has captured\n    keyboard input (two-tier focus: navigation vs widget interaction).\n    \"\"\"\n\n    id: str\n    captured: bool = False\n\n    def focus(self, id: str) -> Focus:\n        \"\"\"Return new Focus on the given id, releasing capture.\"\"\"\n        return Focus(id=id, captured=False)\n\n    def capture(self) -> Focus:\n        \"\"\"Return new Focus with keyboard captured by current component.\"\"\"\n        return replace(self, captured=True)\n\n    def release(self) -> Focus:\n        \"\"\"Return new Focus with keyboard released to navigation.\"\"\"\n        return replace(self, captured=False)\n\n    def toggle_capture(self) -> Focus:\n        \"\"\"Return new Focus with capture toggled.\"\"\"\n        return replace(self, captured=not self.captured)\n",
      "title": "Focus"
    },
    "py:painted.focus:ring_next#definition": {
      "id": "py:painted.focus:ring_next#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 48,
        "path": "src/painted/focus.py",
        "start_line": 40
      },
      "source": "def ring_next(items: Sequence[str], current: str) -> str:\n    \"\"\"Move to next item in ring, wrapping at end.\"\"\"\n    if not items:\n        return current\n    try:\n        idx = list(items).index(current)\n        return items[(idx + 1) % len(items)]\n    except ValueError:\n        return items[0] if items else current\n",
      "title": "ring_next"
    },
    "py:painted.search:Search#definition": {
      "id": "py:painted.search:Search#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 53,
        "path": "src/painted/search.py",
        "start_line": 11
      },
      "source": "@dataclass(frozen=True, slots=True)\nclass Search:\n    \"\"\"Immutable search state for filtered selection.\n\n    Tracks the query string and selected index within filtered results.\n    \"\"\"\n\n    query: str = \"\"\n    selected: int = 0\n\n    def type(self, char: str) -> Search:\n        \"\"\"Return new Search with char appended to query, resetting selection.\"\"\"\n        return Search(query=self.query + char, selected=0)\n\n    def backspace(self) -> Search:\n        \"\"\"Return new Search with last char removed from query.\"\"\"\n        if not self.query:\n            return self\n        return Search(query=self.query[:-1], selected=0)\n\n    def clear(self) -> Search:\n        \"\"\"Return new Search with empty query.\"\"\"\n        return Search(query=\"\", selected=0)\n\n    def select_next(self, match_count: int) -> Search:\n        \"\"\"Return new Search with selection moved to next match (wrapping).\"\"\"\n        if match_count == 0:\n            return self\n        cursor = Cursor(index=self.selected, count=match_count, mode=CursorMode.WRAP).next()\n        return replace(self, selected=cursor.index)\n\n    def select_prev(self, match_count: int) -> Search:\n        \"\"\"Return new Search with selection moved to previous match (wrapping).\"\"\"\n        if match_count == 0:\n            return self\n        cursor = Cursor(index=self.selected, count=match_count, mode=CursorMode.WRAP).prev()\n        return replace(self, selected=cursor.index)\n\n    def selected_item(self, matches: Sequence[str]) -> str | None:\n        \"\"\"Return the currently selected item from matches, or None if empty.\"\"\"\n        if not matches or self.selected >= len(matches):\n            return None\n        return matches[self.selected]\n",
      "title": "Search"
    },
    "py:painted.search:filter_fuzzy#definition": {
      "id": "py:painted.search:filter_fuzzy#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 94,
        "path": "src/painted/search.py",
        "start_line": 75
      },
      "source": "def filter_fuzzy(items: Sequence[str], query: str) -> tuple[str, ...]:\n    \"\"\"Filter items by fuzzy match (characters appear in order).\n\n    Example: \"fb\" matches \"FooBar\" because 'f' and 'b' appear in order.\n    \"\"\"\n    if not query:\n        return tuple(items)\n\n    def matches(item: str, q: str) -> bool:\n        item_lower = item.lower()\n        q_lower = q.lower()\n        idx = 0\n        for char in q_lower:\n            idx = item_lower.find(char, idx)\n            if idx == -1:\n                return False\n            idx += 1\n        return True\n\n    return tuple(item for item in items if matches(item, query))\n",
      "title": "filter_fuzzy"
    },
    "py:painted.span:Line#signature": {
      "id": "py:painted.span:Line#signature",
      "kind": "signature",
      "language": "python",
      "origin": {
        "end_line": 31,
        "path": "src/painted/span.py",
        "start_line": 30
      },
      "source": "@dataclass(frozen=True, slots=True)\nclass Line:\n",
      "title": "Line"
    },
    "py:painted.span:Line.paint#definition": {
      "id": "py:painted.span:Line.paint#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 53,
        "path": "src/painted/span.py",
        "start_line": 47
      },
      "source": "    def paint(self, view: BufferView, x: int, y: int) -> None:\n        \"\"\"Render spans into a BufferView, merging base style onto each span.\"\"\"\n        col = x\n        for span in self.spans:\n            merged = self.style.merge(span.style)\n            view.put_text(col, y, span.text, merged)\n            col += span.width\n",
      "title": "Line.paint"
    },
    "py:painted.span:Span#definition": {
      "id": "py:painted.span:Span#definition",
      "kind": "definition",
      "language": "python",
      "origin": {
        "end_line": 27,
        "path": "src/painted/span.py",
        "start_line": 13
      },
      "source": "@dataclass(frozen=True, slots=True)\nclass Span:\n    \"\"\"A run of text with a single style.\"\"\"\n\n    text: str\n    style: Style = Style()\n\n    @property\n    def width(self) -> int:\n        \"\"\"Display width, accounting for wide characters.\"\"\"\n        w = wcswidth(self.text)\n        if w < 0:\n            # Fallback for strings containing non-printable chars\n            return len(self.text)\n        return w\n",
      "title": "Span"
    }
  },
  "version": 1
}
