labs / static /js /pricing.js
3v324v23's picture
deploy: unified router + dreamy website (2026-06-16T09:46:52Z)
c1a683f
Raw
History Blame Contribute Delete
5.86 kB
/* =========================================================================
pricing.js — fetch /api/pricing, render the rate table + cost estimator
• populates the model dropdown for the calculator
• recomputes credits on any input change
• live filter on the rate table
========================================================================= */
(function () {
'use strict';
var API = '/api/pricing';
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
});
}
function fmtPrice(n) {
if (n == null) return '—';
if (n < 0.01) return '$' + n.toFixed(4);
if (n < 1) return '$' + n.toFixed(3);
return '$' + n.toFixed(2);
}
function fmtCredits(n) {
return Number(n || 0).toLocaleString();
}
// strip the leading "free:" or "anthropic/" prefix for a friendlier display,
// but keep the full id as a title tooltip
function shortName(full) {
var n = full.replace(/^free:/, '');
var slash = n.indexOf('/');
return slash >= 0 ? n.slice(slash + 1) : n;
}
function providerOf(full) {
var n = full.replace(/^free:/, '');
var slash = n.indexOf('/');
return slash >= 0 ? n.slice(0, slash) : '';
}
var STATE = { models: [], rate: null };
async function load() {
var body = document.getElementById('rates-body');
try {
var res = await fetch(API);
if (!res.ok) throw new Error('status ' + res.status);
var data = await res.json();
STATE.models = data.models || [];
STATE.rate = data.rate || { credits_per_usd: 10000, input_ratio: 1 / 3 };
renderTable(STATE.models);
populateCalculator(STATE.models);
} catch (e) {
if (body) body.innerHTML = '<tr><td colspan="5" class="rates-empty">Couldn\u2019t load rates. Please try again.</td></tr>';
}
}
function renderTable(models) {
var body = document.getElementById('rates-body');
if (!body) return;
if (!models.length) {
body.innerHTML = '<tr><td colspan="5" class="rates-empty">No models match your filter.</td></tr>';
return;
}
var html = '';
for (var i = 0; i < models.length; i++) {
var m = models[i];
var prov = providerOf(m.model);
var badge = m.free ? '<span class="free-badge">free</span>' : '';
html +=
'<tr>' +
'<td><span class="model-name" title="' + esc(m.model) + '">' + esc(shortName(m.model)) + '</span>' + badge +
(prov ? '<div style="font-size:.7rem;color:var(--ink-soft,#6b5b7a);">' + esc(prov) + '</div>' : '') + '</td>' +
'<td class="num">' + fmtPrice(m.usd_per_1m_input) + '</td>' +
'<td class="num">' + fmtPrice(m.usd_per_1m_output) + '</td>' +
'<td class="num">' + fmtCredits(m.credits_per_1k_output) + '</td>' +
'<td class="num">' + fmtCredits(m.targets) + '</td>' +
'</tr>';
}
body.innerHTML = html;
}
function populateCalculator(models) {
var sel = document.getElementById('calc-model');
if (!sel) return;
// sort: non-free (claude) first, then by output price descending so premium surfaces
var sorted = models.slice().sort(function (a, b) {
if (a.free !== b.free) return a.free ? 1 : -1;
return b.usd_per_1m_output - a.usd_per_1m_output;
});
var opts = '';
for (var i = 0; i < sorted.length; i++) {
opts += '<option value="' + esc(sorted[i].model) + '">' + esc(sorted[i].model) + '</option>';
}
sel.innerHTML = opts;
recalc();
}
function lookup(name) {
for (var i = 0; i < STATE.models.length; i++) {
if (STATE.models[i].model === name) return STATE.models[i];
}
return null;
}
function recalc() {
var sel = document.getElementById('calc-model');
var inEl = document.getElementById('calc-in');
var outEl = document.getElementById('calc-out');
var credEl = document.getElementById('calc-result').querySelector('.cr-credits');
var usdEl = document.getElementById('calc-usd');
if (!sel || !inEl || !outEl || !credEl) return;
var m = lookup(sel.value);
var tokIn = Math.max(0, parseInt(inEl.value, 10) || 0);
var tokOut = Math.max(0, parseInt(outEl.value, 10) || 0);
if (!m) { credEl.textContent = '—'; usdEl.textContent = ''; return; }
// input priced at 1/3 of output
var inPrice = m.usd_per_1m_output * STATE.rate.input_ratio;
var usd = (tokIn * inPrice + tokOut * m.usd_per_1m_output) / 1e6;
var credits = Math.round(usd * (STATE.rate.credits_per_usd || 10000));
if ((tokIn || tokOut) && credits < 1) credits = 1;
if (!tokIn && !tokOut) credits = 0;
credEl.textContent = fmtCredits(credits);
usdEl.textContent = credits > 0 ? ('\u2248 $' + (credits / (STATE.rate.credits_per_usd || 10000)).toFixed(4) + ' USD') : '';
}
// wire up events once DOM is ready
function init() {
load();
var sel = document.getElementById('calc-model');
var inEl = document.getElementById('calc-in');
var outEl = document.getElementById('calc-out');
var search = document.getElementById('rate-search');
if (sel) sel.addEventListener('change', recalc);
if (inEl) inEl.addEventListener('input', recalc);
if (outEl) outEl.addEventListener('input', recalc);
if (search) search.addEventListener('input', function () {
var q = search.value.trim().toLowerCase();
if (!q) { renderTable(STATE.models); return; }
renderTable(STATE.models.filter(function (m) {
return m.model.toLowerCase().indexOf(q) >= 0;
}));
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();