Skip to content

Instantly share code, notes, and snippets.

@abstractalgo
Created March 28, 2026 08:15
Show Gist options
  • Select an option

  • Save abstractalgo/d0d05a9bd0fd8c064c020a36f9534e30 to your computer and use it in GitHub Desktop.

Select an option

Save abstractalgo/d0d05a9bd0fd8c064c020a36f9534e30 to your computer and use it in GitHub Desktop.

https://x.com/didiercatz/status/2037593968800592359

image image
#!/usr/bin/env node

// Required parameters:
// @raycast.schemaVersion 1
// @raycast.title Timer
// @raycast.mode silent

// Optional parameters:
// @raycast.icon https://cdn-icons-png.flaticon.com/512/5387/5387915.png
// @raycast.argument1 { "type": "text", "placeholder": "Time (e.g. 30s, 5m, 1h)", "optional": false }
// @raycast.argument2 { "type": "text", "placeholder": "Message", "optional": true }
// @raycast.packageName Timer

// Documentation:
// @raycast.description Show a macOS notification after a delay
// @raycast.author abstractalgo

import { execSync } from "child_process";

function parseTime(input) {
  const match = input.match(
    /^(\d+(?:\.\d+)?)\s*(s|sec|secs|seconds?|m|min|mins|minutes?|h|hr|hrs|hours?)$/i,
  );
  if (!match) {
    console.error(`Invalid time format: "${input}". Use e.g. 30s, 5m, 1h`);
    process.exit(1);
  }
  const value = parseFloat(match[1]);
  const unit = match[2].toLowerCase();
  if (unit.startsWith("h")) return value * 3600;
  if (unit.startsWith("m")) return value * 60;
  return value;
}

const seconds = parseTime(process.argv[2]);
const message = process.argv[3] || "Timer done!";
const title = "Timer";

console.log(`Notification in ${process.argv[2]}...`);

setTimeout(() => {
  execSync(
    `osascript -e 'display notification "${message}" with title "${title}" sound name "Glass"'`,
  );
}, seconds * 1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment