List of useful macOS commands

Network speed quality

Since macOS Monterey is there a command to test your network quality:

networkQuality -v

Output will looks like follow:

==== SUMMARY ====
Upload capacity: 16.239 Mbps
Download capacity: 475.129 Mbps
Upload flows: 20
Download flows: 12
Responsiveness: Low (154 RPM)
Base RTT: 26
Start: 19.04.2022 13:56:47
End: 19.04.2022 13:57:04
OS Version: Version 12.3.1 (Build 21E258)

If you’re curious, networkQuality uses Apple’s CDN at https://mensura.cdn-apple.com/api/v1/gm/config as the target for its testing.

List all mounted drives on Mac

df -h

Open any source

open # [url|filename|directory...]

Prevent the system from sleeping

The caffeinate command is used to prevent a Mac from going to sleep.

  • -d - Prevent the display from sleeping.
  • -i - Prevent the system from idle sleeping.
  • -m - Prevent the disk from idle sleeping.
  • -s - Prevent the system from sleeping. This assertion is valid only when system is running on AC power.
  • -u - Declare that a user is active. If the display is off, this option turns the display on and prevents the display from going into idle sleep.
  • -t - Specifies the timeout value in seconds for which the command is valid.
  • -w - Waits for the process with the specified pid to exit.
caffeinate -i make
# caffeinate forks a process, execs "make" in it 
# and prevents idle sleep as long as that 
# process is running
caffeinate -t 18000 # 18000 seconds

Or you can just use free app KeepingYouAwake

#macOS#bash#2022

CSS only toggle switch using TailwindCSS (checkbox replacement)

Learn how to create toggle switch (on/off button) from <input type="checkbox">. Let's starts with a simple HTML code:

<label>
  <input type="checkbox" />
  This is on/off checkbox
</label>

Then you have to add follow code to your tailwind.css file:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {

  label {
    @apply h-6 relative inline-block;
  }

  [type="checkbox"] {
    @apply w-11 h-0 cursor-pointer inline-block;
    @apply focus:outline-0 dark:focus:outline-0;
    @apply border-0 dark:border-0;
    @apply focus:ring-offset-transparent dark:focus:ring-offset-transparent;
    @apply focus:ring-transparent dark:focus:ring-transparent;
    @apply focus-within:ring-0 dark:focus-within:ring-0;
    @apply focus:shadow-none dark:focus:shadow-none;
    
    @apply after:absolute before:absolute;
    @apply after:top-0 before:top-0;
    @apply after:block before:inline-block;
    @apply before:rounded-full after:rounded-full;

    @apply after:content-[''] after:w-5 after:h-5 after:mt-0.5 after:ml-0.5;
    @apply after:shadow-md after:duration-100;

    @apply before:content-[''] before:w-10 before:h-full;
    @apply before:shadow-[inset_0_0_#000];

    @apply after:bg-white dark:after:bg-gray-50;
    @apply before:bg-gray-300 dark:before:bg-gray-600;
    @apply before:checked:bg-lime-500 dark:before:checked:bg-lime-500;
    @apply checked:after:duration-300 checked:after:translate-x-4;

    @apply disabled:after:bg-opacity-75 disabled:cursor-not-allowed;
    @apply disabled:checked:before:bg-opacity-40;
  }
}

https://play.tailwindcss.com/gNadhDPsbj

#Tailwind#css#2022

Simple form validation with Tailwind

Tailwind have multiple utilities for different inputs states like required, invalid, and disabled. See the pseudo-class reference for a complete list of available pseudo-class modifiers.

But what if user just loaded website and there are required inputs with default (empty) value? They will be immediately red and invalid - that's not what you expect!

<input type="url" class="invalid:bg-red-500" value="" required />

You should wait with input validation to user. That's why the CSS pseud-class :user-invalid exists. This class represents any validated form element whose value isn't valid based on their validation constraints, after the user has interacted with it. Right now :user-invalid pseudo-class isn't very well supported - works only in Firefox.

Let's crate following Javascript (sort of polyfill):

/**
 * Form validation for older browsers
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid
 * @see https://caniuse.com/?search=user-invalid
 */
try {
  if (typeof window !== 'undefined') {
    document.querySelector(':user-invalid');
  }
} catch {
  document.addEventListener('DOMContentLoaded', () => {
    for (const input of document.querySelectorAll('input')) {
      input.addEventListener('change', event => event.target.classList.add('dirty'));
    }
  });
}

This code adds a dirty class to all changed input fields, which helps us to simulate the :user-invalid pseudo-class and clearly mark all inputs that have been changed by the user. Then you can easily distinguish between changed and unchanged inputs that are invalid. Following Tailwind code requires tailwindcss-forms plugin:

/* invalid and dirty or :user*/
:is(input:user-invalid, input.dirty) {
  @apply invalid:focus:ring-red-100 invalid:focus:dark:ring-red-500 invalid:focus:dark:ring-opacity-30;
  @apply invalid:dark:bg-red-800/20 invalid:dark:border-red-900;
  @apply invalid:bg-red-50 invalid:border-red-300;
}

and full CSS example:

@tailwind base;

@layer base {
  [type='text'],
  [type='email'],
  [type='url'],
  [type='password'],
  [type='number'],
  [type='date'],
  [type='datetime-local'],
  [type='month'],
  [type='search'],
  [type='tel'],
  [type='time'],
  [type='week'],
  [multiple],
  textarea,
  select {
    @apply block w-full rounded-md shadow-sm;
    @apply border-gray-300 focus:border-blue-300;
    @apply dark:bg-gray-700 dark:border-gray-600 dark:text-white;
    @apply placeholder-gray-500 dark:placeholder-gray-400;
    @apply dark:border-gray-600 dark:focus:border-gray-500;

    @a,pply focus:ring;
    @apply focus:ring-blue-200 dark:focus:ring-gray-600;
    @apply focus:ring-opacity-50 dark:focus:ring-opacity-50;
  }
}

@tailwind components;
@tailwind utilities;

/* invalid and dirty or :user*/
:is(input:user-invalid, input.dirty) {
  @apply invalid:focus:ring-red-100 invalid:focus:dark:ring-red-500 invalid:focus:dark:ring-opacity-30;
  @apply invalid:dark:bg-red-800/20 invalid:dark:border-red-900;
  @apply invalid:bg-red-50 invalid:border-red-300;
}

https://play.tailwindcss.com/awVeicebKX

#Tailwind#css#js#2022

Tailwind tooltip without Javascript (CSS only)!

There's a plenty of libraries (mostly JavaScript) to enable the classic tooltip design pattern. You can easily add tooltips to your elements without JavaScript and in just a few lines of Tailwind code and HTML. Lets starts with HTML code:

<div>
  <div class="py-16 text-center dark:bg-gray-800">
    <p class="dark:text-gray-200 pb-3">Hover over button to show tooltip</p>
    <button
      aria-label="Show tooltip content"
      class="px-4 py-2 border dark:border-gray-700 rounded bg-blue-600 text-white font-semibold text-sm shadow">
      Example button
    </button>
  </div>
</div>

Then you have to add follow code to your tailwind.css file:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  [aria-label] {
    @apply relative;
    @apply after:font-sans;
    @apply before:pointer-events-none after:pointer-events-none;
    @apply after:content-[attr(aria-label)] after:font-semibold;
    @apply after:opacity-0 before:opacity-0;
    @apply after:z-50 before:z-50;
    @apply after:absolute before:absolute;
    @apply after:transition-all before:transition-all after:duration-500 before:duration-500;
    @apply after:left-1/2 before:left-1/2;
    @apply after:bottom-full before:bottom-full;
    @apply after:mb-2.5 after:-translate-x-1/2 before:-translate-x-1/2;
    @apply before:content-[''] before:border-[5px] before:w-0 before:h-0;
    @apply hover:before:opacity-100 hover:after:opacity-100;
    @apply hover:before:bottom-full hover:after:bottom-full;
    @apply after:min-w-fit after:whitespace-pre-wrap after:w-max;
  }

  :where([aria-label]) {
    @apply dark:after:bg-gray-500 after:bg-gray-900 after:text-white after:text-sm after:rounded after:py-1 after:px-2;
    @apply before:border-t-gray-900 dark:before:border-t-gray-500 before:border-transparent after:text-center;
  }
}

For all visual elements of the tooltip, I use :where() pseudo class, which allows us to change these parameters later directly from the HTML code.

<button
  class="after:bg-red-500 before:border-t-red-500"
  aria-label="Show red tooltip">Example button</button>

Width of the tooltip can be limited with after:max-w-[180px] class.

<p aria-label="Fisrt line&#xa;Second line">Example button</p>

You can decide where will be break by including &#xa; HTML Encoded Line Feed character - new line to the aria-label .

<p aria-label="extra long content" class="after:max-w-[180px]">Example button</p>

https://play.tailwindcss.com/8QreaqXGBt

#Tailwind#css#2022

Control Time Machine from the command line

You can enable sudo tmutil enable or disable sudo tmutil disable from command line. If you want to run a Time Machine backup right away, just run tmutil startbackup or tmutil stopbackup if you ever want to stop a backup.

Local snapshots

Follow command will disable and delete "local snapshots":

sudo tmutil disablelocal

You can turn local snapshots back on by running:

sudo tmutil enablelocal

Exclude selected folders

sudo tmutil addexclusion ~/Downloads

There is interesting property -p that controls whether or not folder remains in exclusion when is moved. If you use the above command with the -p flag, then it will not be sticky.

If you are developer there is few quite common folders that should be excluded:

sudo tmutil addexclusion ~/.composer 
sudo tmutil addexclusion ~/.npm 
sudo tmutil addexclusion ~/Library/Developer 
sudo tmutil addexclusion ~/Library/Containers/com.docker.docker/Data/ 

List excluded

sudo mdfind "com_apple_backup_excludeItem = 'com.apple.backupd'"

Get Time Machine stats

tmutil listbackups

#macOS#TimeMachine#2022

Fetch in nodejs

#!/usr/bin/env node --experimental-modules

import https from 'https'; // or http if you need

async function fetch(url) {
    return new Promise((resolve, reject) => {
        const request = https.get(url, {timeout: 1000}, (res) => {
            if (res.statusCode < 200 || res.statusCode > 299) {
                return reject(new Error(`HTTP status code ${res.statusCode}`));
            }

            const body = [];
            res.on('data', (chunk) => body.push(chunk));

            res.on('end', () => {
                resolve(Buffer.concat(body).toString());
            });
        });

        request.on('error', (err) => {
            reject(err);
        });

        request.on('timeout', () => {
            request.destroy();
            reject(new Error('timed out'));
        });
    });
}

const html = await fetch('https://ozana.cz');

#nodejs#Snippets#2021