From dfd2969b3e21f4f5e982132a17efa84a2f6d665b Mon Sep 17 00:00:00 2001 From: nym21 Date: Mon, 9 Jun 2025 17:58:26 +0200 Subject: [PATCH] websites: default: snapshot --- crates/brk_vec/src/variants/eager.rs | 4 +- .../packages/lightweight-charts/wrapper.js | 118 +++-- websites/default/scripts/chart.js | 473 ++++++++++-------- websites/default/scripts/main.js | 304 +++++------ 4 files changed, 476 insertions(+), 423 deletions(-) diff --git a/crates/brk_vec/src/variants/eager.rs b/crates/brk_vec/src/variants/eager.rs index 8c21141e8..cad17d823 100644 --- a/crates/brk_vec/src/variants/eager.rs +++ b/crates/brk_vec/src/variants/eager.rs @@ -1116,12 +1116,12 @@ impl EagerVec { exit: &Exit, ) -> Result<()> { self.validate_computed_version_or_reset_file( - Version::ZERO + self.inner.version() + stacks.version(), + Version::ONE + self.inner.version() + stacks.version(), )?; let index = max_from.min(DateIndex::from(self.len())); - let first_price_date = DateIndex::try_from(Date::new(2010, 8, 16)).unwrap(); + let first_price_date = DateIndex::try_from(Date::new(2010, 7, 12)).unwrap(); stacks.iter_at(index).try_for_each(|(i, stack)| { let stack = stack.into_inner(); diff --git a/websites/default/packages/lightweight-charts/wrapper.js b/websites/default/packages/lightweight-charts/wrapper.js index 79cd10ccc..ee98329a4 100644 --- a/websites/default/packages/lightweight-charts/wrapper.js +++ b/websites/default/packages/lightweight-charts/wrapper.js @@ -59,7 +59,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { horzLines: { visible: false }, }, localization: { - priceFormatter: utils.locale.numberToShortUSFormat, + priceFormatter: numberToShortUSFormat, locale: "en-us", }, ..._options, @@ -99,12 +99,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { index === /** @satisfies {MonthIndex} */ (7) ? 1 : index === /** @satisfies {QuarterIndex} */ (19) - ? 3 - : index === /** @satisfies {YearIndex} */ (23) - ? 12 - : index === /** @satisfies {DecadeIndex} */ (1) - ? 120 - : undefined, + ? 3 + : index === /** @satisfies {YearIndex} */ (23) + ? 12 + : index === /** @satisfies {DecadeIndex} */ (1) + ? 120 + : undefined, }, crosshair: { horzLine: { @@ -118,7 +118,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { mode: 3, }, }); - } + }, ); return chart; @@ -178,7 +178,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { if (fitContentOnResize) { new ResizeObserver(() => ichart?.timeScale().fitContent()).observe( - chartDiv + chartDiv, ); } @@ -266,8 +266,8 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { } }); timeScaleSet = true; - } - ) + }, + ), ); } @@ -278,7 +278,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { activeResources.forEach((v) => { v.fetch(); }); - }) + }), ); const chart = { @@ -298,7 +298,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { vecIndex, vecIndex === /** @satisfies {Height} */ (5) ? "timestamp-fixed" - : "timestamp" + : "timestamp", ); timeResource.fetch(); @@ -325,6 +325,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { * @param {Object} args * @param {string} args.name * @param {Unit} args.unit + * @param {number} args.order * @param {VecId} [args.vecId] * @param {Accessor} [args.data] * @param {number} [args.paneIndex] @@ -335,6 +336,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { vecId, name, unit, + order, paneIndex: _paneIndex, defaultActive, data, @@ -356,7 +358,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { borderVisible: false, visible: defaultActive !== false, }, - paneIndex + paneIndex, ); let url = /** @type {string | undefined} */ (undefined); @@ -372,7 +374,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { signals.runWithOwner(owner, () => signals.createEffect(data, (data) => { series.setData(data); - }) + }), ); } @@ -398,12 +400,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { unit, }); + series.setSeriesOrder(order); + return series; }, /** * @param {Object} args * @param {string} args.name * @param {Unit} args.unit + * @param {number} args.order * @param {Accessor} [args.data] * @param {VecId} [args.vecId] * @param {Color} [args.color] @@ -415,6 +420,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { vecId, name, unit, + order, color, paneIndex: _paneIndex, defaultActive, @@ -436,7 +442,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { color: color(), ...options, }, - paneIndex + paneIndex, ); const priceLineOptions = options?.createPriceLine; @@ -459,7 +465,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { ichart ?.timeScale() .setVisibleLogicalRange({ from: -1, to: data.length }); - }) + }), ); } @@ -485,12 +491,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { unit, }); + series.setSeriesOrder(order); + return series; }, /** * @param {Object} args * @param {string} args.name * @param {Unit} args.unit + * @param {number} args.order * @param {Accessor} [args.data] * @param {VecId} [args.vecId] * @param {number} [args.paneIndex] @@ -501,6 +510,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { vecId, name, unit, + order, paneIndex: _paneIndex, defaultActive, data, @@ -528,7 +538,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { topFillColor2: "transparent", lineVisible: true, }, - paneIndex + paneIndex, ); const priceLineOptions = options?.createPriceLine; @@ -552,7 +562,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { ichart ?.timeScale() .setVisibleLogicalRange({ from: -1, to: data.length }); - }) + }), ); } @@ -581,6 +591,8 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { unit, }); + series.setSeriesOrder(order); + return series; }, /** @@ -645,7 +657,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { Array.from(parent.childNodes).filter( (element) => /** @type {HTMLElement} */ (element).dataset.position === - position + position, ).length ) { return; @@ -664,7 +676,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { fieldset.append(createChild({ owner, pane })); }, - paneIndex ? 50 : 0 + paneIndex ? 50 : 0, ); }, /** @@ -686,7 +698,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { config?.forEach(({ unit, blueprints }, paneIndex) => { chart.create({ index: /** @satisfies {DateIndex} */ (0) }); - blueprints.forEach((blueprint) => { + blueprints.forEach((blueprint, order) => { if (blueprint.type === "Candlestick") { chart.addCandlestickSeries({ name: blueprint.title, @@ -694,6 +706,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { data: blueprint.data, defaultActive: blueprint.defaultActive, paneIndex, + order, }); } else if (blueprint.type === "Baseline") { chart.addBaselineSeries({ @@ -702,6 +715,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { data: blueprint.data, defaultActive: blueprint.defaultActive, paneIndex, + order, }); } else { chart.addLineSeries({ @@ -711,6 +725,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { defaultActive: blueprint.defaultActive, paneIndex, color: blueprint.color, + order, }); } }); @@ -737,7 +752,7 @@ function createLegend({ parent, signals, utils, paneIndex }) { parent.append(legendElement); const hovered = signals.createSignal( - /** @type {ISeriesApi | null} */ (null) + /** @type {ISeriesApi | null} */ (null), ); return { @@ -830,7 +845,7 @@ function createLegend({ parent, signals, utils, paneIndex }) { } else { spanColor.style.backgroundColor = tameColor(color); } - } + }, ); }); @@ -906,7 +921,7 @@ function createOklchToRGBA() { return rgb.map((c) => Math.abs(c) > 0.0031308 ? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055) - : 12.92 * c + : 12.92 * c, ); } /** @@ -918,7 +933,7 @@ function createOklchToRGBA() { 1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586, -0.0638541728258133, 1, -0.0894841775298119, -1.2914855480194092, ]), - lab + lab, ); const LMS = /** @type {[number, number, number]} */ ( LMSg.map((val) => val ** 3) @@ -929,7 +944,7 @@ function createOklchToRGBA() { -0.0405757452148008, 1.112286803280317, -0.0717110580655164, -0.0763729366746601, -0.4214933324022432, 1.5869240198367816, ]), - LMS + LMS, ); } /** @@ -942,7 +957,7 @@ function createOklchToRGBA() { -0.9692436362808796, 1.8759675015077202, 0.04155505740717559, 0.05563007969699366, -0.20397695888897652, 1.0569715142428786, ], - xyz + xyz, ); } @@ -965,8 +980,8 @@ function createOklchToRGBA() { }); const rgb = srgbLinear2rgb( xyz2rgbLinear( - oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))) - ) + oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))), + ), ).map((v) => { return Math.max(Math.min(Math.round(v * 255), 255), 0); }); @@ -1046,3 +1061,46 @@ function createPriceLine(series, options, colors) { lineStyle: 4, }); } + +/** + * @param {number} value + * @param {number} [digits] + * @param {Intl.NumberFormatOptions} [options] + */ +function numberToUSFormat(value, digits, options) { + return value.toLocaleString("en-us", { + ...options, + minimumFractionDigits: digits, + maximumFractionDigits: digits, + }); +} + +/** @param {number} value */ +function numberToShortUSFormat(value) { + const absoluteValue = Math.abs(value); + + if (isNaN(value)) { + return ""; + } else if (absoluteValue < 10) { + return numberToUSFormat(value, 3); + } else if (absoluteValue < 1_000) { + return numberToUSFormat(value, 2); + } else if (absoluteValue < 10_000) { + return numberToUSFormat(value, 1); + } else if (absoluteValue < 1_000_000) { + return numberToUSFormat(value, 0); + } else if (absoluteValue >= 900_000_000_000_000_000) { + return "Inf."; + } + + const log = Math.floor(Math.log10(absoluteValue) - 6); + + const suffices = ["M", "B", "T", "P", "E"]; + const letterIndex = Math.floor(log / 3); + const letter = suffices[letterIndex]; + + return `${numberToUSFormat( + value / (1_000_000 * 1_000 ** letterIndex), + 3, + )}${letter}`; +} diff --git a/websites/default/scripts/chart.js b/websites/default/scripts/chart.js index fa78c7dab..6dd997080 100644 --- a/websites/default/scripts/chart.js +++ b/websites/default/scripts/chart.js @@ -1,5 +1,7 @@ // @ts-check +const keyPrefix = "chart"; + /** * @param {Object} args * @param {Colors} args.colors @@ -43,244 +45,271 @@ export function init({ let firstRun = true; + const { field: seriesTypeField, selected: topSeriesType } = + utils.dom.createHorizontalChoiceField({ + defaultValue: "Line", + keyPrefix, + key: "seriestype-0", + choices: /** @type {const} */ (["Auto", "Candles", "Line"]), + signals, + }); + + const { field: topUnitField, selected: topUnit } = + utils.dom.createHorizontalChoiceField({ + defaultValue: "USD", + keyPrefix, + key: "unit-0", + choices: /** @type {const} */ ([ + /** @satisfies {Unit} */ ("USD"), + /** @satisfies {Unit} */ ("Sats"), + ]), + signals, + sorted: true, + }); + signals.createEffect(selected, (option) => { headingElement.innerHTML = option.title; + const bottomUnits = /** @type {readonly Unit[]} */ ( + Object.keys(option.bottom) + ); + const { field: bottomUnitField, selected: bottomUnit } = + utils.dom.createHorizontalChoiceField({ + defaultValue: bottomUnits.at(0) || "", + keyPrefix, + key: "unit-1", + choices: bottomUnits, + signals, + sorted: true, + }); + + // signals.createEffect(bottomUnit, (bottomUnit) => { + chart.reset({ owner: signals.getOwner() }); + + chart.addFieldsetIfNeeded({ + id: "charts-unit-0", + paneIndex: 0, + position: "nw", + createChild() { + return topUnitField; + }, + }); + + if (bottomUnits.length) { + chart.addFieldsetIfNeeded({ + id: "charts-unit-1", + paneIndex: 1, + position: "nw", + createChild() { + return bottomUnitField; + }, + }); + } + + chart.addFieldsetIfNeeded({ + id: "charts-seriestype-0", + paneIndex: 0, + position: "ne", + createChild() { + return seriesTypeField; + }, + }); + signals.createEffect(index, (index) => { - const { field: topUnitField, selected: topUnit } = - utils.dom.createHorizontalChoiceField({ - defaultValue: "USD", - keyPrefix: "charts", - key: "unit-0", - choices: /** @type {const} */ ([ - /** @satisfies {Unit} */ ("USD"), - /** @satisfies {Unit} */ ("Sats"), - ]), - signals, - sorted: true, - }); + const TIMERANGE_LS_KEY = `chart-timerange-${index}`; - signals.createEffect(topUnit, (topUnit) => { - const { field: seriesTypeField, selected: topSeriesType } = - utils.dom.createHorizontalChoiceField({ - defaultValue: "Line", - keyPrefix: "charts", - key: "seriestype-0", - choices: /** @type {const} */ (["Candles", "Line"]), - signals, - }); + const from = signals.createSignal(/** @type {number | null} */ (null), { + save: { + ...utils.serde.optNumber, + keyPrefix: TIMERANGE_LS_KEY, + key: "from", + serializeParam: firstRun, + }, + }); + const to = signals.createSignal(/** @type {number | null} */ (null), { + save: { + ...utils.serde.optNumber, + keyPrefix: TIMERANGE_LS_KEY, + key: "to", + serializeParam: firstRun, + }, + }); - signals.createEffect(topSeriesType, (topSeriesType) => { - const bottomUnits = /** @type {readonly Unit[]} */ ( - Object.keys(option.bottom) - ); - const { field: bottomUnitField, selected: bottomUnit } = - utils.dom.createHorizontalChoiceField({ - defaultValue: bottomUnits.at(0) || "", - keyPrefix: "charts", - key: "unit-1", - choices: bottomUnits, - signals, - sorted: true, + chart.create({ + index, + timeScaleSetCallback: (unknownTimeScaleCallback) => { + const from_ = from(); + const to_ = to(); + if (from_ !== null && to_ !== null) { + chart.inner()?.timeScale().setVisibleLogicalRange({ + from: from_, + to: to_, }); + } else { + unknownTimeScaleCallback(); + } + }, + }); - signals.createEffect(bottomUnit, (bottomUnit) => { - chart.reset({ owner: signals.getOwner() }); + /** @type {ISeriesApi | null} */ + let prevPriceSeries = null; + signals.createEffect( + () => [topUnit(), topSeriesType()], + ([topUnit, topSeriesType]) => { + if (prevPriceSeries) { + chart.inner()?.removeSeries(prevPriceSeries); + } - chart.addFieldsetIfNeeded({ - id: "charts-unit-0", - paneIndex: 0, - position: "nw", - createChild() { - return topUnitField; - }, - }); - - if (bottomUnits.length) { - chart.addFieldsetIfNeeded({ - id: "charts-unit-1", - paneIndex: 1, - position: "nw", - createChild() { - return bottomUnitField; - }, - }); - } - - chart.addFieldsetIfNeeded({ - id: "charts-seriestype-0", - paneIndex: 0, - position: "ne", - createChild() { - return seriesTypeField; - }, - }); - - const TIMERANGE_LS_KEY = `chart-timerange-${index}`; - - const from = signals.createSignal( - /** @type {number | null} */ (null), - { - save: { - ...utils.serde.optNumber, - keyPrefix: TIMERANGE_LS_KEY, - key: "from", - serializeParam: firstRun, - }, - } - ); - const to = signals.createSignal( - /** @type {number | null} */ (null), - { - save: { - ...utils.serde.optNumber, - keyPrefix: TIMERANGE_LS_KEY, - key: "to", - serializeParam: firstRun, - }, - } - ); - - chart.create({ - index, - timeScaleSetCallback: (unknownTimeScaleCallback) => { - const from_ = from(); - const to_ = to(); - if (from_ !== null && to_ !== null) { - chart.inner()?.timeScale().setVisibleLogicalRange({ - from: from_, - to: to_, + switch (topUnit) { + case "USD": { + switch (topSeriesType) { + case "Candles": { + prevPriceSeries = chart.addCandlestickSeries({ + vecId: "ohlc", + name: "Price", + unit: topUnit, + order: 0, }); - } else { - unknownTimeScaleCallback(); + break; } - }, - }); - - switch (topUnit) { - case "USD": { - switch (topSeriesType) { - case "Candles": { - const candles = chart.addCandlestickSeries({ - vecId: "ohlc", - name: "Price", - unit: topUnit, - }); - break; - } - case "Line": { - const line = chart.addLineSeries({ - vecId: "close", - name: "Price", - unit: topUnit, - color: colors.default, - options: { - priceLineVisible: true, - }, - }); - } + case "Line": { + prevPriceSeries = chart.addLineSeries({ + vecId: "close", + name: "Price", + unit: topUnit, + color: colors.default, + options: { + priceLineVisible: true, + }, + order: 0, + }); } - // signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => { - // if (!latest) return; - // const last = /** @type { CandlestickData | undefined} */ ( - // candles.data().at(-1) - // ); - // if (!last) return; - // candles?.update({ ...last, close: latest.close }); - // }); - break; } - case "Sats": { - switch (topSeriesType) { - case "Candles": { - const candles = chart.addCandlestickSeries({ - vecId: "ohlc-in-sats", - name: "Price", - unit: topUnit, - inverse: true, - }); - break; - } - case "Line": { - const line = chart.addLineSeries({ - vecId: "close-in-sats", - name: "Price", - unit: topUnit, - color: colors.default, - options: { - priceLineVisible: true, - }, - }); - } + // signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => { + // if (!latest) return; + // const last = /** @type { CandlestickData | undefined} */ ( + // candles.data().at(-1) + // ); + // if (!last) return; + // candles?.update({ ...last, close: latest.close }); + // }); + break; + } + case "Sats": { + switch (topSeriesType) { + case "Candles": { + prevPriceSeries = chart.addCandlestickSeries({ + vecId: "ohlc-in-sats", + name: "Price", + unit: topUnit, + inverse: true, + order: 0, + }); + break; } - break; + case "Line": { + prevPriceSeries = chart.addLineSeries({ + vecId: "close-in-sats", + name: "Price", + unit: topUnit, + color: colors.default, + options: { + priceLineVisible: true, + }, + order: 0, + }); + } + } + break; + } + } + }, + ); + + [ + { + blueprints: option.top, + paneIndex: 0, + unit: topUnit, + prevSeriesList: /** @type {ISeriesApi[]} */ ([]), + }, + { + blueprints: option.bottom, + paneIndex: 1, + unit: bottomUnit, + prevSeriesList: /** @type {ISeriesApi[]} */ ([]), + }, + ].forEach(({ blueprints, paneIndex, unit, prevSeriesList }) => { + signals.createEffect(unit, (unit) => { + prevSeriesList.splice(0).forEach((series) => { + chart.inner()?.removeSeries(series); + }); + + blueprints[unit]?.forEach((blueprint, order) => { + order++; + + const indexes = /** @type {readonly number[]} */ ( + vecIdToIndexes[blueprint.key] + ); + + if (indexes.includes(index)) { + switch (blueprint.type) { + case "Baseline": { + prevSeriesList.push( + chart.addBaselineSeries({ + vecId: blueprint.key, + // color: blueprint.color, + name: blueprint.title, + unit, + defaultActive: blueprint.defaultActive, + paneIndex, + options: { + ...blueprint.options, + topLineColor: + blueprint.color?.() ?? blueprint.colors?.[0](), + bottomLineColor: + blueprint.color?.() ?? blueprint.colors?.[1](), + }, + order, + }), + ); + break; + } + case "Candlestick": { + throw Error("TODO"); + break; + } + default: + prevSeriesList.push( + chart.addLineSeries({ + vecId: blueprint.key, + color: blueprint.color, + name: blueprint.title, + unit, + defaultActive: blueprint.defaultActive, + paneIndex, + options: blueprint.options, + order, + }), + ); } } - - [ - { blueprints: option.top, paneIndex: 0 }, - { blueprints: option.bottom, paneIndex: 1 }, - ].forEach(({ blueprints, paneIndex }) => { - const unit = paneIndex ? bottomUnit : topUnit; - - blueprints[unit]?.forEach((blueprint) => { - const indexes = /** @type {readonly number[]} */ ( - vecIdToIndexes[blueprint.key] - ); - if (indexes.includes(index)) { - switch (blueprint.type) { - case "Baseline": { - chart.addBaselineSeries({ - vecId: blueprint.key, - // color: blueprint.color, - name: blueprint.title, - unit, - defaultActive: blueprint.defaultActive, - paneIndex, - options: { - ...blueprint.options, - topLineColor: - blueprint.color?.() ?? blueprint.colors?.[0](), - bottomLineColor: - blueprint.color?.() ?? blueprint.colors?.[1](), - }, - }); - break; - } - case "Candlestick": { - throw Error("TODO"); - break; - } - default: - chart.addLineSeries({ - vecId: blueprint.key, - color: blueprint.color, - name: blueprint.title, - unit, - defaultActive: blueprint.defaultActive, - paneIndex, - options: blueprint.options, - }); - } - } - }); - }); - - chart - .inner() - ?.timeScale() - .subscribeVisibleLogicalRangeChange( - utils.debounce((t) => { - if (t) { - from.set(t.from); - to.set(t.to); - } - }) - ); - - firstRun = false; }); }); + + chart + .inner() + ?.timeScale() + .subscribeVisibleLogicalRangeChange( + utils.debounce((t) => { + if (t) { + from.set(t.from); + to.set(t.to); + } + }), + ); + + firstRun = false; }); }); }); @@ -295,7 +324,7 @@ export function init({ function createIndexSelector({ elements, signals, utils }) { const { field, selected } = utils.dom.createHorizontalChoiceField({ defaultValue: "date", - keyPrefix: "charts", + keyPrefix, key: "index", choices: /**@type {const} */ ([ "timestamp", @@ -335,7 +364,7 @@ function createIndexSelector({ elements, signals, utils }) { case "decade": return /** @satisfies {DecadeIndex} */ (1); } - } + }, ); return index; diff --git a/websites/default/scripts/main.js b/websites/default/scripts/main.js index 60094cd69..27672cc54 100644 --- a/websites/default/scripts/main.js +++ b/websites/default/scripts/main.js @@ -67,14 +67,14 @@ function initPackages() { const imports = { async signals() { return import("../packages/solid-signals/wrapper.js").then((d) => - d.default.then((d) => d) + d.default.then((d) => d), ); }, async lightweightCharts() { return window.document.fonts.ready.then(() => import("../packages/lightweight-charts/wrapper.js").then((d) => - d.default.then((d) => d) - ) + d.default.then((d) => d), + ), ); }, async leanQr() { @@ -82,7 +82,7 @@ function initPackages() { }, async ufuzzy() { return import("../packages/ufuzzy/v1.0.18/script.js").then( - ({ default: d }) => d + ({ default: d }) => d, ); }, }; @@ -575,7 +575,7 @@ function createUtils() { window.history.pushState( null, "", - `${pathname}?${urlParams.toString()}` + `${pathname}?${urlParams.toString()}`, ); } catch (_) {} }, @@ -592,7 +592,7 @@ function createUtils() { window.history.replaceState( null, "", - `${pathname}?${urlParams.toString()}` + `${pathname}?${urlParams.toString()}`, ); } catch (_) {} }, @@ -951,35 +951,6 @@ function createUtils() { const locale = { numberToUSFormat, - /** @param {number} value */ - numberToShortUSFormat(value) { - const absoluteValue = Math.abs(value); - - if (isNaN(value)) { - return ""; - } else if (absoluteValue < 10) { - return numberToUSFormat(value, 3); - } else if (absoluteValue < 100) { - return numberToUSFormat(value, 2); - } else if (absoluteValue < 1_000) { - return numberToUSFormat(value, 1); - } else if (absoluteValue < 1_000_000) { - return numberToUSFormat(value, 0); - } else if (absoluteValue >= 900_000_000_000_000_000) { - return "Inf."; - } - - const log = Math.floor(Math.log10(absoluteValue) - 6); - - const suffices = ["M", "B", "T", "P", "E"]; - const letterIndex = Math.floor(log / 3); - const letter = suffices[letterIndex]; - - return `${numberToUSFormat( - value / (1_000_000 * 1_000 ** letterIndex), - 3 - )}${letter}`; - }, }; const storage = { @@ -1144,8 +1115,8 @@ function createUtils() { today.getUTCDate(), 0, 0, - 0 - ) + 0, + ), ); }, /** @@ -1247,7 +1218,7 @@ function createUtils() { */ function getNumberOfDaysBetweenTwoDates(oldest, youngest) { return Math.round( - Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS) + Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS), ); } @@ -1558,7 +1529,7 @@ function createVecsResources(signals, utils) { index, id, from, - to + to, ) ); fetched.at = new Date(); @@ -1819,7 +1790,7 @@ function initWebSockets(signals, utils) { window.document.addEventListener( "visibilitychange", - reinitWebSocketIfDocumentNotHidden + reinitWebSocketIfDocumentNotHidden, ); window.document.addEventListener("online", reinitWebSocket); @@ -1828,7 +1799,7 @@ function initWebSockets(signals, utils) { ws?.close(); window.document.removeEventListener( "visibilitychange", - reinitWebSocketIfDocumentNotHidden + reinitWebSocketIfDocumentNotHidden, ); window.document.removeEventListener("online", reinitWebSocket); live.set(false); @@ -1855,7 +1826,7 @@ function initWebSockets(signals, utils) { symbol: ["BTC/USD"], interval: 1440, }, - }) + }), ); }); @@ -1888,7 +1859,7 @@ function initWebSockets(signals, utils) { } const kraken1dCandle = createWebsocket((callback) => - krakenCandleWebSocketCreator(callback, 1440) + krakenCandleWebSocketCreator(callback, 1440), ); kraken1dCandle.open(); @@ -1947,7 +1918,7 @@ function main() { } const frame = window.document.getElementById( - /** @type {string} */ (input.value) + /** @type {string} */ (input.value), ); if (!frame) { @@ -2042,23 +2013,23 @@ function main() { function initDark() { const preferredColorSchemeMatchMedia = window.matchMedia( - "(prefers-color-scheme: dark)" + "(prefers-color-scheme: dark)", ); const dark = signals.createSignal( - preferredColorSchemeMatchMedia.matches + preferredColorSchemeMatchMedia.matches, ); preferredColorSchemeMatchMedia.addEventListener( "change", ({ matches }) => { dark.set(matches); - } + }, ); return dark; } const dark = initDark(); const qrcode = signals.createSignal( - /** @type {string | null} */ (null) + /** @type {string | null} */ (null), ); function createLastHeightResource() { @@ -2069,7 +2040,7 @@ function main() { lastHeight.set(h); }, /** @satisfies {Height} */ (5), - "height" + "height", ); } fetchLastHeight(); @@ -2109,136 +2080,131 @@ function main() { function initSelectedFrame() { console.log("selected: init"); - function createApplyOptionEffect() { - const lastChartOption = signals.createSignal( - /** @type {ChartOption | null} */ (null) - ); - const lastSimulationOption = signals.createSignal( - /** @type {SimulationOption | null} */ (null) - ); + const chartOption = signals.createSignal( + /** @type {ChartOption | null} */ (null), + ); + const simOption = signals.createSignal( + /** @type {SimulationOption | null} */ (null), + ); - const owner = signals.getOwner(); + const owner = signals.getOwner(); - let previousElement = /** @type {HTMLElement | undefined} */ ( - undefined - ); - let firstTimeLoadingChart = true; - let firstTimeLoadingTable = true; - let firstTimeLoadingSimulation = true; + let previousElement = /** @type {HTMLElement | undefined} */ ( + undefined + ); + let firstTimeLoadingChart = true; + let firstTimeLoadingTable = true; + let firstTimeLoadingSimulation = true; - signals.createEffect(options.selected, (option) => { - if (previousElement) { - previousElement.hidden = true; - utils.url.resetParams(option); - utils.url.pushHistory(option.id); - } else { - utils.url.replaceHistory({ pathname: option.id }); - } + signals.createEffect(options.selected, (option) => { + if (previousElement) { + previousElement.hidden = true; + utils.url.resetParams(option); + utils.url.pushHistory(option.id); + } else { + utils.url.replaceHistory({ pathname: option.id }); + } - /** @type {HTMLElement} */ - let element; + /** @type {HTMLElement} */ + let element; - switch (option.kind) { - case "chart": { - element = elements.charts; + switch (option.kind) { + case "chart": { + element = elements.charts; - lastChartOption.set(option); + chartOption.set(option); - if (firstTimeLoadingChart) { - const lightweightCharts = packages.lightweightCharts(); - const chartScript = import("./chart.js"); - utils.dom.importStyleAndThen("/styles/chart.css", () => - chartScript.then(({ init: initChartsElement }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - initChartsElement({ - colors, - elements, - lightweightCharts, - selected: /** @type {Accessor} */ ( - lastChartOption - ), - signals, - utils, - webSockets, - vecsResources, - vecIdToIndexes, - }) - ) - ) - ) - ); - } - firstTimeLoadingChart = false; - - break; + if (firstTimeLoadingChart) { + const lightweightCharts = packages.lightweightCharts(); + const chartScript = import("./chart.js"); + utils.dom.importStyleAndThen("/styles/chart.css", () => + chartScript.then(({ init: initChartsElement }) => + lightweightCharts.then((lightweightCharts) => + signals.runWithOwner(owner, () => + initChartsElement({ + colors, + elements, + lightweightCharts, + selected: /** @type {Accessor} */ ( + chartOption + ), + signals, + utils, + webSockets, + vecsResources, + vecIdToIndexes, + }), + ), + ), + ), + ); } - case "table": { - element = elements.table; + firstTimeLoadingChart = false; - if (firstTimeLoadingTable) { - const tableScript = import("./table.js"); - utils.dom.importStyleAndThen("/styles/table.css", () => - tableScript.then(({ init }) => + break; + } + case "table": { + element = elements.table; + + if (firstTimeLoadingTable) { + const tableScript = import("./table.js"); + utils.dom.importStyleAndThen("/styles/table.css", () => + tableScript.then(({ init }) => + signals.runWithOwner(owner, () => + init({ + colors, + elements, + signals, + utils, + vecsResources, + option, + vecIdToIndexes, + }), + ), + ), + ); + } + firstTimeLoadingTable = false; + + break; + } + case "simulation": { + element = elements.simulation; + + simOption.set(option); + + if (firstTimeLoadingSimulation) { + const lightweightCharts = packages.lightweightCharts(); + const simulationScript = import("./simulation.js"); + utils.dom.importStyleAndThen("/styles/simulation.css", () => + simulationScript.then(({ init }) => + lightweightCharts.then((lightweightCharts) => signals.runWithOwner(owner, () => init({ colors, elements, + lightweightCharts, signals, utils, vecsResources, - option, - vecIdToIndexes, - }) - ) - ) - ); - } - firstTimeLoadingTable = false; - - break; + }), + ), + ), + ), + ); } - case "simulation": { - element = elements.simulation; + firstTimeLoadingSimulation = false; - lastSimulationOption.set(option); - - if (firstTimeLoadingSimulation) { - const lightweightCharts = packages.lightweightCharts(); - const simulationScript = import("./simulation.js"); - utils.dom.importStyleAndThen( - "/styles/simulation.css", - () => - simulationScript.then(({ init }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - init({ - colors, - elements, - lightweightCharts, - signals, - utils, - vecsResources, - }) - ) - ) - ) - ); - } - firstTimeLoadingSimulation = false; - - break; - } - case "url": { - return; - } + break; } + case "url": { + return; + } + } - element.hidden = false; - previousElement = element; - }); - } - createApplyOptionEffect(); + element.hidden = false; + previousElement = element; + }); } function createMobileSwitchEffect() { @@ -2328,7 +2294,7 @@ function main() { if (indexes?.length) { const maxIndex = Math.min( (order || indexes).length - 1, - minIndex + RESULTS_PER_PAGE - 1 + minIndex + RESULTS_PER_PAGE - 1, ); list = Array(maxIndex - minIndex + 1); @@ -2404,7 +2370,7 @@ function main() { haystack, needle, undefined, - infoThresh + infoThresh, ); if (!result?.[0]?.length || !result?.[1]) { @@ -2412,7 +2378,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2421,7 +2387,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2430,7 +2396,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2439,7 +2405,7 @@ function main() { haystack, needle, undefined, - infoThresh + infoThresh, ); } @@ -2448,7 +2414,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2550,7 +2516,7 @@ function main() { localStorage.setItem(barWidthLocalStorageKey, String(width)); } else { elements.main.style.width = elements.style.getPropertyValue( - "--default-main-width" + "--default-main-width", ); localStorage.removeItem(barWidthLocalStorageKey); } @@ -2586,8 +2552,8 @@ function main() { window.addEventListener("mouseleave", setResizeFalse); } initDesktopResizeBar(); - }) - ) + }), + ), ); } main();