अपना फ़ेस खुद बनाएँ

HTML, CSS और JavaScript से अपने मीटर फ़ेस खुद बनाएँ। हर फ़ेस हर सोर्स के साथ काम करता है, बैटरी से लेकर आपके खुद भेजे नंबरों तक।

जल्दी शुरू करें

  1. फ़ेस बनाएँ: सेटिंग्स में फ़ेस बनाएँ… (Option-Command-N) पर क्लिक करें। Multimeter स्टार्टर फ़ाइलों का एक फ़ोल्डर बनाता है जो पहले से वर्शन 2 डेटा इस्तेमाल करती हैं।
  2. फ़ाइलें एडिट करें: Multimeter फ़ोल्डर को Finder में दिखाता है। फ़ाइलों को किसी भी कोड एडिटर में एडिट करें।
  3. बदलाव तुरंत देखें: हर बार सेव करने पर Multimeter आपका फ़ेस रीलोड करता है। मीटर जोड़ें… या यह मीटर कस्टमाइज़ करें… में उसे अलग-अलग सोर्स पर आज़माएँ।
  4. शेयर करें: Finder में फ़ोल्डर को कंप्रेस करें, .zip का नाम बदलकर .multimeter करें, और उसे गैलरी में सबमिट करें। कोई भी उसे इम्पोर्ट करें… (Command-O) से या फ़ाइल पर डबल-क्लिक करके जोड़ सकता है।

मीटर कैसे काम करता है

मेनू बार में एक मीटर यानी एक फ़ेस, एक सोर्स और इस्तेमाल करने वाले की सेटिंग्स। फ़ेस आपका फ़ोल्डर है: एक छोटा वेब पेज जिसे Multimeter 22 पॉइंट ऊँचे एक पारदर्शी व्यू में लोड करता है, जिसकी चौड़ाई आपके मैनिफ़ेस्ट के width जितनी होती है। सोर्स वह है जो मीटर मापता है, जिसे व्यक्ति मीटर जोड़ते समय चुनता है।

जब भी कुछ बदलता है, Multimeter window.meterData सेट करता है और एक meterUpdate इवेंट भेजता है। आपके फ़ेस को यह जानने की ज़रूरत नहीं कि नंबर कहाँ से आते हैं: level (0 से 1) ड्रॉ करें और formatted दिखाएँ, और यह CPU, नेटवर्क, टाइमर या बैटरी, सबके लिए एक जैसा काम करेगा।

फ़ाइल संरचना

हर फ़ेस एक फ़ोल्डर है, जिसमें ये होते हैं:

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 draws

manifest.json

मैनिफ़ेस्ट आपके फ़ेस के बारे में बताता है और Multimeter को बताता है कि वह कितना चौड़ा है और किस काम के लिए बना है:

{
  "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
}
कीटाइपइसका मतलब
namestringफ़ेस की सूची और गैलरी में दिखता है।
descriptionstringएक लाइन कि फ़ेस क्या दिखाता है।
versionstringआपका वर्शन नंबर, जैसे 1.0.0।
authorstringआपका नाम, गैलरी में दिखता है।
apiVersion2इस पेज पर बताए गए डेटा के लिए 2 सेट करें।
sourcesstring[]ऐसे फ़ेस के लिए ["any"] जो कोई भी सोर्स ड्रॉ कर सके, या सोर्स id की सूची जैसे ["battery"] या ["battery", "countdown", "timer"]। Multimeter आपका फ़ेस सिर्फ़ उन्हीं सोर्स के लिए दिखाता है।
defaultSourcestringजब कोई पहली बार आपका फ़ेस जोड़ता है तब चुना जाने वाला सोर्स, जैसे "cpu"।
widthnumberपॉइंट में चौड़ाई। ऊँचाई हमेशा 22 होती है।
minWidth / maxWidthnumberकस्टमाइज़ करें में व्यक्ति इसे कम से कम और ज़्यादा से ज़्यादा कितना चौड़ा कर सकता है।
supportsLightMode / supportsDarkModebooleanक्या फ़ेस लाइट और डार्क मेनू बार के लिए डिज़ाइन किया गया है।

जिस मैनिफ़ेस्ट में apiVersion और sources न हों, उसे बैटरी-फ़र्स्ट फ़ेस माना जाता है जो फिर भी किसी भी सोर्स के साथ काम करता है, ताकि पिछले वर्शन के लिए बने मीटर चलते रहें।

मीटर डेटा

Multimeter मौजूदा डेटा window.meterData में रखता है और हर बदलाव पर उसे फिर से भेजता है। इसमें यह सब है (API वर्शन 2):

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
}
फ़ील्डइसका मतलब
sourceसोर्स की id (नीचे की टेबल देखें)। sourceName उसका अनुवादित नाम है।
labelमीटर का लेबल, जैसे CPU या DOWN। व्यक्ति इसका नाम बदल सकता है या इसे खाली कर सकता है, इसलिए यह खाली स्ट्रिंग भी हो सकता है।
valueunit में कच्ची रीडिंग, या null। min और max के साथ आपको पूरी तस्वीर मिलती है।
levelवैल्यू को 0 से 1 की रेंज में रखा गया, उसी सीमा में बाँधकर। ज़्यादातर फ़ेस को बस इसी की ज़रूरत होती है।
formattedरीडिंग टेक्स्ट के रूप में, यूनिट समेत, व्यक्ति की भाषा के हिसाब से फ़ॉर्मैट की हुई: 23%, 12.4 W, 1.2 MB/s, 4:12।
unit%, W, B/s, rpm, s (टाइमर और काउंटडाउन), खाली स्ट्रिंग, या व्यक्ति की अपनी यूनिट।
statenormal, warning या critical, व्यक्ति के सेट किए अलर्ट लेवल के हिसाब से। इसी के आधार पर रंग दें।
tintव्यक्ति का चुना कोई रंग जैसे #30D158, या अपने फ़ेस के रंग इस्तेमाल करने के लिए null।
historyग्राफ़ के लिए हाल के 60 तक लेवल (0 से 1), सबसे पुराना पहले।
warningLevelwarningLevel और criticalLevel अलर्ट लेवल को उसी 0 से 1 स्केल पर रखते हैं जिस पर level है, और alertDirection बताता है कि अलर्ट उनसे above (ऊपर) जाने पर आते हैं या below (नीचे)। मीटर में अलर्ट न हों तो तीनों null होते हैं।
availablefalse जब सोर्स अभी पढ़ा न जा सके: बैटरी नहीं है, एक्सेसरी बंद है, या वेब पता काम नहीं कर रहा। एक डैश दिखाएँ।
colorSchemeमेनू बार का रूप, light या dark। यह वॉलपेपर के हिसाब से चलता है, सिर्फ़ सिस्टम सेटिंग के हिसाब से नहीं।
reduceMotionगति कम करें चालू होने पर true। एनिमेशन छोड़ दें।
batteryMac की बैटरी, जो हमेशा मौजूद रहती है चाहे मीटर कुछ भी मापे: चार्ज, चार्जिंग, प्लग इन, बचा समय, पूरा होने का समय, साइकल, हेल्थ और तापमान।

अपडेट सुनना

window.meterData में जो पहले से है उससे एक बार ड्रॉ करें, फिर हर meterUpdate पर दोबारा। जब आप डेटा के लिए तैयार हों, तो window.meterReady = true सेट करें।

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;

सोर्स

ये वे सोर्स हैं जो किसी मीटर में हो सकते हैं। आपका फ़ेस source में id देखता है और formatted पहले से लिखा हुआ पाता है, इसलिए आपको शायद ही कभी इन्हें अलग तरह से संभालना पड़े।

सोर्सsourceunitformatted ऐसा दिखता है
बैटरीbattery"%"82%
Bluetooth एक्सेसरीbluetooth"%"64%
बिजली की खपतpower"W"12.4 W
CPUcpu"%"23%
मेमोरीmemory"%"71%
डिस्क स्पेसdisk"%"58%
थर्मल प्रेशरthermal""सामान्य, ठीक, गंभीर, क्रिटिकल
फ़ैन स्पीड*fan"rpm"2,400 rpm
नेटवर्कnetwork"B/s"1.2 MB/s, 340 KB/s
टाइमरtimer"s"24:51
काउंटडाउनcountdown"s"3d 4h, 4:12, 12:05
दिन की प्रगतिprogress"%"63%
भेजी गई वैल्यूpush""42
वेब पताurl""1,284
फ़ाइलfile""12
स्क्रिप्ट*script""412

* फ़ैन स्पीड और स्क्रिप्ट के लिए Multimeter का डायरेक्ट डाउनलोड वर्शन चाहिए। Mac App Store वर्शन फ़ैन सेंसर नहीं पढ़ सकता और आपकी स्क्रिप्ट नहीं चला सकता; वहाँ इसकी जगह भेजी गई वैल्यू या फ़ाइल इस्तेमाल करें।

प्रतिशत हमेशा 0 से 100 तक होते हैं। पावर, नेटवर्क और फ़ैन स्पीड अपने हाल के सबसे ऊँचे स्तर के हिसाब से स्केल होते हैं, जब तक व्यक्ति कोई रेंज सेट न करे। थर्मल प्रेशर के चार स्तर हैं, इसलिए level 0, 0.33, 0.67 या 1 होता है। टाइमर और काउंटडाउन में level बचे हुए समय का हिस्सा है और value बचे हुए सेकंड।

अलर्ट और रंग

हर मीटर का एक चेतावनी और एक क्रिटिकल लेवल हो सकता है, जिसे व्यक्ति सेट करता है (बैटरी में डिफ़ॉल्ट 20% और 10% से नीचे, CPU में 70% और 90% से ऊपर, डिस्क में 85% और 95% से ऊपर)। Multimeter आपके लिए state तय करता है। normal होने पर फ़ेस शांत दिखना चाहिए और warning व critical की ओर बढ़ते हुए ज़्यादा ज़ोर से, और जब व्यक्ति ने कोई रंग चुना हो तो tint इस्तेमाल करना चाहिए:

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; }

अलर्ट ज़ोन किसी स्केल पर दिखाने हैं, जैसे लाल सिरे वाला डायल? warningLevel, criticalLevel और alertDirection इस्तेमाल करें।

हिस्ट्री

history में हाल के 60 तक लेवल होते हैं, सबसे पुराना पहले, पहले से 0 से 1 स्केल पर। आख़िर में मौजूदा level जोड़ दें और आपका ग्राफ़ तैयार है। नीचे दिया History Graph उदाहरण ऐसा ही एक ग्राफ़ बनाता है।

बैटरी की जानकारी

बैटरी अब भी ख़ास है। data.battery हमेशा Mac की बैटरी होती है, चाहे मीटर कुछ भी मापे, इसलिए कोई भी फ़ेस चार्जिंग का निशान दिखा सकता है। सिर्फ़ बैटरी के लिए बने फ़ेस के मैनिफ़ेस्ट में "sources": ["battery"] होना चाहिए। timeRemaining और timeToFullCharge मिनट में होते हैं और जब तक macOS अनुमान लगा रहा हो, null रहते हैं।

// 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 ?? '');

वर्शन 1 के लिए लिखे फ़ेस टॉप लेवल पर percentage, isCharging और formattedTime पढ़ते हैं। ये अब भी भेजे जाते हैं: बैटरी सोर्स के लिए ये बैटरी की अपनी वैल्यू हैं, और बाकी सोर्स के लिए isCharging false होता है और formattedTime टाइमर या काउंटडाउन में बचा समय (h:mm) होता है, वरना null।

आपका अपना डेटा

चार सोर्स आपके दिए नंबर दिखाते हैं: भेजी गई वैल्यू, वेब पता, फ़ाइल या स्क्रिप्ट। आपका फ़ेस उन्हें किसी भी दूसरे सोर्स की तरह ही संभालता है।

लिंक से वैल्यू भेजें

कहीं से भी multimeter://set लिंक खोलें: Terminal, कोई shell स्क्रिप्ट, शॉर्टकट्स (URL खोलें), Raycast, कोई cron जॉब, कोई बिल्ड स्क्रिप्ट। भेजी गई वैल्यू सोर्स और उसी की वाला मीटर जोड़ें, और लिंक खुलते ही वह अपडेट हो जाता है। open -g Multimeter को बैकग्राउंड में रखता है।

# 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"
पैरामीटरइसका मतलब
keyयह किस मीटर के लिए है। की में छोटे-बड़े अक्षरों का फ़र्क नहीं पड़ता।
valueनंबर। ज़रूरी है।
min / maxरेंज, ताकि level सही निकले। डिफ़ॉल्ट 0 से 100 है।
labelमीटर के लेबल का टेक्स्ट।
unitनंबर के बाद जोड़ी जाने वाली यूनिट, जैसे cups। प्रतिशत के लिए % इस्तेमाल करें।
formattedformatted के लिए आपका अपना टेक्स्ट, जो नंबर की जगह इस्तेमाल होता है।

भेजी गई वैल्यू Mac App Store वर्शन में भी काम करती हैं। Multimeter हर की की आख़िरी वैल्यू याद रखता है।

वेब पते, फ़ाइलें और स्क्रिप्ट

तीनों एक ही तरह का आउटपुट पढ़ते हैं: एक सादा नंबर, या JSON जिसमें value और कोई भी अतिरिक्त चीज़ हो। value के अलावा सब कुछ वैकल्पिक है।

42
42.5
42%

{"value": 42, "min": 0, "max": 100, "label": "Deploys", "unit": "", "formatted": "42 left"}

बड़े JSON की ओर इशारा कर रहे हैं, जैसे किसी API का जवाब? मीटर को एक की पाथ दें और वह नंबर निकाल लेगा:

{"data": {"count": 17}}                → key path  data.count
{"items": [{"value": 9.5}]}             → key path  items.0.value
  • वेब पता: आपके चुने अंतराल पर लाया जाता है, ज़्यादा से ज़्यादा हर 30 सेकंड में (आप न बदलें तो हर 5 मिनट में)।
  • फ़ाइल: फ़ाइल बदलते ही फिर से पढ़ी जाती है। फ़ाइल लिख सकने वाली किसी भी चीज़ के लिए काम की।
  • स्क्रिप्ट: एक shell कमांड जो आपके चुने अंतराल पर zsh से चलती है (आप न बदलें तो हर 30 सेकंड में)। Multimeter वह नंबर दिखाता है जो वह प्रिंट करती है। Homebrew के फ़ोल्डर PATH में होते हैं, और 10 सेकंड से ज़्यादा लेने वाली कमांड रोक दी जाती है।
# 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}'

Mac App Store वर्शन वेब पते और फ़ाइलें इस्तेमाल कर सकता है, पर स्क्रिप्ट नहीं। वहाँ स्क्रिप्ट के लिए, अपनी स्क्रिप्ट से लिंक भेजें या फ़ाइल लिखवाएँ।

लाइट और डार्क मेनू बार

वॉलपेपर के हिसाब से मेनू बार लाइट या डार्क होता है। Multimeter body में light-mode या dark-mode जोड़ता है, डेटा में colorScheme सेट करता है, और prefers-color-scheme भी मेनू बार से मेल खाता है। जो पसंद हो, वह इस्तेमाल करें:

/* 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; }
}

कम गति

जब गति कम करें चालू होता है, तो reduceMotion true होता है और prefers-reduced-motion मेल खाता है। मीटर को पढ़ने लायक रखें और उछाल, धड़कन और स्वीप हटा दें:

body.reduce-motion * { transition: none !important; animation: none !important; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

पूरे उदाहरण

दो पूरे फ़ेस, इसी पेज पर उसी डेटा से लाइव चलते हुए जो Multimeter भेजता है। फ़ाइलों को एक फ़ोल्डर में कॉपी करें और उसे इम्पोर्ट करें।

फ़ेस बनाएँ का स्टार्टर

ठीक वही जो फ़ेस बनाएँ… लिखता है: एक लेबल, एक बार और फ़ॉर्मैट की हुई वैल्यू, साथ में कमेंट्स में API की जानकारी। यह state के हिसाब से खुद को रंगता है, tint का ध्यान रखता है, और मेनू बार के रूप के साथ चलता है।

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();
}

हिस्ट्री ग्राफ़

history से पिछले एक मिनट का लाइन ग्राफ़, स्क्रीन के असली रेज़ोल्यूशन पर canvas में ड्रॉ किया हुआ।

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;

टेस्टिंग के टिप्स

  • सब कुछ 22 पॉइंट की ऊँचाई के अंदर रखें, और अपने फ़ेस को उसकी minWidth और maxWidth पर जाँचें।
  • सिस्टम फ़ॉन्ट (-apple-system) और font-variant-numeric: tabular-nums इस्तेमाल करें ताकि नंबर हिलें नहीं।
  • मीटर जोड़ें… और यह मीटर कस्टमाइज़ करें… में अपने फ़ेस को कई सोर्स के साथ आज़माएँ: एक प्रतिशत, नेटवर्क स्पीड, एक टाइमर, एक अनुपलब्ध सोर्स।
  • लाइट और डार्क मेनू बार, कोई टिंट, और चेतावनी व क्रिटिकल स्थितियाँ जाँचें।
  • आपके JavaScript की गड़बड़ियाँ Console.app में दिखती हैं। आप अपनी index.html को Safari में खोलकर कंसोल से उसे डेटा भी भेज सकते हैं:
  • फ़ाइलें छोटी रखें और नेटवर्क रिक्वेस्ट से बचें, ताकि फ़ेस तुरंत ड्रॉ हो।
// 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 }));

बनाने के लिए तैयार?

$19.99, एक बार का भुगतान। कोई सब्सक्रिप्शन नहीं। 30 दिन में रिफ़ंड।

$19.99 में खरीदें