Compare commits

...

93 Commits

Author SHA1 Message Date
k 7d211f74d1 website: fixes 2024-09-19 21:48:54 +02:00
k 0f95d41785 website: rm history log 2024-09-19 21:11:37 +02:00
k 6389b530d9 changelog: hide broken image links 2024-09-19 21:10:11 +02:00
k 412769ff03 general: fixes 2024-09-19 21:03:50 +02:00
k d2349741f7 changelog: update 2024-09-19 11:24:13 +02:00
k 821bf8d63a readme: update 2024-09-19 11:01:08 +02:00
k 7b296e4863 parser: price fetch fixes 2024-09-19 10:49:26 +02:00
k 1acfcf088c readme: update 2024-09-18 20:15:32 +02:00
k e9680afdff readme: update 2024-09-18 19:31:33 +02:00
k 9695f12322 server: create in dir if missing 2024-09-18 18:43:36 +02:00
k 4060b7457b index: update geyser url 2024-09-18 18:35:19 +02:00
k a68344959d website: fix pathname on first load 2024-09-18 18:25:27 +02:00
k 41638d10bf server: add date-modified to datasets 2024-09-18 17:58:33 +02:00
k 9b4e166608 website: fixes 2024-09-18 16:56:55 +02:00
k 52a65fcad1 general: update 2024-09-18 00:14:53 +02:00
k da7c114d41 general: snapshot 2024-09-17 10:57:21 +02:00
k 62edee0860 readme 2024-09-17 00:22:51 +02:00
k 22ba5e7c94 assets: update 2024-09-16 23:45:24 +02:00
k 48e9a9c7dd website: add sw 2024-09-16 19:17:19 +02:00
k 9dbffb0c93 global: snapshot 2024-09-16 13:17:18 +02:00
k f95eb0f1c9 script: snapshot 2024-09-14 13:40:58 +02:00
k f3197c1af7 general: fixes 2024-09-14 11:47:02 +02:00
k 59f04c96c5 readme: update 2024-09-13 23:47:25 +02:00
k bf2034b80c global: snapshot 2024-09-13 22:59:20 +02:00
k deffaef2b5 rename: GUIDELINES to CONTRIBUTING 2024-09-11 22:52:53 +02:00
k 157ec003b7 remove: app 2024-09-11 22:47:26 +02:00
k ba4021ad73 global: snapshot 2024-09-10 23:15:13 +02:00
k 5edb8111a2 app: html version almost done 2024-09-10 09:39:06 +02:00
k e206b40468 general: snapshot 2024-08-26 01:23:48 +02:00
k 6ebd9320db app: folder reorg + tradingview notice 2024-08-08 17:11:58 +02:00
k 597a750fff app: unignore paths.d.ts to allow to run app without needing the server first 2024-08-08 09:48:36 +02:00
k 1273da6e71 changelog: update 2024-08-08 09:25:00 +02:00
k eb9b57eef4 parser: split txid_to_txdata db 2024-08-08 09:21:59 +02:00
k 5aaa05d579 parser: rm heed module 2024-08-06 19:52:59 +02:00
k 07abb0840b global: rm heed 2024-08-06 19:51:40 +02:00
k b8064510e3 parser: address_to_address_index: fix bad copy pasta 2024-08-05 12:07:20 +02:00
k a88d84e6e6 changelog: update 2024-08-05 11:58:17 +02:00
k c3c8f16793 parser: fix rpc call 2024-08-05 11:55:08 +02:00
k ce1fed8c16 parser: rm multisig db 2024-08-05 11:47:52 +02:00
k 9a8f5edd58 parser: fix binance time 2024-08-05 11:23:41 +02:00
k 992d45c8af server: update run.sh 2024-08-05 10:42:28 +02:00
k c646d6dc60 global: datasets compression via zstd 2024-08-05 00:44:46 +02:00
k 9067c28d24 parser: switch to biter 2024-08-03 15:17:57 +02:00
k afacea3fbb parser: reactivate bkp url for price fetching 2024-07-26 17:07:19 +02:00
k b68b016091 release: v0.3.0 2024-07-26 00:59:00 +02:00
k f1f4ad2188 global: fix: bugs 2024-07-26 00:44:17 +02:00
k d3d5e7f8d7 general: snapshot 2024-07-25 14:43:20 +02:00
k 0f8d7d5fe2 readme: add bkp api 2024-07-24 00:18:55 +02:00
k 63855e93a1 changelog: update v0.3.0 screenshot 2024-07-24 00:15:35 +02:00
k 07c1f5ab76 changelog: add v0.3.0 screenshot 2024-07-24 00:13:01 +02:00
k 4cd605fd34 global: update versions 2024-07-24 00:09:09 +02:00
k 8f5f28ede6 app: charts: add unit and price mode switch 2024-07-24 00:05:18 +02:00
k bf169d6954 app: add ivo to donators 2024-07-23 08:44:52 +02:00
k 1934c4bfda price: rm folder 2024-07-22 23:40:15 +02:00
k 5a7050df02 app: add chart scroll buttons 2024-07-22 19:06:58 +02:00
k 9871fdffc9 parser: add auto fetch price from main instance 2024-07-22 15:58:02 +02:00
k 232276d106 app: add height datasets 2024-07-22 11:08:58 +02:00
k 8b08a82f07 parser: add recap dataset 2024-07-21 22:59:54 +02:00
k 180d044f5d app: add SOPR 2024-07-21 00:51:36 +02:00
k 5611459f03 server: fix port 2024-07-20 23:18:49 +02:00
k 6eb4b51168 parser: cleanup 2024-07-20 23:15:36 +02:00
k a145b35ad1 general: snapshot 2024-07-20 23:13:41 +02:00
k d8a5b4a2e6 price: update 2024-07-20 13:32:58 +02:00
k 1f9d1542f1 parser: fix utxo panic after soft reset 2024-07-18 12:27:24 +02:00
k 4d23fdef61 general: snapshot 2024-07-18 09:16:18 +02:00
k fb978211ae price: push new 2024-07-16 11:48:41 +02:00
k 4fd67ebd99 app: add address size in sidebar when needed 2024-07-15 19:59:44 +02:00
k 0c899b2c16 parser: fix config file creation and remove panic 2024-07-15 19:43:37 +02:00
k 1be22713f9 parser: create config.toml if needed 2024-07-15 19:07:00 +02:00
k ad51edbe07 parser: setup clap 2024-07-15 18:52:29 +02:00
k 91f2427b44 app: add random chart button 2024-07-15 08:54:55 +02:00
k fbbb0920c5 parser: cohort ratio name changes 2024-07-13 00:40:47 +02:00
k 66bca200b4 parser: cointime forgot to add compute 2024-07-12 20:46:11 +02:00
k 5f11f15fe1 global: cointime ratios 2024-07-12 20:39:27 +02:00
k 96a50dd09a global: snapshot 2024-07-12 19:31:21 +02:00
k dcf605aa69 parser: percentiles fixed 2024-07-12 19:06:52 +02:00
k 6c7bd2a63a parser: percentiles 2024-07-12 18:08:27 +02:00
k 85835ac1d3 parser: add min date and height for percentile datasets 2024-07-12 17:38:22 +02:00
k 61038b07f9 parser: trying to fix ratio smas 2024-07-12 13:55:44 +02:00
k 68700925b0 parser: add 0 and 1 constant datasets 2024-07-12 12:55:46 +02:00
k 46f8e3bafd app: lazy load lean-qr 2024-07-12 12:47:05 +02:00
k 9077fee4d6 parser: add ratio for price smas 2024-07-12 12:02:57 +02:00
k 35fd5054aa general: snapshot 2024-07-12 08:35:41 +02:00
k 350c835873 parser: improve error message when price cannot be found 2024-07-10 21:34:55 +02:00
k 707ed7ec26 parser: update readme 2024-07-10 20:56:36 +02:00
k e159f18bfc parser: add node.args to git ignore 2024-07-10 20:51:27 +02:00
k 2308fa173a parser: allow node args 2024-07-10 20:51:03 +02:00
k 4a82ee0b05 parser: added ratio and co datasets 2024-07-10 18:34:01 +02:00
k 6976f5af0f general: snapshot 2024-07-10 18:33:24 +02:00
k 59cb524226 server: remove cloudflared restart from run script 2024-07-10 13:20:45 +02:00
k 37e1d2ba5b changelog: add mempool links 2024-07-08 20:47:36 +02:00
k 6c21e970aa changelog: update v0.2.0 date 2024-07-08 20:46:17 +02:00
k d3a4e917fb assets: add v0.2.0 screenshot 2024-07-08 20:43:56 +02:00
467 changed files with 27713 additions and 24404 deletions
+43 -4
View File
@@ -1,7 +1,46 @@
# Mac OS
.DS_Store
/datasets
/datasets2
/datasets_*
# To do
/charts
TODO.md
# Builds
dist
target
# I/O
in
out
.log
/datasets
/price
# Sync
.stfolder
# Copies
*\ copy*
# Ignored
ignore
# Scripts
/start-node.sh
# Editors
.vscode
.zed
# Configs
config.toml
# Flamegraph
flamegraph/
flamegraph.svg
# Benchmarks
benches
# Snapshots
snapshots*/
+144 -4
View File
@@ -1,13 +1,149 @@
# Changelog
## v. 0.2.0 | WIP
<!--
## v. 0.X.Y | WIP
![Image of the kibō Web App version 0.X.Y](./assets/v0.X.Y.jpg)
-->
## v. 0.4.1 | WIP
<!-- ![Image of the kibō Web App version 0.4.1](./assets/v0.4.1.jpg) -->
## Website
- Fixed service worker not passing 304 (not modified) response and instead serving cached responses
- Fixed history not being properly registered
- Fixed prices on charts not having a wide enough background due to the font not being fully loaded during the creation of the chart
- Fixed window being moveable on iOS when in standalone mode when it shouldn't be
## Server
- Fixed links in several places missing the `/api` part and thus not working
## v. 0.4.0 | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
![Image of the kibō Web App version 0.4.0](./assets/v0.4.0.jpg)
### Brand
- **Satonomics** is now **kibō** 🎉
### Website
- Complete redesign of the website
- Rewrote the whole application and removed `node`/`npm`/`pnpm` dependencies in favor for pure `HTML`/`CSS`/`Javascript`
- Website is now served by the server
- Added Trading View attribution link to the settings frame and file in the lightweight charts folder
- Many other changes
### Parser
- Changed the block iterator from a custom version of [bitcoin-explorer](https://crates.io/crates/bitcoin-explorer) to the homemade [biter](https://crates.io/crates/biter) which allows the parser to run alongside `bitcoind`
- Added datasets compression thanks to [zstd](https://crates.io/crates/zstd) to reduce disk usage
- Use the Bitcoin RPC server for various calls instead of running cli commands and then parsing the JSON from the output
- **Important database changes that will need a full rescan**:
- Changed databases page size from 1MB to 4KB for improved disk usage
- Split txid_to_tx_data database in 4096 chunks (from 256) for improved disk usage
- Split address_index_to_X databases to chunks of 25_000 instead of 50_000
- Removed local Multisig database
- Updated the config, run with `-h` to see possible args
- Moved outputs from `/target/outputs` to `/out` to allow to run commands like `cargo clean` without side effects
- Various first run fixes
- Added to `-h` which arguments are saved, which is all of them at the time of writing
### Server
- Updated the code to support compressed binaries
- Added serving of the website
- Improved `Cache-Control` behavior
## v. 0.3.0 | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
![Image of the Satonomics Web App version 0.3.0](./assets/v0.3.0.jpg)
### Parser
- Global
- Improved self-hosting by:
- Fixing an incredibly annoying bug that made the program panic because of a wrong utxo/address durable state after a or many new datasets were added/changed after a first successful parse of the chain
- Fixing a bug that would crash the program if launched for the first time ever
- Auto fetch prices from the main Satonomics instance if missing instead of only trying Kraken's and Binance's API which are limited to the last 16 hours
- Merged the core of `HeightMap` and `DateMap` structs into `GenericMap`
- Added `Height` struct and many others
- Reorganized outputs of both the parser and the server for ease of use and easier sync compatibility
- CLI
- Added an argument parser for improved UX with several options
- Datasets
- Added the following datasets for all entities:
- Value destroyed
- Value created
- Spent Output Profit Ratio (SOPR)
- Added the following ratio datasets and their variations to all prices {realized, moving average, any cointime, etc}:
- Market Price to {X}
- Market Price to {X} Ratio
- Market Price to {X} Ratio 1 Week SMA
- Market Price to {X} Ratio 1 Month SMA
- Market Price to {X} Ratio 1 Year SMA
- Market Price to {X} Ratio 1 Year SMA Momentum Oscillator
- Market Price to {X} Ratio 99th Percentile
- Market Price to {X} Ratio 99.5th Percentile
- Market Price to {X} Ratio 99.9th Percentile
- Market Price to {X} Ratio 1st Percentile
- Market Price to {X} Ratio 0.5th Percentile
- {X} 1% Top Probability
- {X} 0.5% Top Probability
- {X} 0.1% Top Probability
- {X} 1% Bottom Probability
- {X} 0.5% Bottom Probability
- {X} 0.1% Bottom Probability
- Added block metadatasets and their variants (raw/sum/average/min/max/percentiles):
- Block size
- Block weight
- Block VBytes
- Block interval
- Price
- Improved error message when price cannot be found
### App
- General
- Added chart scroll button for nice animations à la Wicked
- Added scale mode switch (Linear/Logarithmic) at the bottom right of all charts
- Added unit at the top left of all charts
- Added a backup API in case the main one fails or is offline
- Complete redesign of the datasets object
- Removed import of routes in JSON in favor for hardcoded typed routes in string format which resulted in:
- \+ A much lighter app
- \+ Better Lighthouse score
- \- Slower Typescript server
- Fixed datasets with null values crashing their fetch function
- Added a 'Go to a random chart' button in several places
- Chart
- Fixed series color being set to default ones after hovering the legend
- Fixed chart starting showing candlesticks and quickly switching to a line when it should've started directly with the line
- Separated the QRCode generator library from the main chunk and made it imported on click
- Fixed timescale changing on small screen after changing charts
- Folders
- Added the size in the "filename" of address cohorts grouped by size
- Favorites
- Added a 'favorite' and 'unfavorite' button at the bottom
- Settings
- Removed the horizontal scroll bar which was unintended
### Server
- Run file
- Only run with a watcher if `cargo watch` is available
- Removed id_to_path file in favor for only `paths.d.ts` in `app/src/types`
## v. 0.2.0 | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
![Image of the Satonomics Web App version 0.2.0](./assets/v0.2.0.jpg)
### App
- General
- Added height datasets and many optimizations to make them usable but only available on desktop and tablets for now
- Added the height version of all datasets and many optimizations to make them usable but only available on desktop and tablets for now
- Added a light theme
- Charts
- Added split panes in order to have the vertical axis visible for all datasets
@@ -36,7 +172,7 @@
- Fixed ulimit only being run in Mac OS instead of whenever the program is detected
## v. 0.1.1 | 849240 - 2024/06/24
## v. 0.1.1 | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
![Image of the Satonomics Web App version 0.1.1](./assets/v0.1.1.jpg)
@@ -86,6 +222,10 @@
- Deleted old price datasets and their backups
## v. 0.1.0 | 848642 - 2024/06/19
## v. 0.1.0 | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
![Image of the Satonomics Web App version 0.1.0](./assets/v0.1.0.jpg)
## v. 0.0.X | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
![Image of the Satonomics Web App version 0.0.X](./assets/v0.0.X.jpg)
+8
View File
@@ -0,0 +1,8 @@
# Guidelines
## Parser
- Avoid floats as much as possible
- Use structs like `WAmount` and `Price` for calculations
- **Only** use `WAmount.to_btc()` when inserting or computing inside a dataset. It is **very** expensive.
- No `Arc`, `Rc`, `Mutex` even from third party libraries, they're slower
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 Satonomics
Copyright (c) 2024 kibō
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+144 -57
View File
@@ -1,92 +1,179 @@
# SATONOMICS
<p align="center">
<a href="https://kibo.money" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-full-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-full-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-full-light.svg" width="300" height="auto" style="max-width: 100%;">
</picture>
</a>
</p>
![Image of the Satonomics Web App](./assets/latest.jpg)
<p align="center">
<span>Bitcoin is our only <b><i>hope</i></b> for a better future.</span>
</p>
## Description
Satonomics is a better, FOSS, Bitcoin-only, self-hostable Glassnode.
**kibō** (*hope* in japanese, formerly Satonomics) is a suite of tools that aims to help you understand Bitcoin's various dynamics. To do that, there is a wide number of charts and datasets with a scale by date but also by height free for you to explore. Which allows you to verify an incredible number of things, from the number of UTXOs to the repartition of the supply between different groups over time, with many things in between and it's all made possible thanks to Bitcoin's transparency. Whether you're an enthusiast, a researcher, a miner, an analyst, a trader, a skeptic or just curious, there is something new to learn for everyone !
While [mempool.space](https://mempool.space) gives a very micro view of the network where you can follow the journey of any address, this tool is the exact opposite and very complimentary by giving you a much more global/macro view of the flow and various dynamics of the network via thousands of charts.
While it's not the first tool trying to solve this problem, it's the first that is completely free, open-source and self-hostable. Which is very important as, just like for Bitcoin itself, I strongly believe that everyone should have access to this kind of data.
To promote even more transparency and trust in the network, this project is committed to making on-chain data accessible and verifiable by all, no matter your intentions or financial situation. That is why, the whole project is completely free, from code to services, including a real-time API with thousands and thousands of routes which can be used at will.
If you are a user of [mempool.space](https://mempool.space), you'll find this to be very complimentary, as it's a global and macro view of the chain over time instead.
**Having anyone be able to easily do a health-check of the network is incredibly important and should be wanted by every single bitcoiner.**
If we want the world to move towards and, in the end, to be on a Bitcoin standard, we must have tools like this at our disposal.
## Donations
This project was started as an answer to the outrageous pricing from Glassnode (and their third tier starting at $833.33/month !).
But it is a lot of work and has been worked on **full-time since November of 2023** and has also been operational since then without any ads.
**At the time of writing (2024-09-12), this project has made around 2,200,000 sats, which is around $1300 or $120/month. While I'm very grateful for all donations, it's sadly unsustainable.**
So if you find this project useful, [please send some sats](https://geyser.fund/project/kibo/), it would be really appreciated.
If you're a potential sponsor, feel free to contact me in Nostr !
[Geyser Fund](https://geyser.fund/project/kibo/)
## Warning
This project is in a very early stage. The web app will have bugs, the API might break and the data can definitely to be false or slightly false.
This project is still in an early stage. Until more people look at the code and check the various computations in it, the datasets might be, in the worst case, completely false.
## Instances
Web App:
- [app.satonomics.xyz](https://app.satonomics.xyz)
API:
- [api.satonomics.xyz](https://api.satonomics.xyz)
- [kibo.money](https://kibo.money)
- [backup.kibo.money](https://backup.kibo.money)
## Structure
- `parser`: The backbone of the project, it does most of the work by parsing and then computing datasets from the timechain.
- `server`: A small server which automatically creates routes to access through an API all created datasets.
- `app`: A web app which displays the generated datasets in various charts and dashboards.
- `parser`: The backbone of the project, it does most of the work by parsing and then computing datasets from the timechain
- `website`: A web app which displays the generated datasets in various charts
- `server`: A small server which will serve both the website and the computed datasets via an API
## Goals / Philosophy
## Roadmap
Adjectives that describe what this project is or strives to be, in no particular order:
- **More Datasets/Charts**
- **Simulations**
- **Dashboards**
- **Nostr integration**
- **API Documentation**
- **Descriptions**
- **Start9 support**
- **Best**: Replace Glassnode as the go to
- **Diverse**: Have as many charts/datasets as possible and something for everyone
- **Free**: Is and always will be completely free
- **Open**: With a very permissive license
- **Trustless**: You can verify and see exactly how each dataset is computed
- **Independent**: Only one, easily swappable, dependency (Price API)
- **Educational**: By providing many datasets that can be used to describe how Bitcoin works and why
- **Timeless**: Be relevant and usable 10 years from now by being independent and not do address tagging
- **Sovereign**: Be self-hostable on accessible hardware
- **Versatile**: You can view the data in charts, you can download the data, you can fetch the data via an API
- **Accessible**: Free Website and API with all the datasets for everyone
## Setup
## Milestones
### Requirements
Big features that are planned, in no particular order:
- At least 16 GB of RAM
- 1 TB of free space (will use 60-80% of that)
- A running instance of bitcoin-core with txindex=1 and rpc credentials
- Git
- **Homepage**: A landing page to explains the project and what it does
- **More Datasets/Charts**: If a dataset can be computed, it should exist and have its related charts
- **Dashboards**: For a quick and real-time view of the latest data of all the datasets
- **NOSTR integration**: First to save preferences, later to add some social functionnality
- **Datasets by block timestamp**: In addition to having datasets by block date and block height
- **Descriptions**: Add text to describe all charts and what they mean
- **Start9 Add-on**: By making the whole suite much easier to self-host, it's quite rough right now
### Docker
_Maybe_:
Coming soon
- A Desktop app
- A mobile app
### Manual
First we need to install Rust (https://www.rust-lang.org/tools/install)
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
If you already had Rust installed you could update it just in case
```bash
rustup update
```
Optionally, you can also install `cargo-watch` for the server to automatically restart it on file change, which will be triggered by new code and new datasets from the parser (https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install)
```bash
cargo install cargo-watch --locked
```
Then you need to choose a path where all files related to **kibō** will live
```bash
cd ???
```
We can now clone the repository
```bash
git clone https://github.com/kibo-money/kibo.git
```
In a new terminal, go to the `parser`'s folder of the repository
```bash
cd ???/kibo/parser
```
Now we can finally start by running the parser, you need to use the `./run.sh` script instead of `cargo run -r` as we need to set various system variables for the program to run smoothly
For the first launch, the parser will need several information such as:
- `--datadir`: which is bitcoin data directory path
- `--rpcuser`: the username of the RPC credentials to talk to the bitcoin server
- `--rpcpassword`: the password of the RPC credentials
Everything will be saved in a `config.toml` file, which will allow you to simply run `./run.sh` next time
Here's an example
```bash
./run.sh --datadir=$HOME/Developer/bitcoin --rpcuser=satoshi --rpcpassword=nakamoto
```
In a new terminal, go to the `server`'s folder of the repository
```bash
cd ???/kibo/server
```
And start it also with the `run.sh` script instead of `cargo run -r`
```bash
./run.sh
```
Then the easiest to let others access your server is to use `cloudflared` which will also cache requests. For more information go to: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
## Brand
- **Name**: Willing to change if someone thinks of something better !
- **Logo**: Most likely a placeholder
### Name
## Collaboration
kibō means _**hope**_ in japanese which is what Bitcoin ultimately is for many, hope for a better future.
- Repositories:
- [Github](https://github.com/satonomics-org/satonomics)
- [Codeberg](https://codeberg.org/satonomics/satonomics)
- Issues:
- [Github](https://github.com/satonomics-org/satonomics/issues)
- [NOSTR](https://gitworkshop.dev/r/naddr1qq99xct5dahx7mtfvdesz9thwden5te0wp6hyurvv4ex2mrp0yhxxmmdqgsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03srqsqqqaueek2h03/issues)
- Proposals:
- [Github](https://github.com/satonomics-org/satonomics/pulls)
- [NOSTR](https://gitworkshop.dev/r/naddr1qq99xct5dahx7mtfvdesz9thwden5te0wp6hyurvv4ex2mrp0yhxxmmdqgsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03srqsqqqaueek2h03/proposals)
### Logo
## Proof of Work
The dove (borrowed from [svgrepo](https://www.svgrepo.com/svg/351969/dove) for now) is known to represent hope in many cultures.
Aka: Previous iterations
The orange background is a wink to Bitcoin and when in a circle, it also represents the sun, which means that while it's our hope for a better future, we still have to be careful with our collective goals and actions, to not end up like Icarus.
The initial idea was totally different yet morphed over time into what it is today: a fully FOSS self-hostable on-chain data generator
## Infrastructure
Here's the current infrastructure of the main instance and its backup.
It uses 2 servers, a full and a light one without the parser running but with still datasets syncronized via Syncthing.
Cloudflare is used for their tunnel + CDN services.
Though it's recommended to change to default **Browser Cache TTL** configuration from `4 Hours` to `Respect Existing Headers` (in `Websites / YOUR_DOMAIN / Caching / Configuration / Browser Cache TTL`) and activate `Always use https`.
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-light.svg" width="768" height="auto" style="max-width: 100%;">
</picture>
</p>
## Iterations
A list of all the previous versions and ideas:
- https://github.com/drgarlic/satonomics
- https://github.com/drgarlic/satonomics-parser
-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
-375
View File
@@ -1,375 +0,0 @@
<!doctype html>
<html lang="en" class="overflow-hidden">
<head>
<meta charset="utf-8" />
<title>Satonomics</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
class="text-high-contrast bg-white dark:bg-black"
style="font-size: 15px; line-height: 22px"
>
<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.2.0",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "($npm_execpath outdated || read -p \"Press enter to ignore...\") && vite --host",
"build": "vite build",
"check": "tsc --noEmit --skipLibCheck --pretty",
"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 ./public/logo/white.svg ./public/assets --index ./index.html --manifest ./public/manifest.webmanifest --icon-only --favicon --background \"linear-gradient(to right bottom, rgb(249, 115, 22), rgb(154, 52, 18))\" --padding \"min(15vh, 15vw)\" --path-override \"/assets\" && pnpm pwa-asset-generator ./public/logo/white.svg ./public/assets --index ./index.html --splash-only --background \"linear-gradient(to right bottom, rgb(249, 115, 22), rgb(154, 52, 18))\" --padding \"min(33vh, 33vw)\" --path-override \"/assets\" && pnpm pwa-asset-generator ./public/logo/white.svg ./public/assets --index ./index.html --splash-only --dark-mode --background \"#0c0a09\" --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.25",
"lean-qr": "^2.3.4",
"lightweight-charts": "^4.1.6",
"solid-js": "^1.8.18"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.3.0",
"@iconify-json/tabler": "^1.1.116",
"@tailwindcss/container-queries": "^0.1.1",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"pwa-asset-generator": "^6.3.1",
"rollup-plugin-visualizer": "^5.12.0",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.3",
"unplugin-auto-import": "^0.17.6",
"unplugin-icons": "^0.19.0",
"vite": "^5.3.3",
"vite-plugin-pwa": "^0.20.0",
"vite-plugin-solid": "^2.10.2",
"workbox-window": "^7.1.0",
"wrangler": "^3.63.1"
}
}
-6318
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.
-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

-188
View File
@@ -1,188 +0,0 @@
import { createRWS } from "/src/solid/rws";
const texts = [
"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",
"cyberpunk",
];
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} />
</div>
</div>
<div class="absolute h-full w-full opacity-10 mix-blend-multiply">
<Noise />
</div>
<div class="absolute h-full w-full opacity-10 mix-blend-hard-light">
<Noise />
</div>
</>
);
}
function Line({
mode,
focused,
}: {
mode: SL<"Scroll" | "Static">;
focused: Accessor<boolean>;
}) {
const shuffled = shuffle([...texts]);
shuffled.pop();
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 seconds = joined.length * 2;
const wasOnceOn = createRWS(false);
createEffect(() => {
if (!wasOnceOn() && mode.selected() === "Scroll") {
wasOnceOn.set(true);
}
});
return (
<p
class="inline-block px-2 text-[5dvh] 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 200 200"
preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg"
>
<filter id="noiseFilter">
<feTurbulence
type="fractalNoise"
baseFrequency="3"
numOctaves="3"
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,
children,
dark,
classes,
}: {
flex?: boolean;
absolute?: "top" | "bottom";
padded?: boolean;
dark?: boolean;
classes?: string;
} & ParentProps) {
return (
<div
class={classPropToString([
"p-2",
absolute
? [
"absolute inset-x-0",
absolute === "top"
? "top-0"
: "pointer-events-none bottom-0 bg-gradient-to-b from-transparent to-orange-100 dark:to-black",
]
: "relative",
classes,
])}
>
<div
class={classPropToString([
"border-lighter pointer-events-auto relative overflow-hidden rounded-xl border shadow-md",
dark
? "bg-white/40 backdrop-blur-sm dark:bg-orange-100/5"
: "bg-white/60 backdrop-blur-md dark:bg-orange-200/10",
])}
>
<div
class={classPropToString([
flex && "flex w-full space-x-2",
padded && "p-1.5",
])}
>
{children}
</div>
</div>
</div>
);
}
-13
View File
@@ -1,13 +0,0 @@
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>
);
}
@@ -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 { generate } from "lean-qr";
import { Button } from "./button";
export function ButtonShare({ qrcode }: { qrcode: RWS<string> }) {
return (
<Button
title="Share"
icon={() => IconTablerShare}
onClick={() => {
qrcode.set(() =>
generate(document.location.href).toDataURL({
on: [0xff, 0xff, 0xff, 0xff],
off: [0x00, 0x00, 0x00, 0x00],
padX: 0,
padY: 0,
}),
);
}}
/>
);
}
@@ -1,81 +0,0 @@
import { requestIdleCallbackPossible } from "/src/env";
import { createRWS } from "/src/solid/rws";
export function Chart({
charts,
parentDiv,
presets,
datasets,
legendSetter,
dark,
activeIds,
}: {
charts: RWS<IChartApi[]>;
parentDiv: RWS<HTMLDivElement | undefined>;
presets: Presets;
datasets: Datasets;
legendSetter: Setter<SeriesLegend[]>;
dark: Accessor<boolean>;
activeIds: RWS<number[]>;
}) {
const wasIdle = createRWS(false);
if (requestIdleCallbackPossible) {
const idleCallback = requestIdleCallback(() => {
console.log("idle");
wasIdle.set(true);
cancelIdleCallback(idleCallback);
});
onCleanup(() => {
cancelIdleCallback(idleCallback);
});
} else {
const timeout = setTimeout(() => {
console.log("timeout");
wasIdle.set(true);
}, 500);
onCleanup(() => {
clearTimeout(timeout);
});
}
onMount(() => {
createEffect(() => {
const preset = presets.selected();
const div = parentDiv();
if (!wasIdle() || !div) return;
untrack(() => {
try {
console.log(`preset: ${preset.id}`);
preset.applyPreset({
charts,
parentDiv: div,
datasets,
preset,
legendSetter,
dark,
activeIds,
});
} catch (error) {
console.error("chart: render: failed", error);
}
});
});
onCleanup(() =>
charts.set((charts) => {
charts.forEach((chart) => {
chart.remove();
});
return [];
}),
);
});
return <></>;
}
@@ -1,178 +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-1 p-1.5">
<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()}>
<button
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 rounded-full py-1.5 pl-2 pr-2.5 hover:bg-orange-800/20 active:scale-[0.975] dark:hover:bg-orange-200/20"
>
<span
class="flex size-4 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-high-contrast decoration-high-contrast decoration-wavy decoration-[1.5px]"
style={{
"text-decoration-line": !legend.visible()
? "line-through"
: undefined,
"--tw-text-opacity": legend.visible() ? 1 : 0.5,
}}
>
{legend.title}
</span>
<Show when={legend.dataset.url}>
{(url) => (
<a
title="Dataset"
class="border-superlight -my-0.5 !-mr-1 inline-flex size-6 flex-col overflow-hidden rounded-full border bg-white bg-opacity-5 p-1 pl-0.5 hover:bg-opacity-50 dark:bg-orange-200 dark:bg-opacity-5 dark:hover:bg-opacity-25"
onClick={(event) => {
event.stopPropagation();
}}
href={url()}
target={
url()?.startsWith("/") || url()?.startsWith("http")
? "_blank"
: undefined
}
>
<IconTablerExternalLink />
</a>
)}
</Show>
</button>
</Show>
);
}}
</For>
</Scrollable>
);
}
@@ -1,225 +0,0 @@
import { ONE_DAY_IN_MS } from "/src/scripts/utils/time";
import { classPropToString } from "/src/solid/classes";
import { GENESIS_DAY } from "../../../../../scripts/lightweightCharts/whitespace";
import { Box } from "../../box";
import { Scrollable } from "../../scrollable";
export function TimeScale({
scale,
charts,
}: {
scale: Accessor<ResourceScale>;
charts: RWS<IChartApi[]>;
}) {
const today = new Date();
const disabled = createMemo(() => charts().length === 0);
return (
<Box dark padded={false} classes="short:hidden">
<Scrollable classes="p-1.5 space-x-2">
<Switch>
<Match when={scale() === "date"}>
<Button
disabled={disabled}
onClick={() => setTimeScale({ scale: scale(), charts })}
>
All Time
</Button>
<Button
disabled={disabled}
onClick={() => setTimeScale({ scale: scale(), charts, days: 7 })}
>
1 Week
</Button>
<Button
disabled={disabled}
onClick={() => setTimeScale({ scale: scale(), charts, days: 30 })}
>
1 Month
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), charts, days: 3 * 30 })
}
>
3 Months
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), charts, days: 6 * 30 })
}
>
6 Months
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({
scale: scale(),
charts,
days: Math.ceil(
(today.getTime() -
new Date(`${today.getUTCFullYear()}-01-01`).getTime()) /
ONE_DAY_IN_MS,
),
})
}
>
Year To Date
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), charts, days: 365 })
}
>
1 Year
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), charts, days: 2 * 365 })
}
>
2 Years
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), charts, days: 4 * 365 })
}
>
4 Years
</Button>
<Button
disabled={disabled}
onClick={() =>
setTimeScale({ scale: scale(), charts, 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
disabled={disabled}
onClick={() => setTimeScale({ scale: scale(), charts, year })}
>
{year}
</Button>
)}
</For>
</Match>
<Match when={scale() === "height"}>
<Button disabled={() => true} onClick={() => {}}>
24h
</Button>
<Button disabled={() => true} onClick={() => {}}>
48h
</Button>
<For
each={new Array(9)
.fill(0)
.flatMap((_, i) => [i, i + 0.5])
.reverse()}
>
{(i) => (
<Button
disabled={disabled}
onClick={() =>
setTimeScale({
scale: scale(),
charts,
range: {
from: i * 100_000,
to: (i + 0.5) * 100_000,
},
})
}
>
{`${100 * (i + 0.5)}k`}
</Button>
)}
</For>
</Match>
</Switch>
</Scrollable>
</Box>
);
}
function Button({
onClick,
disabled,
children,
}: ParentProps & { onClick: VoidFunction; disabled: Accessor<boolean> }) {
return (
<button
class={classPropToString([
disabled() ? "opacity-50" : "hover:bg-orange-50/20 active:scale-95",
"min-w-20 flex-shrink-0 flex-grow whitespace-nowrap rounded-lg px-2 py-1.5",
])}
onClick={onClick}
disabled={disabled()}
>
{children}
</button>
);
}
function setTimeScale({
charts,
scale,
days,
year,
range,
}: {
charts: RWS<IChartApi[]>;
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);
}
charts()
.at(0)
?.timeScale()
.setVisibleRange({
from: (from.getTime() / 1000) as Time,
to: (to.getTime() / 1000) as Time,
});
} else if (scale === "height") {
if (range) {
charts()
.at(0)
?.timeScale()
.setVisibleRange({
from: range.from as Time,
to: range.to as Time,
});
}
}
}
@@ -1,10 +0,0 @@
export function Title({ presets }: { presets: Presets }) {
return (
<div class="flex flex-1 items-center overflow-y-auto p-1.5">
<div class="flex-1 -space-y-1 whitespace-nowrap px-1 md:mt-0.5 md:-space-y-1.5">
<h3 class="text-xs opacity-50">{`/ ${[...presets.selected().path.map(({ name }) => name), presets.selected().name].join(" / ")}`}</h3>
<h1 class="text-lg font-bold md:text-xl">{presets.selected().title}</h1>
</div>
</div>
);
}
@@ -1,86 +0,0 @@
import { classPropToString } from "/src/solid/classes";
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[]>([]);
const charts = createRWS<IChartApi[]>([]);
const div = createRWS<HTMLDivElement | undefined>(undefined);
const scale = createMemo(() => presets.selected().scale);
const activeIds = createRWS([] as number[], { equals: false });
const Chart = lazy(() =>
import("./components/chart").then((d) => ({ default: d.Chart })),
);
return (
<div
class={classPropToString([
standalone &&
"border-lighter rounded-2xl border bg-gradient-to-b from-white/15 to-white/30 to-80% shadow-md dark:from-orange-100/5 dark:to-black/10",
"flex size-full min-h-0 flex-1 flex-col overflow-hidden",
])}
style={{
display: (hide ? hide() : false) ? "none" : undefined,
}}
>
<Box flex={false} dark padded={false} classes="short:hidden">
<Title presets={presets} />
<div class="border-lighter border-t" />
<div class="flex">
<Legend
legend={legend}
scale={scale}
activeIds={activeIds}
dark={dark}
/>
<div class="border-lighter border-l" />
<Actions presets={presets} qrcode={qrcode} fullscreen={fullscreen} />
</div>
</Box>
<div ref={div.set} class="-mr-2 -mt-2 flex min-h-0 flex-1 flex-col">
<Chart
parentDiv={div}
charts={charts}
datasets={datasets}
legendSetter={legend.set}
presets={presets}
dark={dark}
activeIds={activeIds}
/>
</div>
<TimeScale charts={charts} scale={scale} />
</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,50 +0,0 @@
import { Header } from "./header";
import { Line } from "./line";
import { Number } from "./number";
export function FavoritesFrame({
presets,
selectedFrame,
}: {
presets: Presets;
selectedFrame: Accessor<FrameName>;
}) {
return (
<div
class="flex-1 overflow-y-auto"
style={{
display: selectedFrame() !== "Favorites" ? "none" : undefined,
}}
>
<div class="flex max-h-full min-h-0 flex-1 flex-col gap-4 p-4">
<Header title="Favorites">
<Number number={() => presets.favorites().length} /> presets marked as
favorites.
</Header>
<div class="border-lighter -mx-4 border-t" />
<div
class="space-y-0.5 py-1"
style={{
display: !presets.favorites().length ? "none" : undefined,
}}
>
<For each={presets.favorites()}>
{(preset) => (
<Line
id={`favorite-${preset.id}`}
name={preset.title}
onClick={() => presets.select(preset)}
active={() => presets.selected() === preset}
header={`/ ${[...preset.path.map(({ name }) => name), preset.name].join(" / ")}`}
/>
)}
</For>
</div>
<div class="h-[25dvh] flex-none" />
</div>
</div>
);
}

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