/** * pi-shimmer-quotes-spinner — Quotes module * * Loads, parses, and manages the quote rotation. * Quotes come from a user-editable file or built-in defaults. */ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { getQuotesFileDir } from "./config"; // ─── Types ────────────────────────────────────────────────────────────── export interface LoadedQuotes { quotes: string[]; source: "user" | "bundled" | "fallback"; } // ─── Built-in Default Quotes ─────────────────────────────────────────── const DEFAULT_QUOTES: string[] = [ "Simplicity is prerequisite for reliability. — Edsger W. Dijkstra", "Programs must be written for people to read. — Harold Abelson", "Debugging is twice as hard as writing the code in the first place. — Brian Kernighan", "First, solve the problem. Then, write the code. — John Johnson", "Code is like humor. When you have to explain it, it's bad. — Cory House", "Make it work, make it right, make it fast. — Kent Beck", "Any fool can write code that a computer can understand. — Martin Fowler", "Talk is cheap. Show me the code. — Linus Torvalds", "Premature optimization is the root of all evil. — Donald Knuth", "The only way to learn a new programming language is by writing programs in it. — Dennis Ritchie", "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates", "There are only two hard things in computer science: cache invalidation and naming things. — Phil Karlton", "If you can't explain it simply, you don't understand it well enough. — Albert Einstein", "The most dangerous phrase in language is: we've always done it this way. — Grace Hopper", "The function of good software is to make the complex appear to be simple. — Grady Booch", "A language that doesn't affect the way you think about programming is not worth knowing. — Alan Perlis", "Sometimes it pays to stay in bed on Monday, rather than spending the rest of the week debugging Monday's code. — Dan Salomon", "The best time to plant a tree was 20 years ago. The second best time is now. — Chinese Proverb", "It's not a bug — it's an undocumented feature. — Anonymous", "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. — John Woods", "Fix the cause, not the symptom. — Steve Maguire", "A good programmer is someone who always looks both ways before crossing a one-way street. — Doug Linder", "Walking on water and developing software from a specification are easy if both are frozen. — Edward V. Berard", "Before software can be reusable it first has to be usable. — Ralph Johnson", "The best way to get a project done faster is to start sooner. — Jim Highsmith", "The best performance improvement is the transition from the nonworking to the working state. — John Ousterhout", ]; const FALLBACK_QUOTE = "Simplicity is prerequisite for reliability. — Edsger W. Dijkstra"; // ─── Paths ────────────────────────────────────────────────────────────── export function getUserQuotesFile(): string { return join(getQuotesFileDir(), "quotes.txt"); } // ─── Parsing ──────────────────────────────────────────────────────────── function parseQuotes(text: string): string[] { return text .split(/\r?\n/) .map((l) => l.trim()) .filter((l) => l.length > 0 && !l.startsWith("#")); } // ─── Loading ──────────────────────────────────────────────────────────── /** * Load quotes from disk. Priority: * 1. User quotes file (if it exists and has content) * 2. Built-in defaults (if enableFallback is true) * 3. Single hard-coded fallback quote */ export function loadQuotes(enableFallback: boolean): LoadedQuotes { const userFile = getUserQuotesFile(); try { if (existsSync(userFile)) { const parsed = parseQuotes(readFileSync(userFile, "utf8")); if (parsed.length > 0) return { quotes: parsed, source: "user" }; } } catch { // Fall through } if (enableFallback) { return { quotes: [...DEFAULT_QUOTES], source: "bundled" }; } return { quotes: [FALLBACK_QUOTE], source: "fallback" }; } /** * Ensure the user quotes file exists, seeding it with bundled defaults if * it doesn't. Returns the file path. */ export function ensureUserQuotesFile(): string { const file = getUserQuotesFile(); if (!existsSync(file)) { mkdirSync(dirname(file), { recursive: true }); writeFileSync( file, [ "# pi-shimmer-quotes-spinner — Custom Quotes", "# One quote per line. Blank lines and lines starting with # are ignored.", "# Edit this file then run /shimmer quotes reload to apply changes.", "", ...DEFAULT_QUOTES, ].join("\n") + "\n", "utf8", ); } return file; } // ─── Rotation ─────────────────────────────────────────────────────────── /** * Pick a random quote from the loaded set. */ export function pickQuote(loaded: LoadedQuotes): string { const pool = loaded.quotes; return pool[Math.floor(Math.random() * pool.length)] ?? FALLBACK_QUOTE; } // ─── Display Helpers ─────────────────────────────────────────────────── export function describeQuotes(loaded: LoadedQuotes): string { switch (loaded.source) { case "user": return `${loaded.quotes.length} User Quote${loaded.quotes.length !== 1 ? "s" : ""}`; case "bundled": return `${loaded.quotes.length} Built-in Quote${loaded.quotes.length !== 1 ? "s" : ""}`; case "fallback": return "Fallback Quote (no quotes loaded)"; } }