mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-11 07:23:32 -07:00
bitview: simplify options tree
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+35
-18
@@ -34,8 +34,11 @@
|
||||
-webkit-text-size-adjust: 100%;
|
||||
tab-size: 4;
|
||||
font-family:
|
||||
"Geist mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
"Liberation Mono", "Courier New", monospace;
|
||||
/* Lilex features */
|
||||
/*font-feature-settings: "ss01";*/
|
||||
/* Geist features */
|
||||
font-feature-settings: "ss03";
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
@@ -278,21 +281,35 @@
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Geist mono";
|
||||
src: url("./assets/fonts/geist_mono_var_v1_5_0.woff2") format("woff2");
|
||||
font-family: "Geist Mono";
|
||||
src: url("/assets/fonts/GeistMono[wght]-v1_5.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: block;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Geist Mono";
|
||||
src: url("/assets/fonts/GeistMono-Italic[wght]-v1_5.woff2")
|
||||
format("woff2");
|
||||
font-style: italic;
|
||||
font-weight: 100 900;
|
||||
font-display: block;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*@font-face {
|
||||
font-family: "Lilex";
|
||||
src: url("./assets/fonts/Lilex-Italic[wght]-2.601.woff2")
|
||||
format("woff2");
|
||||
@font-face {
|
||||
font-family: Lilex;
|
||||
src: url("/assets/fonts/Lilex[wght].woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: block;
|
||||
font-style: normal;
|
||||
}*/
|
||||
}
|
||||
@font-face {
|
||||
font-family: Lilex;
|
||||
src: url("/assets/fonts/Lilex-Italic[wght].woff2") format("woff2");
|
||||
font-style: italic;
|
||||
font-weight: 100 900;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* ---
|
||||
@@ -729,7 +746,7 @@
|
||||
* ---
|
||||
*/
|
||||
|
||||
.tree {
|
||||
nav {
|
||||
margin-top: -0.125rem;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
@@ -745,11 +762,12 @@
|
||||
}
|
||||
|
||||
li[data-highlight] {
|
||||
> details:not([open]) {
|
||||
> details > summary,
|
||||
> a {
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
> label > a::after,
|
||||
> a::after,
|
||||
> details:not([open]) > summary::after {
|
||||
color: var(--orange) !important;
|
||||
content: "";
|
||||
@@ -843,12 +861,13 @@
|
||||
}
|
||||
|
||||
search {
|
||||
text-transform: uppercase;
|
||||
gap: 1rem;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1745,9 +1764,7 @@
|
||||
<div class="shadow-bottom"></div>
|
||||
<div id="resize-bar"></div>
|
||||
|
||||
<nav id="nav" hidden>
|
||||
<!-- <h4>bitview</h4> -->
|
||||
</nav>
|
||||
<nav id="nav" hidden></nav>
|
||||
|
||||
<search id="search" hidden>
|
||||
<header>
|
||||
@@ -1763,7 +1780,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
</header>
|
||||
<ul id="search-results" style="text-transform: lowercase"></ul>
|
||||
<ul id="search-results"></ul>
|
||||
</search>
|
||||
|
||||
<footer>
|
||||
|
||||
@@ -404,9 +404,6 @@ function createChartElement({
|
||||
({ _indexes, values }) => {
|
||||
if (!_indexes?.length || !values?.length) return;
|
||||
|
||||
const consoleTimeLabel = `${vecId}-time`;
|
||||
console.time(consoleTimeLabel);
|
||||
|
||||
const indexes = /** @type {number[]} */ (_indexes);
|
||||
|
||||
let length = Math.min(indexes.length, values.length);
|
||||
@@ -500,8 +497,6 @@ function createChartElement({
|
||||
index,
|
||||
unit,
|
||||
});
|
||||
|
||||
console.timeEnd(consoleTimeLabel);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
@@ -873,7 +868,7 @@ function createLegend({ signals, utils }) {
|
||||
inputId: utils.stringToId(`legend-${series.id}`),
|
||||
inputName: utils.stringToId(`selected-${series.id}`),
|
||||
inputValue: "value",
|
||||
labelTitle: "Click to toggle",
|
||||
title: "Click to toggle",
|
||||
inputChecked: series.active(),
|
||||
onClick: () => {
|
||||
series.active.set(input.checked);
|
||||
|
||||
@@ -428,7 +428,6 @@ export function init({
|
||||
blueprints[unit]?.forEach((blueprint, order) => {
|
||||
order += orderStart;
|
||||
|
||||
console.log(blueprint.key);
|
||||
const indexes = /** @type {readonly number[]} */ (
|
||||
vecIdToIndexes[blueprint.key]
|
||||
);
|
||||
|
||||
+252
-237
@@ -49,9 +49,7 @@
|
||||
* "sat/vB" |
|
||||
* "%pnl" |
|
||||
* "constant" |
|
||||
* "cagr" |
|
||||
* "vB" |
|
||||
* "performance" |
|
||||
* "sd" |
|
||||
* "Epoch" |
|
||||
* "Height" |
|
||||
@@ -220,14 +218,16 @@ function createUtils() {
|
||||
/**
|
||||
* @param {Object} arg
|
||||
* @param {string} arg.href
|
||||
* @param {string} arg.title
|
||||
* @param {string} [arg.text]
|
||||
* @param {boolean} [arg.blank]
|
||||
* @param {VoidFunction} [arg.onClick]
|
||||
* @param {boolean} [arg.preventDefault]
|
||||
*/
|
||||
createAnchorElement({ text, href, blank, onClick, preventDefault }) {
|
||||
createAnchorElement({ text, href, blank, onClick, title, preventDefault }) {
|
||||
const anchor = window.document.createElement("a");
|
||||
anchor.href = href;
|
||||
anchor.title = title.toUpperCase();
|
||||
|
||||
if (text) {
|
||||
anchor.innerText = text;
|
||||
@@ -260,7 +260,7 @@ function createUtils() {
|
||||
|
||||
button.append(text);
|
||||
|
||||
button.title = title;
|
||||
button.title = title.toUpperCase();
|
||||
|
||||
button.addEventListener("click", onClick);
|
||||
|
||||
@@ -273,7 +273,7 @@ function createUtils() {
|
||||
* @param {string} args.inputId
|
||||
* @param {string} args.inputValue
|
||||
* @param {boolean} [args.inputChecked=false]
|
||||
* @param {string} args.labelTitle
|
||||
* @param {string} [args.title]
|
||||
* @param {'radio' | 'checkbox'} args.type
|
||||
* @param {(event: MouseEvent) => void} [args.onClick]
|
||||
*/
|
||||
@@ -282,7 +282,7 @@ function createUtils() {
|
||||
inputName,
|
||||
inputValue,
|
||||
inputChecked = false,
|
||||
labelTitle,
|
||||
title,
|
||||
onClick,
|
||||
type,
|
||||
}) {
|
||||
@@ -303,7 +303,9 @@ function createUtils() {
|
||||
label.append(input);
|
||||
|
||||
label.id = `${inputId}-label`;
|
||||
label.title = labelTitle;
|
||||
if (title) {
|
||||
label.title = title;
|
||||
}
|
||||
label.htmlFor = inputId;
|
||||
|
||||
if (onClick) {
|
||||
@@ -431,7 +433,7 @@ function createUtils() {
|
||||
inputName: id ?? key,
|
||||
inputValue,
|
||||
inputChecked: inputValue === selected(),
|
||||
labelTitle: choice,
|
||||
// title: choice,
|
||||
type: "radio",
|
||||
});
|
||||
|
||||
@@ -587,38 +589,40 @@ function createUtils() {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string | string[]} [pathname]
|
||||
*/
|
||||
function processPathname(pathname) {
|
||||
pathname ||= window.location.pathname;
|
||||
return Array.isArray(pathname) ? pathname.join("/") : pathname;
|
||||
}
|
||||
|
||||
const url = {
|
||||
chartParamsWhitelist: ["from", "to"],
|
||||
/**
|
||||
* @param {string} pathname
|
||||
* @param {string | string[]} pathname
|
||||
*/
|
||||
pushHistory(pathname) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
pathname ||= window.location.pathname;
|
||||
|
||||
pathname = processPathname(pathname);
|
||||
try {
|
||||
window.history.pushState(
|
||||
null,
|
||||
"",
|
||||
`${pathname}?${urlParams.toString()}`,
|
||||
);
|
||||
const url = `/${pathname}?${urlParams.toString()}`;
|
||||
console.log(`push history: ${url}`);
|
||||
window.history.pushState(null, "", url);
|
||||
} catch (_) {}
|
||||
},
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {URLSearchParams} [args.urlParams]
|
||||
* @param {string} [args.pathname]
|
||||
* @param {string | string[]} [args.pathname]
|
||||
*/
|
||||
replaceHistory({ urlParams, pathname }) {
|
||||
urlParams ||= new URLSearchParams(window.location.search);
|
||||
pathname ||= window.location.pathname;
|
||||
|
||||
pathname = processPathname(pathname);
|
||||
try {
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
`${pathname}?${urlParams.toString()}`,
|
||||
);
|
||||
const url = `/${pathname}?${urlParams.toString()}`;
|
||||
console.log(`replace history: ${url}`);
|
||||
window.history.replaceState(null, "", url);
|
||||
} catch (_) {}
|
||||
},
|
||||
/**
|
||||
@@ -626,7 +630,6 @@ function createUtils() {
|
||||
*/
|
||||
resetParams(option) {
|
||||
const urlParams = new URLSearchParams();
|
||||
|
||||
if (option.kind === "chart") {
|
||||
[...new URLSearchParams(window.location.search).entries()]
|
||||
.filter(([key, _]) => this.chartParamsWhitelist.includes(key))
|
||||
@@ -634,8 +637,7 @@ function createUtils() {
|
||||
urlParams.set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
this.replaceHistory({ urlParams, pathname: option.id });
|
||||
this.replaceHistory({ urlParams, pathname: option.path.join("/") });
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
@@ -695,9 +697,6 @@ function createUtils() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get(key);
|
||||
},
|
||||
pathnameToSelectedId() {
|
||||
return window.document.location.pathname.substring(1);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -810,7 +809,9 @@ function createUtils() {
|
||||
(!unit || thoroughUnitCheck) &&
|
||||
(id === "drawdown" ||
|
||||
id.endsWith("oscillator") ||
|
||||
id.endsWith("dominance"))
|
||||
id.endsWith("dominance") ||
|
||||
id.endsWith("returns") ||
|
||||
id.endsWith("cagr"))
|
||||
) {
|
||||
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
|
||||
unit = "percentage";
|
||||
@@ -851,18 +852,11 @@ function createUtils() {
|
||||
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
|
||||
unit = "secs";
|
||||
}
|
||||
if ((!unit || thoroughUnitCheck) && id.endsWith("returns")) {
|
||||
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
|
||||
unit = "performance";
|
||||
}
|
||||
if ((!unit || thoroughUnitCheck) && id.endsWith("locktime")) {
|
||||
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
|
||||
unit = "Locktime";
|
||||
}
|
||||
if ((!unit || thoroughUnitCheck) && id.endsWith("cagr")) {
|
||||
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
|
||||
unit = "cagr";
|
||||
}
|
||||
|
||||
if ((!unit || thoroughUnitCheck) && id.endsWith("version")) {
|
||||
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
|
||||
unit = "Version";
|
||||
@@ -1349,21 +1343,22 @@ function createUtils() {
|
||||
*
|
||||
* @template {(...args: any[]) => any} F
|
||||
* @param {F} callback
|
||||
* @param {number} [wait=250]
|
||||
* @param {number} [wait]
|
||||
*/
|
||||
function throttle(callback, wait = 250) {
|
||||
function throttle(callback, wait = 1000) {
|
||||
/** @type {number | null} */
|
||||
let timeoutId = null;
|
||||
/** @type {Parameters<F>} */
|
||||
let latestArgs;
|
||||
|
||||
return async (/** @type {Parameters<F>} */ ...args) => {
|
||||
return (/** @type {Parameters<F>} */ ...args) => {
|
||||
latestArgs = args;
|
||||
|
||||
if (!timeoutId) {
|
||||
await callback(...latestArgs); // Execute immediately
|
||||
timeoutId = setTimeout(async () => {
|
||||
await callback(...latestArgs); // Execute with latest args
|
||||
// Otherwise it optimizes away timeoutId in Chrome and FF
|
||||
timeoutId = timeoutId;
|
||||
timeoutId = setTimeout(() => {
|
||||
callback(...latestArgs); // Execute with latest args
|
||||
timeoutId = null;
|
||||
}, wait);
|
||||
}
|
||||
@@ -2226,8 +2221,6 @@ function main() {
|
||||
if (!firstRun) throw Error("Unreachable");
|
||||
firstRun = false;
|
||||
|
||||
console.log("selected: init");
|
||||
|
||||
const owner = signals.getOwner();
|
||||
|
||||
const chartOption = signals.createSignal(
|
||||
@@ -2246,13 +2239,12 @@ function main() {
|
||||
let firstTimeLoadingExplorer = true;
|
||||
|
||||
signals.createEffect(options.selected, (option) => {
|
||||
// console.log(utils.url.pathnameToSelectedId(), option);
|
||||
if (previousElement) {
|
||||
previousElement.hidden = true;
|
||||
utils.url.resetParams(option);
|
||||
utils.url.pushHistory(option.id);
|
||||
utils.url.pushHistory(option.path);
|
||||
} else {
|
||||
utils.url.replaceHistory({ pathname: option.id });
|
||||
utils.url.replaceHistory({ pathname: option.path });
|
||||
}
|
||||
|
||||
/** @type {HTMLElement} */
|
||||
@@ -2394,235 +2386,258 @@ function main() {
|
||||
}
|
||||
initSelected();
|
||||
|
||||
function initFolders() {
|
||||
async function scrollToSelected() {
|
||||
if (!options.selected()) throw "Selected should be set by now";
|
||||
const selectedId = options.selected().id;
|
||||
utils.dom.onFirstIntersection(elements.nav, async () => {
|
||||
options.parent.set(elements.nav);
|
||||
|
||||
const path = options.selected().path;
|
||||
const option = options.selected();
|
||||
if (!option) throw "Selected should be set by now";
|
||||
const path = [...option.path];
|
||||
|
||||
let i = 0;
|
||||
while (i !== path.length) {
|
||||
try {
|
||||
const id = path[i];
|
||||
const details = /** @type {HTMLDetailsElement} */ (
|
||||
utils.dom.getElementById(id)
|
||||
);
|
||||
details.open = true;
|
||||
i++;
|
||||
} catch {
|
||||
/** @type {HTMLUListElement | null} */
|
||||
let ul = /** @type {any} */ (null);
|
||||
async function getFirstChild() {
|
||||
try {
|
||||
ul = /** @type {HTMLUListElement} */ (
|
||||
elements.nav.firstElementChild
|
||||
);
|
||||
await utils.next();
|
||||
if (!ul) {
|
||||
await getFirstChild();
|
||||
}
|
||||
} catch (_) {
|
||||
await utils.next();
|
||||
await getFirstChild();
|
||||
}
|
||||
}
|
||||
await getFirstChild();
|
||||
if (!ul) throw Error("Unreachable");
|
||||
|
||||
let i = 0;
|
||||
while (path.length > 1) {
|
||||
const name = path.shift();
|
||||
if (!name) throw "Unreachable";
|
||||
/** @type {HTMLDetailsElement[]} */
|
||||
let detailsList = [];
|
||||
while (!detailsList.length) {
|
||||
detailsList = Array.from(
|
||||
ul.querySelectorAll(":scope > li > details"),
|
||||
);
|
||||
if (!detailsList.length) {
|
||||
await utils.next();
|
||||
}
|
||||
}
|
||||
|
||||
await utils.next();
|
||||
await utils.next();
|
||||
|
||||
utils.dom
|
||||
.getElementById(`${selectedId}-nav-selector`)
|
||||
.scrollIntoView({
|
||||
behavior: "instant",
|
||||
block: "center",
|
||||
});
|
||||
const details = detailsList.find((s) => s.dataset.name == name);
|
||||
if (!details) return;
|
||||
details.open = true;
|
||||
ul = null;
|
||||
while (!ul) {
|
||||
const uls = /** @type {HTMLUListElement[]} */ (
|
||||
Array.from(details.querySelectorAll(":scope > ul"))
|
||||
);
|
||||
if (!uls.length) {
|
||||
await utils.next();
|
||||
} else if (uls.length > 1) {
|
||||
throw "Shouldn't be possible";
|
||||
} else {
|
||||
ul = /** @type {HTMLUListElement} */ (uls.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
utils.dom.onFirstIntersection(elements.nav, () => {
|
||||
options.treeElement.set(() => {
|
||||
const treeElement = window.document.createElement("div");
|
||||
treeElement.classList.add("tree");
|
||||
elements.nav.append(treeElement);
|
||||
return treeElement;
|
||||
/** @type {HTMLAnchorElement[]} */
|
||||
let anchors = [];
|
||||
while (!anchors.length) {
|
||||
anchors = Array.from(ul.querySelectorAll(":scope > li > a"));
|
||||
if (!anchors.length) {
|
||||
await utils.next();
|
||||
}
|
||||
}
|
||||
anchors
|
||||
.find(
|
||||
(a) =>
|
||||
a.getAttribute("href") == window.document.location.pathname,
|
||||
)
|
||||
?.scrollIntoView({
|
||||
behavior: "instant",
|
||||
block: "center",
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(scrollToSelected, 10);
|
||||
});
|
||||
}
|
||||
initFolders();
|
||||
utils.dom.onFirstIntersection(elements.search, () => {
|
||||
console.log("search: init");
|
||||
|
||||
function initSearch() {
|
||||
function initSearchFrame() {
|
||||
console.log("search: init");
|
||||
const haystack = options.list.map((option) => option.title);
|
||||
|
||||
const haystack = options.list.map((option) => option.title);
|
||||
const RESULTS_PER_PAGE = 100;
|
||||
|
||||
const RESULTS_PER_PAGE = 100;
|
||||
packages.ufuzzy().then((ufuzzy) => {
|
||||
/**
|
||||
* @param {uFuzzy.SearchResult} searchResult
|
||||
* @param {number} pageIndex
|
||||
*/
|
||||
function computeResultPage(searchResult, pageIndex) {
|
||||
/** @type {{ option: Option, title: string }[]} */
|
||||
let list = [];
|
||||
|
||||
packages.ufuzzy().then((ufuzzy) => {
|
||||
/**
|
||||
* @param {uFuzzy.SearchResult} searchResult
|
||||
* @param {number} pageIndex
|
||||
*/
|
||||
function computeResultPage(searchResult, pageIndex) {
|
||||
/** @type {{ option: Option, title: string }[]} */
|
||||
let list = [];
|
||||
let [indexes, info, order] = searchResult || [null, null, null];
|
||||
|
||||
let [indexes, info, order] = searchResult || [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
];
|
||||
const minIndex = pageIndex * RESULTS_PER_PAGE;
|
||||
|
||||
const minIndex = pageIndex * RESULTS_PER_PAGE;
|
||||
if (indexes?.length) {
|
||||
const maxIndex = Math.min(
|
||||
(order || indexes).length - 1,
|
||||
minIndex + RESULTS_PER_PAGE - 1,
|
||||
);
|
||||
|
||||
if (indexes?.length) {
|
||||
const maxIndex = Math.min(
|
||||
(order || indexes).length - 1,
|
||||
minIndex + RESULTS_PER_PAGE - 1,
|
||||
);
|
||||
list = Array(maxIndex - minIndex + 1);
|
||||
|
||||
list = Array(maxIndex - minIndex + 1);
|
||||
for (let i = minIndex; i <= maxIndex; i++) {
|
||||
let index = indexes[i];
|
||||
|
||||
for (let i = minIndex; i <= maxIndex; i++) {
|
||||
let index = indexes[i];
|
||||
const title = haystack[index];
|
||||
|
||||
const title = haystack[index];
|
||||
|
||||
list[i % 100] = {
|
||||
option: options.list[index],
|
||||
title,
|
||||
};
|
||||
}
|
||||
list[i % 100] = {
|
||||
option: options.list[index],
|
||||
title,
|
||||
};
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/** @type {uFuzzy.Options} */
|
||||
const config = {
|
||||
intraIns: Infinity,
|
||||
intraChars: `[a-z\d' ]`,
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
const fuzzyMultiInsert = /** @type {uFuzzy} */ (
|
||||
ufuzzy({
|
||||
intraIns: 1,
|
||||
})
|
||||
);
|
||||
const fuzzyMultiInsertFuzzier = /** @type {uFuzzy} */ (
|
||||
ufuzzy(config)
|
||||
);
|
||||
const fuzzySingleError = /** @type {uFuzzy} */ (
|
||||
ufuzzy({
|
||||
intraMode: 1,
|
||||
...config,
|
||||
})
|
||||
);
|
||||
const fuzzySingleErrorFuzzier = /** @type {uFuzzy} */ (
|
||||
ufuzzy({
|
||||
intraMode: 1,
|
||||
...config,
|
||||
})
|
||||
);
|
||||
/** @type {uFuzzy.Options} */
|
||||
const config = {
|
||||
intraIns: Infinity,
|
||||
intraChars: `[a-z\d' ]`,
|
||||
};
|
||||
|
||||
/** @type {VoidFunction | undefined} */
|
||||
let dispose;
|
||||
const fuzzyMultiInsert = /** @type {uFuzzy} */ (
|
||||
ufuzzy({
|
||||
intraIns: 1,
|
||||
})
|
||||
);
|
||||
const fuzzyMultiInsertFuzzier = /** @type {uFuzzy} */ (
|
||||
ufuzzy(config)
|
||||
);
|
||||
const fuzzySingleError = /** @type {uFuzzy} */ (
|
||||
ufuzzy({
|
||||
intraMode: 1,
|
||||
...config,
|
||||
})
|
||||
);
|
||||
const fuzzySingleErrorFuzzier = /** @type {uFuzzy} */ (
|
||||
ufuzzy({
|
||||
intraMode: 1,
|
||||
...config,
|
||||
})
|
||||
);
|
||||
|
||||
function inputEvent() {
|
||||
signals.createRoot((_dispose) => {
|
||||
const needle = /** @type {string} */ (
|
||||
elements.searchInput.value
|
||||
/** @type {VoidFunction | undefined} */
|
||||
let dispose;
|
||||
|
||||
function inputEvent() {
|
||||
signals.createRoot((_dispose) => {
|
||||
const needle = /** @type {string} */ (
|
||||
elements.searchInput.value
|
||||
);
|
||||
|
||||
dispose?.();
|
||||
|
||||
dispose = _dispose;
|
||||
|
||||
elements.searchResults.scrollTo({
|
||||
top: 0,
|
||||
});
|
||||
|
||||
if (!needle) {
|
||||
elements.searchResults.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const outOfOrder = 5;
|
||||
const infoThresh = 5_000;
|
||||
|
||||
let result = fuzzyMultiInsert?.search(
|
||||
haystack,
|
||||
needle,
|
||||
undefined,
|
||||
infoThresh,
|
||||
);
|
||||
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzyMultiInsert?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
|
||||
dispose?.();
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzySingleError?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
|
||||
dispose = _dispose;
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzySingleErrorFuzzier?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
|
||||
elements.searchResults.scrollTo({
|
||||
top: 0,
|
||||
});
|
||||
|
||||
if (!needle) {
|
||||
elements.searchResults.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const outOfOrder = 5;
|
||||
const infoThresh = 5_000;
|
||||
|
||||
let result = fuzzyMultiInsert?.search(
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzyMultiInsertFuzzier?.search(
|
||||
haystack,
|
||||
needle,
|
||||
undefined,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzyMultiInsert?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzyMultiInsertFuzzier?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzySingleError?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
elements.searchResults.innerHTML = "";
|
||||
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzySingleErrorFuzzier?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
const list = computeResultPage(result, 0);
|
||||
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzyMultiInsertFuzzier?.search(
|
||||
haystack,
|
||||
needle,
|
||||
undefined,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
list.forEach(({ option, title }) => {
|
||||
const li = window.document.createElement("li");
|
||||
elements.searchResults.appendChild(li);
|
||||
|
||||
if (!result?.[0]?.length || !result?.[1]) {
|
||||
result = fuzzyMultiInsertFuzzier?.search(
|
||||
haystack,
|
||||
needle,
|
||||
outOfOrder,
|
||||
infoThresh,
|
||||
);
|
||||
}
|
||||
|
||||
elements.searchResults.innerHTML = "";
|
||||
|
||||
const list = computeResultPage(result, 0);
|
||||
|
||||
list.forEach(({ option, title }) => {
|
||||
const li = window.document.createElement("li");
|
||||
elements.searchResults.appendChild(li);
|
||||
|
||||
const element = options.createOptionElement({
|
||||
option,
|
||||
frame: "search",
|
||||
name: title,
|
||||
qrcode,
|
||||
});
|
||||
|
||||
if (element) {
|
||||
li.append(element);
|
||||
}
|
||||
const element = options.createOptionElement({
|
||||
option,
|
||||
frame: "search",
|
||||
name: title,
|
||||
qrcode,
|
||||
});
|
||||
|
||||
if (element) {
|
||||
li.append(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (elements.searchInput.value) {
|
||||
inputEvent();
|
||||
}
|
||||
if (elements.searchInput.value) {
|
||||
inputEvent();
|
||||
}
|
||||
|
||||
elements.searchInput.addEventListener("input", inputEvent);
|
||||
});
|
||||
}
|
||||
utils.dom.onFirstIntersection(elements.search, initSearchFrame);
|
||||
}
|
||||
initSearch();
|
||||
elements.searchInput.addEventListener("input", inputEvent);
|
||||
});
|
||||
});
|
||||
|
||||
function initShare() {
|
||||
const shareDiv = utils.dom.getElementById("share-div");
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
* @property {string} name
|
||||
*
|
||||
* @typedef {Object} ProcessedOptionAddons
|
||||
* @property {string} id
|
||||
* @property {string} title
|
||||
* @property {string[]} path
|
||||
*
|
||||
@@ -110,7 +109,6 @@
|
||||
* @property {PartialOptionsTree} tree
|
||||
*
|
||||
* @typedef {Object} OptionsGroup
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {OptionsTree} tree
|
||||
*
|
||||
@@ -3094,7 +3092,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
type: "Baseline",
|
||||
}),
|
||||
createPriceLine({
|
||||
unit: "performance",
|
||||
unit: "percentage",
|
||||
}),
|
||||
...(`${key}_cagr` in vecIdToIndexes
|
||||
? [
|
||||
@@ -3104,7 +3102,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
type: "Baseline",
|
||||
}),
|
||||
createPriceLine({
|
||||
unit: "cagr",
|
||||
unit: "percentage",
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
@@ -3181,7 +3179,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
type: "Baseline",
|
||||
}),
|
||||
createPriceLine({
|
||||
unit: "performance",
|
||||
unit: "percentage",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -3218,24 +3216,27 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
type: "Baseline",
|
||||
colors: [colors.yellow, colors.pink],
|
||||
}),
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
key: `${key}_dca_cagr`,
|
||||
title: "dca",
|
||||
type: "Baseline",
|
||||
colors: [colors.yellow, colors.pink],
|
||||
}),
|
||||
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
key: `${key}_returns`,
|
||||
title: "lump sum",
|
||||
type: "Baseline",
|
||||
}),
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
key: `${key}_cagr`,
|
||||
title: "lump sum",
|
||||
key: `${key}_dca_cagr`,
|
||||
title: "dca cagr",
|
||||
type: "Baseline",
|
||||
colors: [colors.yellow, colors.pink],
|
||||
defaultActive: false,
|
||||
}),
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
key: `${key}_cagr`,
|
||||
title: "lump sum cagr",
|
||||
type: "Baseline",
|
||||
defaultActive: false,
|
||||
}),
|
||||
createPriceLine({
|
||||
unit: "performance",
|
||||
unit: "percentage",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -3265,7 +3266,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
top: [
|
||||
createBaseSeries({
|
||||
key: `dca_class_${year}_avg_price`,
|
||||
name: `avg. price`,
|
||||
name: "cost basis",
|
||||
color,
|
||||
}),
|
||||
],
|
||||
@@ -3276,7 +3277,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
type: "Baseline",
|
||||
}),
|
||||
createPriceLine({
|
||||
unit: "performance",
|
||||
unit: "percentage",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -3418,6 +3419,10 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
key: "difficulty",
|
||||
name: "Value",
|
||||
}),
|
||||
createBaseSeries({
|
||||
key: "difficultyepoch",
|
||||
name: "Epoch",
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -3467,16 +3472,6 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Difficulty Epoch",
|
||||
title: "Difficulty Epoch",
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
key: "difficultyepoch",
|
||||
name: "Epoch",
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Halving Epoch",
|
||||
title: "Halving Epoch",
|
||||
@@ -4159,8 +4154,9 @@ export function initOptions({
|
||||
}) {
|
||||
const LS_SELECTED_KEY = `selected_id`;
|
||||
|
||||
const urlSelected = utils.url.pathnameToSelectedId();
|
||||
const savedSelectedId = utils.storage.read(LS_SELECTED_KEY);
|
||||
const urlPath_ = window.document.location.pathname.substring(1).split("/");
|
||||
const urlPath = urlPath_.length ? urlPath_ : undefined;
|
||||
const savedPath = utils.storage.read(LS_SELECTED_KEY)?.split("/");
|
||||
|
||||
/** @type {Signal<Option>} */
|
||||
const selected = signals.createSignal(/** @type {any} */ (undefined));
|
||||
@@ -4170,21 +4166,12 @@ export function initOptions({
|
||||
/** @type {Option[]} */
|
||||
const list = [];
|
||||
|
||||
/** @type {HTMLDetailsElement[]} */
|
||||
const detailsList = [];
|
||||
|
||||
const treeElement = signals.createSignal(
|
||||
/** @type {HTMLDivElement | null} */ (null),
|
||||
);
|
||||
|
||||
/** @type {string[] | undefined} */
|
||||
const optionsIds = env.localhost ? [] : undefined;
|
||||
const parent = signals.createSignal(/** @type {HTMLElement | null} */ (null));
|
||||
|
||||
/**
|
||||
* @param {AnyFetchedSeriesBlueprint[]} [arr]
|
||||
* @param {string} id
|
||||
*/
|
||||
function arrayToRecord(id, arr = []) {
|
||||
function arrayToRecord(arr = []) {
|
||||
return (arr || []).reduce((record, blueprint) => {
|
||||
if (env.localhost && !(blueprint.key in vecIdToIndexes)) {
|
||||
throw Error(`${blueprint.key} not recognized`);
|
||||
@@ -4202,16 +4189,16 @@ export function initOptions({
|
||||
* @param {string} args.frame
|
||||
* @param {Signal<string | null>} args.qrcode
|
||||
* @param {string} [args.name]
|
||||
* @param {string} [args.id]
|
||||
*/
|
||||
function createOptionElement({ option, frame, name, id, qrcode }) {
|
||||
function createOptionElement({ option, frame, name, qrcode }) {
|
||||
const title = option.title;
|
||||
if (option.kind === "url") {
|
||||
const href = option.url();
|
||||
|
||||
if (option.qrcode) {
|
||||
return utils.dom.createButtonElement({
|
||||
inside: option.name,
|
||||
title: option.title,
|
||||
title,
|
||||
onClick: () => {
|
||||
qrcode.set(option.url);
|
||||
},
|
||||
@@ -4221,52 +4208,31 @@ export function initOptions({
|
||||
href,
|
||||
blank: true,
|
||||
text: option.name,
|
||||
title,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const { input, label } = utils.dom.createLabeledInput({
|
||||
inputId: `${option.id}-${frame}${id || ""}-selector`,
|
||||
inputValue: option.id,
|
||||
inputName: `option_${frame}${id || ""}`,
|
||||
labelTitle: option.title,
|
||||
onClick: () => {
|
||||
selected.set(option);
|
||||
},
|
||||
type: "radio",
|
||||
});
|
||||
|
||||
const anchor = utils.dom.createAnchorElement({
|
||||
href: `/${option.id}`,
|
||||
return utils.dom.createAnchorElement({
|
||||
href: `/${option.path.join("/")}`,
|
||||
title,
|
||||
text: name || option.name,
|
||||
onClick: () => {},
|
||||
onClick: () => {
|
||||
selected.set(() => option);
|
||||
},
|
||||
});
|
||||
|
||||
label.append(anchor);
|
||||
|
||||
function createCheckEffect() {
|
||||
signals.createEffect(selected, (selected) => {
|
||||
if (selected?.id === option.id) {
|
||||
input.checked = true;
|
||||
utils.storage.write(LS_SELECTED_KEY, option.id);
|
||||
} else if (input.checked) {
|
||||
input.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createCheckEffect();
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Option | undefined} */
|
||||
let savedOption;
|
||||
|
||||
/**
|
||||
* @param {PartialOptionsTree} partialTree
|
||||
* @param {Accessor<HTMLDivElement | HTMLDetailsElement | null>} parent
|
||||
* @param {string[] | undefined} path
|
||||
* @param {Accessor<HTMLElement | null>} parent
|
||||
* @param {string[] | undefined} parentPath
|
||||
* @returns {Accessor<number>}
|
||||
*/
|
||||
function recursiveProcessPartialTree(partialTree, parent, path = undefined) {
|
||||
function recursiveProcessPartialTree(partialTree, parent, parentPath = []) {
|
||||
/** @type {Accessor<number>[]} */
|
||||
const listForSum = [];
|
||||
|
||||
@@ -4311,29 +4277,21 @@ export function initOptions({
|
||||
}, /** @type {HTMLLIElement | null} */ (null));
|
||||
|
||||
if ("tree" in anyPartial) {
|
||||
const folderId = utils.stringToId(
|
||||
`${(path || []).join(" ")} ${anyPartial.name} folder`,
|
||||
);
|
||||
|
||||
/** @type {Omit<OptionsGroup, keyof PartialOptionsGroup>} */
|
||||
const groupAddons = {
|
||||
id: folderId,
|
||||
};
|
||||
const groupAddons = {};
|
||||
|
||||
Object.assign(anyPartial, groupAddons);
|
||||
|
||||
optionsIds?.push(groupAddons.id);
|
||||
|
||||
const thisPath = groupAddons.id;
|
||||
|
||||
const passedDetails = signals.createSignal(
|
||||
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
|
||||
);
|
||||
|
||||
const serName = utils.stringToId(anyPartial.name);
|
||||
|
||||
const childOptionsCount = recursiveProcessPartialTree(
|
||||
anyPartial.tree,
|
||||
passedDetails,
|
||||
[...(path || []), thisPath],
|
||||
[...parentPath, serName],
|
||||
);
|
||||
|
||||
listForSum.push(childOptionsCount);
|
||||
@@ -4345,7 +4303,7 @@ export function initOptions({
|
||||
}
|
||||
|
||||
signals.createEffect(selected, (selected) => {
|
||||
if (selected.path.includes(thisPath)) {
|
||||
if (selected.path.includes(serName)) {
|
||||
li.dataset.highlight = "";
|
||||
} else {
|
||||
delete li.dataset.highlight;
|
||||
@@ -4353,8 +4311,7 @@ export function initOptions({
|
||||
});
|
||||
|
||||
const details = window.document.createElement("details");
|
||||
details.id = folderId;
|
||||
detailsList.push(details);
|
||||
details.dataset.name = serName;
|
||||
li.appendChild(details);
|
||||
|
||||
const summary = window.document.createElement("summary");
|
||||
@@ -4389,62 +4346,68 @@ export function initOptions({
|
||||
/** @type {Option} */
|
||||
let option;
|
||||
|
||||
const name = anyPartial.name;
|
||||
const path = [...parentPath, utils.stringToId(anyPartial.name)];
|
||||
|
||||
if ("kind" in anyPartial && anyPartial.kind === "explorer") {
|
||||
option = /** @satisfies {ExplorerOption} */ ({
|
||||
kind: anyPartial.kind,
|
||||
id: anyPartial.kind,
|
||||
name: anyPartial.name,
|
||||
path: path || [],
|
||||
name,
|
||||
path,
|
||||
title: anyPartial.title,
|
||||
});
|
||||
} else if ("kind" in anyPartial && anyPartial.kind === "table") {
|
||||
option = /** @satisfies {TableOption} */ ({
|
||||
kind: anyPartial.kind,
|
||||
id: anyPartial.kind,
|
||||
name: anyPartial.name,
|
||||
path: path || [],
|
||||
name,
|
||||
path,
|
||||
title: anyPartial.title,
|
||||
});
|
||||
} else if ("kind" in anyPartial && anyPartial.kind === "simulation") {
|
||||
option = /** @satisfies {SimulationOption} */ ({
|
||||
kind: anyPartial.kind,
|
||||
id: anyPartial.kind,
|
||||
name: anyPartial.name,
|
||||
path: path || [],
|
||||
name,
|
||||
path,
|
||||
title: anyPartial.title,
|
||||
});
|
||||
} else if ("url" in anyPartial) {
|
||||
option = /** @satisfies {UrlOption} */ ({
|
||||
kind: "url",
|
||||
id: `${utils.stringToId(anyPartial.name)}-url`,
|
||||
name: anyPartial.name,
|
||||
path: path || [],
|
||||
title: anyPartial.name,
|
||||
name,
|
||||
path,
|
||||
title: name,
|
||||
qrcode: !!anyPartial.qrcode,
|
||||
url: anyPartial.url,
|
||||
});
|
||||
} else {
|
||||
const title = anyPartial.title || anyPartial.name;
|
||||
const id = `chart-${utils.stringToId(title)}`;
|
||||
option = /** @satisfies {ChartOption} */ ({
|
||||
kind: "chart",
|
||||
id,
|
||||
name: anyPartial.name,
|
||||
name,
|
||||
title,
|
||||
path: path || [],
|
||||
top: arrayToRecord(id, anyPartial.top),
|
||||
bottom: arrayToRecord(id, anyPartial.bottom),
|
||||
path,
|
||||
top: arrayToRecord(anyPartial.top),
|
||||
bottom: arrayToRecord(anyPartial.bottom),
|
||||
});
|
||||
}
|
||||
|
||||
if (urlSelected === option.id) {
|
||||
selected.set(option);
|
||||
} else if (!selected() && savedSelectedId === option.id) {
|
||||
selected.set(option);
|
||||
}
|
||||
|
||||
list.push(option);
|
||||
optionsIds?.push(option.id);
|
||||
|
||||
if (urlPath) {
|
||||
const sameAsURLPath =
|
||||
urlPath.length === path.length &&
|
||||
urlPath.every((val, i) => val === path[i]);
|
||||
if (sameAsURLPath) {
|
||||
selected.set(option);
|
||||
}
|
||||
} else if (savedPath) {
|
||||
const sameAsSavedPath =
|
||||
savedPath.length === path.length &&
|
||||
savedPath.every((val, i) => val === path[i]);
|
||||
if (sameAsSavedPath) {
|
||||
savedOption = option;
|
||||
}
|
||||
}
|
||||
|
||||
signals.createEffect(li, (li) => {
|
||||
if (!li) {
|
||||
@@ -4476,101 +4439,22 @@ export function initOptions({
|
||||
listForSum.reduce((acc, s) => acc + s(), 0),
|
||||
);
|
||||
}
|
||||
recursiveProcessPartialTree(partialOptions, treeElement);
|
||||
recursiveProcessPartialTree(partialOptions, parent);
|
||||
|
||||
function setDefaultSelectedIfNeeded() {
|
||||
if (!selected()) {
|
||||
const firstChartOption = list.find((option) => option.kind === "chart");
|
||||
if (firstChartOption) {
|
||||
selected.set(firstChartOption);
|
||||
}
|
||||
if (!selected()) {
|
||||
const option =
|
||||
savedOption || list.find((option) => option.kind === "chart");
|
||||
if (option) {
|
||||
selected.set(option);
|
||||
}
|
||||
}
|
||||
setDefaultSelectedIfNeeded();
|
||||
|
||||
if (env.localhost) {
|
||||
function checkUniqueIds() {
|
||||
if (!optionsIds) {
|
||||
throw "Should be set";
|
||||
} else if (optionsIds.length !== new Set(optionsIds).size) {
|
||||
/** @type {Map<string, number>} */
|
||||
const m = new Map();
|
||||
|
||||
optionsIds.forEach((id) => {
|
||||
m.set(id, (m.get(id) || 0) + 1);
|
||||
});
|
||||
|
||||
console.log(
|
||||
[...m.entries()]
|
||||
.filter(([_, value]) => value > 1)
|
||||
.map(([key, _]) => key),
|
||||
);
|
||||
|
||||
throw Error("ID duplicate");
|
||||
}
|
||||
}
|
||||
checkUniqueIds();
|
||||
}
|
||||
|
||||
return {
|
||||
selected,
|
||||
list,
|
||||
details: detailsList,
|
||||
tree: /** @type {OptionsTree} */ (partialOptions),
|
||||
treeElement,
|
||||
parent,
|
||||
createOptionElement,
|
||||
};
|
||||
}
|
||||
/** @typedef {ReturnType<typeof initOptions>} Options */
|
||||
|
||||
// const size = /** @type {const} */ ([
|
||||
// {
|
||||
// key: "plankton",
|
||||
// name: "Plankton",
|
||||
// size: "1 sat to 0.1 BTC",
|
||||
// },
|
||||
// {
|
||||
// key: "shrimp",
|
||||
// name: "Shrimp",
|
||||
// size: "0.1 sat to 1 BTC",
|
||||
// },
|
||||
// { key: "crab", name: "Crab", size: "1 BTC to 10 BTC" },
|
||||
// { key: "fish", name: "Fish", size: "10 BTC to 100 BTC" },
|
||||
// { key: "shark", name: "Shark", size: "100 BTC to 1000 BTC" },
|
||||
// { key: "whale", name: "Whale", size: "1000 BTC to 10 000 BTC" },
|
||||
// {
|
||||
// key: "humpback",
|
||||
// name: "Humpback",
|
||||
// size: "10 000 BTC to 100 000 BTC",
|
||||
// },
|
||||
// {
|
||||
// key: "megalodon",
|
||||
// name: "Megalodon",
|
||||
// size: "More than 100 000 BTC",
|
||||
// },
|
||||
// ]);
|
||||
|
||||
// const type = /** @type {const} */ ([
|
||||
// { key: "p2pk", name: "P2PK" },
|
||||
// { key: "p2pkh", name: "P2PKH" },
|
||||
// { key: "p2sh", name: "P2SH" },
|
||||
// { key: "p2wpkh", name: "P2WPKH" },
|
||||
// { key: "p2wsh", name: "P2WSH" },
|
||||
// { key: "p2tr", name: "P2TR" },
|
||||
// ]);
|
||||
|
||||
// const address = /** @type {const} */ ([...size, ...type]);
|
||||
|
||||
// const liquidities = /** @type {const} */ ([
|
||||
// {
|
||||
// key: "illiquid",
|
||||
// id: "illiquid",
|
||||
// name: "Illiquid",
|
||||
// },
|
||||
// { key: "liquid", id: "liquid", name: "Liquid" },
|
||||
// {
|
||||
// key: "highly_liquid",
|
||||
// id: "highly_liquid",
|
||||
// name: "Highly Liquid",
|
||||
// },
|
||||
// ]);
|
||||
|
||||
Reference in New Issue
Block a user