halo DocsGitHub

Shapes

<Shape>: a CPU progress ring (ring(), track, strokeEnd with a spring) with its percentage overlaid (position: "absolute"), a CPU history sparkline, and a plain SVG path.

Run it

swift run halo start --config examples/shapes                         # from a clone
halo start --config "$(brew --prefix)/share/halo/examples/shapes"   # Homebrew

Source

main.tsx

// <Shape>: a CPU ring whose strokeEnd animates with a spring, with its percentage overlaid in the middle
// (position: "absolute"), a CPU history sparkline, and a plain SVG path.

import { Box, PerScreen, Shape, Surface, Text, ring, sparkline } from "halo";
import { system } from "halo/services";
import { effect, signal, untrack } from "halo/signals";

const muted = "#9399b2";
const foreground = "#cdd6f4";
const accent = "#89b4fa";
const green = "#a6e3a1";
const heart = "M 50 88 L 12 50 A 22 22 0 0 1 50 18 A 22 22 0 0 1 88 50 Z";

function Cpu() {
  const history = signal<number[]>([]);
  effect(() => {
    const cpu = system.cpu(1000);
    if (cpu !== undefined)
      untrack(() => history.update((list) => [...list, cpu].slice(-40)));
  });
  const percent = () => Math.round((system.cpu(1000) ?? 0) * 100);

  return (
    <Box style={{ gap: 12 }}>
      <Box style={{ flexDirection: "row", align: "center", gap: 14 }}>
        <Box style={{ width: 56, height: 56 }}>
          <Shape
            path={ring()}
            style={{
              width: 56,
              height: 56,
              fill: "transparent",
              stroke: accent,
              track: "#313244",
              strokeWidth: 6,
              lineCap: "round",
              strokeEnd: () => system.cpu(1000) ?? 0,
              transition: { strokeEnd: { curve: "spring" } },
            }}
          />
          <Box
            style={{
              position: "absolute",
              top: 0,
              right: 0,
              bottom: 0,
              left: 0,
              justify: "center",
              align: "center",
            }}
          >
            <Text style={{ fontSize: 13, fontWeight: "bold", color: foreground }}>
              {() => `${percent()}%`}
            </Text>
          </Box>
        </Box>
        <Box style={{ gap: 2, grow: 1 }}>
          <Text style={{ fontSize: 12, color: muted }}>CPU</Text>
          <Text style={{ fontSize: 12, color: foreground }}>last 40 s below</Text>
        </Box>
        <Shape
          path={heart}
          style={{
            width: 32,
            height: 32,
            fill: "transparent",
            stroke: "#f38ba8",
            strokeWidth: 2,
            lineJoin: "round",
          }}
        />
      </Box>
      <Shape
        path={() => sparkline(history(), { min: 0, area: true })}
        fit="stretch"
        style={{
          height: 36,
          fill: "#a6e3a133",
          stroke: green,
          strokeWidth: 1.5,
          lineJoin: "round",
        }}
      />
    </Box>
  );
}

export default () => (
  <PerScreen>
    {(screen) =>
      screen.index === 1 ? (
        <Surface
          screen={screen}
          anchor={["top", "right"]}
          margins={[48, 16, 0, 0]}
          layer="aboveMenu"
        >
          <Box style={{ width: 240, padding: 16, background: "#1e1e2ef0", radius: 14 }}>
            <Cpu />
          </Box>
        </Surface>
      ) : null
    }
  </PerScreen>
);