remove: app

This commit is contained in:
k
2024-09-11 22:47:26 +02:00
parent ba4021ad73
commit 157ec003b7
282 changed files with 1454 additions and 43937 deletions
-9
View File
@@ -1,9 +0,0 @@
node_modules
charts
dist
dev-dist
.DS_Store
visualizer
# Local Netlify folder
.netlify
.wrangler
-17
View File
@@ -1,17 +0,0 @@
# Satonomics - App
## Description
A web app to view the generated datasets in various charts.
## Requirements
- Install `node`
- Install `pnpm`
- If using `cloudflare`, add cache rule to bypass the cache for `/sw.js`
## Deployment
```bash
pnpm deploy-prod
```
-1
View File
@@ -1 +0,0 @@
/* /index.html
-372
View File
@@ -1,372 +0,0 @@
<!doctype html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8" />
<title>kibō</title>
<meta
name="description"
content="A better, FOSS, Bitcoin-only, self-hostable Glassnode"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0c0a09" />
<link
rel="icon"
type="image/png"
sizes="196x196"
href="/assets/favicon-196.png"
/>
<link rel="apple-touch-icon" href="/assets/apple-icon-180.png" />
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2048-2732.jpg"
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2732-2048.jpg"
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1668-2388.jpg"
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2388-1668.jpg"
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1536-2048.jpg"
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2048-1536.jpg"
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1488-2266.jpg"
media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2266-1488.jpg"
media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1640-2360.jpg"
media="(device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2360-1640.jpg"
media="(device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1668-2224.jpg"
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2224-1668.jpg"
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1620-2160.jpg"
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2160-1620.jpg"
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1290-2796.jpg"
media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2796-1290.jpg"
media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1179-2556.jpg"
media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2556-1179.jpg"
media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1284-2778.jpg"
media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2778-1284.jpg"
media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1170-2532.jpg"
media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2532-1170.jpg"
media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1125-2436.jpg"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2436-1125.jpg"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1242-2688.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2688-1242.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-828-1792.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1792-828.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1242-2208.jpg"
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-2208-1242.jpg"
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-750-1334.jpg"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1334-750.jpg"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-640-1136.jpg"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-1136-640.jpg"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2048-2732.jpg"
media="(prefers-color-scheme: dark) and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2732-2048.jpg"
media="(prefers-color-scheme: dark) and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1668-2388.jpg"
media="(prefers-color-scheme: dark) and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2388-1668.jpg"
media="(prefers-color-scheme: dark) and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1536-2048.jpg"
media="(prefers-color-scheme: dark) and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2048-1536.jpg"
media="(prefers-color-scheme: dark) and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1488-2266.jpg"
media="(prefers-color-scheme: dark) and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2266-1488.jpg"
media="(prefers-color-scheme: dark) and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1640-2360.jpg"
media="(prefers-color-scheme: dark) and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2360-1640.jpg"
media="(prefers-color-scheme: dark) and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1668-2224.jpg"
media="(prefers-color-scheme: dark) and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2224-1668.jpg"
media="(prefers-color-scheme: dark) and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1620-2160.jpg"
media="(prefers-color-scheme: dark) and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2160-1620.jpg"
media="(prefers-color-scheme: dark) and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1290-2796.jpg"
media="(prefers-color-scheme: dark) and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2796-1290.jpg"
media="(prefers-color-scheme: dark) and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1179-2556.jpg"
media="(prefers-color-scheme: dark) and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2556-1179.jpg"
media="(prefers-color-scheme: dark) and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1284-2778.jpg"
media="(prefers-color-scheme: dark) and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2778-1284.jpg"
media="(prefers-color-scheme: dark) and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1170-2532.jpg"
media="(prefers-color-scheme: dark) and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2532-1170.jpg"
media="(prefers-color-scheme: dark) and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1125-2436.jpg"
media="(prefers-color-scheme: dark) and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2436-1125.jpg"
media="(prefers-color-scheme: dark) and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1242-2688.jpg"
media="(prefers-color-scheme: dark) and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2688-1242.jpg"
media="(prefers-color-scheme: dark) and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-828-1792.jpg"
media="(prefers-color-scheme: dark) and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1792-828.jpg"
media="(prefers-color-scheme: dark) and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1242-2208.jpg"
media="(prefers-color-scheme: dark) and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-2208-1242.jpg"
media="(prefers-color-scheme: dark) and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-750-1334.jpg"
media="(prefers-color-scheme: dark) and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1334-750.jpg"
media="(prefers-color-scheme: dark) and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-640-1136.jpg"
media="(prefers-color-scheme: dark) and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/assets/apple-splash-dark-1136-640.jpg"
media="(prefers-color-scheme: dark) and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>
-47
View File
@@ -1,47 +0,0 @@
{
"name": "satonomics",
"description": "A better, FOSS, Bitcoin-only, self-hostable Glassnode",
"version": "0.4.0",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "($npm_execpath outdated || read -p \"Press enter to ignore...\") && vite --host",
"check": "tsc --noEmit --skipLibCheck --pretty",
"build": "$npm_execpath run check && vite build",
"check-watch": "$npm_execpath check --watch",
"format": "prettier --write './src'",
"prod": "$npm_execpath run build && vite preview --host",
"pages-prod": "pnpm build && pnpm wrangler pages deploy ./dist",
"pages-dev": "pnpm build && pnpm wrangler pages deploy --branch dev ./dist",
"assets": "pnpm pwa-asset-generator ../assets/logo-icon.svg ./public/assets --index ./index.html --manifest ./public/manifest.webmanifest --icon-only --favicon --background #ffffe3 --padding \"min(15vh, 15vw)\" --path-override \"/assets\" && pnpm pwa-asset-generator ../assets/logo-icon.svg ./public/assets --index ./index.html --splash-only --background #10100e --padding \"min(33vh, 33vw)\" --path-override \"/assets\" && pnpm pwa-asset-generator ../assets/logo-icon.svg ./public/assets --index ./index.html --splash-only --dark-mode --background #10100e --padding \"min(33vh, 33vw)\" --path-override \"/assets\""
},
"dependencies": {
"@leeoniya/ufuzzy": "^1.0.14",
"@solid-primitives/event-listener": "^2.3.3",
"@solid-primitives/intersection-observer": "^2.1.6",
"@solid-primitives/resize-observer": "^2.0.26",
"lean-qr": "^2.3.4",
"lightweight-charts": "^4.2.0",
"solid-js": "^1.8.21"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
"@iconify-json/tabler": "^1.1.120",
"@tailwindcss/container-queries": "^0.1.1",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.41",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"pwa-asset-generator": "^6.3.1",
"rollup-plugin-visualizer": "^5.12.0",
"tailwindcss": "^3.4.10",
"typescript": "^5.5.4",
"unplugin-auto-import": "^0.18.2",
"unplugin-icons": "^0.19.2",
"vite": "^5.4.1",
"vite-plugin-pwa": "^0.20.1",
"vite-plugin-solid": "^2.10.2",
"workbox-window": "^7.1.0",
"wrangler": "^3.71.0"
}
}
-6385
View File
File diff suppressed because it is too large Load Diff
-11
View File
@@ -1,11 +0,0 @@
/** @type {import("prettier").Options} */
export default {
plugins: [
'@ianvs/prettier-plugin-sort-imports',
'prettier-plugin-tailwindcss', // MUST come last
],
tailwindFunctions: ['classList'],
importOrder: ['<THIRD_PARTY_MODULES>', '', '^/?(~|src)/', '', '^[./]'],
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.
Binary file not shown.
-17
View File
@@ -1,17 +0,0 @@
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" fill="black">
<g transform="matrix(1.14102,0,0,2.63158,-0.849652,5.12904)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(1.14102,0,0,2.63158,-0.849652,0.129039)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(1.14102,0,0,2.63158,-0.849652,-4.87096)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(0.285256,0,0,2.63158,8.78759,-9.87096)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(0.285256,0,0,2.63158,8.78759,10.129)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1004 B

-17
View File
@@ -1,17 +0,0 @@
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" fill="white">
<g transform="matrix(1.14102,0,0,2.63158,-0.849652,5.12904)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(1.14102,0,0,2.63158,-0.849652,0.129039)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(1.14102,0,0,2.63158,-0.849652,-4.87096)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(0.285256,0,0,2.63158,8.78759,-9.87096)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
<g transform="matrix(0.285256,0,0,2.63158,8.78759,10.129)">
<rect x="4.25" y="3.751" width="14.023" height="1.52"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1004 B

-37
View File
@@ -1,37 +0,0 @@
{
"name": "Satonomics",
"short_name": "Satonomics",
"description": "A better, FOSS, Bitcoin-only, self-hostable Glassnode",
"start_url": "/",
"display": "standalone",
"theme_color": "#0c0a09",
"background_color": "#0c0a09",
"lang": "en",
"scope": "/",
"icons": [
{
"src": "/assets/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/assets/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
-2
View File
@@ -1,2 +0,0 @@
User-agent: *
Allow: /
-293
View File
@@ -1,293 +0,0 @@
import { createRWS } from "/src/solid/rws";
import { standalone } from "../env";
import { createDatasets } from "../scripts/datasets";
import { createPresets } from "../scripts/presets";
import { createUserConfig } from "../scripts/user/config";
import { sleep } from "../scripts/utils/sleep";
import {
readBooleanFromStorage,
saveToStorage,
} from "../scripts/utils/storage";
import { readBooleanURLParam, writeURLParam } from "../scripts/utils/urlParams";
import { webSockets } from "../scripts/ws";
import { classPropToString } from "../solid/classes";
import { ChartFrame } from "./frames/chart";
import { FoldersFrame } from "./frames/folders";
import { HistoryFrame } from "./frames/history";
import { SettingsFrame } from "./frames/settings";
import { StripDesktop, StripMobile } from "./strip";
import { Update } from "./update";
const LOCAL_STORAGE_BAR_KEY = "bar-width";
const LOCAL_STORAGE_FULLSCREEN = "fullscrenn";
export const INPUT_PRESET_SEARCH_ID = "input-search-preset";
export function App() {
const tabFocused = createRWS(true);
const qrcode = createRWS("");
const dark = createRWS(false);
const userConfig = createUserConfig({
dark,
});
const fullscreen = createRWS(
readBooleanURLParam(LOCAL_STORAGE_FULLSCREEN) ||
readBooleanFromStorage(LOCAL_STORAGE_FULLSCREEN) ||
false,
);
const windowWidth = createRWS(window.innerWidth);
const windowWidth60p = createMemo(() => windowWidth() * 0.6);
const windowResizeCallback = () => {
windowWidth.set(window.innerWidth);
};
window.addEventListener("resize", windowResizeCallback);
onCleanup(() => window.removeEventListener("resize", windowResizeCallback));
const windowSizeIsAtLeastMedium = createMemo(() => windowWidth() >= 768);
const minBarWidth = 384;
const barWidth = createRWS(
Number(localStorage.getItem(LOCAL_STORAGE_BAR_KEY)) || minBarWidth,
);
createEffect(() => {
localStorage.setItem(LOCAL_STORAGE_BAR_KEY, String(barWidth()));
});
createEffect(() => {
if (fullscreen()) {
writeURLParam(LOCAL_STORAGE_FULLSCREEN, "true");
saveToStorage(LOCAL_STORAGE_FULLSCREEN, fullscreen());
} else {
writeURLParam(LOCAL_STORAGE_FULLSCREEN, undefined);
saveToStorage(LOCAL_STORAGE_FULLSCREEN, undefined);
}
});
const _selectedFrame = createRWS<FrameName>("Chart");
const selectedFrame = createMemo(() =>
windowSizeIsAtLeastMedium() && _selectedFrame() === "Chart"
? "Folders"
: _selectedFrame(),
);
const presets = createPresets();
const resizingBarStart = createRWS<number | undefined>(undefined);
const resizingBarWidth = createRWS<number>(0);
createEffect(
() => {
if (!windowSizeIsAtLeastMedium() && presets.selected()) {
_selectedFrame.set("Chart");
}
},
{
deffer: true,
},
);
const datasets = createDatasets();
onMount(() => {
webSockets.openAll();
createEffect(() => {
const latest = webSockets.liveKrakenCandle.latest();
if (latest) {
const close = latest.close;
console.log("close:", close);
document.title = `${latest.close.toLocaleString("en-us")} | Satonomics`;
}
});
});
const documentVisibilityChange = () =>
tabFocused.set(document.visibilityState === "visible");
document.addEventListener("visibilitychange", documentVisibilityChange);
onCleanup(() =>
document.removeEventListener("visibilitychange", documentVisibilityChange),
);
const documentOnKeyDown = async (event: KeyboardEvent) => {
switch (event.key) {
case "Escape": {
event.stopPropagation();
event.preventDefault();
_selectedFrame.set("Chart");
break;
}
case "/": {
event.stopPropagation();
event.preventDefault();
_selectedFrame.set("Search");
await sleep(50);
document.getElementById(INPUT_PRESET_SEARCH_ID)?.focus();
break;
}
}
};
document.addEventListener("keydown", documentOnKeyDown);
onCleanup(() => document.removeEventListener("keydown", documentOnKeyDown));
const SearchFrame = lazy(() =>
import("./frames/search").then((d) => ({
default: d.SearchFrame,
})),
);
const Qrcode = lazy(() =>
import("./qrcode").then((d) => ({
default: d.Qrcode,
})),
);
return (
<>
<div
class="relative h-dvh"
style={{
"user-select": resizingBarStart() !== undefined ? "none" : undefined,
}}
onMouseMove={(event) => {
const startingClientX = resizingBarStart();
if (startingClientX !== undefined) {
barWidth.set(
Math.min(
Math.max(
resizingBarWidth() + event.clientX - startingClientX,
minBarWidth,
),
windowWidth60p(),
),
);
}
}}
onMouseUp={() => resizingBarStart.set(undefined)}
onMouseLeave={() => resizingBarStart.set(undefined)}
onTouchEnd={() => resizingBarStart.set(undefined)}
onTouchCancel={() => resizingBarStart.set(undefined)}
>
<Show when={qrcode()}>
<Qrcode qrcode={qrcode} />
</Show>
<Update />
<div class="flex size-full flex-col md:flex-row">
<Show when={!windowSizeIsAtLeastMedium() || !fullscreen()}>
<div
class={classPropToString([
standalone && "border-t md:border-t-0",
"flex h-full flex-col overflow-hidden md:flex-row md:shadow-md md:short:hidden",
])}
>
<div class="hidden flex-col gap-2 border-r px-3 py-4 backdrop-blur-sm md:flex">
<StripDesktop
selected={selectedFrame}
setSelected={_selectedFrame.set}
/>
</div>
<div
class="relative flex h-full min-h-0"
style={{
...(windowSizeIsAtLeastMedium()
? {
"min-width": `${minBarWidth}px`,
width: `${barWidth()}px`,
"max-width": `${windowWidth60p()}px`,
}
: {}),
}}
>
<Show when={!windowSizeIsAtLeastMedium()}>
<ChartFrame
presets={presets}
hide={() => selectedFrame() !== "Chart"}
qrcode={qrcode}
standalone={false}
datasets={datasets}
dark={dark}
/>
</Show>
<FoldersFrame presets={presets} selectedFrame={selectedFrame} />
<SearchFrame presets={presets} selectedFrame={selectedFrame} />
<HistoryFrame presets={presets} selectedFrame={selectedFrame} />
<SettingsFrame
selectedFrame={selectedFrame}
appTheme={userConfig.settings.appTheme}
backgroundMode={userConfig.settings.background.mode}
backgroundOpacity={userConfig.settings.background.opacity}
/>
<div class="absolute bottom-0 left-0 right-0 z-10 h-6 w-full bg-gradient-to-b from-transparent to-[var(--background-color)]" />
</div>
<div
class={classPropToString([
standalone && "pb-6",
"flex justify-between gap-3 border-t p-2 sm:justify-around md:hidden short:hidden",
])}
>
<StripMobile
selected={selectedFrame}
setSelected={_selectedFrame.set}
/>
</div>
</div>
</Show>
{/* <Show when={!fullscreen()}>
<div
class="mx-[3px] my-8 hidden w-[6px] cursor-col-resize items-center justify-center rounded-full opacity-0 hover:opacity-50 md:block short:hidden"
onMouseDown={(event) => {
if (resizingBarStart() === undefined) {
resizingBarStart.set(event.clientX);
resizingBarWidth.set(barWidth());
}
}}
onTouchStart={(event) => {
if (resizingBarStart() === undefined) {
resizingBarStart.set(event.touches[0].clientX);
resizingBarWidth.set(barWidth());
}
}}
onDblClick={() => {
barWidth.set(0);
}}
/>
</Show> */}
<Show when={windowSizeIsAtLeastMedium()}>
<div class="flex min-w-0 flex-1 border-l">
<ChartFrame
standalone={true}
presets={presets}
qrcode={qrcode}
fullscreen={fullscreen}
datasets={datasets}
dark={dark}
/>
</div>
</Show>
</div>
</div>
</>
);
}
-209
View File
@@ -1,209 +0,0 @@
import { touchScreen } from "/src/env";
import { createRWS } from "/src/solid/rws";
const texts = [
"satonomics",
"satonomics",
"satonomics",
"satonomics",
"satonomics",
"satonomics",
"stay humble, stack sats",
"21 million",
"cold storage",
"utxo",
"satoshi nakamoto",
"hodl",
`don't trust, verify`,
"zap",
"₿itcoin",
"lightning",
"nostr",
"freedom tech",
"2008/10/31",
"2009/01/03",
"2010/05/22",
"hodl!",
"Hal Finney",
"Vote for better money",
"gradually then suddenly",
"timechain",
"self custody",
"be your own bank",
"resistance money",
"foss",
"permissionless",
"great reset",
"orange pill",
"borderless",
"anonymous",
"nyknyc",
"low time preference",
"absolute scarcity",
"time is scarce",
"ride or die",
"cypherpunk",
"we like the coin",
"money for enemies",
"trustless",
"sustainable",
"discriminationless",
];
export function Background({
mode,
opacity,
focused,
}: {
mode: SL<"Scroll" | "Static">;
opacity: SL<{ text: string; value: number }>;
focused: Accessor<boolean>;
}) {
return (
<>
<div
class="absolute h-full w-full overflow-hidden will-change-auto"
style={{
opacity: opacity.selected().value,
}}
>
<div class="-m-[2rem] -space-y-1 overflow-hidden md:-m-[1rem]">
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
<Line mode={mode} focused={focused} />
</div>
</div>
{/* <div class="absolute h-full w-full opacity-15 mix-blend-multiply">
<Noise />
</div> */}
<div class="absolute h-full w-full opacity-15 mix-blend-hard-light">
<Noise />
</div>
</>
);
}
function Line({
mode,
focused,
}: {
mode: SL<"Scroll" | "Static">;
focused: Accessor<boolean>;
}) {
const shuffled = shuffle(texts).slice(0, 21);
const joined = shuffled.join(". ");
return (
<div class="select-none whitespace-nowrap">
<TextWrapper mode={mode} focused={focused} joined={joined} />
</div>
);
}
function TextWrapper({
joined,
mode,
focused,
}: {
mode: SL<"Scroll" | "Static">;
focused: Accessor<boolean>;
joined: string;
}) {
const p = createRWS(undefined as HTMLParagraphElement | undefined);
const seconds = createRWS(joined.length * 2);
const wasOnceOn = createRWS(false);
createEffect(() => {
if (!wasOnceOn() && mode.selected() === "Scroll") {
wasOnceOn.set(true);
}
});
// Bug in Safari iOS, not sure where else, works perfectly on Mac OS though
if (!touchScreen) {
onMount(() => {
seconds.set(Math.round(p()!.clientWidth / 15));
});
}
return (
<p
ref={p.set}
class="inline-block px-2 text-[4dvh] font-black uppercase leading-none"
style={{
...(wasOnceOn()
? {
animation: `marquee ${seconds()}s linear infinite`,
"animation-play-state":
focused() && mode.selected() === "Scroll"
? "running"
: "paused",
}
: {}),
}}
>
{joined} {wasOnceOn() ? joined : undefined}
</p>
);
}
function shuffle<T>([...arr]: T[]): T[] {
let m = arr.length;
while (m) {
const i = Math.floor(Math.random() * m--);
[arr[m], arr[i]] = [arr[i], arr[m]];
}
return arr;
}
function Noise() {
return (
<svg
class="size-full"
viewBox="0 0 210 210"
preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg"
>
<filter id="noiseFilter">
<feTurbulence
type="fractalNoise"
baseFrequency="4"
numOctaves="6"
stitchTiles="stitch"
/>
</filter>
<rect width="100%" height="100%" filter="url(#noiseFilter)" />
</svg>
);
}
-51
View File
@@ -1,51 +0,0 @@
import { classPropToString } from "/src/solid/classes";
export function Box({
flex = true,
absolute,
padded = true,
spaced = true,
children,
dark,
classes,
}: {
flex?: boolean;
absolute?: "top" | "bottom";
padded?: boolean;
spaced?: boolean;
dark?: boolean;
classes?: string;
} & ParentProps) {
return (
<div
class={classPropToString([
absolute
? [
"absolute inset-x-0",
absolute === "top" ? "top-0" : "pointer-events-none bottom-0",
]
: "relative",
classes,
])}
>
<div
class={classPropToString([
"pointer-events-auto relative overflow-hidden rounded-full border shadow-md",
])}
style={{
"background-color": "var(--background-color)",
}}
>
<div
class={classPropToString([
flex && "flex w-full",
spaced && "space-x-2",
padded && "p-1.5",
])}
>
{children}
</div>
</div>
</div>
);
}
-31
View File
@@ -1,31 +0,0 @@
import { random } from "/src/scripts/utils/math/random";
export function Button({
onClick,
children,
}: { onClick: VoidFunction } & ParentProps) {
return (
<button
class="group flex w-full flex-1 items-center justify-center rounded-lg px-2 py-1.5 hover:bg-orange-200/20 active:scale-95"
onClick={onClick}
>
{children}
</button>
);
}
export function ButtonRandomChart({ presets }: { presets: Presets }) {
return (
<button
class="inline-flex rounded-md bg-orange-700 bg-opacity-80 px-1.5 py-0.5 font-medium hover:bg-opacity-100 active:scale-95"
onClick={() => {
const randomPreset = random(presets.list);
if (randomPreset) {
presets.select(randomPreset);
}
}}
>
Open a random chart
</button>
);
}
@@ -1,53 +0,0 @@
import { Button } from "./button";
export function Actions({
presets,
fullscreen,
qrcode,
}: {
presets: Presets;
qrcode: RWS<string>;
fullscreen?: RWS<boolean>;
}) {
const ButtonShare = lazy(() =>
import("./buttonShare").then((d) => ({ default: d.ButtonShare })),
);
return (
<div class="flex space-x-1 p-1.5">
<Show when={fullscreen}>
{(fullscreen) => (
<Button
title="Toggle fullscreen"
icon={() =>
fullscreen()()
? IconTablerLayoutSidebarLeftExpand
: IconTablerLayoutSidebarRightExpand
}
onClick={() => {
fullscreen().set((b) => !b);
}}
classes="hidden md:block"
/>
)}
</Show>
<ButtonShare qrcode={qrcode} />
<Button
title="Favorite"
colors={() =>
presets.selected().isFavorite()
? "text-amber-500 bg-amber-500/15 hover:bg-amber-500/30"
: ""
}
icon={() =>
presets.selected().isFavorite()
? IconTablerStarFilled
: IconTablerStar
}
onClick={() => presets.selected().isFavorite.set((b) => !b)}
/>
</div>
);
}
@@ -1,36 +0,0 @@
import { classPropToString } from "/src/solid/classes";
export function Button({
title,
icon,
colors,
onClick,
disabled,
classes,
}: {
title: string;
icon: () => ValidComponent;
colors?: () => string;
onClick: VoidFunction;
disabled?: () => boolean;
classes?: string;
}) {
return (
<button
title={title}
disabled={disabled?.()}
class={classPropToString([
colors?.() || (disabled?.() ? "" : "hover:bg-orange-200/15"),
!disabled?.() && "group",
classes,
"flex-none rounded-lg p-2 disabled:opacity-50",
])}
onClick={onClick}
>
<Dynamic
component={icon()}
class="size-[1.125rem] group-active:scale-90"
/>
</button>
);
}
@@ -1,22 +0,0 @@
import { Button } from "./button";
export function ButtonShare({ qrcode }: { qrcode: RWS<string> }) {
return (
<Button
title="Share"
icon={() => IconTablerShare}
onClick={() =>
import("lean-qr").then(({ generate }) =>
qrcode.set(() =>
generate(document.location.href).toDataURL({
on: [0xff, 0xff, 0xff, 0xff],
off: [0x00, 0x00, 0x00, 0x00],
padX: 0,
padY: 0,
}),
),
)
}
/>
);
}
@@ -1,361 +0,0 @@
import { requestIdleCallbackPossible } from "/src/env";
import { chunkIdToIndex } from "/src/scripts/datasets/resource";
import { createChart } from "/src/scripts/lightweightCharts/create";
import { createSeriesGroup } from "/src/scripts/lightweightCharts/group";
import { setMinMaxMarkers } from "/src/scripts/lightweightCharts/markers";
import {
debouncedUpdateVisiblePriceSeriesType,
updateVisiblePriceSeriesType,
} from "/src/scripts/lightweightCharts/price";
import {
initTimeScale,
setInitialTimeRange,
} from "/src/scripts/lightweightCharts/time";
import { setWhitespace } from "/src/scripts/lightweightCharts/whitespace";
import { SeriesType } from "/src/scripts/presets/enums";
import { colors } from "/src/scripts/utils/colors";
import { debounce } from "/src/scripts/utils/debounce";
import { createSL } from "/src/scripts/utils/selectableList/static";
import { webSockets } from "/src/scripts/ws";
import { classPropToString } from "/src/solid/classes";
import { createRWS } from "/src/solid/rws";
import { RadioGroup } from "../../settings";
export function Chart({
activeDatasets,
activeIds,
charts,
chartsDrawn,
dark,
datasets,
exactRange,
firstChartSetter,
index,
lastActiveIndex,
lastChartIndex,
legendSetter,
preset: presetAccessor,
priceSeriesType,
seriesConfigs,
seriesCount,
}: {
activeDatasets: ReadWriteSignal<ResourceDataset<any, any>[]>;
activeIds: RWS<number[]>;
charts: ReadWriteSignal<
{
chart: RWS<IChartApi | undefined>;
whitespace: RWS<ISeriesApiAny | undefined>;
}[]
>;
chartsDrawn: Accessor<ReadWriteSignal<boolean>[]>;
dark: Accessor<boolean>;
datasets: Datasets;
exactRange: ReadWriteSignal<TimeRange>;
firstChartSetter: Setter<IChartApi | undefined>;
index: Accessor<number>;
lastActiveIndex: Accessor<number | undefined>;
lastChartIndex: Accessor<number>;
legendSetter: Setter<SeriesLegend[]>;
preset: Accessor<Preset>;
priceSeriesType: ReadWriteSignal<PriceSeriesType>;
seriesConfigs: SeriesConfig[];
seriesCount: Accessor<number>;
}) {
const div = createRWS<HTMLDivElement | undefined>(undefined);
const chartIndex = index();
const isDrawn = chartsDrawn()[chartIndex];
const isLastDrawn = createMemo(
() => chartsDrawn().findLastIndex((drawn) => drawn()) === chartIndex,
);
const chartPriceModeKey = `chart-price-mode-${chartIndex}` as const;
const chartPriceMode = createSL(["Linear", "Log"] as const, {
saveable: {
key: chartPriceModeKey,
mode: "localStorage",
},
defaultValue: chartIndex === 0 ? "Log" : "Linear",
});
createEffect(
on([div, () => charts()[chartIndex]], ([div, chartConfig]) => {
if (!div || !chartConfig) return;
const preset = presetAccessor();
const scale = preset.scale;
const chart = createChart({
scale,
element: div,
dark,
});
if (!chart) {
console.log("chart: undefined");
return;
}
const whitespace = setWhitespace(chart, scale);
batch(() => {
chartConfig.chart.set(chart);
chartConfig.whitespace.set(whitespace);
if (chartIndex === 0) {
firstChartSetter(chart);
}
});
const range = exactRange();
setInitialTimeRange({ chart, range });
if (chartIndex === 0) {
initTimeScale({
scale,
chart,
activeIds,
exactRange,
});
if (range) {
updateVisiblePriceSeriesType({
scale,
chart,
priceSeriesType,
timeRange: range,
});
}
}
const chartLegend: SeriesLegend[] = [];
onCleanup(() => {
chartLegend.length = 0;
});
const markerCallback = () =>
setMinMaxMarkers({
scale,
visibleRange: exactRange(),
legendList: chartLegend,
dark,
activeIds: activeIds,
});
const debouncedSetMinMaxMarkers = requestIdleCallbackPossible
? () => requestIdleCallback(markerCallback)
: debounce(
markerCallback,
seriesCount() * 10 + scale === "date" ? 50 : 100,
);
createEffect(on([exactRange, dark], debouncedSetMinMaxMarkers));
if (chartIndex === 0) {
function createPriceSeries(seriesType: PriceSeriesType) {
const datasetPath: AnyDatasetPath = `${scale}-to-price`;
const dataset = datasets.getOrImport(scale, datasetPath);
// Don't trigger reactivity by design
activeDatasets().push(dataset);
const title = "Price";
let seriesConfig: SeriesConfig;
if (seriesType === "Candlestick") {
seriesConfig = {
datasetPath,
title,
seriesType: SeriesType.Candlestick,
};
} else {
seriesConfig = {
datasetPath,
title,
color: colors.white,
};
}
const priceSeries = createSeriesGroup({
scale,
datasets,
index: -1,
activeIds,
seriesConfig,
chart,
chartLegend,
lastActiveIndex,
preset,
disabled: () => priceSeriesType() !== seriesType,
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");
const priceLineLegend = createPriceSeries("Line");
createEffect(() => {
priceCandlestickLegend.visible.set(priceLineLegend.visible());
});
createEffect(() => {
priceLineLegend.visible.set(priceCandlestickLegend.visible());
});
}
[...seriesConfigs].reverse().forEach((seriesConfig, index) => {
const dataset = datasets.getOrImport(scale, seriesConfig.datasetPath);
// Don't trigger reactivity by design
activeDatasets().push(dataset);
createSeriesGroup({
scale,
datasets,
activeIds,
index,
seriesConfig,
chartLegend,
chart,
preset,
lastActiveIndex,
debouncedSetMinMaxMarkers,
dark,
});
});
chartLegend.forEach((legend) => {
createEffect(on(legend.visible, debouncedSetMinMaxMarkers));
});
legendSetter((l) => {
for (let i = 0; i < chartLegend.length; i++) {
l.splice(0, 0, chartLegend[i]);
}
return l;
});
createEffect(() =>
isDrawn.set(() => chartLegend.some((legend) => legend.drawn())),
);
createEffect(() =>
chart.timeScale().applyOptions({
visible: isLastDrawn(),
}),
);
createEffect(() =>
chart.priceScale("right").applyOptions({
mode: chartPriceMode.selected() === "Linear" ? 0 : 1,
}),
);
chart.timeScale().subscribeVisibleLogicalRangeChange((logicalRange) => {
if (!logicalRange) return;
// Must be the chart with the visible timeScale
if (chartIndex === lastChartIndex()) {
debouncedUpdateVisiblePriceSeriesType({
scale,
chart,
logicalRange,
priceSeriesType,
});
}
for (
let otherChartIndex = 0;
otherChartIndex <= lastChartIndex();
otherChartIndex++
) {
if (chartIndex !== otherChartIndex) {
const chart = charts()[otherChartIndex].chart();
chart?.timeScale().setVisibleLogicalRange(logicalRange);
}
}
});
chart.subscribeCrosshairMove(({ time, sourceEvent }) => {
// Don't override crosshair position from scroll event
if (time && !sourceEvent) return;
for (
let otherChartIndex = 0;
otherChartIndex <= lastChartIndex();
otherChartIndex++
) {
const { whitespace: _whitespace, chart: _otherChart } =
charts()[otherChartIndex];
const otherChart = _otherChart();
const whitespace = _whitespace();
if (otherChart && whitespace && chartIndex !== otherChartIndex) {
if (time) {
otherChart.setCrosshairPosition(NaN, time, whitespace);
} else {
// No time when mouse goes outside the chart
otherChart.clearCrosshairPosition();
}
}
}
});
// Trigger reactivity now
activeDatasets.set((l) => l);
}),
);
return (
<div
style={{
height: isLastDrawn() ? "100%" : "calc(100% - 62px)",
}}
class={classPropToString([
isDrawn()
? [
"max-h-full",
// isLastDrawn() ? "mb-[-2px]"
]
: "max-h-0",
"relative h-full min-h-0 w-full cursor-crosshair",
])}
>
<div ref={div.set} class="size-full" />
<Show when={isDrawn()}>
<div class="pointer-events-none absolute left-0 top-0 z-10 flex items-center space-x-2 px-6 text-xs">
<span>
{chartIndex === 0
? ("US Dollars" satisfies Unit)
: presetAccessor().unit}
</span>
<span class="off"></span>
<RadioGroup size="xs" title={chartPriceModeKey} sl={chartPriceMode} />
</div>
</Show>
</div>
);
}
@@ -1,137 +0,0 @@
import { chunkIdToIndex } from "/src/scripts/datasets/resource";
import {
getInitialTimeRange,
setActiveIds,
} from "/src/scripts/lightweightCharts/time";
import { createRWS } from "/src/solid/rws";
import { Chart } from "./chart";
export function Charts({
firstChartSetter,
preset,
datasets,
legendSetter,
dark,
activeIds,
}: {
firstChartSetter: Setter<IChartApi | undefined>;
preset: Accessor<Preset>;
datasets: Datasets;
legendSetter: Setter<SeriesLegend[]>;
dark: Accessor<boolean>;
activeIds: RWS<number[]>;
}) {
const scale = createMemo(() => preset().scale);
const exactRange = createRWS(getInitialTimeRange(scale()));
const priceSeriesType = createRWS<PriceSeriesType>("Candlestick");
const activeDatasets = createRWS([] as ResourceDataset<any, any>[], {
equals: false,
});
const chartSeriesConfigs = createRWS([] as SeriesConfig[][], {
equals: false,
});
const charts = createRWS(
[] as {
chart: RWS<IChartApi | undefined>;
whitespace: RWS<ISeriesApiAny | undefined>;
}[],
{
equals: false,
},
);
const lastChartIndex = createMemo(() => chartSeriesConfigs().length - 1);
const seriesCount = createMemo(() =>
chartSeriesConfigs().reduce(
(acc, l) => (acc += l.length),
1, // Because of price series
),
);
const lastActiveIndex = createMemo(() => {
const last = activeIds().at(-1);
return last !== undefined
? chunkIdToIndex(preset().scale, last)
: undefined;
});
const chartsDrawn = createMemo(() =>
chartSeriesConfigs().map((_) => createRWS(true)),
);
createEffect(
on([activeIds, activeDatasets], ([ids, activeDatasets]) => {
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
for (let j = 0; j < activeDatasets.length; j++) {
activeDatasets[j].fetch(id);
}
}
}),
);
createEffect(
on(preset, (preset) => {
const scale = preset.scale;
exactRange.set(getInitialTimeRange(scale));
chartSeriesConfigs.set(
[preset.top || [], preset.bottom].flatMap((list) =>
list ? [list] : [],
),
);
charts.set(() =>
new Array(chartSeriesConfigs().length).fill(undefined).map(() => ({
chart: createRWS(undefined as IChartApi | undefined),
whitespace: createRWS(undefined as ISeriesApiAny | undefined),
})),
);
setActiveIds({
exactRange: exactRange(),
activeIds,
});
legendSetter(() => []);
}),
);
onCleanup(() => {
firstChartSetter(undefined);
charts().map(({ chart, whitespace }) => {
chart()?.remove();
chart.set(undefined);
whitespace.set(undefined);
});
});
return (
<For
each={chartSeriesConfigs().filter(
(configs, index) => index === 0 || configs.length !== 0,
)}
>
{(seriesConfigs, index) => (
<Chart
activeDatasets={activeDatasets}
activeIds={activeIds}
charts={charts}
chartsDrawn={chartsDrawn}
dark={dark}
datasets={datasets}
exactRange={exactRange}
firstChartSetter={firstChartSetter}
index={index}
lastActiveIndex={lastActiveIndex}
lastChartIndex={lastChartIndex}
legendSetter={legendSetter}
preset={preset}
priceSeriesType={priceSeriesType}
seriesConfigs={seriesConfigs}
seriesCount={seriesCount}
/>
)}
</For>
);
}
@@ -1,182 +0,0 @@
import { chunkIdToIndex } from "/src/scripts/datasets/resource";
import { createRWS } from "/src/solid/rws";
import { Scrollable } from "../../scrollable";
const transparency = "44";
export function Legend({
scale,
legend: legendList,
dark,
activeIds,
}: {
scale: Accessor<ResourceScale>;
legend: Accessor<SeriesLegend[]>;
dark: Accessor<boolean>;
activeIds: Accessor<number[]>;
}) {
const hovered = createRWS<SeriesLegend | undefined>(undefined);
let toggle = false;
return (
<Scrollable classes="items-center gap-7">
<For each={legendList()}>
{(legend) => {
createEffect(() => {
const range = activeIds();
for (let i = 0; i < range.length; i++) {
const id = range[i];
const initialColors = {} as any;
const darkenColors = {} as any;
const chunkIndex = chunkIdToIndex(scale(), id);
const series = legend.seriesList.at(chunkIndex)?.();
if (!series) return;
const seriesOptions = series.options();
if (!seriesOptions) continue;
Object.entries(seriesOptions).forEach(([k, v]) => {
if (k.toLowerCase().includes("color") && v) {
if (typeof v === "string" && !v.startsWith("#")) {
return;
}
v = (v as string).substring(0, 7);
initialColors[k] = v;
darkenColors[k] = `${v}${transparency}`;
} else if (k === "lastValueVisible" && v) {
initialColors[k] = true;
darkenColors[k] = false;
}
});
createEffect((wasHovering: boolean) => {
const hoveredLegend = hovered();
const hovering = !!hovered();
if (wasHovering === hovering) {
return hovering;
}
if (hoveredLegend) {
if (hoveredLegend.title !== legend.title) {
series.applyOptions(darkenColors);
}
} else {
series.applyOptions(initialColors);
}
return hovering;
}, false);
}
});
let previousClickTime: number = 0;
return (
<Show when={!legend.disabled()}>
<div class="flex flex-none items-center space-x-1.5">
<button
title="Click to toggle, double click to focus"
onMouseEnter={() => legend.visible() && hovered.set(legend)}
onMouseLeave={() => hovered.set(undefined)}
onTouchStart={() => legend.visible() && hovered.set(legend)}
onTouchEnd={() => hovered.set(undefined)}
onClick={() => {
const currentClickTime = new Date().getTime();
if (currentClickTime - previousClickTime > 300) {
legend.visible.set((visible) => !visible);
} else {
legendList().forEach((_legend) => {
if (_legend.title != legend.title) {
_legend.visible.set(toggle);
}
});
legend.visible.set(true);
toggle = !toggle;
}
previousClickTime = currentClickTime;
if (legend.visible()) {
hovered.set(legend);
} else {
hovered.set(undefined);
}
}}
class="flex flex-none items-center space-x-1.5 active:scale-[0.975]"
>
<span
class="flex size-3 flex-col overflow-hidden rounded-full"
style={{
opacity: legend.visible() ? 1 : 0.5,
}}
>
<For
each={
Array.isArray(legend.color)
? legend.color.map((c) => c(dark))
: [legend.color(dark)]
}
>
{(color) => (
<span
class="w-full flex-1"
style={{
"background-color": color,
}}
/>
)}
</For>
</span>
<span
class="text-sm font-medium decoration-wavy decoration-[1.5px]"
style={{
"text-decoration-line": !legend.visible()
? "line-through"
: undefined,
"text-decoration-color": "var(--color)",
color: !legend.visible() ? "var(--off-color)" : undefined,
}}
>
{legend.title}
</span>
</button>
<Show when={legend.dataset.url}>
{(url) => (
<a
title="Dataset"
class="inline-flex size-4 flex-col overflow-hidden active:scale-[0.975]"
onClick={(event) => {
event.stopPropagation();
}}
href={url()}
target={
url()?.startsWith("/") || url()?.startsWith("http")
? "_blank"
: undefined
}
>
<IconTablerDownload />
</a>
)}
</Show>
</div>
</Show>
);
}}
</For>
</Scrollable>
);
}
@@ -1,343 +0,0 @@
import { GENESIS_DAY } from "/src/scripts/lightweightCharts/whitespace";
import { ONE_DAY_IN_MS } from "/src/scripts/utils/time";
import { classPropToString } from "/src/solid/classes";
import { createRWS } from "/src/solid/rws";
import { Box } from "../../box";
import { Scrollable } from "../../scrollable";
const DELAY = 1;
const MULTIPLIER = DELAY / 1000;
const LEFT = -1;
const RIGHT = 1;
export function TimeScale({
scale,
firstChart,
}: {
scale: Accessor<ResourceScale>;
firstChart: RWS<IChartApi | undefined>;
}) {
const today = new Date();
const disabled = createMemo(() => !firstChart());
const scrollDirection = createRWS(0);
const timeScale = createMemo(() => {
const chart = firstChart();
if (!chart) return undefined;
return chart.timeScale();
});
let interval: number | undefined;
function createScrollLoop() {
clearInterval(interval);
const direction = scrollDirection();
if (!direction) return;
// @ts-ignore
interval = setInterval(() => {
const time = timeScale();
if (!time) return;
const range = time.getVisibleLogicalRange();
if (!range) return;
const speed = (range.to - range.from) * MULTIPLIER * direction;
// @ts-ignore
range.from += speed;
// @ts-ignore
range.to += speed;
time.setVisibleLogicalRange(range);
}, DELAY);
}
onCleanup(() => clearInterval(interval));
return (
<Box padded={false} spaced={false} classes="short:hidden text-sm">
<div class="flex items-center p-1.5">
<Button
square
disabled={disabled}
onClick={() => {
scrollDirection.set((v) => (v === LEFT ? 0 : LEFT));
createScrollLoop();
}}
>
<Show
when={scrollDirection() === LEFT}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-5"
>
<path d="M8.5 4.75a.75.75 0 0 0-1.107-.66l-6 3.25a.75.75 0 0 0 0 1.32l6 3.25a.75.75 0 0 0 1.107-.66V8.988l5.393 2.921A.75.75 0 0 0 15 11.25v-6.5a.75.75 0 0 0-1.107-.66L8.5 7.013V4.75Z" />
</svg>
}
>
<IconTablerPlayerPauseFilled class="size-5" />
</Show>
</Button>
</div>
<div class="mr-2 border-l" />
<Scrollable classes="space-x-2">
<Switch>
<Match when={scale() === "date"}>
<Button
minWidth
disabled={disabled}
onClick={() => setTimeScale({ scale: scale(), timeScale })}
>
All Time
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 7 })
}
>
1 Week
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 30 })
}
>
1 Month
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 3 * 30 })
}
>
3 Months
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 6 * 30 })
}
>
6 Months
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({
scale: scale(),
timeScale,
days: Math.ceil(
(today.getTime() -
new Date(`${today.getUTCFullYear()}-01-01`).getTime()) /
ONE_DAY_IN_MS,
),
})
}
>
Year To Date
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 365 })
}
>
1 Year
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 2 * 365 })
}
>
2 Years
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 4 * 365 })
}
>
4 Years
</Button>
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, days: 8 * 365 })
}
>
8 Years
</Button>
<For
each={new Array(
new Date().getFullYear() - new Date("2009-01-01").getFullYear(),
)
.fill(0)
.map((_, index) => index + 2009)
.reverse()}
>
{(year) => (
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), timeScale, year })
}
>
{year}
</Button>
)}
</For>
</Match>
<Match when={scale() === "height"}>
<Button minWidth disabled={() => true} onClick={() => {}}>
24h
</Button>
<Button minWidth disabled={() => true} onClick={() => {}}>
48h
</Button>
<For
each={new Array(9)
.fill(0)
.flatMap((_, i) => [i, i + 0.5])
.reverse()}
>
{(i) => (
<Button
minWidth
disabled={disabled}
onClick={() =>
setTimeScale({
scale: scale(),
timeScale,
range: {
from: i * 100_000,
to: (i + 0.5) * 100_000,
},
})
}
>
{`${100 * (i + 0.5)}k`}
</Button>
)}
</For>
</Match>
</Switch>
</Scrollable>
<div class="ml-2 border-l" />
<div class="flex items-center p-1.5">
<Button
square
disabled={disabled}
onClick={() => {
scrollDirection.set((v) => (v === RIGHT ? 0 : RIGHT));
createScrollLoop();
}}
>
<Show
when={scrollDirection() === RIGHT}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-5"
>
<path d="M2.53 3.956A1 1 0 0 0 1 4.804v6.392a1 1 0 0 0 1.53.848l5.113-3.196c.16-.1.279-.233.357-.383v2.73a1 1 0 0 0 1.53.849l5.113-3.196a1 1 0 0 0 0-1.696L9.53 3.956A1 1 0 0 0 8 4.804v2.731a.992.992 0 0 0-.357-.383L2.53 3.956Z" />
</svg>
}
>
<IconTablerPlayerPauseFilled class="size-5" />
</Show>
</Button>
</div>
</Box>
);
}
function Button({
onClick,
disabled,
children,
minWidth,
square,
}: ParentProps & {
onClick: VoidFunction;
disabled?: Accessor<boolean>;
minWidth?: boolean;
square?: boolean;
}) {
return (
<button
class={classPropToString([
minWidth && "min-w-20",
!disabled?.() && "active:scale-95",
"flex-shrink-0 flex-grow whitespace-nowrap p-1.5 font-medium",
])}
onClick={onClick}
disabled={disabled?.()}
>
{children}
</button>
);
}
function setTimeScale({
timeScale,
scale,
days,
year,
range,
}: {
timeScale: Accessor<ITimeScaleApi<Time> | undefined>;
scale: ResourceScale;
days?: number;
year?: number;
range?: { from: number; to: number };
}) {
if (scale === "date") {
let from = new Date();
let to = new Date();
if (year) {
from = new Date(`${year}-01-01`);
to = new Date(`${year}-12-31`);
} else if (days) {
from.setDate(from.getUTCDate() - days);
} else {
from = new Date(GENESIS_DAY);
}
timeScale()?.setVisibleRange({
from: (from.getTime() / 1000) as Time,
to: (to.getTime() / 1000) as Time,
});
} else if (scale === "height") {
if (range) {
timeScale()?.setVisibleRange({
from: range.from as Time,
to: range.to as Time,
});
}
}
}
@@ -1,15 +0,0 @@
export function Title({ presets }: { presets: Presets }) {
return (
<div
class="flex-0 -mx-6 -mb-4 flex items-center overflow-x-auto px-6 pb-4 pt-1"
style={{
"scrollbar-width": "thin",
}}
>
<div class="flex-1 whitespace-nowrap">
<h1 class="text-lg font-bold md:text-xl">{presets.selected().title}</h1>
<h3 class="off">{`/ ${[...presets.selected().path.map(({ name }) => name), presets.selected().name].join(" / ")}`}</h3>
</div>
</div>
);
}
-78
View File
@@ -1,78 +0,0 @@
import { classPropToString } from "/src/solid/classes";
import { createWasIdleAccessor } from "/src/solid/idle";
import { createRWS } from "/src/solid/rws";
import { Box } from "../box";
import { Actions } from "./components/actions";
import { Legend } from "./components/legend";
import { TimeScale } from "./components/timeScale";
import { Title } from "./components/title";
export function ChartFrame({
presets,
datasets,
hide,
qrcode,
standalone,
fullscreen,
dark,
}: {
presets: Presets;
hide?: Accessor<boolean>;
qrcode: RWS<string>;
datasets: Datasets;
fullscreen?: RWS<boolean>;
dark: Accessor<boolean>;
standalone: boolean;
}) {
const legend = createRWS<SeriesLegend[]>([], { equals: false });
const firstChart = createRWS<IChartApi | undefined>(undefined);
const scale = createMemo(() => presets.selected().scale);
const activeIds = createRWS([] as number[], { equals: false });
const wasIdle = createWasIdleAccessor();
const Charts = lazy(() =>
import("./components/charts").then((d) => ({ default: d.Charts })),
);
return (
<div
class={classPropToString([
"frame flex size-full min-h-0 flex-1 flex-col",
])}
style={{
display: (hide ? hide() : false) ? "none" : undefined,
}}
>
<Title presets={presets} />
<div class="border-t" />
<Legend legend={legend} scale={scale} activeIds={activeIds} dark={dark} />
<div class="!mt-4 flex min-h-0 flex-1 flex-col">
<div class="relative -ml-6 -mr-8 flex min-h-0 flex-1 flex-col pb-2">
<div class="pointer-events-none absolute inset-x-0 top-0 z-10 h-6 w-full flex-none bg-gradient-to-t from-transparent to-[var(--background-color)]" />
<Show when={wasIdle()}>
<Charts
firstChartSetter={firstChart.set}
datasets={datasets}
legendSetter={legend.set}
preset={presets.selected}
dark={dark}
activeIds={activeIds}
/>
</Show>
<div class="pointer-events-none absolute bottom-9 right-0 z-10 h-6 w-[80px] flex-none bg-gradient-to-b from-transparent to-[var(--background-color)]" />
<div class="pointer-events-none absolute inset-y-0 left-0 z-10 h-full w-6 flex-none bg-gradient-to-r from-[var(--background-color)] from-5% to-transparent" />
</div>
<TimeScale firstChart={firstChart} scale={scale} />
</div>
</div>
);
}
-25
View File
@@ -1,25 +0,0 @@
export function Counter({
count,
name,
setRef,
}: {
count: () => number;
name: string;
setRef?: Setter<HTMLDivElement | undefined>;
}) {
return (
<div
ref={setRef}
class="text-orange-100/75"
style={{
"border-style": count() ? "dashed" : "none",
}}
>
Counted{" "}
<span class="font-medium text-orange-400/75">
{count().toLocaleString("en-us")}
</span>{" "}
{name}
</div>
);
}
@@ -1,43 +0,0 @@
import { Line } from "../../line";
export function File({
id,
name,
icon,
active,
depth,
onClick,
favorite,
visited,
}: {
id: string;
name: string;
icon: JSXElement;
active: Accessor<boolean>;
depth: number;
onClick: VoidFunction;
favorite: Accessor<boolean>;
visited: Accessor<boolean>;
}) {
const tail = createMemo(() =>
favorite() ? (
// <span class="p-1">
<IconTablerStarFilled class="orange size-3" />
) : // </span>
!visited() ? (
<span class="ml-1.5 rounded-full bg-orange-500 p-[3px] text-transparent" />
) : undefined,
);
return (
<Line
id={id}
depth={depth}
active={active}
name={name}
icon={() => icon}
onClick={onClick}
tail={tail}
/>
);
}

Some files were not shown because too many files have changed in this diff Show More