global: snapshot

This commit is contained in:
k
2024-11-20 10:50:14 +01:00
parent 9a73ee6952
commit d01ea13de4
61 changed files with 1907 additions and 950 deletions
+1
View File
@@ -39,6 +39,7 @@
- Split `index.html` and `script.js` into multiple js and css files to load only what's necessary at a given time
- Added `Compare` section to all groups, to compare all datasets within a group
- Updated `Solid Signals` library, which had an important breaking change on the `createEffect` function which might bring some bugs
- Fixed some datasets paths
## Parser
+5 -6
View File
@@ -1,15 +1,14 @@
<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">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-long-text-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-long-text-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-long-text-light.svg" width="210" height="auto">
</picture>
</a>
## Description
[**kibō**](https://kibo.money) (_hope_ in japanese) is primarily an open source Bitcoin Core data extractor and visualizer (similar to [Glassnode](https://glassnode.com)).
[**kibō**](https://kibo.money) (_hope_ in japanese) is primarily an open source Bitcoin Core data extractor and visualizer (similar to [Glassnode](https://glassnode.com)). The goal is to empower people with information that is often hard to come by and/or very pricey.
The project is split in 3 parts:
@@ -20,7 +19,7 @@ The project is split in 3 parts:
Whether you're an enthusiast, a researcher, a miner, an analyst, a trader, a skeptic or just curious, there is something for everyone !
This project was created out of frustration by all the alternatives that were either very expensive and thus discriminatory and against bitcoin values or just very limited and none were open-source and verifiable. So while it's not the first tool trying to solve these problems, it's the first that is completely free, open-source and self-hostable.
This project was created out of frustration by all the alternatives that were either very expensive and thus discriminatory and against bitcoin values or just very limited and none were open-source and verifiable. So while it's not the first tool trying to solve these problems, it's the first that is completely free, open-source and self-hostable.
If you are a user of [mempool.space](https://mempool.space), you'll find this to be very complimentary, as it offers a macro view of the chain over time instead of a detailed one.
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

+3 -3
View File
@@ -2,7 +2,7 @@
<svg viewBox="0 0 500 180" xmlns="http://www.w3.org/2000/svg">
<defs/>
<g transform="matrix(1, 0, 0, 1, -252.158997, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: rgb(16, 16, 14);"/>
<path d="M 589.19 97.802 L 589.19 106.23 L 610.948 106.23 C 605.1 112.938 597.446 119.044 587.986 124.376 L 593.404 131.514 C 597.532 128.934 601.488 126.268 605.186 123.43 L 605.186 146.048 L 614.13 146.048 L 614.13 123.43 L 626.944 123.43 L 626.944 149.402 L 635.974 149.402 L 635.974 123.43 L 649.82 123.43 L 649.82 134.008 C 649.82 136.072 649.046 137.104 647.498 137.104 L 640.36 136.674 L 642.768 145.188 L 650.422 145.188 C 655.926 145.188 658.678 142.092 658.678 135.986 L 658.678 115.174 L 635.974 115.174 L 635.974 108.638 L 626.944 108.638 L 626.944 115.174 L 614.388 115.174 C 617.054 112.336 619.548 109.326 621.784 106.23 L 665.128 106.23 L 665.128 97.802 L 626.858 97.802 C 627.89 95.824 628.836 93.76 629.696 91.61 L 620.838 90.492 C 619.806 92.9 618.516 95.394 617.14 97.802 L 589.19 97.802 Z M 648.1 68.734 C 642.338 72.088 636.232 75.098 629.868 77.678 C 621.612 75.012 612.926 72.518 603.896 70.282 L 599.252 77.248 C 605.272 78.624 611.206 80.258 617.226 82.15 C 610.088 84.386 602.606 86.106 594.78 87.482 L 599.596 95.308 C 612.324 92.04 622.472 89.116 630.04 86.364 C 638.124 89.116 646.122 92.298 654.034 95.824 L 658.936 88.428 C 653.26 86.02 647.412 83.698 641.392 81.548 C 646.208 79.226 651.11 76.56 655.926 73.55 L 648.1 68.734 Z M 675.438 77.85 L 675.438 85.848 L 682.404 85.848 L 682.404 98.92 C 682.404 101.5 681.114 103.22 678.62 104.166 L 680.684 110.874 C 692.036 108.896 701.926 106.66 710.182 104.08 L 708.634 96.426 C 703.474 98.146 697.454 99.608 690.574 100.984 L 690.574 85.848 L 712.332 85.848 L 712.332 77.85 L 698.916 77.85 C 698.4 74.668 697.884 71.744 697.368 69.164 L 688.338 70.712 C 688.94 72.862 689.542 75.27 690.144 77.85 L 675.438 77.85 Z M 724.028 89.632 L 739.25 89.632 L 739.25 93.502 L 723.856 93.502 C 723.942 92.47 724.028 91.352 724.028 90.32 L 724.028 89.632 Z M 739.25 83.096 L 724.028 83.096 L 724.028 79.226 L 739.25 79.226 L 739.25 83.096 Z M 722.652 100.038 L 739.25 100.038 L 739.25 100.898 C 739.25 103.048 738.218 104.166 736.24 104.166 C 733.918 104.166 731.424 103.994 728.758 103.822 L 730.822 111.562 L 738.734 111.562 C 744.582 111.562 747.506 108.982 747.506 103.908 L 747.506 72.002 L 715.6 72.002 L 715.6 90.922 C 715.428 97.286 713.192 102.532 708.892 106.746 L 715.342 112.594 C 718.782 109.068 721.276 104.854 722.652 100.038 Z M 708.462 121.452 L 708.462 126.784 L 683.608 126.784 L 683.608 134.352 L 708.462 134.352 L 708.462 139.598 L 675.524 139.598 L 675.524 147.51 L 750 147.51 L 750 139.598 L 717.062 139.598 L 717.062 134.352 L 742.174 134.352 L 742.174 126.784 L 717.062 126.784 L 717.062 121.452 L 746.216 121.452 L 746.216 113.712 L 679.308 113.712 L 679.308 121.452 L 708.462 121.452 Z" style="fill: rgb(192, 192, 171);"/>
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: #fffaf6;"/>
<path d="M 589.19 97.802 L 589.19 106.23 L 610.948 106.23 C 605.1 112.938 597.446 119.044 587.986 124.376 L 593.404 131.514 C 597.532 128.934 601.488 126.268 605.186 123.43 L 605.186 146.048 L 614.13 146.048 L 614.13 123.43 L 626.944 123.43 L 626.944 149.402 L 635.974 149.402 L 635.974 123.43 L 649.82 123.43 L 649.82 134.008 C 649.82 136.072 649.046 137.104 647.498 137.104 L 640.36 136.674 L 642.768 145.188 L 650.422 145.188 C 655.926 145.188 658.678 142.092 658.678 135.986 L 658.678 115.174 L 635.974 115.174 L 635.974 108.638 L 626.944 108.638 L 626.944 115.174 L 614.388 115.174 C 617.054 112.336 619.548 109.326 621.784 106.23 L 665.128 106.23 L 665.128 97.802 L 626.858 97.802 C 627.89 95.824 628.836 93.76 629.696 91.61 L 620.838 90.492 C 619.806 92.9 618.516 95.394 617.14 97.802 L 589.19 97.802 Z M 648.1 68.734 C 642.338 72.088 636.232 75.098 629.868 77.678 C 621.612 75.012 612.926 72.518 603.896 70.282 L 599.252 77.248 C 605.272 78.624 611.206 80.258 617.226 82.15 C 610.088 84.386 602.606 86.106 594.78 87.482 L 599.596 95.308 C 612.324 92.04 622.472 89.116 630.04 86.364 C 638.124 89.116 646.122 92.298 654.034 95.824 L 658.936 88.428 C 653.26 86.02 647.412 83.698 641.392 81.548 C 646.208 79.226 651.11 76.56 655.926 73.55 L 648.1 68.734 Z M 675.438 77.85 L 675.438 85.848 L 682.404 85.848 L 682.404 98.92 C 682.404 101.5 681.114 103.22 678.62 104.166 L 680.684 110.874 C 692.036 108.896 701.926 106.66 710.182 104.08 L 708.634 96.426 C 703.474 98.146 697.454 99.608 690.574 100.984 L 690.574 85.848 L 712.332 85.848 L 712.332 77.85 L 698.916 77.85 C 698.4 74.668 697.884 71.744 697.368 69.164 L 688.338 70.712 C 688.94 72.862 689.542 75.27 690.144 77.85 L 675.438 77.85 Z M 724.028 89.632 L 739.25 89.632 L 739.25 93.502 L 723.856 93.502 C 723.942 92.47 724.028 91.352 724.028 90.32 L 724.028 89.632 Z M 739.25 83.096 L 724.028 83.096 L 724.028 79.226 L 739.25 79.226 L 739.25 83.096 Z M 722.652 100.038 L 739.25 100.038 L 739.25 100.898 C 739.25 103.048 738.218 104.166 736.24 104.166 C 733.918 104.166 731.424 103.994 728.758 103.822 L 730.822 111.562 L 738.734 111.562 C 744.582 111.562 747.506 108.982 747.506 103.908 L 747.506 72.002 L 715.6 72.002 L 715.6 90.922 C 715.428 97.286 713.192 102.532 708.892 106.746 L 715.342 112.594 C 718.782 109.068 721.276 104.854 722.652 100.038 Z M 708.462 121.452 L 708.462 126.784 L 683.608 126.784 L 683.608 134.352 L 708.462 134.352 L 708.462 139.598 L 675.524 139.598 L 675.524 147.51 L 750 147.51 L 750 139.598 L 717.062 139.598 L 717.062 134.352 L 742.174 134.352 L 742.174 126.784 L 717.062 126.784 L 717.062 121.452 L 746.216 121.452 L 746.216 113.712 L 679.308 113.712 L 679.308 121.452 L 708.462 121.452 Z" style="fill: #867e7b;"/>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.8 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" type="image/png" sizes="196x196" href="/assets/pwa/2024-11-20_09-41-25/favicon-196.png">
<link rel="apple-touch-icon" href="/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-startup-image" href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)">
</head>
<body>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+5 -5
View File
@@ -6,7 +6,7 @@ OUTPUT="/assets/pwa/${DATE}"
mkdir ".${OUTPUT}"
cp "./assets/pwa/index.html" ".${OUTPUT}/"
pwa-asset-generator "../assets/logo-icon.svg" ".${OUTPUT}" \
pwa-asset-generator "../assets/dove-orange.svg" ".${OUTPUT}" \
--index ".${OUTPUT}/index.html" \
--manifest "./manifest.webmanifest" \
--favicon \
@@ -15,19 +15,19 @@ pwa-asset-generator "../assets/logo-icon.svg" ".${OUTPUT}" \
--quality "100" \
--opaque "false"
pwa-asset-generator "../assets/logo-dove-light.svg" ".${OUTPUT}" \
pwa-asset-generator "../assets/dove-white.svg" ".${OUTPUT}" \
--index ".${OUTPUT}/index.html" \
--manifest "./manifest.webmanifest" \
--icon-only \
--background "#f26610" \
--padding "5%" \
--padding "10%" \
--path-override "${OUTPUT}" \
--quality "100"
pwa-asset-generator "../assets/logo-dove-light.svg" ".${OUTPUT}" \
pwa-asset-generator "../assets/logo-stamp-orange.svg" ".${OUTPUT}" \
--index ".${OUTPUT}/index.html" \
--splash-only \
--background "#f26610" \
--padding "min(35vh, 35vw)" \
--padding "min(30vh, 30vw)" \
--path-override "${OUTPUT}" \
--quality "100"
+311 -78
View File
@@ -5,7 +5,7 @@
<title>kibō</title>
<meta
name="description"
content="A better, FOSS, Bitcoin-only, self-hostable Glassnode"
content="An open source Bitcoin Core data extractor and visualizer"
/>
<meta
name="viewport"
@@ -318,8 +318,7 @@
}
a {
display: inline-flex;
gap: 0.25rem;
display: flow-root;
align-items: baseline;
text-decoration-style: dotted;
text-decoration-thickness: 1px;
@@ -368,6 +367,7 @@
line-height: var(--line-height-sm);
opacity: 0.5;
color: inherit;
margin-left: 0.25rem;
}
}
@@ -458,6 +458,11 @@
}
}
details {
display: block;
}
field,
h1 {
text-transform: capitalize;
}
@@ -519,7 +524,7 @@
flex-direction: column;
user-select: none;
-webkit-user-select: none;
/* color: var(--off-color); */
color: var(--off-color);
position: relative;
pointer-events: all;
@@ -540,22 +545,6 @@
flex: 1;
}
}
> a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
> span.value {
opacity: 0.5;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
&:has(+ *) {
margin-right: 0.375rem;
}
}
}
&:hover {
@@ -620,16 +609,6 @@
pointer-events: auto;
padding: 0.5rem 1.5rem;
gap: 1rem;
> label {
text-align: center;
min-width: 3.5rem;
color: var(--off-color);
&:has(input:checked) {
color: var(--color);
}
}
}
}
@@ -688,8 +667,8 @@
font-weight: var(--font-weight-base);
h4 + & {
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
}
}
@@ -707,11 +686,12 @@
summary {
list-style: none;
display: inline-flex;
gap: 0.25rem;
align-items: center;
display: block;
cursor: pointer;
position: relative;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&::marker,
&::-webkit-details-marker {
@@ -771,6 +751,8 @@
height: 0.375rem;
border-radius: 9999px;
align-self: center;
display: inline-block;
margin-left: 0.25rem;
}
}
@@ -925,12 +907,6 @@
right: 0;
z-index: 10;
pointer-events: none;
#chart-list > & {
bottom: 1.75rem;
width: 80px;
left: auto;
}
}
.shadow-left {
position: absolute;
@@ -960,6 +936,80 @@
z-index: 10;
pointer-events: none;
}
.chart-list {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
z-index: 20;
> .chart-wrapper {
position: relative;
min-height: 0px;
width: 100%;
cursor: crosshair;
height: 100%;
&:has(+ .chart-wrapper:not([hidden])) {
height: calc(100% - 62px);
}
> fieldset {
pointer-events: none;
position: absolute;
left: 0px;
top: 0px;
z-index: 10;
display: flex;
align-items: center;
padding-left: var(--main-padding);
padding-right: var(--main-padding);
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
gap: 0.5rem;
> div.field {
display: flex;
align-items: center;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
gap: 1rem;
> legend,
> div {
flex-shrink: 0;
}
> hr {
min-width: 1rem;
}
label {
padding: 0.5rem;
margin: -0.5rem;
}
> div {
display: flex;
gap: 0.5rem;
}
}
}
> .chart-div {
width: 100%;
height: 100%;
}
}
> .shadow-bottom {
bottom: 1.75rem;
width: 80px;
left: auto;
}
}
</style>
<!-- ------- -->
@@ -1010,181 +1060,362 @@
rel="icon"
type="image/png"
sizes="196x196"
href="/assets/pwa/2024-09-17_09-06-03/favicon-196.png"
href="/assets/pwa/2024-11-20_09-41-25/favicon-196.png"
/>
<link
rel="apple-touch-icon"
href="/assets/pwa/2024-09-17_09-06-03/apple-icon-180.png"
href="/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link
rel="apple-touch-startup-image"
href="/assets/pwa/2024-09-17_09-06-03/apple-splash-2048-2732.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2732-2048.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1668-2388.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2388-1668.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1536-2048.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2048-1536.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1488-2266.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2266-1488.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1640-2360.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2360-1640.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1668-2224.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2224-1668.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1620-2160.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2160-1620.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1290-2796.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2796-1290.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1179-2556.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2556-1179.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1284-2778.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2778-1284.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1170-2532.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2532-1170.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1125-2436.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2436-1125.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1242-2688.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2688-1242.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-828-1792.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1792-828.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1242-2208.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-2208-1242.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-750-1334.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1334-750.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-640-1136.jpg"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-09-17_09-06-03/apple-splash-1136-640.jpg"
href="/assets/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="icon"
type="image/png"
sizes="196x196"
href="/assets/pwa/2024-11-20_09-41-25/favicon-196.png"
/>
<link
rel="apple-touch-icon"
href="/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link
rel="apple-touch-startup-image"
href="/assets/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/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/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
</head>
@@ -1300,12 +1531,14 @@
</header>
<div id="charts" hidden>
<fieldset id="legend"></fieldset>
<div id="chart-list">
<div id="charts-chart-list" class="chart-list">
<div class="shadow-top"></div>
<div class="shadow-bottom"></div>
</div>
</div>
<div id="simulation" hidden></div>
<div id="live-price" hidden></div>
<div id="moscow-time" hidden></div>
</section>
</aside>
<div id="share-div" hidden>
+10 -6
View File
@@ -2,7 +2,11 @@
"name": "kibō",
"short_name": "kibō",
"description": "A better, FOSS, Bitcoin-only, self-hostable Glassnode",
"categories": ["bitcoin", "on-chain", "data"],
"categories": [
"bitcoin",
"on-chain",
"data"
],
"start_url": "/",
"scope": "/",
"display": "standalone",
@@ -12,28 +16,28 @@
"lang": "en",
"icons": [
{
"src": "/assets/pwa/2024-09-17_09-06-03/manifest-icon-192.maskable.png",
"src": "/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/pwa/2024-09-17_09-06-03/manifest-icon-192.maskable.png",
"src": "/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/assets/pwa/2024-09-17_09-06-03/manifest-icon-512.maskable.png",
"src": "/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/pwa/2024-09-17_09-06-03/manifest-icon-512.maskable.png",
"src": "/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
}
+27 -107
View File
@@ -224,14 +224,6 @@ export function init({
}
createFetchChunksOfVisibleDatasetsEffect();
function resetChartListElement() {
while (
elements.chartList.lastElementChild?.classList.contains("chart-wrapper")
) {
elements.chartList.lastElementChild?.remove();
}
}
/**
* @param {HTMLElement} parent
* @param {number} chartIndex
@@ -767,7 +759,7 @@ export function init({
/** @type {AnyDatasetPath} */
const datasetPath = `${s}-to-price`;
const dataset = datasets.getOrImport(s, datasetPath);
const dataset = datasets.getOrCreate(s, datasetPath);
// Don't trigger reactivity by design
activeDatasets().add(dataset);
@@ -806,7 +798,7 @@ export function init({
});
function createLiveCandleUpdateEffect() {
signals.createEffect(webSockets.krakenCandle.latest, (latest) => {
signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
if (!latest) return;
const index = utils.chunkIdToIndex(s, latest.year);
@@ -825,95 +817,6 @@ export function init({
return priceSeries;
}
function resetLegendElement() {
elements.legend.innerHTML = "";
}
function initTimeScaleElement() {
const GENESIS_DAY = "2009-01-03";
/**
* @param {HTMLButtonElement} button
* @param {ChartOption} option
*/
function setTimeScale(button, option) {
const chart = charts.at(-1);
if (!chart) return;
const timeScale = chart.timeScale();
const year = button.dataset.year;
let days = button.dataset.days;
let toHeight = button.dataset.to;
switch (option.scale) {
case "date": {
let from = new Date();
let to = new Date();
to.setUTCHours(0, 0, 0, 0);
if (!days && typeof button.dataset.yearToDate === "string") {
days = String(
Math.ceil(
(to.getTime() -
new Date(`${to.getUTCFullYear()}-01-01`).getTime()) /
consts.ONE_DAY_IN_MS,
),
);
}
if (year) {
from = new Date(`${year}-01-01`);
to = new Date(`${year}-12-31`);
} else if (days) {
from.setDate(from.getUTCDate() - Number(days));
} else {
from = new Date(GENESIS_DAY);
}
timeScale.setVisibleRange({
from: /** @type {Time} */ (from.getTime() / 1000),
to: /** @type {Time} */ (to.getTime() / 1000),
});
break;
}
case "height": {
timeScale.setVisibleRange({
from: /** @type {Time} */ (0),
to: /** @type {Time} */ (Number(toHeight?.slice(0, -1)) * 1_000),
});
break;
}
}
}
/**
* @param {HTMLElement} timeScaleButtons
*/
function initGoToButtons(timeScaleButtons) {
Array.from(timeScaleButtons.children).forEach((button) => {
if (button.tagName !== "BUTTON") throw "Expect a button";
button.addEventListener("click", () => {
const option = options.selected();
if (option.kind === "chart") {
setTimeScale(/** @type {HTMLButtonElement} */ (button), option);
}
});
});
}
// initGoToButtons(elements.timeScaleDateButtons);
// initGoToButtons(elements.timeScaleHeightButtons);
// function createScaleButtonsToggleEffect() {
// const isDate = signals.createMemo(() => scale() === "date");
// signals.createEffect(isDate, (isDate) => {
// elements.timeScaleDateButtons.hidden = !isDate;
// elements.timeScaleHeightButtons.hidden = isDate;
// });
// }
// createScaleButtonsToggleEffect();
}
initTimeScaleElement();
/**
* @param {ChartOption} option
*/
@@ -933,15 +836,12 @@ export function init({
(list) => (list ? [list] : []),
);
resetLegendElement();
resetChartListElement();
/** @type {Series[]} */
const allSeries = [];
charts = chartsBlueprints.map((seriesBlueprints, chartIndex) => {
const { chartDiv, unitName, chartMode } = createChartDiv(
elements.chartList,
elements.chartsChartList,
chartIndex,
);
@@ -1148,7 +1048,7 @@ export function init({
}
[...seriesBlueprints].reverse().forEach((seriesBlueprint, index) => {
const dataset = datasets.getOrImport(
const dataset = datasets.getOrCreate(
scale,
seriesBlueprint.datasetPath,
);
@@ -1266,11 +1166,31 @@ export function init({
});
}
function resetLegendElement() {
elements.legend.innerHTML = "";
}
function resetChartListElement() {
while (
elements.chartsChartList.lastElementChild?.classList.contains(
"chart-wrapper",
)
) {
elements.chartsChartList.lastElementChild?.remove();
}
}
function reset() {
charts.forEach((chart) => chart.remove());
charts = [];
resetLegendElement();
resetChartListElement();
}
function createApplyChartOptionEffect() {
signals.createEffect(selected, (option) => {
signals.createRoot(() => {
applyChartOption(option);
});
reset();
applyChartOption(option);
});
}
createApplyChartOptionEffect();
+41
View File
@@ -0,0 +1,41 @@
/**
* @import {Options} from './options';
*/
/**
* @param {Object} args
* @param {Colors} args.colors
* @param {Consts} args.consts
* @param {LightweightCharts} args.lightweightCharts
* @param {Signals} args.signals
* @param {Utilities} args.utils
* @param {Options} args.options
* @param {Datasets} args.datasets
* @param {WebSockets} args.webSockets
* @param {Elements} args.elements
* @param {Ids} args.ids
* @param {Accessor<boolean>} args.dark
*/
export function init({
colors,
consts,
dark,
datasets,
elements,
ids,
lightweightCharts,
options,
signals,
utils,
webSockets,
}) {
const livePriceElement = elements.livePrice;
const price = window.document.createElement("h1");
livePriceElement.append(price);
signals.createEffect(webSockets.kraken1dCandle.latest, (candle) => {
if (!candle) return;
price.innerHTML = utils.formatters.dollars.format(candle.close);
});
}
+327 -27
View File
@@ -4,7 +4,7 @@
* @import { Option, ResourceDataset, TimeScale, TimeRange, Unit, Marker, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, SeriesBlueprint, BaselineSpecificSeriesBlueprint, CandlestickSpecificSeriesBlueprint, LineSpecificSeriesBlueprint, SpecificSeriesBlueprintWithChart, Signal, Color, DatasetCandlestickData, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption } from "./types/self"
* @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions} from "../packages/lightweight-charts/v4.2.0/types";
* @import * as _ from "../packages/ufuzzy/v1.0.14/types"
* @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/types"
* @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LineData, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/types"
* @import { DatePath, HeightPath, LastPath } from "./types/paths";
* @import { SignalOptions } from "../packages/solid-signals/2024-11-02/types/core/core"
* @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/2024-11-02/types/core/owner"
@@ -19,7 +19,22 @@ function initPackages() {
createSolidSignal: /** @type {CreateSignal} */ (
_signals.createSignal
),
createEffect: /** @type {CreateEffect} */ (_signals.createEffect),
createSolidEffect: /** @type {CreateEffect} */ (
_signals.createEffect
),
createEffect: /** @type {CreateEffect} */ (compute, effect) => {
let dispose = /** @type {VoidFunction | null} */ (null);
// @ts-ignore
_signals.createEffect(compute, (v) => {
dispose?.();
signals.createRoot((_dispose) => {
dispose = _dispose;
effect(v);
});
signals.onCleanup(() => dispose?.());
});
signals.onCleanup(() => dispose?.());
},
createMemo: /** @type {CreateMemo} */ (_signals.createMemo),
createRoot: /** @type {CreateRoot} */ (_signals.createRoot),
getOwner: /** @type {GetOwner} */ (_signals.getOwner),
@@ -220,8 +235,15 @@ function initPackages() {
* @param {HTMLElement} args.element
* @param {Signals} args.signals
* @param {Colors} args.colors
* @param {DeepPartial<ChartOptions>} [args.options]
*/
function createChart({ scale, element, signals, colors }) {
function createChart({
scale,
element,
signals,
colors,
options: _options = {},
}) {
/** @satisfies {DeepPartial<ChartOptions>} */
const options = {
autoSize: true,
@@ -257,6 +279,7 @@ function initPackages() {
}
: {}),
},
..._options,
};
/** @type {IChartApi} */
@@ -597,7 +620,6 @@ function initPackages() {
let packagePromise = null;
return function () {
let p = null;
if (!packagePromise) {
// @ts-ignore
packagePromise = imports[key]();
@@ -640,6 +662,20 @@ const utils = {
yield() {
return this.sleep(0);
},
array: {
/**
* @param {number} start
* @param {number} end
*/
range(start, end) {
const range = [];
while (start <= end) {
range.push(start);
start += 1;
}
return range;
},
},
dom: {
/**
* @param {string} id
@@ -743,13 +779,16 @@ const utils = {
/**
* @param {Object} arg
* @param {string} arg.text
* @param {string} arg.title
* @param {VoidFunction} arg.onClick
*/
createButtonElement({ text, onClick }) {
createButtonElement({ text, onClick, title }) {
const button = window.document.createElement("button");
button.innerHTML = text;
button.title = title;
button.addEventListener("click", onClick);
return button;
@@ -773,6 +812,8 @@ const utils = {
}) {
const label = window.document.createElement("label");
inputId = inputId.toLowerCase();
const input = window.document.createElement("input");
input.type = "radio";
input.name = inputName;
@@ -898,6 +939,130 @@ const utils = {
return field;
},
createUlElement() {
return window.document.createElement("ul");
},
createLiElement() {
return window.document.createElement("li");
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
* @param {number} args.min
* @param {number} args.max
* @param {number} args.step
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputNumberElement({ id, title, signal, min, max, step, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "number";
input.min = String(min);
input.max = String(max);
input.step = String(step);
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const value = signal();
return value ? String(value) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("input", () => {
const valueSer = input.value;
const value = Number(valueSer);
if (value >= min && value <= max) {
stateValue = valueSer;
signal.set(value);
}
});
return input;
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<Date | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDate({ id, title, signal, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "date";
const min = "2011-01-01";
const minDate = new Date(min);
const maxDate = new Date();
const max = utils.date.toString(maxDate);
input.min = min;
input.max = max;
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const date = signal();
return date ? utils.date.toString(date) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("change", () => {
const value = input.value;
const date = new Date(value);
if (date >= minDate && date <= maxDate) {
stateValue = value;
signal.set(value ? date : null);
}
});
return input;
},
/**
* @param {Object} param0
* @param {string} param0.title
* @param {string} param0.description
*/
createHeader({ title, description }) {
const headerElement = window.document.createElement("header");
const div = window.document.createElement("div");
headerElement.append(div);
const h1 = window.document.createElement("h1");
div.append(h1);
const titleElement = window.document.createElement("span");
titleElement.append(title);
h1.append(titleElement);
const descriptionElement = window.document.createElement("small");
descriptionElement.append(description);
h1.append(descriptionElement);
return {
headerElement,
titleElement,
descriptionElement,
};
},
},
url: {
chartParamsWhitelist: ["from", "to"],
@@ -1124,6 +1289,20 @@ const utils = {
return Number(v);
},
},
date: {
/**
* @param {Date} v
*/
serialize(v) {
return utils.date.toString(v);
},
/**
* @param {string} v
*/
deserialize(v) {
return new Date(v);
},
},
},
formatters: {
dollars: new Intl.NumberFormat("en-US", {
@@ -1168,6 +1347,25 @@ const utils = {
: // @ts-ignore
new Date(time.year, time.month, time.day);
},
/**
* @param {Date} start
*/
getRangeUpToToday(start) {
return this.getRange(start, new Date());
},
/**
* @param {Date} start
* @param {Date} end
*/
getRange(start, end) {
const dates = /** @type {Date[]} */ ([]);
let currentDate = new Date(start);
while (currentDate <= end) {
dates.push(new Date(currentDate));
currentDate.setUTCDate(currentDate.getUTCDate() + 1);
}
return dates;
},
},
/**
*
@@ -1328,14 +1526,14 @@ const elements = {
selectedTitle: utils.dom.getElementById("selected-title"),
selectedDescription: utils.dom.getElementById("selected-description"),
selectors: utils.dom.getElementById("frame-selectors"),
chartList: utils.dom.getElementById("chart-list"),
chartsChartList: utils.dom.getElementById("charts-chart-list"),
legend: utils.dom.getElementById("legend"),
style: getComputedStyle(window.document.documentElement),
// timeScaleDateButtons: utils.dom.getElementById("timescale-date-buttons"),
// timeScaleHeightButtons: utils.dom.getElementById("timescale-height-buttons"),
selectedHeader: utils.dom.getElementById("selected-header"),
charts: utils.dom.getElementById("charts"),
simulation: utils.dom.getElementById("simulation"),
livePrice: utils.dom.getElementById("live-price"),
moscowTime: utils.dom.getElementById("moscow-time"),
};
/** @typedef {typeof elements} Elements */
@@ -1951,6 +2149,21 @@ function createDatasets(signals) {
scale,
url: baseURL,
fetch: _fetch,
fetchRange(start, end) {
const promises = /** @type {Promise<void>[]} */ ([]);
switch (scale) {
case "date": {
utils.array.range(start, end).forEach((year) => {
promises.push(this.fetch(year));
});
break;
}
default: {
throw "Unsupported";
}
}
return Promise.all(promises);
},
fetchedJSONs,
// drop() {
// dispose();
@@ -1985,7 +2198,7 @@ function createDatasets(signals) {
* @param {DatasetPath<S>} path
* @returns {ResourceDataset<S>}
*/
function getOrImport(scale, path) {
function getOrCreate(scale, path) {
if (scale === "date") {
const found = date.get(/** @type {DatePath} */ (path));
if (found) return /** @type {ResourceDataset<S>} */ (found);
@@ -2018,7 +2231,7 @@ function createDatasets(signals) {
}
return {
getOrImport,
getOrCreate,
};
}
/** @typedef {ReturnType<typeof createDatasets>} Datasets */
@@ -2088,18 +2301,18 @@ function initWebSockets(signals) {
/**
* @param {(candle: DatasetCandlestickData) => void} callback
* @returns
* @param {number} interval
*/
function krakenCandleWebSocketCreator(callback) {
const ws = new WebSocket("wss://ws.kraken.com");
function krakenCandleWebSocketCreator(callback, interval) {
const ws = new WebSocket("wss://ws.kraken.com/v2");
ws.addEventListener("open", () => {
ws.send(
JSON.stringify({
event: "subscribe",
pair: ["XBT/USD"],
subscription: {
name: "ohlc",
method: "subscribe",
params: {
channel: "ohlc",
symbol: ["BTC/USD"],
interval: 1440,
},
}),
@@ -2109,11 +2322,11 @@ function initWebSockets(signals) {
ws.addEventListener("message", (message) => {
const result = JSON.parse(message.data);
if (!Array.isArray(result)) return;
if (result.channel !== "ohlc") return;
const [timestamp, _, open, high, low, close, __, volume] = result[1];
const { timestamp, open, high, low, close } = result.data.at(-1);
const date = new Date(Number(timestamp) * 1000);
const date = new Date(timestamp);
const dateStr = utils.date.toString(date);
@@ -2134,12 +2347,18 @@ function initWebSockets(signals) {
return ws;
}
const krakenCandle = createWebsocket(krakenCandleWebSocketCreator);
const kraken1dCandle = createWebsocket((callback) =>
krakenCandleWebSocketCreator(callback, 1440),
);
const kraken5mnCandle = createWebsocket((callback) =>
krakenCandleWebSocketCreator(callback, 5),
);
krakenCandle.open();
kraken1dCandle.open();
kraken5mnCandle.open();
function createDocumentTitleEffect() {
signals.createEffect(krakenCandle.latest, (latest) => {
signals.createEffect(kraken5mnCandle.latest, (latest) => {
if (latest) {
const close = latest.close;
console.log("close:", close);
@@ -2153,7 +2372,8 @@ function initWebSockets(signals) {
createDocumentTitleEffect();
return {
krakenCandle,
kraken1dCandle,
// kraken5mnCandle,
};
}
/** @typedef {ReturnType<typeof initWebSockets>} WebSockets */
@@ -2252,6 +2472,8 @@ packages.signals().then((signals) =>
);
let firstChartOption = true;
let firstSimulationOption = true;
let firstLivePriceOption = true;
let firstMoscowTimeOption = true;
signals.createEffect(options.selected, (option) => {
if (previousElement) {
@@ -2262,7 +2484,13 @@ packages.signals().then((signals) =>
utils.url.replaceHistory({ pathname: option.id });
}
const hideTop = option.kind === "home" || option.kind === "pdf";
const hideTop =
option.kind === "home" ||
option.kind === "pdf" ||
option.kind === "live-price" ||
option.kind === "converter" ||
option.kind === "moscow-time";
elements.selectedHeader.hidden = hideTop;
elements.selectedTitle.innerHTML = option.title;
@@ -2277,6 +2505,8 @@ packages.signals().then((signals) =>
// break;
// }
case "chart": {
console.log("chart", option);
element = elements.charts;
lastChartOption.set(option);
@@ -2333,7 +2563,7 @@ packages.signals().then((signals) =>
ids,
lightweightCharts,
options,
selected: /** @type {any} */ (lastChartOption),
selected: option,
signals,
utils,
webSockets,
@@ -2347,7 +2577,77 @@ packages.signals().then((signals) =>
break;
}
default: {
case "live-price": {
console.log("live-price");
element = elements.livePrice;
if (firstLivePriceOption) {
const lightweightCharts = packages.lightweightCharts();
const script = import("./live-price.js");
utils.dom.importStyleAndThen("/styles/live-price.css", () =>
script.then(({ init }) =>
lightweightCharts.then((lightweightCharts) =>
signals.runWithOwner(owner, () =>
init({
colors,
consts,
dark,
datasets,
elements,
ids,
lightweightCharts,
options,
signals,
utils,
webSockets,
}),
),
),
),
);
}
firstLivePriceOption = false;
break;
}
case "moscow-time": {
console.log("moscow-time");
element = elements.moscowTime;
if (firstLivePriceOption) {
const lightweightCharts = packages.lightweightCharts();
const script = import("./moscow-time.js");
utils.dom.importStyleAndThen("/styles/moscow-time.css", () =>
script.then(({ init }) =>
signals.runWithOwner(owner, () =>
init({
colors,
consts,
dark,
datasets,
elements,
ids,
options,
signals,
utils,
webSockets,
}),
),
),
);
}
firstLivePriceOption = false;
break;
}
case "converter":
case "home":
case "pdf":
case "url": {
return;
}
}
+45
View File
@@ -0,0 +1,45 @@
/**
* @import {Options} from './options';
*/
/**
* @param {Object} args
* @param {Colors} args.colors
* @param {Consts} args.consts
* @param {Signals} args.signals
* @param {Utilities} args.utils
* @param {Options} args.options
* @param {Datasets} args.datasets
* @param {WebSockets} args.webSockets
* @param {Elements} args.elements
* @param {Ids} args.ids
* @param {Accessor<boolean>} args.dark
*/
export function init({
colors,
consts,
dark,
datasets,
elements,
ids,
options,
signals,
utils,
webSockets,
}) {
const moscowTimeElement = elements.moscowTime;
const satsPerDollar = signals.createMemo(
() =>
100_000_000 /
// webSockets.kraken5mnCandle.latest()?.close ||
(webSockets.kraken1dCandle.latest()?.close || 0),
);
const p = window.document.createElement("h1");
moscowTimeElement.append(p);
signals.createEffect(satsPerDollar, (satsPerDollar) => {
p.innerHTML = utils.formatters.dollars.format(satsPerDollar);
});
}
+555 -438
View File
File diff suppressed because it is too large Load Diff
+449 -207
View File
@@ -7,7 +7,7 @@
* @param {Colors} args.colors
* @param {Consts} args.consts
* @param {LightweightCharts} args.lightweightCharts
* @param {Accessor<ChartOption>} args.selected
* @param {SimulationOption} args.selected
* @param {Signals} args.signals
* @param {Utilities} args.utils
* @param {Options} args.options
@@ -38,22 +38,27 @@ export function init({
const resultsElement = window.document.createElement("div");
simulationElement.append(resultsElement);
const getDefaultIntervalStart = () => new Date("2021-04-15");
const getDefaultIntervalEnd = () => new Date();
const storagePrefix = "save-in-bitcoin";
const settings = {
initial: signals.createSignal(/** @type {number | null} */ (1000), {
save: {
...utils.serde.number,
id: `${storagePrefix}-initial-amount`,
param: "initial-amount",
},
}),
later: signals.createSignal(/** @type {number | null} */ (0), {
save: {
...utils.serde.number,
id: `${storagePrefix}-later-amount`,
param: "later-amount",
},
}),
initial: {
firstDay: signals.createSignal(/** @type {number | null} */ (1000), {
save: {
...utils.serde.number,
id: `${storagePrefix}-initial-amount`,
param: "initial-amount",
},
}),
overTime: signals.createSignal(/** @type {number | null} */ (0), {
save: {
...utils.serde.number,
id: `${storagePrefix}-later-amount`,
param: "later-amount",
},
}),
},
recurrent: {
amount: signals.createSignal(/** @type {number | null} */ (100), {
save: {
@@ -63,8 +68,45 @@ export function init({
},
}),
},
interval: {
start: signals.createSignal(
/** @type {Date | null} */ (getDefaultIntervalStart()),
{
save: {
...utils.serde.date,
id: `${storagePrefix}-interval-start`,
param: "interval-start",
},
},
),
end: signals.createSignal(
/** @type {Date | null} */ (getDefaultIntervalEnd()),
{
save: {
...utils.serde.date,
id: `${storagePrefix}-interval-end`,
param: "interval-end",
},
},
),
},
fees: {
percentage: signals.createSignal(/** @type {number | null} */ (0.25), {
save: {
...utils.serde.number,
id: `${storagePrefix}-percentage`,
param: "percentage",
},
}),
},
};
const { headerElement } = utils.dom.createHeader({
title: selected.title,
description: selected.serializedPath,
});
parametersElement.append(headerElement);
const initialGroup = createParameterGroup({
title: "Initial",
description:
@@ -78,7 +120,7 @@ export function init({
input: createInputDollar({
id: "simulation-dollars-initial",
title: "Initial amount of dollars converted",
signal: settings.initial,
signal: settings.initial.firstDay,
}),
}),
);
@@ -89,12 +131,17 @@ export function init({
input: createInputDollar({
id: "simulation-dollars-later",
title: "Dollars to spread over time",
signal: settings.later,
signal: settings.initial.overTime,
}),
}),
);
parametersElement.append(createHrElement());
const topUpGroup = createParameterGroup({
title: "Top Up",
description:
"The topUp amount of dollars you're willing to eventually save in Bitcoin.",
});
parametersElement.append(topUpGroup);
const recurrentGroup = createParameterGroup({
title: "Recurrent",
@@ -105,7 +152,7 @@ export function init({
recurrentGroup.append(
createInputField({
name: "Amount",
name: "Maximum Amount",
input: createInputDollar({
id: "simulation-dollars-recurrent",
title: "Recurrent dollar amount",
@@ -114,34 +161,92 @@ export function init({
}),
);
const frequencyUL = appendUl({ parent: recurrentGroup });
[{ name: "Daily" }, { name: "Weekly" }, { name: "Monthly" }].forEach(
({ name }) => {
const li = appendLi({ name, parent: frequencyUL });
},
);
const frequencyChoiceUL = appendUl({ parent: recurrentGroup });
const frequencyUL = utils.dom.createUlElement();
recurrentGroup.append(frequencyUL);
[
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
].forEach((name) => {
const li = appendLi({ name, parent: frequencyChoiceUL });
{ name: "Daily" },
{
name: "Weekly",
sub: [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
],
},
{
name: "Monthly",
sub: [
"The 1st",
"The 2nd",
"The 3rd",
"The 4th",
"The 5th",
"The 6th",
"The 7th",
"The 8th",
"The 9th",
"The 10th",
"The 11th",
"The 12th",
"The 13th",
"The 14th",
"The 15th",
"The 16th",
"The 17th",
"The 18th",
"The 19th",
"The 20th",
"The 21st",
"The 22nd",
"The 23rd",
"The 24th",
"The 25th",
"The 26th",
"The 27th",
"The 28th",
],
},
].forEach(({ name, sub }, index) => {
const li = utils.dom.createLiElement();
const { label, input } = utils.dom.createLabeledInput({
inputId: `frequency-${name}`,
inputName: "frequency",
inputValue: name.toLowerCase(),
labelTitle: name,
inputChecked: !index,
onClick: () => {},
});
label.append(name);
li.append(label);
if (sub) {
const parentName = name;
const ul = utils.dom.createUlElement();
li.append(ul);
sub.forEach((name) => {
const li = utils.dom.createLiElement();
const { label, input } = utils.dom.createLabeledInput({
inputId: `frequency-${parentName}-${name}`,
inputName: `frequency-${parentName}`,
inputValue: name.toLowerCase(),
labelTitle: name,
inputChecked: !index,
onClick: () => {},
});
label.append(name);
li.append(label);
ul.append(li);
});
}
frequencyUL.append(li);
});
parametersElement.append(createHrElement());
const today = signals.createSignal(utils.date.todayUTC());
setInterval(() => {
today.set(utils.date.todayUTC());
}, consts.FIVE_SECONDS_IN_MS);
const frequencyChoiceUL = utils.dom.createUlElement();
recurrentGroup.append(frequencyChoiceUL);
const intervalGroup = createParameterGroup({
title: "Interval",
@@ -149,42 +254,22 @@ export function init({
});
parametersElement.append(intervalGroup);
/**
* @param {Object} args
* @param {HTMLElement} args.parent
*/
function appendDiv({ parent }) {
const div = window.document.createElement("div");
parent.append(div);
return div;
}
console.log("weofpwklfpwkofwepokf");
function createInputDateField() {
const div = appendDiv({ parent: intervalGroup });
appendInputDate({
id: "",
title: "",
value: "2021-04-15",
parent: div,
signals,
today,
});
appendButton({
onClick: () => {},
text: "Reset",
title: "",
parent: div,
});
return div;
}
createInputDateField();
createInputDateField();
parametersElement.append(createHrElement());
createInputDateField({
signal: settings.interval.start,
getDefault: getDefaultIntervalStart,
parent: intervalGroup,
signals,
utils,
});
createInputDateField({
signal: settings.interval.end,
getDefault: getDefaultIntervalEnd,
parent: intervalGroup,
signals,
utils,
});
const feesGroup = createParameterGroup({
title: "Fees",
@@ -193,39 +278,275 @@ export function init({
});
parametersElement.append(feesGroup);
createInputNumber({
const input = utils.dom.createInputNumberElement({
id: "",
title: "",
value: 0.25,
parent: feesGroup,
signal: settings.fees.percentage,
min: 0,
max: 10,
max: 50,
step: 0.01,
signals,
});
feesGroup.append(input);
// parametersElement.append(utils.dom.createHrElement());
// const strategyGroup = createParameterGroup({
// title: "Strategy",
// description: "The strategy used to convert your fiat into Bitcoin",
// });
// parametersElement.append(strategyGroup);
// const ulStrategies = utils.dom.createUlElement();
// strategyGroup.append(ulStrategies);
// ["All in", "Weighted Local", "Weighted Cycle"].forEach((strategy) => {
// const li = utils.dom.createLiElement();
// li.append(strategy);
// ulStrategies.append(li);
// });
const parent = window.document.createElement("div");
parent.classList.add("chart-list");
resultsElement.append(parent);
signals.createEffect(settings.interval.start, (start) => {
console.log("start", start);
});
parametersElement.append(createHrElement());
const strategyGroup = createParameterGroup({
title: "Strategy",
description: "The strategy used to convert your fiat into Bitcoin",
signals.createEffect(settings.interval.end, (end) => {
console.log("end", end);
});
parametersElement.append(strategyGroup);
const ulStrategies = appendUl({ parent: strategyGroup });
const owner = signals.getOwner();
["All in", "Weighted Local", "Weighted Cycle"].forEach((strategy) => {
appendLi({
name: strategy,
parent: ulStrategies,
const closes = datasets.getOrCreate("date", "date-to-close");
closes.fetchRange(2009, new Date().getUTCFullYear()).then(() => {
signals.runWithOwner(owner, () => {
signals.createEffect(
() => ({
initialAmount: settings.initial.firstDay() || 0,
recurrentAmount: settings.recurrent.amount() || 0,
dollarsLeft: settings.initial.overTime() || 0,
start: settings.interval.start(),
end: settings.interval.end(),
fees: settings.fees.percentage(),
}),
// ({ initialAmount, recurrentAmount, dollarsLeft, start, end }) => {
// console.log({
// start,
// end,
// });
// },
({ initialAmount, recurrentAmount, dollarsLeft, start, end, fees }) => {
console.log({ start, end });
parent.innerHTML = "";
if (!start || !end || start > end) return;
const range = utils.date.getRange(start, end);
let investedAmount = 0;
/** @type {LineData<Time>[]} */
const investedData = [];
/** @type {LineData<Time>[]} */
const returnData = [];
/** @type {LineData<Time>[]} */
const bitcoinData = [];
/** @type {LineData<Time>[]} */
const resultData = [];
/** @type {LineData<Time>[]} */
const dollarsData = [];
/** @type {LineData<Time>[]} */
const totalData = [];
/** @type {LineData<Time>[]} */
const investmentData = [];
/** @type {LineData<Time>[]} */
const bitcoinAddedData = [];
let bitcoin = 0;
let feesPaid = 0;
range.forEach((date, index) => {
const year = date.getUTCFullYear();
const time = utils.date.toString(date);
const close = closes.fetchedJSONs
.at(utils.chunkIdToIndex("date", year))
?.json()?.dataset.map[utils.date.toString(date)];
if (!close) return;
let investmentPreFees =
(!index ? initialAmount : 0) + recurrentAmount;
if (dollarsLeft > 0) {
if (dollarsLeft >= recurrentAmount) {
investmentPreFees += recurrentAmount;
dollarsLeft -= recurrentAmount;
} else {
investmentPreFees += dollarsLeft;
dollarsLeft = 0;
}
}
let investment = investmentPreFees * (1 - (fees || 0) / 100);
feesPaid += investmentPreFees - investment;
const bitcoinAdded = investment / close;
bitcoin += bitcoinAdded;
investedAmount += investment;
const _return = close * bitcoin;
bitcoinData.push({
time,
value: bitcoin,
});
investedData.push({
time,
value: investedAmount,
});
returnData.push({
time,
value: _return,
});
resultData.push({
time,
value: (_return / investedAmount - 1) * 100,
});
dollarsData.push({
time,
value: dollarsLeft,
});
totalData.push({
time,
value: dollarsLeft + _return,
});
investmentData.push({
time,
value: investment,
});
bitcoinAddedData.push({
time,
value: bitcoinAdded,
});
});
(() => {
const chartWrapper = window.document.createElement("div");
chartWrapper.classList.add("chart-wrapper");
parent.append(chartWrapper);
const chartDiv = window.document.createElement("div");
chartDiv.classList.add("chart-div");
chartWrapper.append(chartDiv);
const chart = lightweightCharts.createChart({
scale: "date",
element: chartDiv,
signals,
colors,
options: {
handleScale: false,
handleScroll: false,
},
});
const line = chart.addLineSeries();
line.setData(investedData);
const line2 = chart.addLineSeries();
line2.setData(returnData);
const line3 = chart.addLineSeries();
line3.setData(dollarsData);
const line4 = chart.addLineSeries();
line4.setData(totalData);
const line5 = chart.addLineSeries();
line5.setData(investmentData);
chart.timeScale().fitContent();
})();
(() => {
const chartWrapper = window.document.createElement("div");
chartWrapper.classList.add("chart-wrapper");
parent.append(chartWrapper);
const chartDiv = window.document.createElement("div");
chartDiv.classList.add("chart-div");
chartWrapper.append(chartDiv);
const chart = lightweightCharts.createChart({
scale: "date",
element: chartDiv,
signals,
colors,
options: {
handleScale: false,
handleScroll: false,
},
});
const line = chart.addLineSeries();
line.setData(bitcoinData);
const line2 = chart.addLineSeries();
line2.setData(bitcoinAddedData);
chart.timeScale().fitContent();
})();
(() => {
const chartWrapper = window.document.createElement("div");
chartWrapper.classList.add("chart-wrapper");
parent.append(chartWrapper);
const chartDiv = window.document.createElement("div");
chartDiv.classList.add("chart-div");
chartWrapper.append(chartDiv);
const chart = lightweightCharts.createChart({
scale: "date",
element: chartDiv,
signals,
colors,
options: {
handleScale: false,
handleScroll: false,
},
});
const line = chart.addLineSeries();
line.setData(resultData);
chart.timeScale().fitContent();
})();
},
);
});
});
//
// On the side
// Value in Bitcoin
// Value in Dollars + total converted
//
// Value min estimated value in 4 years
//
}
/**
@@ -269,31 +590,6 @@ function createParameterGroup({ title, description }) {
return div;
}
function createHrElement() {
return window.document.createElement("hr");
}
/**
*@param {Object} args
*@param {string} args.id
*@param {string} args.title
*@param {number} args.value
*@param {HTMLElement} args.parent
*@param {number} args.min
*@param {number} args.max
*/
function createInputNumber({ id, title, value, parent, min, max }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "number";
input.value = String(value);
input.min = String(min);
input.max = String(max);
parent.append(input);
return input;
}
/**
* @param {Object} args
* @param {string} args.id
@@ -320,90 +616,36 @@ function createInputDollar({ id, title, signal }) {
}
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
*
* @param {Object} arg
* @param {Signal<Date | null>} arg.signal
* @param {() => Date | null} arg.getDefault
* @param {HTMLElement} arg.parent
* @param {Utilities} arg.utils
* @param {Signals} arg.signals
*/
function createInputRangePercentage({ id, title, signal }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "range";
input.min = "0";
input.max = "100";
function createInputDateField({ signal, getDefault, parent, signals, utils }) {
const div = window.document.createElement("div");
parent.append(div);
const value = signal();
input.value = value !== null ? String(value) : "";
div.append(
utils.dom.createInputDate({
id: "",
title: "",
signal,
signals,
}),
);
input.addEventListener("input", () => {
const value = input.value;
signal.set(value ? Number(value) : null);
const button = utils.dom.createButtonElement({
onClick: () => {
signal.set(getDefault());
},
text: "Reset",
title: "Reset field",
});
return input;
}
/**
* @param {Object} args
* @param {HTMLElement} args.parent
*/
function appendUl({ parent }) {
const ul = window.document.createElement("ul");
parent.append(ul);
return ul;
}
/**
* @param {Object} args
* @param {string} args.name
* @param {HTMLUListElement} args.parent
*/
function appendLi({ name, parent }) {
const li = window.document.createElement("li");
li.innerHTML = name;
parent.append(li);
return li;
}
/**
*@param {Object} args
*@param {string} args.id
*@param {string} args.title
*@param {string} args.value
*@param {HTMLElement} args.parent
*@param {Accessor<Date>} args.today
*@param {Signals} args.signals
*/
function appendInputDate({ id, title, value, parent, today, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "date";
input.value = value;
input.min = "2011-01-01";
signals.createEffect(today, (today) => {
input.max = today.toJSON().split("T")[0];
});
parent.append(input);
return input;
}
/**
*@param {Object} args
*@param {string} args.title
*@param {string} args.text
*@param {HTMLElement} args.parent
*@param {VoidFunction} args.onClick
*/
function appendButton({ title, text, onClick, parent }) {
const button = window.document.createElement("button");
button.title = title;
button.innerHTML = text;
button.addEventListener("click", onClick);
parent.append(button);
return button;
div.append(button);
return div;
}
+23 -2
View File
@@ -101,7 +101,6 @@ type Unit =
| "Weight";
interface PartialOption {
// icon: string;
name: string;
}
@@ -111,6 +110,18 @@ interface PartialHomeOption extends PartialOption {
name: "Home";
}
interface PartialLivePriceOption extends PartialOption {
kind: "live-price";
}
interface PartialMoscowTimeOption extends PartialOption {
kind: "moscow-time";
}
interface PartialConverterOption extends PartialOption {
kind: "converter";
}
interface PartialChartOption extends PartialOption {
scale: TimeScale;
title: string;
@@ -147,6 +158,9 @@ interface PartialOptionsGroup {
type AnyPartialOption =
| PartialHomeOption
| PartialLivePriceOption
| PartialMoscowTimeOption
| PartialConverterOption
| PartialChartOption
| PartialSimulationOption
| PartialPdfOption
@@ -168,6 +182,9 @@ type OptionPath = {
type HomeOption = PartialHomeOption & ProcessedOptionAddons;
type SimulationOption = PartialSimulationOption & ProcessedOptionAddons;
type LivePriceOption = PartialLivePriceOption & ProcessedOptionAddons;
type MoscowTimeOption = PartialMoscowTimeOption & ProcessedOptionAddons;
type ConverterOption = PartialConverterOption & ProcessedOptionAddons;
interface PdfOption extends PartialPdfOption, ProcessedOptionAddons {
kind: "pdf";
@@ -183,6 +200,9 @@ interface ChartOption extends PartialChartOption, ProcessedOptionAddons {
type Option =
| HomeOption
| LivePriceOption
| MoscowTimeOption
| ConverterOption
| PdfOption
| UrlOption
| ChartOption
@@ -208,7 +228,8 @@ interface ResourceDataset<
> {
scale: Scale;
url: string;
fetch: (id: number) => void;
fetch: (id: number) => Promise<void>;
fetchRange: (start: number, end: number) => Promise<void[]>;
fetchedJSONs: FetchedResult<Scale, Type>[];
// drop: VoidFunction;
}
+1 -68
View File
@@ -57,75 +57,8 @@
}
}
> #chart-list {
margin-top: 1rem;
position: relative;
.chart-list {
margin-left: var(--negative-main-padding);
margin-right: calc(var(--negative-main-padding) - 0.5rem);
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
z-index: 20;
> .chart-wrapper {
height: 100%;
position: relative;
min-height: 0px;
width: 100%;
cursor: crosshair;
&:has(+ .chart-wrapper:not([hidden])) {
height: calc(100% - 62px);
}
> fieldset {
pointer-events: none;
position: absolute;
left: 0px;
top: 0px;
z-index: 10;
display: flex;
align-items: center;
padding-left: var(--main-padding);
padding-right: var(--main-padding);
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
gap: 0.5rem;
color: var(--off-color);
> div.field {
display: flex;
align-items: center;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
gap: 1rem;
> legend,
> div {
flex-shrink: 0;
}
> hr {
min-width: 1rem;
}
label {
padding: 0.5rem;
margin: -0.5rem;
}
> div {
display: flex;
gap: 0.5rem;
}
}
}
> .chart-div {
width: 100%;
height: 100%;
}
}
}
}
+7
View File
@@ -0,0 +1,7 @@
#live-price {
> h1 {
font-size: 2.5rem;
line-height: 4rem;
text-align: center;
}
}
View File
+31 -3
View File
@@ -4,14 +4,17 @@
height: 100%;
width: 100%;
> div:first-child {
max-width: 20rem;
border-right: 1px;x
}
> div {
flex: 1;
/* min-height: 0; */
overflow-y: auto;
display: flex;
flex-direction: column;
width: 32rem;
gap: 1rem;
gap: 3rem;
> div {
display: flex;
@@ -26,4 +29,29 @@
gap: 0.5rem;
align-items: center;
}
.chart-list {
max-height: 500px;
/* margin-left: var(--negative-main-padding);*/
margin-right: calc(var(--negative-main-padding) - 0.5rem);
}
li {
label:has(input:not(:checked)) + ul {
display: none;
}
ul {
padding-left: 0.75rem;
margin-left:0.25rem;
border-left: 1px;
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
li {
display: inline;
}
}
}
}