diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index ceb6026..0aa09cc 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 24 - - run: node --check app-0.3.0.js && node --check siblings-0.3.0.js && node --check candidate/redirect.js + - run: node --check app-0.4.0.js && node --check siblings-0.4.0.js && node --check candidate/redirect.js - run: node --test test/*.test.js deploy: if: github.event_name != 'pull_request' diff --git a/README.md b/README.md index f77b63c..189a8d0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Public interactive demo: https://deployanyway.github.io/ -Static HTML, CSS and browser ES modules. No analytics, accounts or backend. Inputs stay in the browser. The five demos use vendored 0.3.0 package source; the dog logger uses a single-string `node:util` adapter because the form does not accept formatting arguments. This adapter is not a replacement for the npm library's full Node formatting behavior. +Static HTML, CSS and browser ES modules. No analytics, accounts or backend. Inputs stay in the browser. The five demos use vendored 0.4.0 package source; the dog logger uses a single-string `node:util` adapter because the form does not accept formatting arguments. This adapter is not a replacement for the npm library's full Node formatting behavior. ## Develop @@ -12,6 +12,16 @@ The root introduces DeployAnyway and gives all five released tools equal discove To refresh demos, copy the four companion packages' browser-safe `src/*.js` and MIT licenses into `vendor//`. Build bro-say and copy its `dist/browser.js` and third-party license notices into this site's `dist/`. Review API changes and verify all browser interactions. The site does not fetch package code from a third-party CDN at runtime. +## Explore the released libraries + +- bro-say: 13 original characters, 20 moods, six themes and 48 message presets. Choose a category to inspect all six messages; seeded selection is repeatable within this version. +- error-translator: 46 supported errors, full catalog, custom messages and JSON batches. Unknown input receives an explicit fallback. +- excuse-js: 132 original excuses across eleven categories. Inspect each category, generate seeded batches or pair a phrase with a practical next step. +- doggo-log: 48 optional commentary lines across six levels. Explore classic or rotating commentary, an eight-log sequence, filtering and structured context. Sequence and catalog examples use the Node API and include installation/run instructions. +- ship-it-meter: twelve example evidence scenarios, editable JSON, prioritized release plans, gates and scores. These are heuristics based on supplied facts; the browser does not run your CI. + +The organization palette and home badge follow the supplied husky artwork. Dallas and Benji inspire the story and playful tone. All five tools retain equal discovery and feedback links. + ## License MIT. Vendored package licenses are included in their directories. diff --git a/app-0.4.0.js b/app-0.4.0.js new file mode 100644 index 0000000..a4fba30 --- /dev/null +++ b/app-0.4.0.js @@ -0,0 +1,126 @@ +import { + renderBro, + listCharacters, + moods, + listThemes, + broMessage, + messageCategories, + messagePresets, +} from "./dist/browser.js"; + +const element = (id) => document.getElementById(id); +for (const [id, names, selected] of [ + ["character", listCharacters(), "husky"], + ["mood", moods(), "sarcastic"], + ["theme", listThemes(), "classic"], +]) { + for (const name of names) { + const option = document.createElement("option"); + option.value = option.textContent = name; + option.selected = name === selected; + element(id).append(option); + } +} +const presetLabel = document.createElement("label"); +presetLabel.textContent = "Message source"; +const presetSelect = document.createElement("select"); +presetSelect.id = "bro-preset"; +for (const value of ["Your message", ...messageCategories()]) { + const option = document.createElement("option"); + option.value = option.textContent = value; + presetSelect.append(option); +} +presetLabel.append(presetSelect); +element("controls").prepend(presetLabel); +const presetList = document.createElement("pre"); +presetList.className = "preset-library"; +presetList.setAttribute("aria-label", "Message preset library"); +element("controls").after(presetList); +function render() { + try { + const random = element("bro-selection").value === "random"; + for (const id of ["character", "mood", "theme"]) + element(id).disabled = random; + const preset = element("bro-preset").value; + const generated = preset !== "Your message"; + element("message").disabled = generated; + element("bro-seed").disabled = !random && !generated; + const text = generated + ? broMessage(preset, { seed: element("bro-seed").value }) + : element("message").value; + presetList.hidden = !generated; + presetList.textContent = generated + ? "All six " + + preset + + " presets:\n" + + messagePresets(preset) + .map((line, i) => i + 1 + ". " + line) + .join("\n") + : ""; + const result = renderBro({ + text, + character: random ? undefined : element("character").value, + mood: random ? undefined : element("mood").value, + theme: random ? undefined : element("theme").value, + mode: element("mode").value, + width: Number(element("width").value), + wrap: element("bro-wrap").checked, + ...(element("bro-layout").value === "plain" + ? { layout: "plain" } + : element("bro-layout").value === "box" + ? { box: true } + : {}), + ...(random ? { random: true, seed: element("bro-seed").value } : {}), + }); + element("output").textContent = + element("bro-format").value === "json" + ? JSON.stringify(result, null, 2) + : result.rendered; + const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'"; + element("bro-command").textContent = + `npx @deployanyway/bro-say@0.4.0 ${generated ? `--preset ${preset}` : quote(text)} --width ${element("width").value}` + + (random + ? ` --random --seed ${quote(element("bro-seed").value)}` + : `${generated ? ` --seed ${quote(element("bro-seed").value)}` : ""} --character ${element("character").value} --mood ${element("mood").value} --theme ${element("theme").value}`) + + (element("mode").value === "think" ? " --think" : "") + + (element("bro-format").value === "json" ? " --json" : "") + + (!element("bro-wrap").checked ? " --no-wrap" : "") + + (element("bro-layout").value === "plain" + ? " --plain" + : element("bro-layout").value === "box" + ? " --box" + : ""); + element("status").textContent = ""; + element("copy").disabled = false; + element("bro-copy-command").disabled = false; + } catch (error) { + element("status").textContent = error.message; + element("output").textContent = ""; + element("copy").disabled = true; + element("bro-copy-command").disabled = true; + element("bro-command").textContent = ""; + } +} +element("controls").addEventListener("input", render); +element("controls").addEventListener("submit", (event) => + event.preventDefault(), +); +element("copy").addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(element("output").textContent); + element("status").textContent = "Copied. Ship the message responsibly."; + } catch { + element("status").textContent = + "Clipboard unavailable. Select and copy the output manually."; + } +}); +render(); + +element("bro-copy-command").addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(element("bro-command").textContent); + element("status").textContent = "Command copied."; + } catch { + element("status").textContent = "Select and copy the command manually."; + } +}); diff --git a/dist/browser.js b/dist/browser.js index 58bbbfe..4f66eb5 100644 --- a/dist/browser.js +++ b/dist/browser.js @@ -186,6 +186,46 @@ var characters = Object.fromEntries( // src/personas.js var personas = { + dallas: { + intro: "DALLAS HAS ARRIVED AT FULL ZOOMIE SPEED.", + label: "ENTHUSIASM WITH EVIDENCE", + outro: "Take the victory lap after the checks pass." + }, + benji: { + intro: "BENJI HAS AN IDEA AND WOULD LIKE YOUR ATTENTION.", + label: "NEXT ADVENTURE", + outro: "Investigate the interesting thing. Keep a return route." + }, + "rubber-duck": { + intro: "Tell me what you expected. Then tell me what happened.", + label: "QUACK-SIZED REPRODUCTION", + outro: "One clear example beats a pond full of guesses." + }, + "on-call": { + intro: "The alert is loud. We can stay methodical.", + label: "INCIDENT NOTE", + outro: "Check impact, capture evidence, and confirm rollback." + }, + "code-review": { + intro: "A friendly question before the merge.", + label: "KINDNESS IS A REVIEW TOOL", + outro: "Make the reasoning easy for the next developer to follow." + }, + minimalist: { + intro: "Less magic. More clarity.", + label: "SMALLEST USEFUL STEP", + outro: "Keep the part that solves the problem." + }, + optimist: { + intro: "This can get better. Let us prove it.", + label: "HOPE WITH A TEST CASE", + outro: "A small verified improvement is still an improvement." + }, + skeptic: { + intro: "Interesting claim. Where are the receipts?", + label: "SHOW THE EVIDENCE", + outro: "Confidence is welcome. Verification gets the merge." + }, classic: { intro: "BRO...", label: "", outro: "" }, hype: { intro: "BRO! THE TERMINAL IS APPLAUDING!", @@ -1368,12 +1408,102 @@ function brothink(text, options = {}) { throw new TypeError("Options must be an object."); return renderBro({ ...options, text, mode: "think" }).rendered; } + +// src/messages.js +var presets = { + deployment: [ + "The release has landed. Please keep your monitoring inside the vehicle.", + "Shipped with confidence. Verified with health checks.", + "Today we deploy code and a reasonable rollback plan.", + "The canary is singing. Let us keep listening.", + "A small release for the app. A large relief for the team.", + "The deployment passed. The dashboard gets the final word." + ], + testing: [ + "Red, green, refactor. Snacks are an optional fourth step.", + "The test failed usefully. That is a good beginning.", + "A passing test is a receipt, not a prophecy.", + "Make the edge case boring enough to test.", + "One regression test today saves a mystery tomorrow.", + "The suite is green. Check what it actually covered." + ], + debugging: [ + "Read the first error before collecting the entire stack trace.", + "The rubber duck requests a smaller reproduction.", + "Follow the value. It knows where it has been.", + "A breakpoint is cheaper than a theory committee.", + "The bug cannot hide from a clear input and a patient developer.", + "One hypothesis. One experiment. One less guess." + ], + review: [ + "Review the change, not the person who wrote it.", + "A kind question can save a very loud incident.", + "The clever line would appreciate a simple explanation.", + "If future-you needs a map, leave a comment today.", + "LGTM comes after understanding the diff.", + "Small commits make excellent conversation starters." + ], + coffee: [ + "The code is brewing. So is the coffee.", + "A refill is not a retry policy, but it can improve the meeting.", + "This mug contains the unofficial build coordinator.", + "The coffee is hot. The take can wait.", + "Hydrate between compilations. Your compiler does not need another cup.", + "A short break is a legitimate debugging technique." + ], + celebration: [ + "The fix worked. Give the test its share of the applause.", + "Celebrate the boring release. It took good work to make it boring.", + "One fewer alert. One more reason to take a walk.", + "The team shipped a useful thing. That deserves a victory lap.", + "Success has receipts and a very happy tail.", + "Treat budget approved after verification." + ], + husky: [ + "Dallas brings the zoomies. Benji brings the next idea.", + "Two huskies, one keyboard, absolutely no quiet roadmap.", + "Commit small. Run fast. Leave the socks out of production.", + "Dallas heard deploy. Benji heard play. Both approve a smoke test.", + "High energy is a feature. Good boundaries keep it useful.", + "Our huskies believe every adventure deserves enthusiasm and a return route." + ], + focus: [ + "Pick the next useful step. Finish that one.", + "Reduce the scope until the problem fits in your afternoon.", + "Your terminal can have personality. Your interfaces should have clarity.", + "Small progress counts even without a dramatic soundtrack.", + "A helpful tool should make the next action easier to see.", + "Close one loop before opening six more tabs." + ] +}; +var messageCategories = () => Object.keys(presets); +function messagePresets(category) { + if (typeof category !== "string" || !Object.hasOwn(presets, category.trim().toLowerCase())) + throw new RangeError( + "Choose a supported message category: " + messageCategories().join(", ") + ); + return [...presets[category.trim().toLowerCase()]]; +} +function broMessage(category = "deployment", options = {}) { + if (!options || typeof options !== "object" || Array.isArray(options)) + throw new TypeError("Options must be an object."); + if (options.seed !== void 0 && typeof options.seed !== "string" && !(typeof options.seed === "number" && Number.isFinite(options.seed))) + throw new TypeError("seed must be a string or finite number."); + return choose( + messagePresets(category), + options.seed, + "message:" + category.trim().toLowerCase() + ); +} export { + broMessage, brosay2 as brosay, brothink, displayWidth, listCharacters, listThemes, + messageCategories, + messagePresets, moods, renderBro }; diff --git a/index.html b/index.html index 38ab8b6..f1355c6 100644 --- a/index.html +++ b/index.html @@ -12,12 +12,12 @@ - + - - + +
@@ -31,7 +31,7 @@ width="220" height="220" /> -

0.3.0 · AVAILABLE ON NPM

+

0.4.0 · AVAILABLE ON NPM

DeployAnyway.

Software nobody requested, built with questionable priorities, and @@ -72,8 +72,8 @@

Serious about the work. Playful about the day.

>error-translatorMake errors understandableTranslate familiar error codes and messages into explanations and - practical debugging advice.Explore 46 supported error codes, a full catalog, custom messages + and batches with explanations and practical debugging advice.excuse-jsSerious about the work. Playful about the day. >

- All five packages are AVAILABLE ON NPM at version 0.3.0. These demos run + All five packages are AVAILABLE ON NPM at version 0.4.0. These demos run locally in your browser.

@@ -165,12 +165,13 @@

bro-say

- 13 original characters, 12 personalities and 6 themes. Say or think; - Unicode-aware wrapping. Terminal fonts may disagree on emoji width. + 13 original characters, 20 moods, 6 themes and 48 message presets + across 8 categories. Say or think; Unicode-aware wrapping. Terminal + fonts may disagree on emoji width.

Runs entirely in your browser. No message is sent anywhere.

- npm install -g @deployanyway/bro-say@0.3.0 · + npm install -g @deployanyway/bro-say@0.4.0 · All five packages on npm @@ -189,8 +190,8 @@

bro-say

>

error-translator

- Translate familiar error codes and messages into explanations and - practical debugging advice. + Explore 46 supported error codes, a full catalog, custom messages and + batches with explanations and practical debugging advice.