Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ration",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"description": "Ration — AI Quota Tracker. A browser extension showing remaining quota across your AI subscriptions in one glance.",
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Ration — AI Quota Tracker",
"version": "0.1.0",
"version": "0.1.1",
"description": "One glance at remaining quota across your AI subscriptions. No accounts, no telemetry, no credential access.",
"minimum_chrome_version": "120",
"permissions": ["storage", "alarms"],
Expand All @@ -16,7 +16,7 @@
},
"action": {
"default_popup": "popup.html",
"default_title": "Ration",
"default_title": "Ration — AI Quota Tracker",
"default_icon": {
"16": "icons/icon16.png",
"32": "icons/icon32.png"
Expand Down
66 changes: 62 additions & 4 deletions public/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
}

body {
width: 340px;
width: 356px;
margin: 0;
background: var(--bg);
color: var(--fg);
Expand All @@ -44,23 +44,64 @@ body {

#header {
display: flex;
align-items: baseline;
align-items: center;
justify-content: space-between;
padding: 10px 14px 8px;
padding: 8px 14px;
border-bottom: 1px solid var(--border);
}

#header h1 {
margin: 0;
font-size: 14px;
font-weight: 600;
white-space: nowrap;
}

#subtitle {
color: var(--fg-muted);
font-size: 11px;
font-weight: 400;
}

#header-right {
display: flex;
align-items: center;
gap: 6px;
}

#age {
color: var(--fg-muted);
font-size: 11px;
}

#refresh {
border: 0;
padding: 2px 4px;
background: none;
color: var(--fg-muted);
font: inherit;
font-size: 14px;
line-height: 1;
cursor: pointer;
border-radius: 4px;
}

#refresh:hover {
color: var(--accent);
background: var(--card);
}

#refresh.spinning {
animation: spin 0.8s linear infinite;
pointer-events: none;
}

@keyframes spin {
to {
transform: rotate(360deg);
}
}

#cards {
padding: 6px 10px;
}
Expand Down Expand Up @@ -123,12 +164,20 @@ body {

.lane {
display: grid;
grid-template-columns: minmax(72px, auto) 1fr auto;
grid-template-columns: minmax(72px, auto) 1fr auto auto;
align-items: center;
gap: 8px;
margin: 6px 0 0 20px;
}

.lane-pct {
color: var(--fg-muted);
font-size: 11px;
font-variant-numeric: tabular-nums;
min-width: 30px;
text-align: right;
}

.lane-label {
color: var(--fg-muted);
overflow: hidden;
Expand Down Expand Up @@ -208,6 +257,15 @@ body {
text-decoration: underline;
}

#footer-right {
display: flex;
gap: 10px;
}

#issues {
color: var(--fg-muted);
}

#repo {
color: var(--fg-muted);
text-decoration: none;
Expand Down
31 changes: 22 additions & 9 deletions public/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,34 @@
</head>
<body>
<header id="header">
<h1>Ration</h1>
<span id="age" title="Age of the freshest reading"></span>
<h1>Ration <span id="subtitle">— AI Quota Tracker</span></h1>
<div id="header-right">
<span id="age" title="Age of the freshest reading"></span>
<button id="refresh" title="Refresh now" aria-label="Refresh now">⟳</button>
</div>
</header>
<main id="cards" aria-live="polite"></main>
<footer id="footer">
<button id="wipe" class="link-button" title="Delete everything Ration has stored locally">
Clear all data
</button>
<a
id="repo"
href="https://github.com/devudilip/ration"
target="_blank"
rel="noreferrer noopener"
>v0.1.0</a
>
<span id="footer-right">
<a
id="issues"
href="https://github.com/devudilip/ration/issues/new/choose"
target="_blank"
rel="noreferrer noopener"
>Report issue</a
>
<a
id="repo"
href="https://github.com/devudilip/ration"
target="_blank"
rel="noreferrer noopener"
title="Ration on GitHub"
></a
>
</span>
</footer>
<script type="module" src="popup.js"></script>
</body>
Expand Down
20 changes: 19 additions & 1 deletion src/popup/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import type { Msg, ProviderAdapter, ProviderSnapshot, Settings } from '../types'
const cardsEl = document.getElementById('cards') as HTMLElement;
const ageEl = document.getElementById('age') as HTMLElement;
const wipeEl = document.getElementById('wipe') as HTMLButtonElement;
const refreshEl = document.getElementById('refresh') as HTMLButtonElement;
const repoEl = document.getElementById('repo') as HTMLAnchorElement;

const send = (msg: Msg): Promise<unknown> => chrome.runtime.sendMessage(msg);

Expand Down Expand Up @@ -69,8 +71,13 @@ function renderOkCard(adapter: ProviderAdapter, snap: ProviderSnapshot, now: Dat
bar.title = `${Math.round(lane.headroomPct)}% left`;
bar.append(fill);
row.append(bar);
row.append(el('span', 'lane-pct', `${Math.round(lane.headroomPct)}%`));
const countdown = formatCountdown(lane.resetsAt, now);
row.append(el('span', 'lane-reset', countdown ? `resets ${countdown}` : ''));
const reset = el('span', 'lane-reset', countdown ? `resets ${countdown}` : '');
if (lane.resetsAt && countdown) {
reset.title = `Resets at ${new Date(lane.resetsAt).toLocaleString()}`;
}
row.append(reset);
card.append(row);
}
return card;
Expand Down Expand Up @@ -192,6 +199,17 @@ wipeEl.addEventListener('click', () => {
void send({ type: 'wipeAll' }).then(render);
});

refreshEl.addEventListener('click', () => {
// The worker still applies its politeness gates (60s/provider floor,
// backoff) — the button just asks now instead of waiting for the alarm.
refreshEl.classList.add('spinning');
void send({ type: 'refresh' })
.then(render)
.finally(() => refreshEl.classList.remove('spinning'));
});

repoEl.textContent = `v${chrome.runtime.getManifest().version}`;

chrome.storage.onChanged.addListener((_changes, area) => {
if (area === 'local') void render();
});
Expand Down
Loading