bundler: init working version

This commit is contained in:
nym21
2025-06-14 20:17:49 +02:00
parent 65685c23e1
commit e9f362cc87
58 changed files with 5049 additions and 2239 deletions
+1
View File
@@ -3,6 +3,7 @@
# Builds
target
dist
# Copies
*\ copy*
Generated
+1691 -112
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -22,6 +22,7 @@ axum = "0.8.4"
bincode = { version = "2.0.1", features = ["serde"] }
bitcoin = { version = "0.32.6", features = ["serde"] }
bitcoincore-rpc = "0.19.0"
brk_bundler = { version = "0.0.56", path = "crates/brk_bundler" }
brk_cli = { version = "0.0.56", path = "crates/brk_cli" }
brk_computer = { version = "0.0.56", path = "crates/brk_computer" }
brk_core = { version = "0.0.56", path = "crates/brk_core" }
+1
View File
@@ -71,6 +71,7 @@ In contrast, existing alternatives tend to be either [very costly](https://studi
- [`brk_state`](https://crates.io/crates/brk_state): Various states used mainly by the computer
- [`brk_store`](https://crates.io/crates/brk_store): A thin wrapper around [`fjall`](https://crates.io/crates/fjall)
- [`brk_vec`](https://crates.io/crates/brk_vec): A push-only, truncable, compressable, saveable Vec
- [`brk_bundler`](https://crates.io/crates/brk_bundler): A crate that bundles the selected website for the server
## Hosting as a service
+3
View File
@@ -10,6 +10,7 @@ version.workspace = true
[features]
full = [
"bundler",
"core",
"computer",
"exit",
@@ -23,6 +24,7 @@ full = [
"store",
"vec",
]
bundler = ["brk_bundler"]
core = ["brk_core"]
computer = ["brk_computer"]
exit = ["brk_exit"]
@@ -37,6 +39,7 @@ store = ["brk_store"]
vec = ["brk_vec"]
[dependencies]
brk_bundler = { workspace = true, optional = true }
brk_cli = { workspace = true }
brk_core = { workspace = true, optional = true }
brk_computer = { workspace = true, optional = true }
+1
View File
@@ -0,0 +1 @@
fn main() {}
+7
View File
@@ -1,5 +1,12 @@
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
#[cfg(feature = "bundler")]
#[doc(inline)]
pub use brk_bundle as bundle;
#[doc(inline)]
pub use brk_cli as cli;
#[cfg(feature = "core")]
#[doc(inline)]
pub use brk_core as core;
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "brk_bundler"
description = "A crate that bundles the selected website for the server"
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
log = { workspace = true }
minify-html-onepass = "0.16.4"
notify = "8.0.0"
rolldown = { git = "https://github.com/rolldown/rolldown", branch = "main" }
sugar_path = { version = "1.2.0", features = ["cached_current_dir"] }
tokio = { workspace = true }
+136
View File
@@ -0,0 +1,136 @@
use std::{fs, io, path::Path, sync::Arc};
use log::error;
use minify_html_onepass::Cfg;
use notify::{EventKind, RecursiveMode, Watcher};
use rolldown::{Bundler, BundlerOptions, RawMinifyOptions, SourceMapType};
use sugar_path::SugarPath;
use tokio::sync::Mutex;
const VERSION: &str = env!("CARGO_PKG_VERSION");
pub async fn bundle(websites_path: &Path, source_folder: &str, watch: bool) -> io::Result<()> {
let source_path = websites_path.join(source_folder);
let dist_path = websites_path.join("dist");
let _ = fs::remove_dir_all(&dist_path);
copy_dir_all(&source_path, &dist_path)?;
let source_scripts = format!("./{source_folder}/scripts");
let source_entry = format!("{source_scripts}/entry.js");
let absolute_websites_path = websites_path.absolutize();
let mut bundler = Bundler::new(BundlerOptions {
input: Some(vec![source_entry.into()]),
dir: Some("./dist/scripts".to_string()),
cwd: Some(absolute_websites_path),
minify: Some(RawMinifyOptions::Bool(true)),
sourcemap: Some(SourceMapType::File),
..Default::default()
});
bundler.write().await.unwrap();
let absolute_source_path = source_path.absolutize();
let absolute_source_path_clone = absolute_source_path.clone();
let absolute_dist_path = dist_path.absolutize();
let absolute_dist_path_clone = absolute_dist_path.clone();
let absolute_source_scripts_path = websites_path.join(source_scripts).absolutize();
let absolute_source_index_path = source_path.join("index.html").absolutize();
let absolute_source_index_path_clone = absolute_source_index_path.clone();
let absolute_dist_index_path = dist_path.join("index.html").absolutize();
let absolute_source_sw_path = source_path.join("service-worker.js").absolutize();
let absolute_source_sw_path_clone = absolute_source_sw_path.clone();
let absolute_dist_sw_path = dist_path.join("service-worker.js").absolutize();
let write_index = move || {
let mut contents = fs::read_to_string(&absolute_source_index_path).unwrap();
if let Ok(entry) = fs::read_to_string(absolute_dist_path_clone.join("scripts/entry.js")) {
let start = entry.find("main").unwrap();
let end = entry.find(".js").unwrap();
let main_hashed = &entry[start..end];
contents = contents.replace("/scripts/main.js", &format!("/scripts/{main_hashed}.js"));
}
if let Ok(contents) = minify_html_onepass::in_place_str(
contents.as_mut_str(),
&Cfg {
minify_js: false,
minify_css: false,
},
) {
let _ = fs::write(&absolute_dist_index_path, contents);
}
};
let write_sw = move || {
let contents = fs::read_to_string(&absolute_source_sw_path)
.unwrap()
.replace("__VERSION__", &format!("v{VERSION}"));
let _ = fs::write(&absolute_dist_sw_path, contents);
};
write_index();
write_sw();
if !watch {
return Ok(());
}
tokio::spawn(async move {
let mut watcher = notify::recommended_watcher(
move |res: Result<notify::Event, notify::Error>| match res {
Ok(event) => match event.kind {
EventKind::Create(_) => event.paths,
EventKind::Modify(_) => event.paths,
_ => vec![],
}
.into_iter()
.filter(|path| path.starts_with(&absolute_source_path))
.filter(|path| !path.starts_with(&absolute_source_scripts_path))
.for_each(|source_path| {
let suffix = source_path.strip_prefix(&absolute_source_path).unwrap();
let dist_path = absolute_dist_path.join(suffix);
if source_path == absolute_source_index_path_clone {
write_index();
} else if source_path == absolute_source_sw_path_clone {
write_sw();
} else {
let _ = fs::copy(&source_path, &dist_path);
}
}),
Err(e) => error!("watch error: {:?}", e),
},
)
.unwrap();
if watch {
watcher
.watch(&absolute_source_path_clone, RecursiveMode::Recursive)
.unwrap();
let watcher =
rolldown::Watcher::new(vec![Arc::new(Mutex::new(bundler))], None).unwrap();
watcher.start().await;
}
});
Ok(())
}
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
fs::create_dir_all(&dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
} else {
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
+15 -14
View File
@@ -63,8 +63,9 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
let server = Server::new(served_indexer, served_computer, config.website())?;
let watch = config.watch();
let opt = Some(tokio::spawn(async move {
server.serve().await.unwrap();
server.serve(watch).await.unwrap();
}));
sleep(Duration::from_secs(1));
@@ -178,6 +179,11 @@ pub struct RunConfig {
#[arg(long, value_name = "SECONDS")]
delay: Option<u64>,
/// DEV: Activate to watch the selected website's folder for changes, default: false, saved
#[serde(default, deserialize_with = "default_on_error")]
#[arg(long, value_name = "BOOL")]
watch: Option<bool>,
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
#[serde(default, deserialize_with = "default_on_error")]
#[arg(long, value_name = "BOOL")]
@@ -255,6 +261,10 @@ impl RunConfig {
config_saved.check_collisions = Some(check_collisions);
}
if let Some(watch) = config_args.watch.take() {
config_saved.watch = Some(watch);
}
if config_args != RunConfig::default() {
dbg!(config_args);
panic!("Didn't consume the full config")
@@ -267,19 +277,6 @@ impl RunConfig {
config.write(&path)?;
// info!("Configuration {{");
// info!(" bitcoindir: {:?}", config.bitcoindir);
// info!(" brkdir: {:?}", config.brkdir);
// info!(" services: {:?}", config.services);
// info!(" website: {:?}", config.website);
// info!(" rpcconnect: {:?}", config.rpcconnect);
// info!(" rpcport: {:?}", config.rpcport);
// info!(" rpccookiefile: {:?}", config.rpccookiefile);
// info!(" rpcuser: {:?}", config.rpcuser);
// info!(" rpcpassword: {:?}", config.rpcpassword);
// info!(" delay: {:?}", config.delay);
// info!("}}");
Ok(config)
}
@@ -448,6 +445,10 @@ impl RunConfig {
pub fn check_collisions(&self) -> bool {
self.check_collisions.is_some_and(|b| b)
}
pub fn watch(&self) -> bool {
self.watch.is_some_and(|b| b)
}
}
#[derive(
+6 -2
View File
@@ -25,7 +25,7 @@ pub fn init(path: Option<&Path>) {
.unwrap()
});
Builder::from_env(Env::default().default_filter_or("info,fjall=off,lsm_tree=off"))
Builder::from_env(Env::default().default_filter_or("info,fjall=off,lsm_tree=off,rolldown=off"))
.format(move |buf, record| {
let date_time = Timestamp::now()
.to_zoned(tz::TimeZone::system())
@@ -80,5 +80,9 @@ fn write(
args: impl Display,
) -> Result<(), std::io::Error> {
writeln!(buf, "{} {} {} {}", date_time, dash, level, args)
// writeln!(buf, "{} {} {} {} {}", date_time, _target, level, dash, args)
// writeln!(
// buf,
// "{} {} {} {} {}",
// date_time, _target, level, dash, args
// )
}
+2 -1
View File
@@ -10,6 +10,7 @@ repository.workspace = true
[dependencies]
axum = { workspace = true }
bitcoincore-rpc = { workspace = true }
brk_bundler = { workspace = true }
brk_computer = { workspace = true }
brk_exit = { workspace = true }
brk_core = { workspace = true }
@@ -25,7 +26,7 @@ color-eyre = { workspace = true }
jiff = { workspace = true }
log = { workspace = true }
minreq = { workspace = true }
oxc = { version = "0.72.3", features = ["codegen", "minifier"] }
oxc = { version = "0.73.0", features = ["codegen", "minifier"] }
serde = { workspace = true }
tokio = { workspace = true }
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
+1 -1
View File
@@ -51,7 +51,7 @@ pub fn main() -> color_eyre::Result<()> {
let server = Server::new(served_indexer, served_computer, Website::Default)?;
let server = tokio::spawn(async move {
server.serve().await.unwrap();
server.serve(true).await.unwrap();
});
if process {
+4 -8
View File
@@ -32,16 +32,12 @@ fn any_handler(
app_state: AppState,
path: Option<extract::Path<String>>,
) -> Response {
let website_path = app_state
.websites_path
.as_ref()
.expect("Should never reach here is websites_path is None")
.join(app_state.website.to_folder_name());
let dist_path = app_state.dist_path();
if let Some(path) = path.as_ref() {
let path = path.0.replace("..", "").replace("\\", "");
let mut path = website_path.join(&path);
let mut path = dist_path.join(&path);
if !path.exists() || path.is_dir() {
if path.extension().is_some() {
@@ -55,13 +51,13 @@ fn any_handler(
return response;
} else {
path = website_path.join("index.html");
path = dist_path.join("index.html");
}
}
path_to_response(&headers, &path)
} else {
path_to_response(&headers, &website_path.join("index.html"))
path_to_response(&headers, &dist_path.join("index.html"))
}
}
+15 -1
View File
@@ -19,6 +19,7 @@ use axum::{
routing::get,
serve,
};
use brk_bundler::bundle;
use brk_computer::Computer;
use brk_core::dot_brk_path;
use brk_indexer::Indexer;
@@ -45,6 +46,15 @@ pub struct AppState {
websites_path: Option<PathBuf>,
}
impl AppState {
pub fn dist_path(&self) -> PathBuf {
self.websites_path
.as_ref()
.expect("Should never reach here is websites_path is None")
.join("dist")
}
}
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
const DEV_PATH: &str = "../..";
@@ -103,9 +113,13 @@ impl Server {
}))
}
pub async fn serve(self) -> color_eyre::Result<()> {
pub async fn serve(self, watch: bool) -> color_eyre::Result<()> {
let state = self.0;
if let Some(websites_path) = state.websites_path.clone() {
bundle(&websites_path, state.website.to_folder_name(), watch).await?;
}
let compression_layer = CompressionLayer::new()
.br(true)
.deflate(true)
+1 -1
View File
@@ -167,7 +167,7 @@ impl HeaderMapExtended for HeaderMap {
fn insert_content_type(&mut self, path: &Path) {
match path.extension().unwrap().to_str().unwrap() {
"js" => self.insert_content_type_application_javascript(),
"json" => self.insert_content_type_application_json(),
"json" | "map" => self.insert_content_type_application_json(),
"html" => self.insert_content_type_text_html(),
"css" => self.insert_content_type_text_css(),
"toml" | "txt" => self.insert_content_type_text_plain(),
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env bash
DATE=$(date -u '+%Y-%m-%d_%H-%M-%S')
OUTPUT="/assets/pwa/${DATE}"
mkdir ".${OUTPUT}"
cp "./assets/pwa/index.html" ".${OUTPUT}/"
pwa-asset-generator "../assets/dove-orange.svg" ".${OUTPUT}" \
--index ".${OUTPUT}/index.html" \
--manifest "./manifest.webmanifest" \
--favicon \
--padding "0%" \
--path-override "${OUTPUT}" \
--quality "100" \
--opaque "false"
pwa-asset-generator "../assets/dove-white.svg" ".${OUTPUT}" \
--index ".${OUTPUT}/index.html" \
--manifest "./manifest.webmanifest" \
--icon-only \
--background "#f26610" \
--padding "10%" \
--path-override "${OUTPUT}" \
--quality "100"
pwa-asset-generator "../assets/logo-stamp-orange.svg" ".${OUTPUT}" \
--index ".${OUTPUT}/index.html" \
--splash-only \
--background "#f26610" \
--padding "min(30vh, 30vw)" \
--path-override "${OUTPUT}" \
--quality "100"
+268 -6
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
@@ -12,7 +12,7 @@
/>
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="mobile-web-app-capable" content="yes" />
<script type="module" crossorigin src="/scripts/main.js"></script>
<script type="module" src="/scripts/main.js"></script>
<!-- ------ -->
<!-- Styles -->
@@ -281,7 +281,7 @@
@font-face {
font-family: "Geist mono";
src: url("./assets/fonts/geist_mono_var_1_4_01.woff2") format("woff2");
src: url("./assets/fonts/geist_mono_var_v1_5_0.woff2") format("woff2");
font-weight: 100 900;
font-display: block;
font-style: normal;
@@ -1103,6 +1103,268 @@
}
}
}
#charts {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
min-height: 0;
padding: var(--main-padding);
header {
flex-shrink: 0;
display: flex;
white-space: nowrap;
overflow-x: auto;
padding-bottom: 1rem;
margin-bottom: -2rem;
padding-left: var(--main-padding);
margin-left: var(--negative-main-padding);
padding-right: var(--main-padding);
margin-right: var(--negative-main-padding);
& > * {
flex: 1;
}
}
.chart {
flex: 1;
}
> .chart > legend,
> fieldset {
z-index: 20;
}
.lightweight-chart {
z-index: 40;
}
}
#table {
width: 100%;
display: flex;
flex-direction: column;
gap: 2rem;
padding: var(--main-padding);
> div {
display: flex;
font-size: var(--font-size-xs);
line--line-height: var(--line-height-xs);
font-weight: 450;
margin-left: var(--negative-main-padding);
margin-right: var(--negative-main-padding);
table {
z-index: 10;
border-top-width: 1px;
border-style: dashed !important;
/* width: 100%; */
line-height: var(--line-height-sm);
text-transform: uppercase;
table-layout: auto;
border-collapse: separate;
border-spacing: 0;
/* border: 3px solid purple; */
/* min-height: 100%; */
}
th {
font-weight: 600;
}
th,
td {
/* border-top: 1px; */
border-right: 1px;
border-bottom: 1px;
border-color: var(--off-color);
border-style: dashed !important;
padding: 0.25rem 0.75rem;
}
td {
text-transform: lowercase;
}
a {
margin: -0.2rem 0;
font-size: 1.2rem;
}
th:first-child {
padding-left: var(--main-padding);
}
th[scope="col"] {
position: sticky;
top: 0;
background-color: var(--background-color);
> div {
display: flex;
flex-direction: column;
padding-top: 0.275rem;
> div {
display: flex;
gap: 0.25rem;
text-transform: lowercase;
color: var(--off-color);
text-align: left;
&:first-child {
gap: 0.5rem;
}
&:last-child {
gap: 1rem;
}
> span {
width: 100%;
}
> button {
padding: 0 0.25rem;
margin: 0 -0.25rem;
font-size: 0.75rem;
line-height: 0;
}
}
}
&:first-child {
button {
display: none;
}
}
&:nth-child(2) {
button:nth-of-type(1) {
display: none;
}
}
&:last-child {
button:nth-of-type(2) {
display: none;
}
}
}
select {
margin-right: -4px;
/* width: 100%; */
}
tbody {
text-align: right;
}
> button {
padding: 1rem;
min-width: 10rem;
display: flex;
flex-direction: column;
flex: 1;
position: relative;
border-top-width: 1px;
width: 100%;
border-bottom-width: 1px;
border-style: dashed !important;
> span {
text-align: left;
position: sticky;
top: 2rem;
left: 0;
right: 0;
}
}
}
}
#simulation {
min-height: 0;
width: 100%;
> div {
display: flex;
flex-direction: column;
gap: 2rem;
padding: var(--main-padding);
}
@media (max-width: 767px) {
overflow-y: auto;
> div:first-child {
border-bottom: 1px;
}
}
@media (min-width: 768px) {
display: flex;
flex-direction: column;
height: 100%;
flex-direction: row;
> div {
flex: 1;
overflow-y: auto;
padding-bottom: var(--bottom-area);
}
> div:first-child {
max-width: var(--default-main-width);
border-right: 1px;
}
}
header {
margin-bottom: 0.5rem;
}
> div:last-child {
display: flex;
flex-direction: column;
gap: 1.5rem;
overflow-x: hidden;
p {
text-wrap: pretty;
}
}
label {
> span {
display: block;
}
small {
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
display: block;
}
}
.chart {
flex: none;
height: 400px;
.lightweight-chart {
margin-left: calc(var(--negative-main-padding) * 0.75);
fieldset {
margin-left: -0.5rem;
}
}
}
}
</style>
<!-- ------- -->
@@ -1113,7 +1375,7 @@
// @ts-check
const preferredColorSchemeMatchMedia = window.matchMedia(
"(prefers-color-scheme: dark)"
"(prefers-color-scheme: dark)",
);
const themeColor = window.document.createElement("meta");
@@ -1123,7 +1385,7 @@
/** @param {boolean} dark */
function updateThemeColor(dark) {
const theme = getComputedStyle(
window.document.documentElement
window.document.documentElement,
).getPropertyValue(dark ? "--black" : "--white");
themeColor.content = theme;
}
@@ -1133,7 +1395,7 @@
"change",
({ matches }) => {
updateThemeColor(matches);
}
},
);
if ("standalone" in window.navigator && !!window.navigator.standalone) {
-46
View File
@@ -1,46 +0,0 @@
(async () => {
const theme = await (
await fetch(
"https://github.com/tailwindlabs/tailwindcss/blob/main/packages/tailwindcss/theme.css",
)
).text();
console.log(
[
"red",
"orange",
"amber",
"yellow",
"lime",
"green",
"emerald",
"teal",
"cyan",
"sky",
"blue",
"indigo",
"violet",
"purple",
"fuchsia",
"pink",
"rose",
]
.map((color) => {
const [a, b] = [500, 600].map((shade) => {
const regExp = new RegExp(
`(?<=${`${color}-${shade}: oklch\(`})(.*?)(?=\\s*${`\);`})`,
"g",
);
let res = regExp.exec(theme)?.[2];
if (!res) throw "err";
res = res.replace("(", "");
res = res.replace(")", "");
// return res
return res.split(" ").map((s) => Number(s));
});
const mult = 10_000;
return `--${color}: oklch(${[0, 1, 2].map((i) => Math.round(((a[i] + b[i]) / 2) * mult) / mult).join(" ")})`;
})
.join(";\n"),
);
})();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
https://app.unpkg.com/@solidjs/signals@latest/files/dist/prod.js
-4
View File
@@ -1,4 +0,0 @@
import { Accessor, Setter } from "./v0.3.2-treeshaked/types/signals";
export type Signal<T> = Accessor<T> & { set: Setter<T>; reset: VoidFunction };
export type Signals = Awaited<typeof import("./wrapper.js").default>;
@@ -1,679 +0,0 @@
// @ts-nocheck
// src/core/error.ts
var NotReadyError = class extends Error {};
var EffectError = class extends Error {
constructor(effect, cause) {
super("");
this.cause = cause;
}
};
// src/core/constants.ts
var STATE_CLEAN = 0;
var STATE_CHECK = 1;
var STATE_DIRTY = 2;
var STATE_DISPOSED = 3;
var EFFECT_PURE = 0;
var EFFECT_RENDER = 1;
var EFFECT_USER = 2;
// src/core/scheduler.ts
var clock = 0;
function getClock() {
return clock;
}
function incrementClock() {
clock++;
}
var scheduled = false;
function schedule() {
if (scheduled) return;
scheduled = true;
if (!globalQueue.u) queueMicrotask(flushSync);
}
var pureQueue = [];
var Queue = class {
i = null;
u = false;
v = [[], []];
t = [];
created = clock;
enqueue(type, fn) {
pureQueue.push(fn);
if (type) this.v[type - 1].push(fn);
schedule();
}
run(type) {
if (type === EFFECT_PURE) {
pureQueue.length && runQueue(pureQueue, type);
pureQueue = [];
return;
} else if (this.v[type - 1].length) {
const effects = this.v[type - 1];
this.v[type - 1] = [];
runQueue(effects, type);
}
for (let i = 0; i < this.t.length; i++) {
this.t[i].run(type);
}
}
flush() {
if (this.u) return;
this.u = true;
try {
this.run(EFFECT_PURE);
incrementClock();
scheduled = false;
this.run(EFFECT_RENDER);
this.run(EFFECT_USER);
} finally {
this.u = false;
}
}
addChild(child) {
this.t.push(child);
child.i = this;
}
removeChild(child) {
const index = this.t.indexOf(child);
if (index >= 0) this.t.splice(index, 1);
}
notify(...args) {
if (this.i) return this.i.notify(...args);
return false;
}
};
var globalQueue = new Queue();
function flushSync() {
while (scheduled) {
globalQueue.flush();
}
}
function runQueue(queue, type) {
for (let i = 0; i < queue.length; i++) queue[i](type);
}
// src/core/owner.ts
var currentOwner = null;
var defaultContext = {};
function getOwner() {
return currentOwner;
}
function setOwner(owner) {
const out = currentOwner;
currentOwner = owner;
return out;
}
var Owner = class {
// We flatten the owner tree into a linked list so that we don't need a pointer to .firstChild
// However, the children are actually added in reverse creation order
// See comment at the top of the file for an example of the _nextSibling traversal
i = null;
h = null;
l = null;
a = STATE_CLEAN;
f = null;
j = defaultContext;
g = globalQueue;
F = 0;
id = null;
constructor(id = null, skipAppend = false) {
this.id = id;
if (currentOwner) {
!skipAppend && currentOwner.append(this);
}
}
append(child) {
child.i = this;
child.l = this;
if (this.h) this.h.l = child;
child.h = this.h;
this.h = child;
if (this.id != null && child.id == null) child.id = this.getNextChildId();
if (child.j !== this.j) {
child.j = { ...this.j, ...child.j };
}
if (this.g) child.g = this.g;
}
dispose(self = true) {
if (this.a === STATE_DISPOSED) return;
let head = self ? this.l || this.i : this,
current = this.h,
next = null;
while (current && current.i === this) {
current.dispose(true);
current.o();
next = current.h;
current.h = null;
current = next;
}
this.F = 0;
if (self) this.o();
if (current) current.l = !self ? this : this.l;
if (head) head.h = current;
}
o() {
if (this.l) this.l.h = null;
this.i = null;
this.l = null;
this.j = defaultContext;
this.a = STATE_DISPOSED;
this.emptyDisposal();
}
emptyDisposal() {
if (!this.f) return;
if (Array.isArray(this.f)) {
for (let i = 0; i < this.f.length; i++) {
const callable = this.f[i];
callable.call(callable);
}
} else {
this.f.call(this.f);
}
this.f = null;
}
getNextChildId() {
if (this.id != null) return formatId(this.id, this.F++);
throw new Error("Cannot get child id from owner without an id");
}
};
function onCleanup(fn) {
if (!currentOwner) return fn;
const node = currentOwner;
if (!node.f) {
node.f = fn;
} else if (Array.isArray(node.f)) {
node.f.push(fn);
} else {
node.f = [node.f, fn];
}
return fn;
}
function formatId(prefix, id) {
const num = id.toString(36),
len = num.length - 1;
return prefix + (len ? String.fromCharCode(64 + len) : "") + num;
}
// src/core/flags.ts
var ERROR_OFFSET = 0;
var ERROR_BIT = 1 << ERROR_OFFSET;
var LOADING_OFFSET = 1;
var LOADING_BIT = 1 << LOADING_OFFSET;
var UNINITIALIZED_OFFSET = 2;
var UNINITIALIZED_BIT = 1 << UNINITIALIZED_OFFSET;
var DEFAULT_FLAGS = ERROR_BIT;
// src/core/core.ts
var currentObserver = null;
var currentMask = DEFAULT_FLAGS;
var newSources = null;
var newSourcesIndex = 0;
var newFlags = 0;
var notStale = false;
var UNCHANGED = Symbol(0);
var Computation = class extends Owner {
b = null;
c = null;
e;
w;
p;
// Used in __DEV__ mode, hopefully removed in production
J;
// Using false is an optimization as an alternative to _equals: () => false
// which could enable more efficient DIRTY notification
A = isEqual;
G;
/** Whether the computation is an error or has ancestors that are unresolved */
d = 0;
/** Which flags raised by sources are handled, vs. being passed through. */
B = DEFAULT_FLAGS;
q = -1;
n = false;
constructor(initialValue, compute2, options) {
super(options?.id, compute2 === null);
this.p = compute2;
this.a = compute2 ? STATE_DIRTY : STATE_CLEAN;
this.d = compute2 && initialValue === void 0 ? UNINITIALIZED_BIT : 0;
this.e = initialValue;
if (options?.equals !== void 0) this.A = options.equals;
if (options?.unobserved) this.G = options?.unobserved;
}
H() {
if (this.p) {
if (this.d & ERROR_BIT && this.q <= getClock()) update(this);
else this.r();
}
track(this);
newFlags |= this.d & ~currentMask;
if (this.d & ERROR_BIT) {
throw this.w;
} else {
return this.e;
}
}
/**
* Return the current value of this computation
* Automatically re-executes the surrounding computation when the value changes
*/
read() {
return this.H();
}
/**
* Return the current value of this computation
* Automatically re-executes the surrounding computation when the value changes
*
* If the computation has any unresolved ancestors, this function waits for the value to resolve
* before continuing
*/
wait() {
if (this.p && this.d & ERROR_BIT && this.q <= getClock()) {
update(this);
} else {
this.r();
}
track(this);
if ((notStale || this.d & UNINITIALIZED_BIT) && this.d & LOADING_BIT) {
throw new NotReadyError();
}
return this.H();
}
/** Update the computation with a new value. */
write(value, flags = 0, raw = false) {
const newValue =
!raw && typeof value === "function" ? value(this.e) : value;
const valueChanged =
newValue !== UNCHANGED &&
(!!(this.d & UNINITIALIZED_BIT) ||
this.d & LOADING_BIT & ~flags ||
this.A === false ||
!this.A(this.e, newValue));
if (valueChanged) {
this.e = newValue;
this.w = void 0;
}
const changedFlagsMask = this.d ^ flags,
changedFlags = changedFlagsMask & flags;
this.d = flags;
this.q = getClock() + 1;
if (this.c) {
for (let i = 0; i < this.c.length; i++) {
if (valueChanged) {
this.c[i].k(STATE_DIRTY);
} else if (changedFlagsMask) {
this.c[i].I(changedFlagsMask, changedFlags);
}
}
}
return this.e;
}
/**
* Set the current node's state, and recursively mark all of this node's observers as STATE_CHECK
*/
k(state, skipQueue) {
if (this.a >= state && !this.n) return;
this.n = !!skipQueue;
this.a = state;
if (this.c) {
for (let i = 0; i < this.c.length; i++) {
this.c[i].k(STATE_CHECK, skipQueue);
}
}
}
/**
* Notify the computation that one of its sources has changed flags.
*
* @param mask A bitmask for which flag(s) were changed.
* @param newFlags The source's new flags, masked to just the changed ones.
*/
I(mask, newFlags2) {
if (this.a >= STATE_DIRTY) return;
if (mask & this.B) {
this.k(STATE_DIRTY);
return;
}
if (this.a >= STATE_CHECK) return;
const prevFlags = this.d & mask;
const deltaFlags = prevFlags ^ newFlags2;
if (newFlags2 === prevFlags);
else if (deltaFlags & prevFlags & mask) {
this.k(STATE_CHECK);
} else {
this.d ^= deltaFlags;
if (this.c) {
for (let i = 0; i < this.c.length; i++) {
this.c[i].I(mask, newFlags2);
}
}
}
}
C(error) {
this.w = error;
this.write(
UNCHANGED,
(this.d & ~LOADING_BIT) | ERROR_BIT | UNINITIALIZED_BIT
);
}
/**
* This is the core part of the reactivity system, which makes sure that the values are updated
* before they are read. We've also adapted it to return the loading state of the computation,
* so that we can propagate that to the computation's observers.
*
* This function will ensure that the value and states we read from the computation are up to date
*/
r() {
if (!this.p) {
return;
}
if (this.a === STATE_DISPOSED) {
return;
}
if (this.a === STATE_CLEAN) {
return;
}
let observerFlags = 0;
if (this.a === STATE_CHECK) {
for (let i = 0; i < this.b.length; i++) {
this.b[i].r();
observerFlags |= this.b[i].d;
if (this.a === STATE_DIRTY) {
break;
}
}
}
if (this.a === STATE_DIRTY) {
update(this);
} else {
this.write(UNCHANGED, observerFlags);
this.a = STATE_CLEAN;
}
}
/**
* Remove ourselves from the owner graph and the computation graph
*/
o() {
if (this.a === STATE_DISPOSED) return;
if (this.b) removeSourceObservers(this, 0);
super.o();
}
};
function track(computation) {
if (currentObserver) {
if (
!newSources &&
currentObserver.b &&
currentObserver.b[newSourcesIndex] === computation
) {
newSourcesIndex++;
} else if (!newSources) newSources = [computation];
else if (computation !== newSources[newSources.length - 1]) {
newSources.push(computation);
}
}
}
function update(node) {
const prevSources = newSources,
prevSourcesIndex = newSourcesIndex,
prevFlags = newFlags;
newSources = null;
newSourcesIndex = 0;
newFlags = 0;
try {
node.dispose(false);
node.emptyDisposal();
const result = compute(node, node.p, node);
node.write(result, newFlags, true);
} catch (error) {
if (error instanceof NotReadyError) {
node.write(
UNCHANGED,
newFlags | LOADING_BIT | (node.d & UNINITIALIZED_BIT)
);
} else {
node.C(error);
}
} finally {
if (newSources) {
if (node.b) removeSourceObservers(node, newSourcesIndex);
if (node.b && newSourcesIndex > 0) {
node.b.length = newSourcesIndex + newSources.length;
for (let i = 0; i < newSources.length; i++) {
node.b[newSourcesIndex + i] = newSources[i];
}
} else {
node.b = newSources;
}
let source;
for (let i = newSourcesIndex; i < node.b.length; i++) {
source = node.b[i];
if (!source.c) source.c = [node];
else source.c.push(node);
}
} else if (node.b && newSourcesIndex < node.b.length) {
removeSourceObservers(node, newSourcesIndex);
node.b.length = newSourcesIndex;
}
newSources = prevSources;
newSourcesIndex = prevSourcesIndex;
newFlags = prevFlags;
node.q = getClock() + 1;
node.a = STATE_CLEAN;
}
}
function removeSourceObservers(node, index) {
let source;
let swap;
for (let i = index; i < node.b.length; i++) {
source = node.b[i];
if (source.c) {
swap = source.c.indexOf(node);
source.c[swap] = source.c[source.c.length - 1];
source.c.pop();
if (!source.c.length) source.G?.();
}
}
}
function isEqual(a, b) {
return a === b;
}
function untrack(fn) {
if (currentObserver === null) return fn();
return compute(getOwner(), fn, null);
}
function latest(fn, fallback) {
const argLength = arguments.length;
const prevFlags = newFlags;
const prevNotStale = notStale;
notStale = false;
try {
return fn();
} catch (err) {
if (argLength > 1 && err instanceof NotReadyError) return fallback;
throw err;
} finally {
newFlags = prevFlags;
notStale = prevNotStale;
}
}
function compute(owner, fn, observer) {
const prevOwner = setOwner(owner),
prevObserver = currentObserver,
prevMask = currentMask,
prevNotStale = notStale;
currentObserver = observer;
currentMask = observer?.B ?? DEFAULT_FLAGS;
notStale = true;
try {
return fn(observer ? observer.e : void 0);
} finally {
setOwner(prevOwner);
currentObserver = prevObserver;
currentMask = prevMask;
notStale = prevNotStale;
}
}
// src/core/effect.ts
var Effect = class extends Computation {
x;
y;
s;
D = false;
z;
m;
constructor(initialValue, compute2, effect, error, options) {
super(initialValue, compute2, options);
this.x = effect;
this.y = error;
this.z = initialValue;
this.m = options?.render ? EFFECT_RENDER : EFFECT_USER;
if (this.m === EFFECT_RENDER) {
this.p = (p) =>
getClock() > this.g.created && !(this.d & ERROR_BIT)
? latest(() => compute2(p))
: compute2(p);
}
this.r();
!options?.defer &&
(this.m === EFFECT_USER
? this.g.enqueue(this.m, this.E.bind(this))
: this.E(this.m));
}
write(value, flags = 0) {
if (this.a == STATE_DIRTY) {
this.d;
this.d = flags;
if (this.m === EFFECT_RENDER) {
this.g.notify(this, LOADING_BIT | ERROR_BIT, flags);
}
}
if (value === UNCHANGED) return this.e;
this.e = value;
this.D = true;
return value;
}
k(state, skipQueue) {
if (this.a >= state || skipQueue) return;
if (this.a === STATE_CLEAN) this.g.enqueue(this.m, this.E.bind(this));
this.a = state;
}
C(error) {
this.w = error;
this.s?.();
this.g.notify(this, LOADING_BIT, 0);
this.d = ERROR_BIT;
if (this.m === EFFECT_USER) {
try {
return this.y
? (this.s = this.y(error))
: console.error(new EffectError(this.x, error));
} catch (e) {
error = e;
}
}
if (!this.g.notify(this, ERROR_BIT, ERROR_BIT)) throw error;
}
o() {
if (this.a === STATE_DISPOSED) return;
this.x = void 0;
this.z = void 0;
this.y = void 0;
this.s?.();
this.s = void 0;
super.o();
}
E(type) {
if (type) {
if (this.D && this.a !== STATE_DISPOSED) {
this.s?.();
try {
this.s = this.x(this.e, this.z);
} catch (e) {
if (!this.g.notify(this, ERROR_BIT, ERROR_BIT)) throw e;
} finally {
this.z = this.e;
this.D = false;
}
}
} else this.a !== STATE_CLEAN && runTop(this);
}
};
function runTop(node) {
const ancestors = [];
for (let current = node; current !== null; current = current.i) {
if (current.a !== STATE_CLEAN) {
ancestors.push(current);
}
}
for (let i = ancestors.length - 1; i >= 0; i--) {
if (ancestors[i].a !== STATE_DISPOSED) ancestors[i].r();
}
}
// src/signals.ts
function createSignal(first, second, third) {
if (typeof first === "function") {
const memo = createMemo((p) => {
const node2 = new Computation(
first(p ? untrack(p[0]) : second),
null,
third
);
return [node2.read.bind(node2), node2.write.bind(node2)];
});
return [() => memo()[0](), (value) => memo()[1](value)];
}
const o = getOwner();
const needsId = o?.id != null;
const node = new Computation(
first,
null,
needsId ? { id: o.getNextChildId(), ...second } : second
);
return [node.read.bind(node), node.write.bind(node)];
}
function createMemo(compute2, value, options) {
let node = new Computation(value, compute2, options);
let resolvedValue;
return () => {
if (node) {
if (node.a === STATE_DISPOSED) {
node = void 0;
return resolvedValue;
}
resolvedValue = node.wait();
if (!node.b?.length && node.h?.i !== node) {
node.dispose();
node = void 0;
}
}
return resolvedValue;
};
}
function createEffect(compute2, effect, error, value, options) {
void new Effect(value, compute2, effect, error, options);
}
function createRoot(init, options) {
const owner = new Owner(options?.id);
return compute(
owner,
!init.length ? init : () => init(() => owner.dispose()),
null
);
}
function runWithOwner(owner, run) {
return compute(owner, run, null);
}
export {
Owner,
createEffect,
createMemo,
createRoot,
createSignal,
getOwner,
onCleanup,
runWithOwner,
untrack,
};
File diff suppressed because it is too large Load Diff
+141 -137
View File
@@ -1,157 +1,161 @@
// @ts-check
/**
* @import { SignalOptions } from "./v0.3.2-treeshaked/types/core/core"
* @import { getOwner as GetOwner, onCleanup as OnCleanup } from "./v0.3.2-treeshaked/types/core/owner"
* @import { createSignal as CreateSignal, createEffect as CreateEffect, createMemo as CreateMemo, createRoot as CreateRoot, runWithOwner as RunWithOwner, Accessor } from "./v0.3.2-treeshaked/types/signals";
* @import { Signal } from "./types";
* @import { SignalOptions } from "./v0.3.2/types/core/core"
* @import { getOwner as GetOwner, onCleanup as OnCleanup } from "./v0.3.2/types/core/owner"
* @import { createSignal as CreateSignal, createEffect as CreateEffect, createMemo as CreateMemo, createRoot as CreateRoot, runWithOwner as RunWithOwner, Setter } from "./v0.3.2/types/signals";
*/
/**
* @template T
* @typedef {() => T} Accessor
*/
/**
* @template T
* @typedef {Accessor<T> & { set: Setter<T>; reset: VoidFunction }} Signal
*/
import {
createSignal,
createEffect,
getOwner,
createMemo,
createRoot,
runWithOwner,
onCleanup,
} from "./v0.3.2/script.js";
let effectCount = 0;
const importSignals = import("./v0.3.2-treeshaked/script.js").then(
(_signals) => {
const signals = {
createSolidSignal: /** @type {typeof CreateSignal} */ (
_signals.createSignal
),
createSolidEffect: /** @type {typeof CreateEffect} */ (
_signals.createEffect
),
createEffect: /** @type {typeof CreateEffect} */ (
// @ts-ignore
(compute, effect) => {
let dispose = /** @type {VoidFunction | null} */ (null);
const signals = {
createSolidSignal: /** @type {typeof CreateSignal} */ (createSignal),
createSolidEffect: /** @type {typeof CreateEffect} */ (createEffect),
createEffect: /** @type {typeof CreateEffect} */ (
// @ts-ignore
(compute, effect) => {
let dispose = /** @type {VoidFunction | null} */ (null);
if (_signals.getOwner() === null) {
throw Error("No owner");
}
if (getOwner() === null) {
throw Error("No owner");
}
function cleanup() {
if (dispose) {
dispose();
dispose = null;
// console.log("effectCount = ", --effectCount);
}
}
// @ts-ignore
_signals.createEffect(compute, (v, oldV) => {
// console.log("effectCount = ", ++effectCount);
cleanup();
signals.createRoot((_dispose) => {
dispose = _dispose;
return effect(v, oldV);
});
signals.onCleanup(cleanup);
});
signals.onCleanup(cleanup);
function cleanup() {
if (dispose) {
dispose();
dispose = null;
// console.log("effectCount = ", --effectCount);
}
),
createMemo: /** @type {typeof CreateMemo} */ (_signals.createMemo),
createRoot: /** @type {typeof CreateRoot} */ (_signals.createRoot),
getOwner: /** @type {typeof GetOwner} */ (_signals.getOwner),
runWithOwner: /** @type {typeof RunWithOwner} */ (_signals.runWithOwner),
onCleanup: /** @type {typeof OnCleanup} */ (_signals.onCleanup),
/**
* @template T
* @param {T} initialValue
* @param {SignalOptions<T> & {save?: {keyPrefix: string | Accessor<string>; key: string; serialize: (v: T) => string; deserialize: (v: string) => T; serializeParam?: boolean}}} [options]
* @returns {Signal<T>}
*/
createSignal(initialValue, options) {
const [get, set] = this.createSolidSignal(
/** @type {any} */ (initialValue),
options
);
}
// @ts-ignore
get.set = set;
// @ts-ignore
createEffect(compute, (v, oldV) => {
// console.log("effectCount = ", ++effectCount);
cleanup();
signals.createRoot((_dispose) => {
dispose = _dispose;
return effect(v, oldV);
});
signals.onCleanup(cleanup);
});
signals.onCleanup(cleanup);
}
),
createMemo: /** @type {typeof CreateMemo} */ (createMemo),
createRoot: /** @type {typeof CreateRoot} */ (createRoot),
getOwner: /** @type {typeof GetOwner} */ (getOwner),
runWithOwner: /** @type {typeof RunWithOwner} */ (runWithOwner),
onCleanup: /** @type {typeof OnCleanup} */ (onCleanup),
/**
* @template T
* @param {T} initialValue
* @param {SignalOptions<T> & {save?: {keyPrefix: string | Accessor<string>; key: string; serialize: (v: T) => string; deserialize: (v: string) => T; serializeParam?: boolean}}} [options]
* @returns {Signal<T>}
*/
createSignal(initialValue, options) {
const [get, set] = this.createSolidSignal(
/** @type {any} */ (initialValue),
options,
);
// @ts-ignore
get.reset = () => set(initialValue);
// @ts-ignore
get.set = set;
if (options?.save) {
const save = options.save;
// @ts-ignore
get.reset = () => set(initialValue);
const paramKey = save.key;
const storageKey = this.createMemo(
() =>
`${
typeof save.keyPrefix === "string"
? save.keyPrefix
: save.keyPrefix()
}-${paramKey}`
);
if (options?.save) {
const save = options.save;
let serialized = /** @type {string | null} */ (null);
if (options.save.serializeParam !== false) {
serialized = new URLSearchParams(window.location.search).get(
paramKey
);
const paramKey = save.key;
const storageKey = this.createMemo(
() =>
`${
typeof save.keyPrefix === "string"
? save.keyPrefix
: save.keyPrefix()
}-${paramKey}`,
);
let serialized = /** @type {string | null} */ (null);
if (options.save.serializeParam !== false) {
serialized = new URLSearchParams(window.location.search).get(paramKey);
}
if (serialized === null) {
serialized = localStorage.getItem(storageKey());
}
if (serialized) {
set(() => (serialized ? save.deserialize(serialized) : initialValue));
}
let firstRun1 = true;
this.createEffect(storageKey, (storageKey) => {
if (!firstRun1) {
serialized = localStorage.getItem(storageKey);
set(() => (serialized ? save.deserialize(serialized) : initialValue));
}
firstRun1 = false;
});
let firstRun2 = true;
this.createEffect(get, (value) => {
if (!save) return;
if (!firstRun2) {
if (
value !== undefined &&
value !== null &&
(initialValue === undefined ||
initialValue === null ||
save.serialize(value) !== save.serialize(initialValue))
) {
localStorage.setItem(storageKey(), save.serialize(value));
} else {
localStorage.removeItem(storageKey());
}
if (serialized === null) {
serialized = localStorage.getItem(storageKey());
}
if (serialized) {
set(() =>
serialized ? save.deserialize(serialized) : initialValue
);
}
let firstRun1 = true;
this.createEffect(storageKey, (storageKey) => {
if (!firstRun1) {
serialized = localStorage.getItem(storageKey);
set(() =>
serialized ? save.deserialize(serialized) : initialValue
);
}
firstRun1 = false;
});
let firstRun2 = true;
this.createEffect(get, (value) => {
if (!save) return;
if (!firstRun2) {
if (
value !== undefined &&
value !== null &&
(initialValue === undefined ||
initialValue === null ||
save.serialize(value) !== save.serialize(initialValue))
) {
localStorage.setItem(storageKey(), save.serialize(value));
} else {
localStorage.removeItem(storageKey());
}
}
if (
value !== undefined &&
value !== null &&
(initialValue === undefined ||
initialValue === null ||
save.serialize(value) !== save.serialize(initialValue))
) {
writeParam(paramKey, save.serialize(value));
} else {
removeParam(paramKey);
}
firstRun2 = false;
});
}
// @ts-ignore
return get;
},
};
if (
value !== undefined &&
value !== null &&
(initialValue === undefined ||
initialValue === null ||
save.serialize(value) !== save.serialize(initialValue))
) {
writeParam(paramKey, save.serialize(value));
} else {
removeParam(paramKey);
}
return signals;
}
);
firstRun2 = false;
});
}
// @ts-ignore
return get;
},
};
/** @typedef {typeof signals} Signals */
/**
* @param {string} key
@@ -170,7 +174,7 @@ function writeParam(key, value) {
window.history.replaceState(
null,
"",
`${window.location.pathname}?${urlParams.toString()}`
`${window.location.pathname}?${urlParams.toString()}`,
);
} catch (_) {}
}
@@ -182,4 +186,4 @@ function removeParam(key) {
writeParam(key, undefined);
}
export default importSignals;
export default signals;
+1 -2
View File
@@ -66,7 +66,6 @@ export function init({
});
const chart = lightweightCharts.createChartElement({
owner: signals.getOwner(),
parent: elements.charts,
signals,
colors,
@@ -156,7 +155,7 @@ export function init({
/**
* @param {Object} params
* @param {ISeriesApi<any, number>} params.iseries
* @param {ISeries} params.iseries
* @param {Unit} params.unit
* @param {Index} params.index
*/
+3
View File
@@ -0,0 +1,3 @@
// DO NOT CHANGE, Exact format is expected in `brk_bundler`
// @ts-ignore
import("./main.js");
+48 -79
View File
@@ -1,14 +1,10 @@
// @ts-check
/**
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex,CreatePriceLineOptions, CreatePriceLine, SeriesType } from "./options"
* @import { Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple, Series } from "../packages/lightweight-charts/wrapper"
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex, SeriesType } from "./options"
* @import { Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple, Series, ISeries, LineData, BaselineData, PartialLineStyleOptions, PartialBaselineStyleOptions, PartialCandlestickStyleOptions } from "../packages/lightweight-charts/wrapper"
* @import * as _ from "../packages/ufuzzy/v1.0.18/types"
* @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.7-treeshaked/types"
* @import { SignalOptions } from "../packages/solid-signals/v0.3.2-treeshaked/types/core/core"
* @import {Signal, Signals} from "../packages/solid-signals/types";
* @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/v0.3.2-treeshaked/types/core/owner"
* @import { createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo } from "../packages/solid-signals/v0.3.2-treeshaked/types/signals";
* @import { Signal, Signals, Accessor } from "../packages/solid-signals/wrapper";
* @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33Index, P2PK65Index, P2PKHIndex, P2SHIndex, P2MSIndex, P2AIndex, P2TRIndex, P2WPKHIndex, P2WSHIndex, TxIndex, InputIndex, OutputIndex, VecId, WeekIndex, YearIndex, VecIdToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex } from "./vecid-to-indexes"
*/
@@ -66,14 +62,14 @@ const localhost = window.location.hostname === "localhost";
function initPackages() {
const imports = {
async signals() {
return import("../packages/solid-signals/wrapper.js").then((d) =>
d.default.then((d) => d),
return import("../packages/solid-signals/wrapper.js").then(
(d) => d.default,
);
},
async lightweightCharts() {
return window.document.fonts.ready.then(() =>
import("../packages/lightweight-charts/wrapper.js").then((d) =>
d.default.then((d) => d),
import("../packages/lightweight-charts/wrapper.js").then(
(d) => d.default,
),
);
},
@@ -350,13 +346,6 @@ function createUtils() {
head.appendChild(link);
return link;
},
/**
* @param {string} href
* @param {VoidFunction} callback
*/
importStyleAndThen(href, callback) {
this.importStyle(href).addEventListener("load", callback);
},
/**
* @template {Readonly<string[]>} T
* @param {Object} args
@@ -366,7 +355,7 @@ function createUtils() {
* @param {string} [args.keyPrefix]
* @param {string} args.key
* @param {boolean} [args.sorted]
* @param {{createEffect: CreateEffect, createMemo: CreateMemo, createSignal: Signals["createSignal"]}} args.signals
* @param {Signals} args.signals
*/
createHorizontalChoiceField({
id,
@@ -1268,15 +1257,6 @@ function createUtils() {
return 0;
return this.differenceBetween(date, new Date("2009-01-09"));
},
/**
* @param {Time} time
*/
fromTime(time) {
return typeof time === "string"
? new Date(time)
: // @ts-ignore
new Date(time.year, time.month, time.day);
},
/**
* @param {Date} start
*/
@@ -1924,7 +1904,7 @@ function initWebSockets(signals, utils) {
/** @type {CandlestickData} */
const candle = {
index: -1,
// index: -1,
time: new Date(interval_begin).valueOf() / 1000,
open: Number(open),
high: Number(high),
@@ -2203,25 +2183,22 @@ function main() {
if (firstTimeLoadingChart) {
const lightweightCharts = packages.lightweightCharts();
const chartScript = import("./chart.js");
utils.dom.importStyleAndThen("/styles/chart.css", () =>
chartScript.then(({ init: initChartsElement }) =>
lightweightCharts.then((lightweightCharts) =>
signals.runWithOwner(owner, () =>
initChartsElement({
colors,
elements,
lightweightCharts,
option: /** @type {Accessor<ChartOption>} */ (
chartOption
),
signals,
utils,
webSockets,
vecsResources,
vecIdToIndexes,
}),
),
import("./chart.js").then(({ init: initChartsElement }) =>
lightweightCharts.then((lightweightCharts) =>
signals.runWithOwner(owner, () =>
initChartsElement({
colors,
elements,
lightweightCharts,
option: /** @type {Accessor<ChartOption>} */ (
chartOption
),
signals,
utils,
webSockets,
vecsResources,
vecIdToIndexes,
}),
),
),
);
@@ -2234,20 +2211,17 @@ function main() {
element = elements.table;
if (firstTimeLoadingTable) {
const tableScript = import("./table.js");
utils.dom.importStyleAndThen("/styles/table.css", () =>
tableScript.then(({ init }) =>
signals.runWithOwner(owner, () =>
init({
colors,
elements,
signals,
utils,
vecsResources,
option,
vecIdToIndexes,
}),
),
import("./table.js").then(({ init }) =>
signals.runWithOwner(owner, () =>
init({
colors,
elements,
signals,
utils,
vecsResources,
option,
vecIdToIndexes,
}),
),
);
}
@@ -2262,24 +2236,19 @@ function main() {
if (firstTimeLoadingSimulation) {
const lightweightCharts = packages.lightweightCharts();
const simulationScript = import("./simulation.js");
utils.dom.importStyleAndThen(
"/styles/simulation.css",
() =>
simulationScript.then(({ init }) =>
lightweightCharts.then((lightweightCharts) =>
signals.runWithOwner(owner, () =>
init({
colors,
elements,
lightweightCharts,
signals,
utils,
vecsResources,
}),
),
),
import("./simulation.js").then(({ init }) =>
lightweightCharts.then((lightweightCharts) =>
signals.runWithOwner(owner, () =>
init({
colors,
elements,
lightweightCharts,
signals,
utils,
vecsResources,
}),
),
),
);
}
firstTimeLoadingSimulation = false;
+28 -41
View File
@@ -16,32 +16,26 @@
* @property {string} title
* @property {boolean} [defaultActive]
*
* @typedef {Object} CreatePriceLine
* @property {number} value
*
* @typedef {Object} CreatePriceLineOptions
* @property {CreatePriceLine} createPriceLine
*
* @typedef {Object} BaselineSeriesBlueprintSpecific
* @property {"Baseline"} type
* @property {Color} [color]
* @property {[Color, Color]} [colors]
* @property {DeepPartial<BaselineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [options]
* @property {Accessor<BaselineData<number>[]>} [data]
* @property {PartialBaselineStyleOptions} [options]
* @property {Accessor<BaselineData[]>} [data]
* @typedef {BaseSeriesBlueprint & BaselineSeriesBlueprintSpecific} BaselineSeriesBlueprint
*
* @typedef {Object} CandlestickSeriesBlueprintSpecific
* @property {"Candlestick"} type
* @property {Color} [color]
* @property {DeepPartial<CandlestickStyleOptions & SeriesOptionsCommon>} [options]
* @property {PartialCandlestickStyleOptions} [options]
* @property {Accessor<CandlestickData[]>} [data]
* @typedef {BaseSeriesBlueprint & CandlestickSeriesBlueprintSpecific} CandlestickSeriesBlueprint
*
* @typedef {Object} LineSeriesBlueprintSpecific
* @property {"Line"} [type]
* @property {Color} [color]
* @property {DeepPartial<LineStyleOptions & SeriesOptionsCommon & CreatePriceLineOptions>} [options]
* @property {Accessor<LineData<number>[]>} [data]
* @property {PartialLineStyleOptions} [options]
* @property {Accessor<LineData[]>} [data]
* @typedef {BaseSeriesBlueprint & LineSeriesBlueprintSpecific} LineSeriesBlueprint
*
* @typedef {BaselineSeriesBlueprint | CandlestickSeriesBlueprint | LineSeriesBlueprint} AnySeriesBlueprint
@@ -823,7 +817,7 @@ function createPartialOptions(colors) {
* @param {string} args.name
* @param {Color} [args.color]
* @param {boolean} [args.defaultActive]
* @param {DeepPartial<LineStyleOptions & SeriesOptionsCommon>} [args.options]
* @param {PartialLineStyleOptions} [args.options]
*/
function createBaseSeries({ key, name, color, defaultActive, options }) {
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
@@ -1430,7 +1424,7 @@ function createPartialOptions(colors) {
key: `${fixKey(key)}realized-price`,
name,
color,
}),
})
),
}
: createPriceWithRatio({
@@ -1513,7 +1507,7 @@ function createPartialOptions(colors) {
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: `${fixKey(
key,
key
)}net-realized-profit-and-loss-relative-to-realized-cap`,
title: useGroupName ? name : "Net",
color: useGroupName ? color : undefined,
@@ -1584,7 +1578,7 @@ function createPartialOptions(colors) {
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: `${fixKey(
key,
key
)}adjusted-spent-output-profit-ratio`,
title: useGroupName ? name : "asopr",
color: useGroupName ? color : undefined,
@@ -1607,7 +1601,7 @@ function createPartialOptions(colors) {
key: `${fixKey(key)}sell-side-risk-ratio`,
name: useGroupName ? name : "Risk",
color: color,
}),
})
),
},
],
@@ -1696,7 +1690,7 @@ function createPartialOptions(colors) {
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: `${fixKey(
key,
key
)}net-unrealized-profit-and-loss-relative-to-market-cap`,
title: useGroupName ? name : "Net",
color: useGroupName ? color : undefined,
@@ -1874,7 +1868,7 @@ function createPartialOptions(colors) {
key: `${key}-sma`,
name: key,
color,
}),
})
),
},
...averages.map(({ key, name, color }) =>
@@ -1884,7 +1878,7 @@ function createPartialOptions(colors) {
title: `${name} Market Price Moving Average`,
legend: "average",
color,
}),
})
),
],
},
@@ -1975,7 +1969,7 @@ function createPartialOptions(colors) {
},
}),
],
}),
})
),
.../** @type {const} */ ([
{ name: "2 Year", key: "2y" },
@@ -2046,7 +2040,7 @@ function createPartialOptions(colors) {
},
}),
],
}),
})
),
],
},
@@ -2062,7 +2056,7 @@ function createPartialOptions(colors) {
name: `${year}`,
color,
defaultActive,
}),
})
),
},
...dcaClasses.map(
@@ -2089,7 +2083,7 @@ function createPartialOptions(colors) {
},
}),
],
}),
})
),
],
},
@@ -2150,10 +2144,10 @@ function createPartialOptions(colors) {
bottom: [
...createAverageSumCumulativeMinMaxPercentilesSeries("fee"),
...createAverageSumCumulativeMinMaxPercentilesSeries(
"fee-in-btc",
"fee-in-btc"
),
...createAverageSumCumulativeMinMaxPercentilesSeries(
"fee-in-usd",
"fee-in-usd"
),
],
},
@@ -2909,7 +2903,7 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
const detailsList = [];
const treeElement = signals.createSignal(
/** @type {HTMLDivElement | null} */ (null),
/** @type {HTMLDivElement | null} */ (null)
);
/** @type {string[] | undefined} */
@@ -2935,9 +2929,8 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
* @param {Signal<string | null>} args.qrcode
* @param {string} [args.name]
* @param {string} [args.id]
* @param {Owner | null} [args.owner]
*/
function createOptionElement({ option, frame, name, id, owner, qrcode }) {
function createOptionElement({ option, frame, name, id, qrcode }) {
if (option.kind === "url") {
const href = option.url();
@@ -2987,13 +2980,7 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
});
}
if (owner !== undefined) {
signals.runWithOwner(owner, () => {
createCheckEffect();
});
} else {
createCheckEffect();
}
createCheckEffect();
return label;
}
@@ -3028,7 +3015,7 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
return null;
}
},
null,
null
);
partialTree.forEach((anyPartial, partialIndex) => {
@@ -3051,7 +3038,7 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
if ("tree" in anyPartial) {
const folderId = utils.stringToId(
`${(path || []).join(" ")} ${anyPartial.name} folder`,
`${(path || []).join(" ")} ${anyPartial.name} folder`
);
/** @type {Omit<OptionsGroup, keyof PartialOptionsGroup>} */
@@ -3066,13 +3053,13 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
const thisPath = groupAddons.id;
const passedDetails = signals.createSignal(
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null)
);
const childOptionsCount = recursiveProcessPartialTree(
anyPartial.tree,
passedDetails,
[...(path || []), thisPath],
[...(path || []), thisPath]
);
listForSum.push(childOptionsCount);
@@ -3204,7 +3191,7 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
});
return signals.createMemo(() =>
listForSum.reduce((acc, s) => acc + s(), 0),
listForSum.reduce((acc, s) => acc + s(), 0)
);
}
recursiveProcessPartialTree(partialOptions, treeElement);
@@ -3231,7 +3218,7 @@ export function initOptions({ colors, signals, env, utils, qrcode }) {
console.log(
[...m.entries()]
.filter(([_, value]) => value > 1)
.map(([key, _]) => key),
.map(([key, _]) => key)
);
throw Error("ID duplicate");
+73 -99
View File
@@ -40,7 +40,7 @@ export function init({
* @param {number} args.min
* @param {number} args.step
* @param {number} [args.max]
* @param {{createEffect: typeof CreateEffect}} args.signals
* @param {Signals} args.signals
*/
createInputNumberElement({
id,
@@ -76,7 +76,7 @@ export function init({
input.value = value;
stateValue = value;
}
}
},
);
input.addEventListener("input", () => {
@@ -95,7 +95,7 @@ export function init({
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
* @param {Signals} args.signals
*/
createInputDollar({ id, title, signal, signals }) {
return this.createInputNumberElement({
@@ -113,7 +113,7 @@ export function init({
* @param {string} args.id
* @param {string} args.title
* @param {Signal<Date | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
* @param {Signals} args.signals
*/
createInputDate({ id, title, signal, signals }) {
const input = window.document.createElement("input");
@@ -139,7 +139,7 @@ export function init({
input.value = value;
stateValue = value;
}
}
},
);
input.addEventListener("change", () => {
@@ -328,7 +328,7 @@ export function init({
keyPrefix,
key: "top-up-freq",
},
}
},
),
},
},
@@ -356,7 +356,7 @@ export function init({
keyPrefix,
key: "swap-freq",
},
}
},
),
},
},
@@ -369,7 +369,7 @@ export function init({
keyPrefix,
key: "interval-start",
},
}
},
),
end: signals.createSignal(/** @type {Date | null} */ (new Date()), {
save: {
@@ -391,7 +391,7 @@ export function init({
};
parametersElement.append(
utils.dom.createHeader("Save in Bitcoin").headerElement
utils.dom.createHeader("Save in Bitcoin").headerElement,
);
/**
@@ -431,9 +431,9 @@ export function init({
title: "Initial Dollar Amount",
signal: settings.dollars.initial.amount,
signals,
})
}),
),
})
}),
);
parametersElement.append(
@@ -451,9 +451,9 @@ export function init({
list: frequencies.list,
signal: settings.dollars.topUp.frenquency,
deep: true,
})
}),
),
})
}),
);
parametersElement.append(
@@ -471,9 +471,9 @@ export function init({
title: "Top Up Dollar Amount",
signal: settings.dollars.topUp.amount,
signals,
})
}),
),
})
}),
);
parametersElement.append(
@@ -491,9 +491,9 @@ export function init({
title: "Initial Swap Amount",
signal: settings.bitcoin.investment.initial,
signals,
})
}),
),
})
}),
);
parametersElement.append(
@@ -510,9 +510,9 @@ export function init({
list: frequencies.list,
signal: settings.bitcoin.investment.frequency,
deep: true,
})
}),
),
})
}),
);
parametersElement.append(
@@ -530,9 +530,9 @@ export function init({
title: "Bitcoin Recurrent Investment",
signal: settings.bitcoin.investment.recurrent,
signals,
})
}),
),
})
}),
);
parametersElement.append(
@@ -549,9 +549,9 @@ export function init({
title: "First Simulation Date",
signal: settings.interval.start,
signals,
})
}),
),
})
}),
);
parametersElement.append(
@@ -568,9 +568,9 @@ export function init({
title: "Last Simulation Day",
signal: settings.interval.end,
signals,
})
}),
),
})
}),
);
parametersElement.append(
@@ -591,9 +591,9 @@ export function init({
step: 0.01,
signals,
placeholder: "Fees",
})
}),
),
})
}),
);
const p1 = window.document.createElement("p");
@@ -608,101 +608,79 @@ export function init({
const owner = signals.getOwner();
const totalInvestedAmountData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
},
);
const bitcoinValueData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
);
const bitcoinData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
);
const resultData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
);
const dollarsLeftData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
);
const totalValueData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
);
const investmentData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
},
);
const bitcoinData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const resultData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const dollarsLeftData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const totalValueData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const investmentData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const bitcoinAddedData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
},
);
const averagePricePaidData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
},
);
const bitcoinPriceData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
);
const buyCountData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
},
);
const buyCountData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const totalFeesPaidData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
);
const daysCountData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
{
equals: false,
}
},
);
const daysCountData = signals.createSignal(/** @type {LineData[]} */ ([]), {
equals: false,
});
const profitableDaysRatioData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
},
);
const unprofitableDaysRatioData = signals.createSignal(
/** @type {LineData<number>[]} */ ([]),
/** @type {LineData[]} */ ([]),
{
equals: false,
}
},
);
const index = () => /** @type {DateIndex} */ (0);
lightweightCharts.createChartElement({
index,
owner,
parent: resultsElement,
signals,
colors,
@@ -748,7 +726,6 @@ export function init({
lightweightCharts.createChartElement({
index,
owner,
parent: resultsElement,
signals,
colors,
@@ -774,7 +751,6 @@ export function init({
lightweightCharts.createChartElement({
index,
owner,
parent: resultsElement,
signals,
colors,
@@ -806,7 +782,6 @@ export function init({
lightweightCharts.createChartElement({
index,
owner,
parent: resultsElement,
signals,
colors,
@@ -839,7 +814,6 @@ export function init({
vecsResources,
utils,
elements,
owner,
config: [
{
unit: "percentage",
@@ -1096,7 +1070,7 @@ export function init({
p1.innerHTML = `After exchanging ${serInvestedAmount} in the span of ${serDaysCount} days, you would have accumulated ${serSats} Satoshis (${serBitcoin} Bitcoin) worth today ${serBitcoinValue} at an average price of ${serAveragePricePaid} per Bitcoin with a return of investment of ${serRoi}, have ${serDollars} left and paid a total of ${serTotalFeesPaid} in fees.`;
const dayDiff = Math.floor(
utils.date.differenceBetween(new Date(), lastInvestDay)
utils.date.differenceBetween(new Date(), lastInvestDay),
);
const serDailyInvestment = c("emerald", fd(dailyInvestment));
const setLastSatsAdded = c("orange", f(lastSatsAdded));
@@ -1104,13 +1078,13 @@ export function init({
"blue",
dayDiff
? `${f(dayDiff)} ${dayDiff > 1 ? "days" : "day"} ago`
: "today"
: "today",
)} and exchanged ${serDailyInvestment} for approximately ${setLastSatsAdded} Satoshis`;
const serProfitableDaysRatio = c("green", fp(profitableDaysRatio));
const serUnprofitableDaysRatio = c(
"red",
fp(unprofitableDaysRatio)
fp(unprofitableDaysRatio),
);
p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
@@ -1120,7 +1094,7 @@ export function init({
(lowestAnnual4YReturn) => {
const serLowestAnnual4YReturn = c(
"cyan",
`${fp(lowestAnnual4YReturn)}`
`${fp(lowestAnnual4YReturn)}`,
);
const lowestAnnual4YReturnPercentage = 1 + lowestAnnual4YReturn;
@@ -1136,22 +1110,22 @@ export function init({
const bitcoinValueAfter4y = bitcoinValueReturn(4);
const serBitcoinValueAfter4y = c(
"purple",
fd(bitcoinValueAfter4y)
fd(bitcoinValueAfter4y),
);
const bitcoinValueAfter10y = bitcoinValueReturn(10);
const serBitcoinValueAfter10y = c(
"fuchsia",
fd(bitcoinValueAfter10y)
fd(bitcoinValueAfter10y),
);
const bitcoinValueAfter21y = bitcoinValueReturn(21);
const serBitcoinValueAfter21y = c(
"pink",
fd(bitcoinValueAfter21y)
fd(bitcoinValueAfter21y),
);
/** @param {number} v */
p4.innerHTML = `The lowest annual return after 4 years has historically been ${serLowestAnnual4YReturn}.<br/>Using it as the baseline, your Bitcoin would be worth ${serBitcoinValueAfter4y} after 4 years, ${serBitcoinValueAfter10y} after 10 years and ${serBitcoinValueAfter21y} after 21 years.`;
}
},
);
totalInvestedAmountData.set((a) => a);
@@ -1169,7 +1143,7 @@ export function init({
daysCountData.set((a) => a);
profitableDaysRatioData.set((a) => a);
unprofitableDaysRatioData.set((a) => a);
}
},
);
});
});
+1 -1
View File
@@ -2,7 +2,7 @@
// File auto-generated, any modifications will be overwritten
//
export const VERSION = "v0.0.55";
export const VERSION = "v0.0.56";
/** @typedef {0} DateIndex */
/** @typedef {1} DecadeIndex */
+26 -15
View File
@@ -1,24 +1,36 @@
const CACHE_NAME = "cache";
// DO NOT CHANGE, Exact format is expected in `brk_bundler`
const CACHE_VERSION = "__VERSION__";
const SHELL_FILES = ["/", "/index.html"];
/** @type {ServiceWorkerGlobalScope} */
const sw = /** @type {any} */ (self);
sw.addEventListener("install", (event) => {
console.log("sw: install");
event.waitUntil(sw.skipWaiting());
event.waitUntil(
caches
.open(CACHE_VERSION)
.then((c) => c.addAll(SHELL_FILES))
.then(() => sw.skipWaiting()),
);
});
sw.addEventListener("activate", (event) => {
console.log("sw: active");
sw.clients.claim();
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(
keys.filter((key) => key !== "api").map((key) => caches.delete(key)),
Promise.all([
sw.clients.claim(),
caches
.keys()
.then((keys) =>
Promise.all(
keys
.filter((key) => key !== "api" && key !== CACHE_VERSION)
.map((key) => caches.delete(key)),
),
),
),
]),
);
});
@@ -42,7 +54,7 @@ sw.addEventListener("fetch", (event) => {
return; // let the browser handle it
}
const cache = caches.open(CACHE_NAME);
const cache = caches.open(CACHE_VERSION);
// 2) NAVIGATION: networkfirst on your shell
if (req.mode === "navigate") {
@@ -76,14 +88,13 @@ sw.addEventListener("fetch", (event) => {
}
return response;
})
.catch(async () => {
return caches
.catch(async () =>
caches
.match(req)
.then((cached) => {
return cached || indexHTMLOrOffline();
})
.catch(indexHTMLOrOffline);
})
.catch(indexHTMLOrOffline),
.catch(indexHTMLOrOffline),
),
);
});
-39
View File
@@ -1,39 +0,0 @@
#charts {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
min-height: 0;
padding: var(--main-padding);
header {
flex-shrink: 0;
display: flex;
white-space: nowrap;
overflow-x: auto;
padding-bottom: 1rem;
margin-bottom: -2rem;
padding-left: var(--main-padding);
margin-left: var(--negative-main-padding);
padding-right: var(--main-padding);
margin-right: var(--negative-main-padding);
& > * {
flex: 1;
}
}
.chart {
flex: 1;
}
> .chart > legend,
> fieldset {
z-index: 20;
}
.lightweight-chart {
z-index: 40;
}
}
-76
View File
@@ -1,76 +0,0 @@
#simulation {
min-height: 0;
width: 100%;
> div {
display: flex;
flex-direction: column;
gap: 2rem;
padding: var(--main-padding);
}
@media (max-width: 767px) {
overflow-y: auto;
> div:first-child {
border-bottom: 1px;
}
}
@media (min-width: 768px) {
display: flex;
flex-direction: column;
height: 100%;
flex-direction: row;
> div {
flex: 1;
overflow-y: auto;
padding-bottom: var(--bottom-area);
}
> div:first-child {
max-width: var(--default-main-width);
border-right: 1px;
}
}
header {
margin-bottom: 0.5rem;
}
> div:last-child {
display: flex;
flex-direction: column;
gap: 1.5rem;
overflow-x: hidden;
p {
text-wrap: pretty;
}
}
label {
> span {
display: block;
}
small {
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
display: block;
}
}
.chart {
flex: none;
height: 400px;
.lightweight-chart {
margin-left: calc(var(--negative-main-padding) * 0.75);
fieldset {
margin-left: -0.5rem;
}
}
}
}
-144
View File
@@ -1,144 +0,0 @@
#table {
width: 100%;
display: flex;
flex-direction: column;
gap: 2rem;
padding: var(--main-padding);
> div {
display: flex;
font-size: var(--font-size-xs);
line--line-height: var(--line-height-xs);
font-weight: 450;
margin-left: var(--negative-main-padding);
margin-right: var(--negative-main-padding);
table {
z-index: 10;
border-top-width: 1px;
border-style: dashed !important;
/* width: 100%; */
line-height: var(--line-height-sm);
text-transform: uppercase;
table-layout: auto;
border-collapse: separate;
border-spacing: 0;
/* border: 3px solid purple; */
/* min-height: 100%; */
}
th {
font-weight: 600;
}
th,
td {
/* border-top: 1px; */
border-right: 1px;
border-bottom: 1px;
border-color: var(--off-color);
border-style: dashed !important;
padding: 0.25rem 0.75rem;
}
td {
text-transform: lowercase;
}
a {
margin: -0.2rem 0;
font-size: 1.2rem;
}
th:first-child {
padding-left: var(--main-padding);
}
th[scope="col"] {
position: sticky;
top: 0;
background-color: var(--background-color);
> div {
display: flex;
flex-direction: column;
padding-top: 0.275rem;
> div {
display: flex;
gap: 0.25rem;
text-transform: lowercase;
color: var(--off-color);
text-align: left;
&:first-child {
gap: 0.5rem;
}
&:last-child {
gap: 1rem;
}
> span {
width: 100%;
}
> button {
padding: 0 0.25rem;
margin: 0 -0.25rem;
font-size: 0.75rem;
line-height: 0;
}
}
}
&:first-child {
button {
display: none;
}
}
&:nth-child(2) {
button:nth-of-type(1) {
display: none;
}
}
&:last-child {
button:nth-of-type(2) {
display: none;
}
}
}
select {
margin-right: -4px;
/* width: 100%; */
}
tbody {
text-align: right;
}
> button {
padding: 1rem;
min-width: 10rem;
display: flex;
flex-direction: column;
flex: 1;
position: relative;
border-top-width: 1px;
width: 100%;
border-bottom-width: 1px;
border-style: dashed !important;
> span {
text-align: left;
position: sticky;
top: 2rem;
left: 0;
right: 0;
}
}
}
}