# Plugins

> Plugins change the scene, and the terminal draws the scene. A plugin that adds lines, circles and text works in the terminal as it does in SVG.

A Chartist plugin listens to the phases of a chart and changes its scene: it adds nodes, replaces them or drops them. The terminal draws that same scene, so a plugin needs no terminal version. Pass it in `plugins`, as you would on an SVG chart.

## A target line

This plugin draws a labeled line at a target value, keeps a circle on each value that reaches it, and drops the other points. It's an ordinary 2.x plugin, written with nothing but `chartist`:

**A target line plugin** (runs in the browser):

`src/target-line.ts`:

```ts
import { node, type ChartPlugin, type SceneNode } from 'chartist';

/**
 * A labeled line at a target value, and a circle on each value that
 * reaches it. The other points go.
 */
export const targetLine = (target: number, label = 'target'): ChartPlugin => ({
  name: 'target-line',
  install(chart) {
    let marker: SceneNode | undefined;
    chart.on('compose', current => {
      // A grid line of the Y axis knows its axis, and so where the target is.
      if (
        current.role === 'grid' &&
        current.ctx.axis.dimension === 'y' &&
        current.ctx.index === 0
      ) {
        const { axis, index, x1, x2, y1 } = current.ctx;
        const y = y1 - (axis.project(target) - axis.project(axis.ticks[index]));
        marker = node('g', {}, 'ct-target')
          .append(node('line', { x1, x2, y1: y, y2: y }))
          .append(node('text', { x: x1, y: y - 6 }).text(label));
        return;
      }
      if (current.role === 'point') {
        const { x, y, value } = current.ctx;
        const reached =
          value && typeof value === 'object' && 'y' in value
            ? (value.y ?? 0) >= target
            : false;
        return reached
          ? node('circle', { cx: x, cy: y, r: 6 }, 'ct-hit', {
              role: current.role,
              ctx: current.ctx,
              key: current.key
            })
          : null;
      }
    });
    // Drawn last, on top of the series.
    chart.on('scene', ({ scene }) =>
      marker ? { scene: scene.append(marker) } : undefined
    );
  }
});
```

`src/main.ts`:

```ts
import '@chartist-js/terminal/styles.css';
import './style.css';
import { lineChart } from '@chartist-js/terminal';
import { targetLine } from './target-line';

// The same plugin works on lineChart() from chartist, in SVG.
const art = document.querySelector('#art');
if (art) {
  art.innerHTML = lineChart(
    {
      labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
      series: [[620, 780, 940, 1050, 990, 1210]]
    },
    {
      low: 0,
      plugins: [targetLine(1000)],
      columns: 34,
      rows: 16,
      format: 'html'
    }
  );
}
```

`src/style.css`:

```css
/* A terminal window around the text art, for the looks. It takes the
   chart's size: more columns or rows make it bigger. */
.window {
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  border-radius: 10px;
  overflow: hidden;
  background: #17161c;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.06) inset,
    0 12px 32px rgba(0, 0, 0, 0.35);
}

.title-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: #2a2830;
}

.dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #ff5f57;
}

.dot:nth-child(2) {
  background: #febc2e;
}

.dot:nth-child(3) {
  background: #28c840;
}

.title {
  flex: 1;
  margin-right: 52px;
  text-align: center;
  color: #a8a6b0;
  font:
    600 12px system-ui,
    sans-serif;
}

.screen {
  padding: 12px;
  color: #e6e4ec;
  /* Text art reads the same custom properties as SVG charts. */
  --ct-label-color: rgba(255, 255, 255, 0.7);
  --ct-grid-color: rgba(255, 255, 255, 0.16);
}

.screen .ct-terminal {
  margin: 0;
  font-size: 13px;
  /* Narrower than the chart, the window cuts it off. */
  overflow: hidden;
  /* Bold labels; the dots and lines come from the stylesheet's own font. */
  font-weight: 700;
}

/* On the dark screen, the area shading needs a little more. */
.screen .ct-area {
  opacity: 0.75;
}
```

`index.html`:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>A target line plugin</title>
  </head>
  <body>
    <div class="window">
      <div class="title-bar" aria-hidden="true">
        <span class="dot"></span>
        <span class="dot"></span>
        <span class="dot"></span>
        <span class="title">ct-cli</span>
      </div>
      <div class="screen">
        <div id="art"></div>
      </div>
    </div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
```

How it works, step by step:

- A grid line of the Y axis carries its axis in `ctx.axis`. `axis.project()` turns the target into px, measured from the same tick as the grid line.
- The `scene` listener appends the line and its label to the root, so they're drawn on top of the series.
- For points, the listener returns a `circle` with the point's `role`, `ctx` and `key`, or `null` to drop it. The circles stay in their series' group, so they take its color.

On an SVG chart, the same plugin goes into the same option:

```ts nocheck
import { lineChart } from 'chartist';
import { targetLine } from './target-line';

lineChart('#chart', data, { low: 0, plugins: [targetLine(1000)] });
```

[Events and plugins](https://chartist.dev/docs/events-and-plugins/) covers the phases, the scene nodes and [writing a plugin](https://chartist.dev/docs/events-and-plugins/#writing-a-plugin).

## What the terminal draws

Nodes with one of Chartist's roles (`line`, `point`, `bar`, `slice` and the others) are drawn as the terminal draws that role. Every other node is drawn by its element:

| Element                 | Drawn as                                        |
| ----------------------- | ----------------------------------------------- |
| `line`                  | a braille line between its two ends             |
| `path`                  | its outline as a braille line, curves flattened |
| `circle`                | a point at its center                           |
| `text`, `foreignObject` | a label, one cell per character                 |
| anything else           | nothing, but its children are drawn             |

That goes for nodes a plugin replaced too. The circles above keep the role `point`, but they're circles now, so the terminal draws them as circles.

A node in a series' group, or with a `ct-series-x` class, takes that series' color. Other lines and points use the text color, and text the label color. Sizes and styles (a circle's `r`, `stroke-width`, dashes) and the plugin's own classes don't show: a cell has a character and a color, and in HTML its classes are the kind of mark and the series.

## Plugins that need a page

A terminal chart is laid out and drawn, never mounted. Listeners of the `render` and `mount` phases don't run, and calling `update()` on the chart throws. So plugins that work with the page, such as tooltips, hover effects and zoom, have nothing to do here. Plugins that change the scene in `compose` or `scene` work.

Next: Chartist's options in the terminal in [Customizing charts](https://chartist.dev/packages/terminal/customizing-charts/), or every function on the [API page](https://chartist.dev/packages/terminal/api/).
