Créer votre propre cadran
Créez vos propres cadrans de jauge en HTML, CSS et JavaScript. Chaque cadran fonctionne avec toutes les sources, de la batterie aux chiffres que vous lui envoyez vous-même.
Démarrage rapide
- Créez un cadran : Dans les Réglages, cliquez sur Créer un cadran… (Option + Commande + N). Multimeter crée un dossier de fichiers de départ qui utilisent déjà les données de la version 2.
- Modifiez les fichiers : Multimeter affiche le dossier dans le Finder. Modifiez les fichiers dans n’importe quel éditeur de code.
- Voyez les changements en direct : Multimeter recharge votre cadran à chaque enregistrement. Essayez-le avec différentes sources dans Ajouter une jauge… ou Personnaliser cette jauge….
- Partagez-la : Compressez le dossier dans le Finder, renommez le .zip en
.multimeteret proposez-le à la galerie. N’importe qui peut l’ajouter avec Importer… (Commande + O) ou en double-cliquant sur le fichier.
Comment fonctionne une jauge
Une jauge dans la barre des menus, c’est un cadran plus une source plus les réglages de la personne. Le cadran, c’est votre dossier : une petite page web que Multimeter charge dans une vue transparente de 22 points de haut, aussi large que le width de votre manifest. La source, c’est ce que mesure la jauge, choisie par la personne quand elle ajoute la jauge.
Dès que quelque chose change, Multimeter met à jour window.meterData et envoie un événement meterUpdate. Votre cadran n’a pas besoin de savoir d’où viennent les chiffres : dessinez level (de 0 à 1) et affichez formatted, et il fonctionnera aussi bien pour le CPU, le réseau, un minuteur ou la batterie.
Structure des fichiers
Chaque cadran est un dossier contenant :
my-meter/
├── manifest.json # name, size, which sources it's made for
├── index.html # the page Multimeter loads
├── styles.css
└── meter.js # listens for meterUpdate and drawsmanifest.json
Le manifest décrit votre cadran et indique à Multimeter sa largeur et ce pour quoi il est conçu :
{
"name": "My Meter",
"description": "What it shows, in one line.",
"version": "1.0.0",
"author": "Your Name",
"apiVersion": 2,
"sources": ["any"],
"defaultSource": "cpu",
"width": 60,
"minWidth": 40,
"maxWidth": 100,
"supportsLightMode": true,
"supportsDarkMode": true
}| Clé | Type | Signification |
|---|---|---|
name | string | Affiché dans la liste des cadrans et dans la galerie. |
description | string | Une ligne sur ce qu’affiche le cadran. |
version | string | Votre numéro de version, comme 1.0.0. |
author | string | Votre nom, affiché dans la galerie. |
apiVersion | 2 | Mettez 2 pour les données décrites sur cette page. |
sources | string[] | ["any"] pour un cadran capable de dessiner n’importe quelle source, ou une liste d’ids de source comme ["battery"] ou ["battery", "countdown", "timer"]. Multimeter ne propose votre cadran que pour ces sources. |
defaultSource | string | La source choisie quand quelqu’un ajoute votre cadran pour la première fois, comme "cpu". |
width | number | Largeur en points. La hauteur est toujours de 22. |
minWidth / maxWidth | number | La largeur minimale et maximale que la personne peut choisir dans Personnaliser. |
supportsLightMode / supportsDarkMode | boolean | Si le cadran est conçu pour les barres des menus claires et sombres. |
Un manifest sans apiVersion ni sources est traité comme un cadran pensé pour la batterie qui fonctionne quand même avec n’importe quelle source, donc les jauges créées pour les versions précédentes fonctionnent toujours.
Données de la jauge
Multimeter place les données actuelles dans window.meterData et les renvoie à chaque changement. Voici tout ce qu’elles contiennent (version 2 de l’API) :
window.meterData = {
apiVersion: 2,
source: "cpu", // what this meter measures
sourceName: "CPU", // that source's name, translated
label: "CPU", // the meter's label ("" if the person cleared it)
value: 23.4, // raw value in `unit` (null if unavailable)
min: 0, max: 100, // the range `level` is measured against
level: 0.234, // value placed in the range, 0 to 1
formatted: "23%", // ready to show, unit included, in the person's language
unit: "%", // "%", "W", "B/s", "rpm", "s", or your own
state: "normal", // "normal", "warning" or "critical"
tint: null, // "#30D158" if the person picked a color
history: [0.21, 0.25], // up to 60 recent levels, oldest first
warningLevel: 0.7, // alert levels on the same 0 to 1 scale (or null)
criticalLevel: 0.9,
alertDirection: "above", // "above" or "below" (or null)
available: true, // false when the source can't be read right now
colorScheme: "dark", // the menu bar: "light" or "dark"
reduceMotion: false, // System Settings > Accessibility > Reduce motion
strings: { timeLeft: "left" },
battery: { // always the Mac's battery, whatever the source
level: 0.82, percentage: 82, isCharging: false, isPluggedIn: false,
hasBattery: true, timeRemaining: 312, timeToFullCharge: null,
formattedTime: "5:12", cycleCount: 212, health: 0.94, temperature: 30.5
},
// Version 1 fields, so battery meters keep working with any source:
percentage: 23, isCharging: false, isPluggedIn: false,
timeRemaining: null, timeToFullCharge: null, formattedTime: null,
cycleCount: 212, health: 0.94, temperature: 30.5, hasBattery: true
}| Champ | Signification |
|---|---|
source | L’id de la source (voir le tableau ci-dessous). sourceName est son nom traduit. |
label | Le libellé de la jauge, comme CPU ou DOWN. La personne peut le renommer ou l’effacer, il peut donc s’agir d’une chaîne vide. |
value | La mesure brute en unit, ou null. Avec min et max, vous avez le tableau complet. |
level | La valeur ramenée à la plage de 0 à 1, bornée. La plupart des cadrans n’ont besoin que de ça. |
formatted | La mesure sous forme de texte, unité comprise, formatée selon la langue de la personne : 23%, 12.4 W, 1.2 MB/s, 4:12. |
unit | %, W, B/s, rpm, s (minuteurs et comptes à rebours), une chaîne vide, ou l’unité choisie par la personne. |
state | normal, warning ou critical, selon les seuils d’alerte définis par la personne. Choisissez vos couleurs en fonction. |
tint | Une couleur comme #30D158 choisie par la personne, ou null pour utiliser les couleurs propres à votre cadran. |
history | Jusqu’à 60 niveaux récents (de 0 à 1), du plus ancien au plus récent, pour les graphiques. |
warningLevel | warningLevel et criticalLevel placent les seuils d’alerte sur la même échelle de 0 à 1 que level, et alertDirection indique si les alertes se déclenchent au-dessus (above) ou en dessous (below). Les trois valent null quand la jauge n’a pas d’alertes. |
available | false quand la source est illisible pour le moment : pas de batterie, accessoire éteint, adresse web en échec. Affichez un tiret. |
colorScheme | L’apparence de la barre des menus, light ou dark. Elle suit le fond d’écran, pas seulement le réglage du système. |
reduceMotion | true quand Réduire les animations est activé. Passez les animations. |
battery | La batterie du Mac, toujours présente quoi que mesure la jauge : charge, en charge, branché, temps restant, temps avant la charge complète, cycles, santé et température. |
Écouter les mises à jour
Dessinez une première fois avec ce qui se trouve déjà dans window.meterData, puis à chaque meterUpdate. Définissez window.meterReady = true quand vous êtes prêt à recevoir des données.
function render(data) {
// draw the meter from data.level, data.formatted, data.state ...
}
window.addEventListener('meterUpdate', (e) => render(e.detail));
render(window.meterData || { level: 0.5, formatted: '50%', state: 'normal' });
window.meterReady = true;Sources
Voici les sources qu’une jauge peut avoir. Votre cadran voit l’id dans source et reçoit formatted déjà rédigé, vous avez donc rarement besoin de les traiter différemment.
| Source | source | unit | Exemple de formatted |
|---|---|---|---|
| Batterie | battery | "%" | 82% |
| Accessoire Bluetooth | bluetooth | "%" | 64% |
| Consommation | power | "W" | 12.4 W |
| CPU | cpu | "%" | 23% |
| Mémoire | memory | "%" | 71% |
| Espace disque | disk | "%" | 58% |
| Pression thermique | thermal | "" | Normale, Modérée, Élevée, Critique |
| Vitesse du ventilateur* | fan | "rpm" | 2,400 rpm |
| Réseau | network | "B/s" | 1.2 MB/s, 340 KB/s |
| Minuteur | timer | "s" | 24:51 |
| Compte à rebours | countdown | "s" | 3d 4h, 4:12, 12:05 |
| Avancée de la journée | progress | "%" | 63% |
| Valeur envoyée | push | "" | 42 |
| Adresse web | url | "" | 1,284 |
| Fichier | file | "" | 12 |
| Script* | script | "" | 412 |
* La vitesse du ventilateur et les scripts nécessitent la version en téléchargement direct de Multimeter. La version du Mac App Store ne peut ni lire les capteurs des ventilateurs ni exécuter vos scripts ; utilisez-y plutôt une valeur envoyée ou un fichier.
Les pourcentages vont toujours de 0 à 100. La consommation, le réseau et la vitesse du ventilateur s’ajustent à leur pic récent, sauf si la personne définit une plage. La pression thermique a quatre paliers, donc level vaut 0, 0.33, 0.67 ou 1. Pour les minuteurs et les comptes à rebours, level est la part de temps restante et value le nombre de secondes restantes.
Alertes et couleurs
Chaque jauge peut avoir un seuil d’alerte et un seuil critique, définis par la personne (par défaut, la batterie sous 20% et 10%, le CPU au-dessus de 70% et 90%, le disque au-dessus de 85% et 95%). Multimeter calcule state pour vous. Un cadran devrait rester calme en normal et se faire plus voyant en passant à warning puis critical, et utiliser tint quand la personne a choisi une couleur :
document.body.classList.remove('warning', 'critical');
if (data.state !== 'normal') document.body.classList.add(data.state);
// The person's color wins over the face's own
if (data.tint) document.body.style.setProperty('--accent', data.tint);
else document.body.style.removeProperty('--accent');body.warning { --accent: #ff9f0a; }
body.critical { --accent: #ff453a; }Vous voulez dessiner les zones d’alerte sur une échelle, comme un compteur avec une zone rouge au bout ? Utilisez warningLevel, criticalLevel et alertDirection.
Historique
history contient jusqu’à 60 niveaux récents, du plus ancien au plus récent, déjà sur l’échelle de 0 à 1. Ajoutez le level actuel à la fin et vous avez un graphique. L’exemple Graphique d’historique ci-dessous en dessine un.
Détails de la batterie
La batterie reste à part. data.battery est toujours la batterie du Mac, quoi que mesure la jauge, donc n’importe quel cadran peut afficher un éclair de charge. Un cadran conçu uniquement pour la batterie devrait indiquer "sources": ["battery"] dans son manifest. timeRemaining et timeToFullCharge sont en minutes et valent null tant que macOS est encore en train d’estimer.
// A battery-only face: "sources": ["battery"] in manifest.json
const b = data.battery;
bolt.hidden = !b.isCharging;
time.textContent = b.isCharging
? (b.timeToFullCharge ? `${b.timeToFullCharge} min to full` : '')
: (b.formattedTime ?? '');Les cadrans écrits pour la version 1 lisent percentage, isCharging et formattedTime au premier niveau. Ils sont toujours envoyés : pour la source batterie, ce sont les valeurs de la batterie elle-même, et pour les autres sources isCharging vaut false et formattedTime est le temps restant d’un minuteur ou d’un compte à rebours (h:mm), sinon null.
Vos propres données
Quatre sources affichent des chiffres que vous fournissez : une valeur envoyée, une adresse web, un fichier ou un script. Votre cadran les traite comme n’importe quelle autre source.
Envoyer une valeur avec un lien
Ouvrez un lien multimeter://set de n’importe où : Terminal, un script shell, Raccourcis (Ouvrir les URL), Raycast, une tâche cron, un script de build. Ajoutez une jauge avec la source Valeur envoyée et la même clé, et elle se met à jour dès que le lien s’ouvre. open -g laisse Multimeter en arrière-plan.
# Set a value (the meter shows the "deploys" key)
open -g "multimeter://set?key=deploys&value=42"
# Range, label, unit and your own text ride along
open -g "multimeter://set?key=coffee&value=3&min=0&max=5&label=Coffee&unit=cups"
open -g "multimeter://set?key=build&value=0.8&max=1&formatted=80%25%20done"| Paramètre | Signification |
|---|---|
key | La jauge concernée. Les clés ne tiennent pas compte de la casse. |
value | Le chiffre. Obligatoire. |
min / max | La plage, pour que level soit juste. De 0 à 100 par défaut. |
label | Le texte du libellé de la jauge. |
unit | Une unité ajoutée après le chiffre, comme cups. Utilisez % pour un pourcentage. |
formatted | Votre propre texte pour formatted, utilisé à la place du chiffre. |
Les valeurs envoyées fonctionnent aussi dans la version du Mac App Store. Multimeter retient la dernière valeur de chaque clé.
Adresses web, fichiers et scripts
Les trois lisent la même sortie : un simple chiffre, ou du JSON avec une value et n’importe lequel des extras. Tout sauf value est facultatif.
42
42.5
42%
{"value": 42, "min": 0, "max": 100, "label": "Deploys", "unit": "", "formatted": "42 left"}Vous pointez vers un JSON plus gros, comme une réponse d’API ? Donnez à la jauge un chemin de clé et elle va chercher le chiffre :
{"data": {"count": 17}} → key path data.count
{"items": [{"value": 9.5}]} → key path items.0.value- Adresse web : interrogée à l’intervalle de votre choix, au plus toutes les 30 secondes (toutes les 5 minutes si vous ne changez rien).
- Fichier : relu dès que le fichier change. Pratique pour tout ce qui sait écrire un fichier.
- Script : une commande shell exécutée avec
zshà l’intervalle de votre choix (toutes les 30 secondes si vous ne changez rien). Multimeter affiche le chiffre qu’elle imprime. Les dossiers de Homebrew sont dans lePATH, et une commande qui dure plus de 10 secondes est arrêtée.
# Open pull requests waiting on you (direct download version)
gh pr list --search "review-requested:@me" --json number --jq length
# Free memory pressure as a percentage
memory_pressure | awk '/percentage/ {print 100 - $5}'La version du Mac App Store peut utiliser les adresses web et les fichiers, mais pas les scripts. Dans ce cas, faites en sorte que votre script envoie un lien ou écrive un fichier.
Barres des menus claires et sombres
La barre des menus est claire ou sombre selon le fond d’écran. Multimeter ajoute light-mode ou dark-mode à body, renseigne colorScheme dans les données, et prefers-color-scheme suit aussi la barre des menus. Utilisez ce que vous préférez :
/* Dark menu bar (the default) */
body { color: white; }
/* Light menu bar */
body.light-mode { color: black; }
/* Same thing with the media query, which follows the menu bar too */
@media (prefers-color-scheme: light) {
body { color: black; }
}Animations réduites
Quand Réduire les animations est activé, reduceMotion vaut true et prefers-reduced-motion correspond. Gardez la jauge lisible et supprimez les rebonds, les pulsations et les balayages :
body.reduce-motion * { transition: none !important; animation: none !important; }
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; animation: none !important; }
}Exemples complets
Deux cadrans complets, qui tournent en direct sur cette page avec les mêmes données que celles envoyées par Multimeter. Copiez les fichiers dans un dossier et importez-le.
Le cadran de départ de Créer un cadran
Exactement ce qu’écrit Créer un cadran… : un libellé, une barre et la valeur formatée, avec l’API dans ses commentaires. Il se colore selon state, respecte tint et suit l’apparence de la barre des menus.
starter/manifest.json
{
"name": "My Face",
"description": "A label, a bar and the value. Works with any source.",
"version": "1.0.0",
"author": "Your Name",
"apiVersion": 2,
"sources": ["any"],
"defaultSource": "cpu",
"width": 70,
"minWidth": 50,
"maxWidth": 120,
"supportsLightMode": true,
"supportsDarkMode": true
}starter/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="meter">
<span id="label"></span>
<span id="track"><span id="fill"></span></span>
<span id="value">--</span>
</div>
<script src="meter.js"></script>
</body>
</html>starter/styles.css
* { margin: 0; padding: 0; box-sizing: border-box; }
/* The meter is 22 points tall and as wide as manifest.json says. */
body {
font: 600 11px/1 -apple-system, BlinkMacSystemFont, sans-serif;
font-variant-numeric: tabular-nums;
color: white; /* dark menu bar */
background: transparent;
height: 22px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
--accent: #30d158; /* replaced by the meter's color, if one is set */
}
/* Light menu bar: the system setting, or the preview's light mode. */
#track { background: rgba(255, 255, 255, 0.25); }
@media (prefers-color-scheme: light) { body { color: black; } #track { background: rgba(0, 0, 0, 0.18); } }
body.light-mode { color: black; }
body.light-mode #track { background: rgba(0, 0, 0, 0.18); }
body.dark-mode { color: white; }
body.dark-mode #track { background: rgba(255, 255, 255, 0.25); }
#meter { display: flex; align-items: center; gap: 4px; }
#label { font-size: 8px; letter-spacing: 0.04em; opacity: 0.7; }
#track { width: 18px; height: 6px; border-radius: 3px; position: relative; overflow: hidden; }
#fill { position: absolute; inset: 0 auto 0 0; width: 0; background: var(--accent); border-radius: 3px; transition: width 0.4s ease; }
/* state is "normal", "warning" or "critical" (from the meter's alert levels). */
body.warning { --accent: #ffd60a; }
body.critical { --accent: #ff453a; }
@media (prefers-reduced-motion: reduce) { #fill { transition: none; } }starter/meter.js
// Multimeter meter API, version 2. The app sets window.meterData and fires
// a 'meterUpdate' event whenever anything changes. The fields you'll use most:
//
// level 0.0 to 1.0, the value within the meter's range
// formatted ready-to-show text with its unit: "23%", "1.2 MB/s", "4:12"
// label short label ("CPU"), may be "" if hidden
// value, unit, min, max the raw number and its range
// state "normal", "warning" or "critical"
// tint the color picked in Customize ("#30D158"), or null
// history up to 60 recent levels (0 to 1), oldest first, for graphs
// source what it measures: "battery", "cpu", "memory", "network", "timer"...
// available false when there's nothing to read (no battery, URL failing)
// battery the Mac's battery, always: level, isCharging, isPluggedIn,
// hasBattery, timeRemaining, timeToFullCharge, health...
// isCharging true only when this meter shows the battery and it's charging
// colorScheme "light" or "dark"; reduceMotion true if animations should stop
//
// Full reference: https://multimeterapp.com/creating-meters
class Meter {
constructor() {
this.label = document.getElementById('label');
this.fill = document.getElementById('fill');
this.value = document.getElementById('value');
window.addEventListener('meterUpdate', (e) => this.render(e.detail));
if (window.meterData) this.render(window.meterData);
window.meterReady = true;
}
render(data) {
const level = Math.max(0, Math.min(1, data.level || 0));
this.label.textContent = data.label || '';
this.label.style.display = data.label ? '' : 'none';
this.value.textContent = data.available === false ? '\u2013' : (data.formatted || Math.round(level * 100) + '%');
this.fill.style.width = (level * 100) + '%';
document.body.classList.toggle('warning', data.state === 'warning');
document.body.classList.toggle('critical', data.state === 'critical');
if (data.tint) document.body.style.setProperty('--accent', data.tint);
else document.body.style.removeProperty('--accent');
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => new Meter());
} else {
new Meter();
}Graphique d’historique
Un graphique en courbe de la dernière minute à partir de history, dessiné sur un canvas à la vraie résolution de l’écran.
history-graph/manifest.json
{
"name": "History Graph",
"description": "The last minute as a little line graph.",
"version": "1.0.0",
"author": "Your Name",
"apiVersion": 2,
"sources": ["any"],
"defaultSource": "network",
"width": 44,
"minWidth": 30,
"maxWidth": 80,
"supportsLightMode": true,
"supportsDarkMode": true
}history-graph/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
html, body { margin: 0; height: 22px; background: transparent; overflow: hidden; }
canvas { display: block; width: 100%; height: 22px; }
</style>
</head>
<body>
<canvas id="graph"></canvas>
<script src="meter.js"></script>
</body>
</html>history-graph/meter.js
const canvas = document.getElementById('graph');
const ctx = canvas.getContext('2d');
function render(data) {
// Draw at the screen's real resolution so the line stays sharp
const scale = window.devicePixelRatio || 1;
const w = canvas.clientWidth, h = 22;
canvas.width = w * scale;
canvas.height = h * scale;
ctx.setTransform(scale, 0, 0, scale, 0, 0);
ctx.clearRect(0, 0, w, h);
const points = (data.history || []).concat(data.level ?? 0);
if (points.length < 2) return;
const dark = data.colorScheme !== 'light';
const color = data.tint
|| (data.state === 'critical' ? '#ff453a' : data.state === 'warning' ? '#ff9f0a' : dark ? '#fff' : '#1d1d1f');
const x = (i) => (i / (points.length - 1)) * (w - 2) + 1;
const y = (v) => h - 3 - v * (h - 6);
// Soft fill under the line, then the line
ctx.beginPath();
points.forEach((v, i) => (i ? ctx.lineTo(x(i), y(v)) : ctx.moveTo(x(i), y(v))));
ctx.lineTo(x(points.length - 1), h);
ctx.lineTo(x(0), h);
ctx.closePath();
ctx.globalAlpha = 0.22;
ctx.fillStyle = color;
ctx.fill();
ctx.globalAlpha = 1;
ctx.beginPath();
points.forEach((v, i) => (i ? ctx.lineTo(x(i), y(v)) : ctx.moveTo(x(i), y(v))));
ctx.strokeStyle = color;
ctx.lineWidth = 1.5;
ctx.lineJoin = 'round';
ctx.stroke();
}
window.addEventListener('meterUpdate', (e) => render(e.detail));
render(window.meterData || { history: [0.2, 0.4, 0.3, 0.6, 0.5], level: 0.7 });
window.meterReady = true;Conseils de test
- Gardez tout dans une hauteur de 22 points, et vérifiez votre cadran à sa
minWidthet à samaxWidth. - Utilisez la police système (
-apple-system) etfont-variant-numeric: tabular-numspour que les chiffres ne sautillent pas. - Essayez votre cadran avec plusieurs sources dans Ajouter une jauge… et Personnaliser cette jauge… : un pourcentage, un débit réseau, un minuteur, une source indisponible.
- Vérifiez les barres des menus claires et sombres, une teinte, et les états d’avertissement et critique.
- Les erreurs de votre JavaScript apparaissent dans Console.app. Vous pouvez aussi ouvrir votre
index.htmldans Safari et lui envoyer des données depuis la console : - Gardez des fichiers légers et évitez les requêtes réseau, pour que le cadran s’affiche instantanément.
// Paste in Safari's console with your index.html open to try a value
window.meterData = { level: 0.92, formatted: '92%', label: 'CPU', state: 'critical',
available: true, colorScheme: 'dark', history: [] };
window.dispatchEvent(new CustomEvent('meterUpdate', { detail: window.meterData }));Prêt à créer ?
$19.99 en achat unique. Sans abonnement. Remboursement sous 30 jours.
Acheter pour $19.99