#!/usr/bin/env node // qs-doc.mjs: open or print docs for the installed Quickshell version. // Cross-platform: no shell, no POSIX assumptions. Pure Node builtins. // // Usage: // qs-doc # print URL for a type // qs-doc --list # print type index URL // qs-doc --open # open in default browser // qs-doc --print # print URL (explicit; same as default) // // Type names use the dot-path shown in the docs (e.g. PanelWindow, // Quickshell.Services.Pipewire.Pipewire). import { spawnSync } from "node:child_process"; import { platform } from "node:os"; function getVersion() { for (const bin of ["qs", "quickshell"]) { const r = spawnSync(bin, ["--version"], { encoding: "utf8" }); if (r.status === 0) { const m = r.stdout.match(/(\d+\.\d+\.\d+)/); if (m) return m[1]; } } return null; } function usage() { process.stderr.write( "usage: qs-doc [--open|--print]\n" + " qs-doc --list\n" + " Type: doc path like PanelWindow or Quickshell.Services.Pipewire.Pipewire\n" + " --open open the URL in the default browser\n" + " --print print the URL to stdout (default if stdout is a tty)\n", ); } function openUrl(url) { const os = platform(); // Try each platform's open command. Each is a separate spawn without a shell. const candidates = os === "darwin" ? [["open", [url]]] : os === "win32" ? [["cmd", ["/c", "start", "", url]]] : [ ["xdg-open", [url]], ["wslview", [url]], ["sensible-browser", [url]], ]; for (const [cmd, args] of candidates) { const r = spawnSync(cmd, args, { stdio: "ignore" }); if (r.status === 0) return true; } return false; } const argv = process.argv.slice(2); if (argv.length === 0) { usage(); process.exit(1); } const wantList = argv[0] === "--list"; const wantOpen = argv.includes("--open"); const wantPrint = argv.includes("--print") || !wantOpen; const type = wantList ? null : argv.find((a) => !a.startsWith("--")); if (!wantList && !type) { usage(); process.exit(1); } const version = getVersion(); if (!version) { process.stderr.write("qs-doc: cannot find `qs` or `quickshell` on PATH\n"); process.exit(1); } const url = wantList ? `https://quickshell.org/docs/v${version}/types` : (() => { // Type path: "PanelWindow" -> "Quickshell/PanelWindow", // "Quickshell.Services.Pipewire.Pipewire" -> "Quickshell/Services/Pipewire/Pipewire" const path = type.includes("/") ? type : type.startsWith("Quickshell") ? type.replace(/\./g, "/") : `Quickshell/${type.replace(/\./g, "/")}`; return `https://quickshell.org/docs/v${version}/types/${path}`; })(); if (wantOpen) { if (!openUrl(url)) { process.stderr.write(`qs-doc: could not open browser; URL: ${url}\n`); process.exit(1); } process.stdout.write(`${url}\n`); process.exit(0); } if (wantPrint) { process.stdout.write(`${url}\n`); process.exit(0); }