# plugins


<div class="boopcell boop-raw">

</div>

<div class="boopcell boop-note">

<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

</div>

<div class="boopcell boop-note">

## The plugin protocol

[`Plugin`](https://drscotthawley.github.io/boopiter/plugins.html#plugin)
is the base class: subclass it, set `name`/`icon`/`trigger`, and
implement `render()` for the hover popup (plus an optional always-on
background task).
[`register`](https://drscotthawley.github.io/boopiter/plugins.html#register)
is a class decorator that adds an instance to `PLUGINS`;
[`start_plugins`](https://drscotthawley.github.io/boopiter/plugins.html#start_plugins)
kicks off each one’s background task at server startup.

</div>

<div class="prose" data-markdown="1">

------------------------------------------------------------------------

<a
href="https://github.com/drscotthawley/boopiter/blob/main/boopiter/plugins.py#L38"
target="_blank" style="float:right; font-size:smaller">source</a>

### start_plugins

``` python
def start_plugins()->None:
```

*Call every registered plugin’s on_start() once. Wired into cells.py’s
@app.on_event(‘startup’) so background samplers spawn on real boot
only.*

</div>

<div class="prose" data-markdown="1">

------------------------------------------------------------------------

<a
href="https://github.com/drscotthawley/boopiter/blob/main/boopiter/plugins.py#L33"
target="_blank" style="float:right; font-size:smaller">source</a>

### register

``` python
def register(
    cls
):
```

*Class decorator: instantiate `cls` and append it to PLUGINS.
Registration = importing this module. Returns `cls` unchanged.*

</div>

<div class="prose" data-markdown="1">

------------------------------------------------------------------------

<a
href="https://github.com/drscotthawley/boopiter/blob/main/boopiter/plugins.py#L19"
target="_blank" style="float:right; font-size:smaller">source</a>

### Plugin

``` python
def Plugin(
    *args, **kwargs
):
```

*Base class for a top-bar plugin. Subclass it, set
`name`/`icon`/`trigger` (or in **init**), and override
`on_start`/`render` as needed; decorate the subclass with @register to
add it to PLUGINS. `icon` is ICONS-style path data (plain ‘d’ strings
and/or (d, scale) pairs) so cells.py renders it through the exact same
SVG machinery as its built-in icons. `trigger` is ‘hover’ (the only kind
wired up so far) or ‘click’.*

</div>

<div class="boopcell boop-note">

## The system-monitor plugin

The first concrete plugin:
[`SystemMonitor`](https://drscotthawley.github.io/boopiter/plugins.html#systemmonitor)
samples CPU/RAM/GPU/VRAM into ring buffers on a background thread and
renders them as
[`_sparkline`](https://drscotthawley.github.io/boopiter/plugins.html#_sparkline)
mini-charts in its popup. GPU comes from
[`_gpu_sample`](https://drscotthawley.github.io/boopiter/plugins.html#_gpu_sample)
shelling out to `nvidia-smi` (the same approach as
[`slmn.misc.gpu_free`](https://drscotthawley.github.io/slmn/misc.html#gpu_free))
on non-Mac hosts, or
[`_gpu_sample_darwin`](https://drscotthawley.github.io/boopiter/plugins.html#_gpu_sample_darwin)
reading Apple Silicon’s IOKit registry on macOS.

</div>

<div class="prose" data-markdown="1">

------------------------------------------------------------------------

<a
href="https://github.com/drscotthawley/boopiter/blob/main/boopiter/plugins.py#L87"
target="_blank" style="float:right; font-size:smaller">source</a>

### SystemMonitor

``` python
def SystemMonitor(
    maxlen:int=300, interval:float=2.0
):
```

\*First plugin: continuously logs CPU%, system-RAM%, GPU-util%, and
VRAM% into ring buffers in the background (whether or not its panel is
open), and plots the accumulated history as inline-SVG sparklines when
hovered. GPU comes from nvidia-smi (\_gpu_sample) on non-Mac hosts or
the IOKit registry (\_gpu_sample_darwin) on macOS; VRAM is nvidia-only –
Apple Silicon’s unified memory has no separate pool to report. Rows just
go empty/‘–’ if unavailable, no separate placeholder needed.\*

</div>

``` python
# quick sanity check (does NOT start the sampler thread -- that only happens via on_start())
assert any(isinstance(p, SystemMonitor) for p in PLUGINS), "SystemMonitor should auto-register"
sm = next(p for p in PLUGINS if isinstance(p, SystemMonitor))
sm.cpu.extend([12, 34, 56, 30]); sm.ram.extend([40, 42, 41])
sm.gpu.extend([0, 5, 80]); sm.vram.extend([20, 21, 55])
panel = sm.render()             # should not raise
spark = _sparkline([10, 90, 50])
assert 'polyline' in to_xml(spark).lower()
assert 'System monitor' in to_xml(panel)
print('registered plugins:', [p.name for p in PLUGINS])
print('_gpu_sample() ->', _gpu_sample())  # None if no nvidia-smi on this box; a (util%, vram%) tuple otherwise
print('_gpu_sample_darwin() ->', _gpu_sample_darwin())  # None off macOS or if ioreg has no AGXAccelerator; a bare util% otherwise
```
