release: v0.2.0

This commit is contained in:
k
2024-07-08 19:57:30 +02:00
parent 04359fbf31
commit 2481878892
11 changed files with 163 additions and 119 deletions
+3
View File
@@ -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);
+2 -4
View File
@@ -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">
-2
View File
@@ -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
View File
@@ -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(
+95 -88
View File
@@ -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) {}
}
+8 -7
View File
@@ -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) {
+39 -10
View File
@@ -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: "",
+4 -1
View File
@@ -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),
+1 -1
View File
@@ -10,4 +10,4 @@ interface Valued {
value: number;
}
type DatasetCandlestickData = DatasetValue<CandlestickData>;
type DatasetCandlestickData = DatasetValue<CandlestickData> & { year: number };