mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-29 21:52:09 -07:00
release: v0.2.0
This commit is contained in:
@@ -5,6 +5,7 @@ const texts = [
|
||||
"satonomics",
|
||||
"satonomics",
|
||||
"satonomics",
|
||||
"satonomics",
|
||||
|
||||
"stay humble, stack sats",
|
||||
"21 million",
|
||||
@@ -39,6 +40,8 @@ const texts = [
|
||||
"low time preference",
|
||||
"absolute scarcity",
|
||||
"time is scarce",
|
||||
"ride or die",
|
||||
"cyberpunk",
|
||||
];
|
||||
|
||||
export function Background({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { requestIdleCallbackPossible } from "/src/env";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
export function Chart({
|
||||
@@ -19,7 +20,7 @@ export function Chart({
|
||||
}) {
|
||||
const wasIdle = createRWS(false);
|
||||
|
||||
if ("requestIdleCallback" in window) {
|
||||
if (requestIdleCallbackPossible) {
|
||||
const idleCallback = requestIdleCallback(() => {
|
||||
console.log("idle");
|
||||
wasIdle.set(true);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { version } from "/src/../package.json";
|
||||
import { chrome, ipad, iphone, macOS, safari, standalone } from "/src/env";
|
||||
import { ipad, iphone, macOS, safariOnly, standalone } from "/src/env";
|
||||
import { classPropToString } from "/src/solid/classes";
|
||||
|
||||
import { AnchorAPI } from "../strip/components/anchorAPI";
|
||||
@@ -156,9 +156,7 @@ export function SettingsFrame({
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<Show
|
||||
when={!standalone && !chrome && safari && (macOS || ipad || iphone)}
|
||||
>
|
||||
<Show when={!standalone && safariOnly && (macOS || ipad || iphone)}>
|
||||
<hr class="border-lighter -mx-4 border-t" />
|
||||
|
||||
<div class="space-y-4">
|
||||
|
||||
@@ -225,8 +225,6 @@ export function App() {
|
||||
document.addEventListener("keydown", documentOnKeyDown);
|
||||
onCleanup(() => document.removeEventListener("keydown", documentOnKeyDown));
|
||||
|
||||
const resizeInitialRange = createRWS<TimeRange | null>(null);
|
||||
|
||||
const SearchFrame = lazy(() =>
|
||||
import("./components/frames/search").then((d) => ({
|
||||
default: d.SearchFrame,
|
||||
|
||||
+9
-1
@@ -6,6 +6,8 @@ export const touchScreen =
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
(navigator as any).msMaxTouchPoints > 0;
|
||||
|
||||
export const requestIdleCallbackPossible = "requestIdleCallback" in window;
|
||||
|
||||
console.log(navigator.userAgent);
|
||||
export const macOS = navigator.userAgent.toLowerCase().includes("mac os");
|
||||
|
||||
@@ -13,9 +15,15 @@ export const iphone = navigator.userAgent.toLowerCase().includes("iphone");
|
||||
|
||||
export const ipad = navigator.userAgent.toLowerCase().includes("ipad");
|
||||
|
||||
export const chrome = navigator.userAgent.toLowerCase().includes("chrome");
|
||||
|
||||
export const firefox = navigator.userAgent.toLowerCase().includes("firefox");
|
||||
|
||||
export const gecko = navigator.userAgent.toLowerCase().includes("gecko");
|
||||
|
||||
export const safari = navigator.userAgent.toLowerCase().includes("safari");
|
||||
|
||||
export const chrome = navigator.userAgent.toLowerCase().includes("chrome");
|
||||
export const safariOnly = safari && !chrome;
|
||||
|
||||
export const phone =
|
||||
/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
|
||||
@@ -1,128 +1,135 @@
|
||||
import { colors } from "/src/scripts/utils/colors";
|
||||
|
||||
import { chunkIdToIndex } from "../datasets/resource";
|
||||
import { valueToString } from "../utils/locale";
|
||||
|
||||
export function setMinMaxMarkers({
|
||||
scale,
|
||||
visibleRange,
|
||||
legendList,
|
||||
activeIds,
|
||||
dark,
|
||||
}: {
|
||||
scale: ResourceScale;
|
||||
visibleRange: TimeRange | undefined;
|
||||
legendList: SeriesLegend[];
|
||||
activeIds: Accessor<number[]>;
|
||||
dark: Accessor<boolean>;
|
||||
}) {
|
||||
if (!visibleRange) return;
|
||||
try {
|
||||
if (!visibleRange) return;
|
||||
|
||||
const { from, to } = visibleRange;
|
||||
const { from, to } = visibleRange;
|
||||
|
||||
const dateFrom = new Date(from as string);
|
||||
const dateTo = new Date(to as string);
|
||||
const dateFrom = new Date(from as string);
|
||||
const dateTo = new Date(to as string);
|
||||
|
||||
let max = undefined as [number, Time, number, ISeriesApi<any>] | undefined;
|
||||
let min = undefined as [number, Time, number, ISeriesApi<any>] | undefined;
|
||||
let max = undefined as [number, Time, number, ISeriesApi<any>] | undefined;
|
||||
let min = undefined as [number, Time, number, ISeriesApi<any>] | undefined;
|
||||
|
||||
legendList.forEach(({ seriesList, dataset }) => {
|
||||
activeIds().forEach((id) => {
|
||||
const seriesIndex = chunkIdToIndex(scale, id);
|
||||
const ids = activeIds();
|
||||
|
||||
const series = seriesList.at(seriesIndex)?.();
|
||||
for (let i = 0; i < legendList.length; i++) {
|
||||
const { seriesList, dataset } = legendList[i];
|
||||
|
||||
if (!series || !series?.options().visible) return;
|
||||
for (let j = 0; j < ids.length; j++) {
|
||||
const id = ids[j];
|
||||
|
||||
series.setMarkers([]);
|
||||
const seriesIndex = chunkIdToIndex(scale, id);
|
||||
|
||||
const isCandlestick = series.seriesType() === "Candlestick";
|
||||
const series = seriesList.at(seriesIndex)?.();
|
||||
|
||||
const vec = dataset.fetchedJSONs.at(seriesIndex)?.vec();
|
||||
if (!series || !series?.options().visible) continue;
|
||||
|
||||
if (!vec) return;
|
||||
series.setMarkers([]);
|
||||
|
||||
for (let i = 0; i < vec.length; i++) {
|
||||
const data = vec[i];
|
||||
const isCandlestick = series.seriesType() === "Candlestick";
|
||||
|
||||
let number;
|
||||
const vec = dataset.fetchedJSONs.at(seriesIndex)?.vec();
|
||||
|
||||
if (scale === "date") {
|
||||
const date = new Date(
|
||||
typeof data.time === "string"
|
||||
? data.time
|
||||
: // @ts-ignore
|
||||
`${data.time.year}-${data.time.month}-${data.time.day}`,
|
||||
);
|
||||
if (!vec) return;
|
||||
|
||||
number = date.getTime();
|
||||
for (let k = 0; k < vec.length; k++) {
|
||||
const data = vec[k];
|
||||
|
||||
if (date <= dateFrom || date >= dateTo) {
|
||||
continue;
|
||||
let number;
|
||||
|
||||
if (scale === "date") {
|
||||
const date =
|
||||
typeof data.time === "string"
|
||||
? new Date(data.time)
|
||||
: // @ts-ignore
|
||||
new Date(data.time.year, data.time.month, data.time.day);
|
||||
|
||||
number = date.getTime();
|
||||
|
||||
if (date <= dateFrom || date >= dateTo) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const height = data.time;
|
||||
|
||||
number = height as number;
|
||||
|
||||
if (height <= from || height >= to) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const height = data.time;
|
||||
|
||||
number = height as number;
|
||||
// @ts-ignore
|
||||
const high = isCandlestick ? data["high"] : data.value;
|
||||
// @ts-ignore
|
||||
const low = isCandlestick ? data["low"] : data.value;
|
||||
|
||||
if (height <= from || height >= to) {
|
||||
continue;
|
||||
if (!max || high > max[2]) {
|
||||
max = [number, data.time, high, series];
|
||||
}
|
||||
if (!min || low < min[2]) {
|
||||
min = [number, data.time, low, series];
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const high = isCandlestick ? data["high"] : data.value;
|
||||
// @ts-ignore
|
||||
const low = isCandlestick ? data["low"] : data.value;
|
||||
|
||||
if (!max || high > max[2]) {
|
||||
max = [number, data.time, high, series];
|
||||
}
|
||||
if (!min || low < min[2]) {
|
||||
min = [number, data.time, low, series];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let minMarker: (SeriesMarker<Time> & { weight: number }) | undefined;
|
||||
let maxMarker: (SeriesMarker<Time> & { weight: number }) | undefined;
|
||||
|
||||
if (min) {
|
||||
minMarker = {
|
||||
weight: min[0],
|
||||
time: min[1],
|
||||
color: colors.white,
|
||||
position: "belowBar" as const,
|
||||
shape: "arrowUp" as const,
|
||||
size: 0,
|
||||
text: min[2].toLocaleString("en-us"),
|
||||
};
|
||||
}
|
||||
|
||||
if (max) {
|
||||
maxMarker = {
|
||||
weight: max[0],
|
||||
time: max[1],
|
||||
color: colors.white,
|
||||
position: "aboveBar" as const,
|
||||
shape: "arrowDown" as const,
|
||||
size: 0,
|
||||
text: max[2].toLocaleString("en-us"),
|
||||
};
|
||||
}
|
||||
|
||||
if (min && max && min[3] === max[3] && minMarker && maxMarker) {
|
||||
const series = min[3];
|
||||
series.setMarkers(
|
||||
[minMarker, maxMarker].sort((a, b) => a.weight - b.weight),
|
||||
);
|
||||
} else {
|
||||
if (min && minMarker) {
|
||||
const series = min[3];
|
||||
series.setMarkers([minMarker]);
|
||||
}
|
||||
|
||||
if (max && maxMarker) {
|
||||
const series = max[3];
|
||||
series.setMarkers([maxMarker]);
|
||||
let minMarker: (SeriesMarker<Time> & { weight: number }) | undefined;
|
||||
let maxMarker: (SeriesMarker<Time> & { weight: number }) | undefined;
|
||||
|
||||
if (min) {
|
||||
minMarker = {
|
||||
weight: min[0],
|
||||
time: min[1],
|
||||
color: colors.white(dark),
|
||||
position: "belowBar" as const,
|
||||
shape: "arrowUp" as const,
|
||||
size: 0,
|
||||
text: valueToString(min[2]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (max) {
|
||||
maxMarker = {
|
||||
weight: max[0],
|
||||
time: max[1],
|
||||
color: colors.white(dark),
|
||||
position: "aboveBar" as const,
|
||||
shape: "arrowDown" as const,
|
||||
size: 0,
|
||||
text: valueToString(max[2]),
|
||||
};
|
||||
}
|
||||
|
||||
if (min && max && min[3] === max[3] && minMarker && maxMarker) {
|
||||
min[3].setMarkers(
|
||||
[minMarker, maxMarker].sort((a, b) => a.weight - b.weight),
|
||||
);
|
||||
} else {
|
||||
if (min && minMarker) {
|
||||
min[3].setMarkers([minMarker]);
|
||||
}
|
||||
|
||||
if (max && maxMarker) {
|
||||
max[3].setMarkers([maxMarker]);
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { HEIGHT_CHUNK_SIZE } from "../datasets";
|
||||
import { debounce } from "../utils/debounce";
|
||||
import { run } from "../utils/run";
|
||||
import { tick } from "../utils/tick";
|
||||
import { writeURLParam } from "../utils/urlParams";
|
||||
|
||||
@@ -84,14 +85,14 @@ export function initTimeScale({
|
||||
debouncedSaveTimeRange({ scale, range });
|
||||
});
|
||||
|
||||
setTimeScale(firstChart, exactRange());
|
||||
}
|
||||
const range = exactRange();
|
||||
|
||||
async function setTimeScale(chart: IChartApi, range: TimeRange | null) {
|
||||
if (range) {
|
||||
await tick();
|
||||
chart.timeScale().setVisibleRange(range);
|
||||
}
|
||||
run(async () => {
|
||||
if (range) {
|
||||
await tick();
|
||||
firstChart.timeScale().setVisibleRange(range);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getLocalStorageKey(scale: ResourceScale) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { requestIdleCallbackPossible } from "/src/env";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { chunkIdToIndex } from "../datasets/resource";
|
||||
@@ -20,6 +21,7 @@ import { setWhitespace } from "../lightweightCharts/whitespace";
|
||||
import { colors } from "../utils/colors";
|
||||
import { debounce } from "../utils/debounce";
|
||||
import { stringToId } from "../utils/id";
|
||||
import { webSockets } from "../ws";
|
||||
|
||||
export enum SeriesType {
|
||||
Line,
|
||||
@@ -150,7 +152,7 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
|
||||
const div = document.createElement("div");
|
||||
|
||||
div.className = "w-full cursor-crosshair min-h-0 border-orange-200/10";
|
||||
div.className = "w-full cursor-crosshair min-h-0 border-lighter";
|
||||
|
||||
parentDiv.appendChild(div);
|
||||
|
||||
@@ -221,21 +223,23 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
|
||||
const chartLegend: SeriesLegend[] = [];
|
||||
|
||||
function _setMinMaxMarkers() {
|
||||
const markerCallback = () =>
|
||||
setMinMaxMarkers({
|
||||
scale,
|
||||
visibleRange: exactRange(),
|
||||
legendList: chartLegend,
|
||||
dark,
|
||||
activeIds: activeIds,
|
||||
});
|
||||
}
|
||||
|
||||
const debouncedSetMinMaxMarkers = debounce(
|
||||
_setMinMaxMarkers,
|
||||
seriesNumber * 10,
|
||||
);
|
||||
const debouncedSetMinMaxMarkers = requestIdleCallbackPossible
|
||||
? () => requestIdleCallback(markerCallback)
|
||||
: debounce(
|
||||
markerCallback,
|
||||
seriesNumber * 10 + scale === "date" ? 50 : 100,
|
||||
);
|
||||
|
||||
createEffect(on(exactRange, debouncedSetMinMaxMarkers));
|
||||
createEffect(on([exactRange, dark], debouncedSetMinMaxMarkers));
|
||||
|
||||
if (index === 0) {
|
||||
const dataset =
|
||||
@@ -274,7 +278,8 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
};
|
||||
}
|
||||
|
||||
return createSeriesGroup({
|
||||
const priceSeries = createSeriesGroup({
|
||||
index: -1,
|
||||
activeIds,
|
||||
seriesConfig,
|
||||
chart,
|
||||
@@ -285,6 +290,20 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const latest = webSockets.liveKrakenCandle.latest();
|
||||
|
||||
if (!latest) return;
|
||||
|
||||
const index = chunkIdToIndex(scale, latest.year);
|
||||
|
||||
const series = priceSeries.seriesList.at(index)?.();
|
||||
|
||||
series?.update(latest);
|
||||
});
|
||||
|
||||
return priceSeries;
|
||||
}
|
||||
|
||||
const priceCandlestickLegend = createPriceSeries("Candlestick");
|
||||
@@ -299,11 +318,12 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
});
|
||||
}
|
||||
|
||||
seriesConfigList.reverse().forEach((seriesConfig) => {
|
||||
seriesConfigList.reverse().forEach((seriesConfig, index) => {
|
||||
activeDatasets.push(seriesConfig.dataset);
|
||||
|
||||
createSeriesGroup({
|
||||
activeIds: activeIds,
|
||||
index,
|
||||
seriesConfig,
|
||||
chartLegend,
|
||||
chart,
|
||||
@@ -351,6 +371,7 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
|
||||
div.style.height = last ? "100%" : "calc(100% - 62px)";
|
||||
div.style.borderBottomWidth = last ? "none" : "1px";
|
||||
div.style.marginBottom = last ? "" : "-2px";
|
||||
|
||||
chart.timeScale().applyOptions({
|
||||
visible: last,
|
||||
@@ -457,6 +478,7 @@ function createSeriesGroup<Scale extends ResourceScale>({
|
||||
preset,
|
||||
chartLegend,
|
||||
chart,
|
||||
index: seriesIndex,
|
||||
disabled,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
@@ -466,6 +488,7 @@ function createSeriesGroup<Scale extends ResourceScale>({
|
||||
seriesConfig: SeriesConfig<Scale>;
|
||||
preset: Preset;
|
||||
chart: IChartApi;
|
||||
index: number;
|
||||
chartLegend: SeriesLegend[];
|
||||
lastActiveIndex: Accessor<number | undefined>;
|
||||
disabled?: Accessor<boolean>;
|
||||
@@ -511,6 +534,12 @@ function createSeriesGroup<Scale extends ResourceScale>({
|
||||
const values = json.vec();
|
||||
if (!values) return;
|
||||
|
||||
if (seriesIndex > 0) {
|
||||
let previous = chartLegend.at(seriesIndex - 1)?.seriesList[index];
|
||||
|
||||
if (!previous?.()) return;
|
||||
}
|
||||
|
||||
untrack(() => {
|
||||
let s = series();
|
||||
|
||||
|
||||
@@ -50,10 +50,6 @@ function createPresetFolder({
|
||||
key: AverageName;
|
||||
}) {
|
||||
return {
|
||||
// id,
|
||||
// name,
|
||||
// tree: [
|
||||
// {
|
||||
scale,
|
||||
name,
|
||||
description: "",
|
||||
|
||||
@@ -27,10 +27,13 @@ export const krakenAPI = {
|
||||
|
||||
const [timestamp, _, open, high, low, close, __, volume] = result[1];
|
||||
|
||||
const dateStr = dateToString(new Date(Number(timestamp) * 1000));
|
||||
const date = new Date(Number(timestamp) * 1000);
|
||||
|
||||
const dateStr = dateToString(date);
|
||||
|
||||
const candle: DatasetCandlestickData = {
|
||||
time: dateStr,
|
||||
year: date.getUTCFullYear(),
|
||||
open: Number(open),
|
||||
high: Number(high),
|
||||
low: Number(low),
|
||||
|
||||
Vendored
+1
-1
@@ -10,4 +10,4 @@ interface Valued {
|
||||
value: number;
|
||||
}
|
||||
|
||||
type DatasetCandlestickData = DatasetValue<CandlestickData>;
|
||||
type DatasetCandlestickData = DatasetValue<CandlestickData> & { year: number };
|
||||
|
||||
Reference in New Issue
Block a user