website: snap

This commit is contained in:
nym21
2026-04-19 17:13:47 +02:00
parent fd2b93367d
commit a5d3be465e
13 changed files with 247 additions and 186 deletions

View File

@@ -81,13 +81,13 @@
* Relative patterns by capability:
* Unrealized patterns by capability level
* @typedef {Brk.LossNetNuplProfitPattern} BasicRelativePattern
* @typedef {Brk.GrossInvestedInvestorLossNetNuplProfitSentimentPattern2} FullRelativePattern
* @typedef {Brk.CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} FullRelativePattern
*
* Profitability bucket pattern (supply + realized_cap + unrealized_pnl + nupl)
* @typedef {Brk.NuplRealizedSupplyUnrealizedPattern} RealizedSupplyPattern
*
* Realized pattern (full: cap + gross + capitalized + loss + mvrv + net + peak + price + profit + sell + sopr)
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
* @typedef {Brk.CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
*
* Transfer volume pattern (block + cumulative + inProfit/inLoss + sum windows)
* @typedef {Brk.AverageBlockCumulativeInSumPattern} TransferVolumePattern
@@ -242,19 +242,6 @@
* @typedef {CohortFull | CohortLongTerm} CohortWithNuplPercentiles
* @typedef {{ name: string, title: string, list: readonly CohortWithNuplPercentiles[], all: CohortAll }} CohortGroupWithNuplPercentiles
*
* Cohorts with RealizedWithExtras (realizedCapRelToOwnMarketCap + realizedProfitToLossRatio)
* @typedef {CohortAll | CohortFull | CohortWithPercentiles} CohortWithRealizedExtras
*
* Cohorts with circulating supply relative series (supplyRelToCirculatingSupply etc.)
* These have GlobalRelativePattern or FullRelativePattern (same as RelativeWithMarketCap/RelativeWithNupl)
* @typedef {CohortFull | CohortLongTerm | CohortWithAdjusted | CohortBasicWithMarketCap} UtxoCohortWithCirculatingSupplyRelative
*
* Address cohorts with circulating supply relative series (all address amount cohorts have these)
* @typedef {AddrCohortObject} AddrCohortWithCirculatingSupplyRelative
*
* All cohorts with circulating supply relative series
* @typedef {UtxoCohortWithCirculatingSupplyRelative | AddrCohortWithCirculatingSupplyRelative} CohortWithCirculatingSupplyRelative
*
* Delta patterns with absolute + rate rolling windows
* @typedef {Brk.AbsoluteRatePattern} DeltaPattern
* @typedef {Brk.AbsoluteRatePattern2} FiatDeltaPattern

View File

@@ -38,6 +38,7 @@ export function initChain(parent, callbacks) {
olderObserver = new IntersectionObserver(
(entries) => {
return; // edge fetching disabled for layout debugging
if (entries[0].isIntersecting) loadOlder();
},
{ root: chainEl },
@@ -46,6 +47,7 @@ export function initChain(parent, callbacks) {
chainEl.addEventListener(
"scroll",
() => {
return; // edge fetching disabled for layout debugging
const nearStart =
(chainEl.scrollHeight > chainEl.clientHeight &&
chainEl.scrollTop <= 50) ||

View File

@@ -38,32 +38,37 @@ export function createCointimeSection() {
},
]);
const prices = /** @type {const} */ ([
/** @type {readonly { pattern: PriceRatioPercentilesPattern, name: string, title: (name: string) => string, color: Color, defaultActive: boolean }[]} */
const prices = [
{
pattern: cointimePrices.trueMarketMean,
name: "True Market Mean",
title: (name) => name,
color: colors.trueMarketMean,
defaultActive: true,
},
{
pattern: cointimePrices.vaulted,
name: "Vaulted",
title: (name) => `${name} Price`,
color: colors.vaulted,
defaultActive: true,
},
{
pattern: cointimePrices.active,
name: "Active",
title: (name) => `${name} Price`,
color: colors.active,
defaultActive: true,
},
{
pattern: cointimePrices.cointime,
name: "Cointime",
title: (name) => `${name} Price`,
color: colors.cointime,
defaultActive: true,
},
]);
];
const caps = /** @type {const} */ ([
{
@@ -187,11 +192,11 @@ export function createCointimeSection() {
),
],
},
...prices.map(({ pattern, name, color }) => ({
...prices.map(({ pattern, name, title, color }) => ({
name,
tree: priceRatioPercentilesTree({
pattern,
title: `${name} Price`,
title: title(name),
legend: name,
color,
priceReferences: [

View File

@@ -254,9 +254,6 @@ export function createSelect({
? unsortedChoices.toSorted((a, b) => toLabel(a).localeCompare(toLabel(b)))
: unsortedChoices;
const field = window.document.createElement("div");
field.classList.add("field");
const initialKey = toKey(initialValue);
/** @param {string} key */
@@ -266,56 +263,59 @@ export function createSelect({
if (choices.length === 1) {
const span = window.document.createElement("span");
span.textContent = toLabel(choices[0]);
field.append(span);
} else {
const select = window.document.createElement("select");
select.id = id ?? "";
select.name = id ?? "";
field.append(select);
/** @param {T} choice */
const createOption = (choice) => {
const option = window.document.createElement("option");
option.value = toKey(choice);
option.textContent = toLabel(choice);
if (toKey(choice) === initialKey) {
option.selected = true;
}
return option;
};
if (groups) {
groups.forEach(({ label, items }) => {
const optgroup = window.document.createElement("optgroup");
optgroup.label = label;
items.forEach((choice) => optgroup.append(createOption(choice)));
select.append(optgroup);
});
} else {
choices.forEach((choice) => select.append(createOption(choice)));
}
select.addEventListener("change", () => {
onChange?.(fromKey(select.value));
});
const remaining = choices.length - 1;
if (remaining > 0) {
const small = window.document.createElement("small");
small.textContent = `+${remaining}`;
field.append(small);
const arrow = window.document.createElement("span");
arrow.textContent = "↓";
field.append(arrow);
}
field.addEventListener("click", (e) => {
if (e.target !== select) {
select.showPicker();
}
});
return span;
}
const field = window.document.createElement("div");
field.classList.add("field");
const select = window.document.createElement("select");
select.id = id ?? "";
select.name = id ?? "";
field.append(select);
/** @param {T} choice */
const createOption = (choice) => {
const option = window.document.createElement("option");
option.value = toKey(choice);
option.textContent = toLabel(choice);
if (toKey(choice) === initialKey) {
option.selected = true;
}
return option;
};
if (groups) {
groups.forEach(({ label, items }) => {
const optgroup = window.document.createElement("optgroup");
optgroup.label = label;
items.forEach((choice) => optgroup.append(createOption(choice)));
select.append(optgroup);
});
} else {
choices.forEach((choice) => select.append(createOption(choice)));
}
select.addEventListener("change", () => {
onChange?.(fromKey(select.value));
});
const remaining = choices.length - 1;
if (remaining > 0) {
const small = window.document.createElement("small");
small.textContent = `+${remaining}`;
field.append(small);
const arrow = window.document.createElement("span");
arrow.textContent = "↓";
field.append(arrow);
}
field.addEventListener("click", (e) => {
if (e.target !== select) {
select.showPicker();
}
});
return field;
}