// ==UserScript== // @name HMHW Offer Oven — Prefill from briefing // @namespace https://github.com/timfarr-ai/rt-companion // @version 1.0 // @description Auto-fills the HMHW Offer Oven calculator with deal data passed via #prefill={json} hash. Used by the rt-companion briefing to one-tap verify deal numbers. // @author Tim Farr // @match https://www.hmhw.group/tools/offer-oven* // @match https://hmhw.group/tools/offer-oven* // @grant none // @run-at document-idle // @updateURL https://raw.githubusercontent.com/timfarr-ai/rt-companion/main/scripts/offer-oven-prefill.user.js // @downloadURL https://raw.githubusercontent.com/timfarr-ai/rt-companion/main/scripts/offer-oven-prefill.user.js // ==/UserScript== (function () { 'use strict'; const HASH_PREFIX = '#prefill='; const POLL_INTERVAL_MS = 250; const POLL_TIMEOUT_MS = 12000; // Map briefing field names → label patterns the userscript will match against // (case-insensitive, substring-of input's nearby label text or placeholder) const FIELD_MAP = { price: ['Purchase Price', 'purchase price'], down: ['Down Payment', 'down payment'], rate: ['Interest Rate', 'interest rate'], // Carryback rate (first match) term: ['Term (Years)', 'amortization', 'Term'], balloon: ['Balloon', 'balloon scenarios', 'balloon years'], rent: ['Rental Revenue', 'annual rent', 'rent (annual)'], tax: ['Property Tax', 'property tax (annual)', 'taxes'], insurance: ['Insurance', 'insurance (annual)'], hoa: ['HOA', 'hoa (annual)'], other: ['Other (Annual)', 'other expense'], assignment: ['Assignment Fee', 'assignment fees'], closing: ['Closing Cost', 'closing costs'], }; function getPrefillData() { const h = location.hash || ''; if (!h.startsWith(HASH_PREFIX)) return null; try { const raw = decodeURIComponent(h.slice(HASH_PREFIX.length)); return JSON.parse(raw); } catch (e) { console.warn('[Offer Oven Prefill] Invalid JSON in hash:', e); return null; } } function setReactValue(input, value) { const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; setter.call(input, String(value)); input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); } function getNearbyLabel(input) { // Try: associated