Show live Bitcoin price in page title
build / build (push) Successful in 2m45s

This commit is contained in:
2026-09-15 19:51:55 +02:00
parent 61d6d21d88
commit 6c21f172db
3 changed files with 39 additions and 3 deletions
+4 -1
View File
@@ -5,7 +5,7 @@ VENV := .venv
UVICORN := $(VENV)/bin/uvicorn UVICORN := $(VENV)/bin/uvicorn
TEST_DEPS := $(VENV)/.test-deps TEST_DEPS := $(VENV)/.test-deps
.PHONY: clean run run-local tailwind test venv .PHONY: clean run run-local run-citadel tailwind test venv
venv: $(UVICORN) venv: $(UVICORN)
@@ -23,6 +23,9 @@ run: venv
run-local: venv run-local: venv
BTCDATA_URL=$(BTCDATA_LOCAL_URL) $(UVICORN) app.main:app --host 0.0.0.0 --port 4000 --reload BTCDATA_URL=$(BTCDATA_LOCAL_URL) $(UVICORN) app.main:app --host 0.0.0.0 --port 4000 --reload
run-citadel: venv
BTCDATA_URL=$(BTCDATA_URL) $(UVICORN) app.main:app --host 0.0.0.0 --port 4000 --reload
tailwind: tailwind:
npm run css:watch npm run css:watch
+18 -2
View File
@@ -78,6 +78,21 @@ HOME_HTML = """\
</div> </div>
<script> <script>
(function() { (function() {
function updateTitleFromPrice() {
const priceDisplay = document.getElementById('price-display');
const price = priceDisplay && priceDisplay.dataset.titlePrice;
if (price) {
document.title = price + ' | BTC Price';
}
}
const pricePanel = document.getElementById('price-panel');
new MutationObserver(updateTitleFromPrice).observe(pricePanel, {
childList: true,
subtree: true,
});
updateTitleFromPrice();
function initChart(containerId, endpoint, intervalMs) { function initChart(containerId, endpoint, intervalMs) {
const container = document.getElementById(containerId); const container = document.getElementById(containerId);
const chart = LightweightCharts.createChart(container, { const chart = LightweightCharts.createChart(container, {
@@ -233,11 +248,12 @@ def price_json_to_html(text: str) -> str:
else: else:
ts = "" ts = ""
formatted_price = f'${da_fmt(",.2f", p)}'
html = ( html = (
'<div id="price-display">' f'<div id="price-display" data-title-price="{formatted_price}">'
'<div class="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">' '<div class="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">'
'<div class="text-center">' '<div class="text-center">'
f'<p class="text-4xl font-bold {color}">${da_fmt(",.2f", p)}</p>' f'<p class="text-4xl font-bold {color}">{formatted_price}</p>'
) )
if data.get("price_eur") is not None: if data.get("price_eur") is not None:
+17
View File
@@ -32,6 +32,23 @@ def test_health_is_independent_of_btcdata():
assert response.json() == {"status": "up"} assert response.json() == {"status": "up"}
def test_home_updates_title_from_latest_price():
response = TestClient(main.app).get("/")
assert response.status_code == 200
assert "new MutationObserver(updateTitleFromPrice)" in response.text
assert "document.title = price + ' | BTC Price'" in response.text
def test_price_fragment_exposes_formatted_price_for_title():
html = main.price_json_to_html(
'{"price": 67432.1, "prev_price": 67400, "recorded_at": ""}'
)
assert 'data-title-price="$67.432,10"' in html
assert ">$67.432,10</p>" in html
def test_ready_when_btcdata_is_healthy(monkeypatch): def test_ready_when_btcdata_is_healthy(monkeypatch):
request = httpx.Request("GET", "http://btcdata.test/api/health") request = httpx.Request("GET", "http://btcdata.test/api/health")
upstream = httpx.Response(200, request=request) upstream = httpx.Response(200, request=request)