IO
Files and processes: a click counter saved across reloads (readFile/writeFile), a note followed with watchFile (edit it from a terminal), and live latency from a long-running ping (spawn).
Run it
swift run halo start --config examples/io # from a clone
halo start --config "$(brew --prefix)/share/halo/examples/io" # Homebrew
Source
main.tsx
// Files and processes: a click counter kept across reloads (readFile/writeFile), a note you edit from a terminal
// (watchFile), and live latency from a long-running ping (spawn). Try:
//
// echo "ship it" > ~/.cache/halo-examples/note.txt
import {
Box,
PerScreen,
Surface,
Text,
readFile,
spawn,
watchFile,
writeFile,
} from "halo";
import { signal } from "halo/signals";
const folder = "~/.cache/halo-examples";
const muted = "#9399b2";
const foreground = "#cdd6f4";
const accent = "#89b4fa";
function Counter() {
const clicks = signal(0);
readFile(`${folder}/clicks.txt`).then(
(text) => clicks.set(Number(text) || 0),
() => {}, // first run: nothing saved yet
);
const click = () => {
clicks.update((n) => n + 1);
writeFile(`${folder}/clicks.txt`, String(clicks()));
};
return (
<Box
style={{ flexDirection: "row", justify: "space-between", align: "center" }}
onClick={click}
>
<Text style={{ fontSize: 13, color: muted }}>Clicks (saved)</Text>
<Text style={{ fontSize: 15, fontWeight: "semibold", color: accent }}>
{() => String(clicks())}
</Text>
</Box>
);
}
function Latency() {
const ms = signal<number | undefined>(undefined);
spawn("/sbin/ping", ["-i", "2", "1.1.1.1"], {
onLine: (line) => {
const match = line.match(/time=([\d.]+) ms/);
if (match) ms.set(Number(match[1]));
},
onError: () => ms.set(undefined),
});
return (
<Box style={{ flexDirection: "row", justify: "space-between", align: "center" }}>
<Text style={{ fontSize: 13, color: muted }}>Ping 1.1.1.1</Text>
<Text style={{ fontSize: 15, fontWeight: "semibold", color: foreground }}>
{() => (ms() === undefined ? "–" : `${Math.round(ms() ?? 0)} ms`)}
</Text>
</Box>
);
}
function Note() {
const note = watchFile(`${folder}/note.txt`);
return (
<Text style={{ fontSize: 13, color: foreground, lines: 3 }}>
{() => note()?.trim() || "No note yet: write to ~/.cache/halo-examples/note.txt"}
</Text>
);
}
export default () => (
<PerScreen>
{(screen) =>
screen.index === 1 ? (
<Surface
screen={screen}
anchor={["top", "right"]}
margins={[48, 16, 0, 0]}
layer="aboveMenu"
input="interactive"
>
<Box
style={{
width: 260,
padding: 16,
gap: 12,
background: "#1e1e2ef0",
radius: 14,
}}
>
<Counter />
<Latency />
<Note />
</Box>
</Surface>
) : null
}
</PerScreen>
);