global: snapshot

This commit is contained in:
nym21
2026-03-29 23:10:31 +02:00
parent f6960c61d6
commit d038141a8a
15 changed files with 1367 additions and 4883 deletions
+1
View File
@@ -53,6 +53,7 @@
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern3} EmptyPattern
* @typedef {Brk._0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} Ratio1ySdPattern
* @typedef {Brk.Dollars} Dollars
* @typedef {Brk.BlockInfo} BlockInfo
* ActivePriceRatioPattern: ratio pattern with price (extended)
* @typedef {Brk.BpsPriceRatioPattern} ActivePriceRatioPattern
* PriceRatioPercentilesPattern: price pattern with ratio + percentiles (no SMAs/stdDev)
+14 -2
View File
@@ -7,7 +7,8 @@ import {
init as initChart,
setOption as setChartOption,
} from "./panes/chart.js";
import { initSearch } from "./panes/search.js";
import { init as initExplorer } from "./panes/explorer.js";
import { init as initSearch } from "./panes/search.js";
import { replaceHistory } from "./utils/url.js";
import { idle } from "./utils/timing.js";
import { removeStored, writeToStorage } from "./utils/storage.js";
@@ -16,6 +17,7 @@ import {
asideLabelElement,
bodyElement,
chartElement,
explorerElement,
frameSelectorsElement,
mainElement,
navElement,
@@ -145,12 +147,23 @@ function initSelected() {
let previousElement = /** @type {HTMLElement | undefined} */ (undefined);
let firstTimeLoadingChart = true;
let firstTimeLoadingExplorer = true;
options.selected.onChange((option) => {
/** @type {HTMLElement | undefined} */
let element;
switch (option.kind) {
case "explorer": {
element = explorerElement;
if (firstTimeLoadingExplorer) {
initExplorer();
}
firstTimeLoadingExplorer = false;
break;
}
case "chart": {
element = chartElement;
@@ -224,7 +237,6 @@ function initDesktopResizeBar() {
* @param {number | null} width
*/
function setBarWidth(width) {
// TODO: Check if should be a signal ??
try {
if (typeof width === "number") {
mainElement.style.width = `${width}px`;
+5
View File
@@ -57,6 +57,11 @@ export function createPartialOptions() {
} = buildCohortData();
return [
{
name: "Explorer",
kind: "explorer",
title: "Explorer",
},
{
name: "Charts",
tree: [
-99
View File
@@ -1,99 +0,0 @@
// import { randomFromArray } from "../utils/array.js";
// import { explorerElement } from "../utils/elements.js";
// export function init() {
// const chain = window.document.createElement("div");
// chain.id = "chain";
// explorerElement.append(chain);
// // vecsResources.getOrCreate(/** @satisfies {Height}*/ (5), "height");
// //
// const miners = [
// { name: "Foundry USA", color: "orange" },
// { name: "Via BTC", color: "teal" },
// { name: "Ant Pool", color: "emerald" },
// { name: "F2Pool", color: "indigo" },
// { name: "Spider Pool", color: "yellow" },
// { name: "Mara Pool", color: "amber" },
// { name: "SEC Pool", color: "violet" },
// { name: "Luxor", color: "orange" },
// { name: "Brains Pool", color: "cyan" },
// ];
// for (let i = 0; i <= 10; i++) {
// const { name, color: _color } = randomFromArray(miners);
// const { cubeElement, leftFaceElement, rightFaceElement, topFaceElement } =
// createCube();
// // cubeElement.style.setProperty("--color", `var(--${color})`);
// const heightElement = window.document.createElement("p");
// const height = (1_000_002 - i).toString();
// const prefixLength = 7 - height.length;
// const spanPrefix = window.document.createElement("span");
// spanPrefix.style.opacity = "0.5";
// spanPrefix.style.userSelect = "none";
// heightElement.append(spanPrefix);
// spanPrefix.innerHTML = "#" + "0".repeat(prefixLength);
// const spanHeight = window.document.createElement("span");
// heightElement.append(spanHeight);
// spanHeight.innerHTML = height;
// rightFaceElement.append(heightElement);
// const feesElement = window.document.createElement("div");
// feesElement.classList.add("fees");
// leftFaceElement.append(feesElement);
// const averageFeeElement = window.document.createElement("p");
// feesElement.append(averageFeeElement);
// averageFeeElement.innerHTML = `~1.41`;
// const feeRangeElement = window.document.createElement("p");
// feesElement.append(feeRangeElement);
// const minFeeElement = window.document.createElement("span");
// minFeeElement.innerHTML = `0.11`;
// feeRangeElement.append(minFeeElement);
// const dashElement = window.document.createElement("span");
// dashElement.style.opacity = "0.5";
// dashElement.innerHTML = `-`;
// feeRangeElement.append(dashElement);
// const maxFeeElement = window.document.createElement("span");
// maxFeeElement.innerHTML = `12.1`;
// feeRangeElement.append(maxFeeElement);
// const feeUnitElement = window.document.createElement("p");
// feesElement.append(feeUnitElement);
// feeUnitElement.style.opacity = "0.5";
// feeUnitElement.innerHTML = `sat/vB`;
// const spanMiner = window.document.createElement("span");
// spanMiner.innerHTML = name;
// topFaceElement.append(spanMiner);
// chain.prepend(cubeElement);
// }
// }
// function createCube() {
// const cubeElement = window.document.createElement("div");
// cubeElement.classList.add("cube");
// const rightFaceElement = window.document.createElement("div");
// rightFaceElement.classList.add("face");
// rightFaceElement.classList.add("right");
// cubeElement.append(rightFaceElement);
// const leftFaceElement = window.document.createElement("div");
// leftFaceElement.classList.add("face");
// leftFaceElement.classList.add("left");
// cubeElement.append(leftFaceElement);
// const topFaceElement = window.document.createElement("div");
// topFaceElement.classList.add("face");
// topFaceElement.classList.add("top");
// cubeElement.append(topFaceElement);
// return {
// cubeElement,
// leftFaceElement,
// rightFaceElement,
// topFaceElement,
// };
// }
+183
View File
@@ -0,0 +1,183 @@
import { explorerElement } from "../utils/elements.js";
import { brk } from "../client.js";
/** @type {HTMLDivElement} */
let chain;
/** @type {HTMLDivElement} */
let sentinel;
let newestHeight = -1;
let oldestHeight = Infinity;
let loading = false;
/** @type {number | undefined} */
let pollInterval;
function startPolling() {
stopPolling();
loadLatest();
pollInterval = setInterval(loadLatest, 15_000);
}
function stopPolling() {
if (pollInterval !== undefined) {
clearInterval(pollInterval);
pollInterval = undefined;
}
}
export function init() {
chain = window.document.createElement("div");
chain.id = "chain";
explorerElement.append(chain);
sentinel = window.document.createElement("div");
sentinel.classList.add("sentinel");
chain.append(sentinel);
// Infinite scroll: load older blocks when sentinel becomes visible
new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadOlder();
}
}).observe(sentinel);
// Self-contained lifecycle: poll when visible, stop when hidden
new MutationObserver(() => {
if (explorerElement.hidden) {
stopPolling();
} else {
startPolling();
}
}).observe(explorerElement, { attributes: true, attributeFilter: ["hidden"] });
document.addEventListener("visibilitychange", () => {
if (!document.hidden && !explorerElement.hidden) {
loadLatest();
}
});
loadLatest();
}
async function loadLatest() {
if (loading) return;
loading = true;
try {
const blocks = await brk.getBlocks();
// First load: insert all blocks before sentinel
if (newestHeight === -1) {
for (const block of blocks) {
sentinel.before(createBlockCube(block));
}
newestHeight = blocks[0].height;
oldestHeight = blocks[blocks.length - 1].height;
} else {
// Subsequent polls: prepend only new blocks
const newBlocks = blocks.filter((b) => b.height > newestHeight);
if (newBlocks.length) {
chain.prepend(...newBlocks.map((b) => createBlockCube(b)));
newestHeight = newBlocks[0].height;
}
}
} catch (e) {
console.error("explorer poll:", e);
}
loading = false;
}
async function loadOlder() {
if (loading || oldestHeight <= 0) return;
loading = true;
try {
const blocks = await brk.getBlocksFromHeight(oldestHeight - 1);
for (const block of blocks) {
sentinel.before(createBlockCube(block));
}
if (blocks.length) {
oldestHeight = blocks[blocks.length - 1].height;
}
} catch (e) {
console.error("explorer loadOlder:", e);
}
loading = false;
}
/** @param {BlockInfo} block */
function createBlockCube(block) {
const { cubeElement, leftFaceElement, rightFaceElement, topFaceElement } =
createCube();
// cubeElement.style.setProperty("--face-color", `var(--${color})`);
const heightElement = window.document.createElement("p");
const height = block.height.toString();
const prefixLength = 7 - height.length;
const spanPrefix = window.document.createElement("span");
spanPrefix.style.opacity = "0.5";
spanPrefix.style.userSelect = "none";
heightElement.append(spanPrefix);
spanPrefix.innerHTML = "#" + "0".repeat(prefixLength);
const spanHeight = window.document.createElement("span");
heightElement.append(spanHeight);
spanHeight.innerHTML = height;
rightFaceElement.append(heightElement);
const feesElement = window.document.createElement("div");
feesElement.classList.add("fees");
leftFaceElement.append(feesElement);
const averageFeeElement = window.document.createElement("p");
feesElement.append(averageFeeElement);
averageFeeElement.innerHTML = `~1.41`;
const feeRangeElement = window.document.createElement("p");
feesElement.append(feeRangeElement);
const minFeeElement = window.document.createElement("span");
minFeeElement.innerHTML = `0.11`;
feeRangeElement.append(minFeeElement);
const dashElement = window.document.createElement("span");
dashElement.style.opacity = "0.5";
dashElement.innerHTML = `-`;
feeRangeElement.append(dashElement);
const maxFeeElement = window.document.createElement("span");
maxFeeElement.innerHTML = `12.1`;
feeRangeElement.append(maxFeeElement);
const feeUnitElement = window.document.createElement("p");
feesElement.append(feeUnitElement);
feeUnitElement.style.opacity = "0.5";
feeUnitElement.innerHTML = `sat/vB`;
const spanMiner = window.document.createElement("span");
spanMiner.innerHTML = "TODO";
topFaceElement.append(spanMiner);
return cubeElement;
}
function createCube() {
const cubeElement = window.document.createElement("div");
cubeElement.classList.add("cube");
const rightFaceElement = window.document.createElement("div");
rightFaceElement.classList.add("face");
rightFaceElement.classList.add("right");
cubeElement.append(rightFaceElement);
const leftFaceElement = window.document.createElement("div");
leftFaceElement.classList.add("face");
leftFaceElement.classList.add("left");
cubeElement.append(leftFaceElement);
const topFaceElement = window.document.createElement("div");
topFaceElement.classList.add("face");
topFaceElement.classList.add("top");
cubeElement.append(topFaceElement);
return {
cubeElement,
leftFaceElement,
rightFaceElement,
topFaceElement,
};
}
+3 -1
View File
@@ -8,7 +8,7 @@ import { QuickMatch } from "../modules/quickmatch-js/0.4.1/src/index.js";
/**
* @param {Options} options
*/
export function initSearch(options) {
export function init(options) {
console.log("search: init");
const haystack = options.list.map((option) => option.title.toLowerCase());
@@ -65,9 +65,11 @@ export function initSearch(options) {
document.addEventListener("keydown", (e) => {
const el = document.activeElement;
const isTextInput =
el?.tagName === "INPUT" &&
/** @type {HTMLInputElement} */ (el).type === "text";
if (e.key === "/" && !isTextInput) {
e.preventDefault();
searchLabelElement.click();
+9
View File
@@ -23,3 +23,12 @@ export const fromEntries = (pairs) => /** @type {Record<K, V>} */ (Object.fromEn
* @returns {value is T}
*/
export const includes = (arr, value) => arr.includes(/** @type {T} */ (value));
/**
* @template T
* @param {readonly T[]} arr
* @returns {T}
*/
export function randomFromArray(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
+1
View File
@@ -10,6 +10,7 @@ export const asideElement = getElementById("aside");
export const searchElement = getElementById("search");
export const navElement = getElementById("nav");
export const chartElement = getElementById("chart");
export const explorerElement = getElementById("explorer");
export const asideLabelElement = getElementById("aside-selector-label");
export const navLabelElement = getElementById(`nav-selector-label`);