mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-08 14:11:56 -07:00
website: big update
This commit is contained in:
+4
-3
@@ -25,12 +25,13 @@
|
||||
- Added `Hash Price Min`
|
||||
- Added `Hash Price Rebound`
|
||||
- Removed all year datasets (25) in favor for epoch datasets (5), the former was too granular to be really useful
|
||||
- Removed datasets split by liquidity for all datasets already split by any address kind, while fun to have, they took time to compute, ram, and space to store and no one was actually checking them
|
||||
- Removed datasets split by liquidity for all datasets **already split by any address kind**, while fun to have, they took time to compute, ram, and space to store and no one was actually checking them
|
||||
- Fixed a lot of values in split by liquidity datasets
|
||||
|
||||
## Website
|
||||
|
||||
- ~Added a dashboard~ Added the latest values to the tree next to each option (in "Folders") instead, while less values are visible at a time, it's much more readable and organised
|
||||
- Updated the design yet again which made the website even easier on the eyes
|
||||
- ~Added a dashboard~ Added the latest values to the tree next to each option instead, while less values are visible at a time, it's much more readable and organised
|
||||
- Added a library of PDFs
|
||||
- Fixed service worker not passing 304 (not modified) response and instead serving cached responses
|
||||
- Fixed history not being properly registered
|
||||
@@ -50,7 +51,7 @@
|
||||
- Added `--recompute_computed true` argument, to allow recomputation of computed datasets in case of a bug
|
||||
- Fixed not saved arguments, not being processed properly
|
||||
- Fixed bug in `generic_map.multi_insert_simple_average`
|
||||
- Added defragmentation of databases to save space
|
||||
- Added defragmentation option `--first-defragment true` of databases to save space (which can save up to 50%)
|
||||
|
||||
## Server
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
## Description
|
||||
|
||||
*TLDR*: **A better, FOSS, Bitcoin-only, self-hostable Glassnode**
|
||||
_TLDR_: **A better, FOSS, Bitcoin-only, self-hostable Glassnode**
|
||||
|
||||
**kibō** (*hope* in japanese) is a suite of tools that aims to help you understand Bitcoin's various dynamics. To do that, there is a wide number of charts and datasets with a scale by date but also by height free for you to explore. Which allows you to verify an incredible number of things, from the number of UTXOs to the repartition of the supply between different groups over time, with many things in between and it's all made possible thanks to Bitcoin's transparency. Whether you're an enthusiast, a researcher, a miner, an analyst, a trader, a skeptic or just curious, there is something new to learn for everyone !
|
||||
**kibō** (_hope_ in japanese) is a suite of tools that aims to help you understand Bitcoin's various dynamics. To do that, there is a wide number of charts and datasets with a scale by date but also by height free for you to explore. Which allows you to verify an incredible number of things, from the number of UTXOs to the repartition of the supply between different groups over time, with many things in between and it's all made possible thanks to Bitcoin's transparency. Whether you're an enthusiast, a researcher, a miner, an analyst, a trader, a skeptic or just curious, there is something new to learn for everyone !
|
||||
|
||||
While it's not the first tool trying to solve this problem, it's the first that is completely free, open-source and self-hostable. Which is very important as, just like for Bitcoin itself, I strongly believe that everyone should have access to this kind of data.
|
||||
|
||||
@@ -36,7 +36,7 @@ So if you find this project useful, [please send some sats](https://geyser.fund/
|
||||
|
||||
If you're a potential sponsor, feel free to contact me in Nostr !
|
||||
|
||||
Bitcoin address: bc1q950q4ukpxxm6wjjkv6cpq8jzpazaxrrwftctkt
|
||||
Bitcoin address: [bc1q950q4ukpxxm6wjjkv6cpq8jzpazaxrrwftctkt](bitcoin:bc1q950q4ukpxxm6wjjkv6cpq8jzpazaxrrwftctkt)
|
||||
|
||||
Lightning address: lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkxmmww3jkuar8d35kgetj8yuq363hv4
|
||||
|
||||
@@ -48,11 +48,11 @@ This project is still in an early stage. Until more people look at the code and
|
||||
|
||||
## Instances
|
||||
|
||||
| URL | Type | Version | Status | Last Height | Up Time Ratio |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| [kibo.money](https://kibo.money) | Main |  |  |  |  |
|
||||
| [backup.kibo.money](https://backup.kibo.money) | Backup |  |  |  |  |
|
||||
| [preview.kibo.money](https://preview.kibo.money) | Dev |  |  |  |  |
|
||||
| URL | Type | Version | Status | Last Height | Up Time Ratio |
|
||||
| ------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [kibo.money](https://kibo.money) | Main |  |  |  |  |
|
||||
| [backup.kibo.money](https://backup.kibo.money) | Backup |  |  |  |  |
|
||||
| [preview.kibo.money](https://preview.kibo.money) | Dev |  |  |  |  |
|
||||
|
||||
Please open an issue if you want to add another instance
|
||||
|
||||
@@ -107,7 +107,7 @@ Working on it
|
||||
|
||||
### Manual
|
||||
|
||||
*Mac OS and Linux only, Windows is unsupported*
|
||||
_Mac OS and Linux only, Windows is unsupported_
|
||||
|
||||
First we need to install Rust (https://www.rust-lang.org/tools/install)
|
||||
|
||||
@@ -122,6 +122,7 @@ rustup update
|
||||
```
|
||||
|
||||
> If you're on Ubuntu you'll probably also need to install `open-ssl` with
|
||||
>
|
||||
> ```bash
|
||||
> sudo apt install libssl-dev pkg-config
|
||||
> ```
|
||||
@@ -153,11 +154,13 @@ cd ???/kibo/parser
|
||||
Now we can finally start by running the parser, you need to use the `./run.sh` script instead of `cargo run -r` as we need to set various system variables for the program to run smoothly
|
||||
|
||||
For the first launch, the parser will need several information such as:
|
||||
|
||||
- `--datadir`: which is bitcoin data directory path, prefer `$HOME` to `~` as the latter might not work
|
||||
- `--rpcuser`: the username of the RPC credentials to talk to the bitcoin server
|
||||
- `--rpcpassword`: the password of the RPC credentials
|
||||
|
||||
Optionally you can also specify:
|
||||
|
||||
- `--rpcconnect`: if the bitcoin core server's IP is different than `localhost`
|
||||
- `--rpcport`: if the port is different than `8332`
|
||||
|
||||
@@ -196,21 +199,3 @@ Formerly Satonomics
|
||||
The dove (borrowed from [svgrepo](https://www.svgrepo.com/svg/351969/dove) for now) is known to represent hope in many cultures.
|
||||
|
||||
The orange background is a wink to Bitcoin and when in a circle, it also represents the sun, which means that while it's our hope for a better future, we still have to be careful with our collective goals and actions, to not end up like Icarus.
|
||||
|
||||
## Infrastructure
|
||||
|
||||
Here's the current infrastructure of the main instance and its backup.
|
||||
|
||||
It uses 2 servers, a full and a light one without the parser running but with still datasets syncronized via Syncthing.
|
||||
|
||||
Cloudflare is used for their tunnel + CDN services.
|
||||
|
||||
Though it's recommended to change to default **Browser Cache TTL** configuration from `4 Hours` to `Respect Existing Headers` (in `Websites / YOUR_DOMAIN / Caching / Configuration / Browser Cache TTL`) and activate `Always use https`.
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-light.svg">
|
||||
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-light.svg" width="768" height="auto" style="max-width: 100%;">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 32 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 32 KiB |
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn iter_blocks(
|
||||
config: &Config,
|
||||
config: &mut Config,
|
||||
rpc: &biter::bitcoincore_rpc::Client,
|
||||
approx_block_count: usize,
|
||||
exit: Exit,
|
||||
@@ -33,6 +33,7 @@ pub fn iter_blocks(
|
||||
|
||||
if config.first_defragment() {
|
||||
databases.defragment(&exit);
|
||||
config.disable_defragment();
|
||||
}
|
||||
|
||||
log("Imported databases");
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
|
||||
reset_logs();
|
||||
|
||||
let config = Config::import()?;
|
||||
let mut config = Config::import()?;
|
||||
|
||||
let rpc = create_rpc(&config).unwrap();
|
||||
|
||||
@@ -19,7 +19,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
|
||||
log(&format!("{block_count} blocks found."));
|
||||
|
||||
iter_blocks(&config, &rpc, block_count, exit.clone())?;
|
||||
iter_blocks(&mut config, &rpc, block_count, exit.clone())?;
|
||||
|
||||
if let Some(delay) = config.delay {
|
||||
sleep(Duration::from_secs(delay))
|
||||
|
||||
@@ -171,4 +171,8 @@ impl Config {
|
||||
pub fn first_defragment(&self) -> bool {
|
||||
self.first_defragment.is_some_and(|b| b)
|
||||
}
|
||||
|
||||
pub fn disable_defragment(&mut self) {
|
||||
self.first_defragment.take();
|
||||
}
|
||||
}
|
||||
|
||||
+342
-1091
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
import { Computation, type MemoOptions } from './core';
|
||||
/**
|
||||
* By default, changes are batched on the microtask queue which is an async process. You can flush
|
||||
* the queue synchronously to get the latest updates by calling `flushSync()`.
|
||||
*/
|
||||
export declare function flushSync(): void;
|
||||
/**
|
||||
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
||||
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
||||
* sources and recompute
|
||||
*/
|
||||
export declare class Effect<T = any> extends Computation<T> {
|
||||
constructor(initialValue: T, compute: () => T, options?: MemoOptions<T>);
|
||||
_notify(state: number): void;
|
||||
write(value: T): T;
|
||||
_setError(error: unknown): void;
|
||||
}
|
||||
export declare class RenderEffect<T = any> extends Computation<T> {
|
||||
effect: (val: T) => void;
|
||||
modified: boolean;
|
||||
constructor(initialValue: T, compute: () => T, effect: (val: T) => void, options?: MemoOptions<T>);
|
||||
_notify(state: number): void;
|
||||
write(value: T): T;
|
||||
_setError(error: unknown): void;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export { ContextNotFoundError, NoOwnerError, NotReadyError, type ErrorHandler, } from './error';
|
||||
export { Owner, createContext, getContext, setContext, hasContext, getOwner, setOwner, onCleanup, type Context, type ContextRecord, type Disposable, } from './owner';
|
||||
export { Computation, compute, getObserver, isEqual, untrack, type MemoOptions, type SignalOptions, } from './core';
|
||||
export { flushSync, Effect, RenderEffect } from './effect';
|
||||
export { indexArray, mapArray, type Maybe } from './map';
|
||||
export { createSelector, type SelectorOptions, type SelectorSignal, } from './selector';
|
||||
export * from './signals';
|
||||
export * from './store';
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { Accessor } from './signals';
|
||||
export type Maybe<T> = T | void | null | undefined | false;
|
||||
/**
|
||||
* Reactive map helper that caches each item by index to reduce unnecessary mapping on updates.
|
||||
* It only runs the mapping function once per item and adds/removes as needed. In a non-keyed map
|
||||
* like this the index is fixed but value can change (opposite of a keyed map).
|
||||
*
|
||||
* Prefer `mapArray` when referential checks are required.
|
||||
*
|
||||
* @see {@link https://github.com/solidjs/x-reactivity#indexarray}
|
||||
*/
|
||||
export declare function indexArray<Item, MappedItem>(list: Accessor<Maybe<readonly Item[]>>, map: (value: Accessor<Item>, index: number) => MappedItem, options?: {
|
||||
name?: string;
|
||||
}): Accessor<MappedItem[]>;
|
||||
/**
|
||||
* Reactive map helper that caches each list item by reference to reduce unnecessary mapping on
|
||||
* updates. It only runs the mapping function once per item and then moves or removes it as needed.
|
||||
* In a keyed map like this the value is fixed but the index changes (opposite of non-keyed map).
|
||||
*
|
||||
* Prefer `indexArray` when working with primitives to avoid unnecessary re-renders.
|
||||
*
|
||||
* @see {@link https://github.com/solidjs/x-reactivity#maparray}
|
||||
*/
|
||||
export declare function mapArray<Item, MappedItem>(list: Accessor<Maybe<readonly Item[]>>, map: (value: Item, index: Accessor<number>) => MappedItem, options?: {
|
||||
name?: string;
|
||||
}): Accessor<MappedItem[]>;
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Accessor } from './signals';
|
||||
export interface SelectorSignal<T> {
|
||||
(key: T): Boolean;
|
||||
}
|
||||
export interface SelectorOptions<Key, Value> {
|
||||
name?: string;
|
||||
equals?: (key: Key, value: Value | undefined) => boolean;
|
||||
}
|
||||
/**
|
||||
* Creates a signal that observes the given `source` and returns a new signal who only notifies
|
||||
* observers when entering or exiting a specified key.
|
||||
*
|
||||
* @see {@link https://github.com/solidjs/x-reactivity#createselector}
|
||||
*/
|
||||
export declare function createSelector<Source, Key = Source>(source: Accessor<Source>, options?: SelectorOptions<Key, Source>): SelectorSignal<Key>;
|
||||
@@ -1,6 +0,0 @@
|
||||
export { ContextNotFoundError, NoOwnerError, NotReadyError, type ErrorHandler, } from './error';
|
||||
export { Owner, createContext, getContext, setContext, hasContext, getOwner, setOwner, onCleanup, type Context, type ContextRecord, type Disposable, } from './owner';
|
||||
export { Computation, compute, getObserver, isEqual, untrack, hasUpdated, type SignalOptions, } from './core';
|
||||
export { Effect, RenderEffect } from './effect';
|
||||
export { flushSync } from './scheduler';
|
||||
export * from './signals';
|
||||
+207
-179
@@ -1,28 +1,33 @@
|
||||
// src/error.ts
|
||||
var NotReadyError = class extends Error {};
|
||||
// src/core/error.ts
|
||||
var NotReadyError = class extends Error {
|
||||
};
|
||||
var NoOwnerError = class extends Error {
|
||||
constructor() {
|
||||
super("");
|
||||
super(
|
||||
""
|
||||
);
|
||||
}
|
||||
};
|
||||
var ContextNotFoundError = class extends Error {
|
||||
constructor() {
|
||||
super("");
|
||||
super(
|
||||
""
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// src/constants.ts
|
||||
var STATE_CLEAN = 0;
|
||||
var STATE_CHECK = 1;
|
||||
var STATE_DIRTY = 2;
|
||||
var STATE_DISPOSED = 3;
|
||||
|
||||
// src/utils.ts
|
||||
function isUndefined(value) {
|
||||
return typeof value === "undefined";
|
||||
}
|
||||
|
||||
// src/owner.ts
|
||||
// src/core/constants.ts
|
||||
var STATE_CLEAN = 0;
|
||||
var STATE_CHECK = 1;
|
||||
var STATE_DIRTY = 2;
|
||||
var STATE_DISPOSED = 3;
|
||||
|
||||
// src/core/owner.ts
|
||||
var currentOwner = null;
|
||||
var defaultContext = {};
|
||||
function getOwner() {
|
||||
@@ -37,7 +42,7 @@ 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
|
||||
k = null;
|
||||
l = null;
|
||||
g = null;
|
||||
j = null;
|
||||
a = STATE_CLEAN;
|
||||
@@ -45,12 +50,14 @@ var Owner = class {
|
||||
h = defaultContext;
|
||||
f = null;
|
||||
constructor(signal = false) {
|
||||
if (currentOwner && !signal) currentOwner.append(this);
|
||||
if (currentOwner && !signal)
|
||||
currentOwner.append(this);
|
||||
}
|
||||
append(child) {
|
||||
child.k = this;
|
||||
child.l = this;
|
||||
child.j = this;
|
||||
if (this.g) this.g.j = child;
|
||||
if (this.g)
|
||||
this.g.j = child;
|
||||
child.g = this.g;
|
||||
this.g = child;
|
||||
if (child.h !== this.h) {
|
||||
@@ -61,24 +68,27 @@ var Owner = class {
|
||||
}
|
||||
}
|
||||
dispose(self = true) {
|
||||
if (this.a === STATE_DISPOSED) return;
|
||||
let head = self ? this.j || this.k : this,
|
||||
current = this.g,
|
||||
next = null;
|
||||
while (current && current.k === this) {
|
||||
if (this.a === STATE_DISPOSED)
|
||||
return;
|
||||
let head = self ? this.j || this.l : this, current = this.g, next = null;
|
||||
while (current && current.l === this) {
|
||||
current.dispose(true);
|
||||
current.n();
|
||||
next = current.g;
|
||||
current.g = null;
|
||||
current = next;
|
||||
}
|
||||
if (self) this.n();
|
||||
if (current) current.j = !self ? this : this.j;
|
||||
if (head) head.g = current;
|
||||
if (self)
|
||||
this.n();
|
||||
if (current)
|
||||
current.j = !self ? this : this.j;
|
||||
if (head)
|
||||
head.g = current;
|
||||
}
|
||||
n() {
|
||||
if (this.j) this.j.g = null;
|
||||
this.k = null;
|
||||
if (this.j)
|
||||
this.j.g = null;
|
||||
this.l = null;
|
||||
this.j = null;
|
||||
this.h = defaultContext;
|
||||
this.f = null;
|
||||
@@ -86,7 +96,8 @@ var Owner = class {
|
||||
this.emptyDisposal();
|
||||
}
|
||||
emptyDisposal() {
|
||||
if (!this.e) return;
|
||||
if (!this.e)
|
||||
return;
|
||||
if (Array.isArray(this.e)) {
|
||||
for (let i = 0; i < this.e.length; i++) {
|
||||
const callable = this.e[i];
|
||||
@@ -98,9 +109,9 @@ var Owner = class {
|
||||
this.e = null;
|
||||
}
|
||||
handleError(error) {
|
||||
if (!this.f) throw error;
|
||||
let i = 0,
|
||||
len = this.f.length;
|
||||
if (!this.f)
|
||||
throw error;
|
||||
let i = 0, len = this.f.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
try {
|
||||
this.f[i](error);
|
||||
@@ -109,7 +120,8 @@ var Owner = class {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
if (i === len) throw error;
|
||||
if (i === len)
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
function createContext(defaultValue, description) {
|
||||
@@ -119,9 +131,7 @@ function getContext(context, owner = currentOwner) {
|
||||
if (!owner) {
|
||||
throw new NoOwnerError();
|
||||
}
|
||||
const value = hasContext(context, owner)
|
||||
? owner.h[context.id]
|
||||
: context.defaultValue;
|
||||
const value = hasContext(context, owner) ? owner.h[context.id] : context.defaultValue;
|
||||
if (isUndefined(value)) {
|
||||
throw new ContextNotFoundError();
|
||||
}
|
||||
@@ -133,14 +143,15 @@ function setContext(context, value, owner = currentOwner) {
|
||||
}
|
||||
owner.h = {
|
||||
...owner.h,
|
||||
[context.id]: isUndefined(value) ? context.defaultValue : value,
|
||||
[context.id]: isUndefined(value) ? context.defaultValue : value
|
||||
};
|
||||
}
|
||||
function hasContext(context, owner = currentOwner) {
|
||||
return !isUndefined(owner?.h[context.id]);
|
||||
}
|
||||
function onCleanup(disposable) {
|
||||
if (!currentOwner) return;
|
||||
if (!currentOwner)
|
||||
return;
|
||||
const node = currentOwner;
|
||||
if (!node.e) {
|
||||
node.e = disposable;
|
||||
@@ -151,36 +162,39 @@ function onCleanup(disposable) {
|
||||
}
|
||||
}
|
||||
|
||||
// src/flags.ts
|
||||
// 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 DEFAULT_FLAGS = ERROR_BIT;
|
||||
|
||||
// src/scheduler.ts
|
||||
// src/core/scheduler.ts
|
||||
var scheduled = false;
|
||||
var runningScheduled = false;
|
||||
var Computations = [];
|
||||
var RenderEffects = [];
|
||||
var Effects = [];
|
||||
function flushSync() {
|
||||
if (!runningScheduled) runScheduled();
|
||||
if (!runningScheduled)
|
||||
runScheduled();
|
||||
}
|
||||
function flushQueue() {
|
||||
if (scheduled) return;
|
||||
if (scheduled)
|
||||
return;
|
||||
scheduled = true;
|
||||
queueMicrotask(runScheduled);
|
||||
}
|
||||
function runTop(node) {
|
||||
const ancestors = [];
|
||||
for (let current = node; current !== null; current = current.k) {
|
||||
for (let current = node; current !== null; current = current.l) {
|
||||
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].l();
|
||||
if (ancestors[i].a !== STATE_DISPOSED)
|
||||
ancestors[i].m();
|
||||
}
|
||||
}
|
||||
function runScheduled() {
|
||||
@@ -208,7 +222,8 @@ function runScheduled() {
|
||||
}
|
||||
function runPureQueue(queue) {
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
if (queue[i].a !== STATE_CLEAN) runTop(queue[i]);
|
||||
if (queue[i].a !== STATE_CLEAN)
|
||||
runTop(queue[i]);
|
||||
}
|
||||
}
|
||||
function runEffectQueue(queue) {
|
||||
@@ -221,13 +236,14 @@ function runEffectQueue(queue) {
|
||||
}
|
||||
}
|
||||
|
||||
// src/core.ts
|
||||
// src/core/core.ts
|
||||
var currentObserver = null;
|
||||
var currentMask = DEFAULT_FLAGS;
|
||||
var newSources = null;
|
||||
var newSourcesIndex = 0;
|
||||
var newFlags = 0;
|
||||
var clock = 0;
|
||||
var syncResolve = false;
|
||||
var updateCheck = null;
|
||||
function getObserver() {
|
||||
return currentObserver;
|
||||
@@ -259,12 +275,16 @@ var Computation2 = class extends Owner {
|
||||
this.s = compute2;
|
||||
this.a = compute2 ? STATE_DIRTY : STATE_CLEAN;
|
||||
this.d = initialValue;
|
||||
if (options?.equals !== void 0) this.t = options.equals;
|
||||
if (options?.unobserved) this.x = options?.unobserved;
|
||||
if (options?.equals !== void 0)
|
||||
this.t = options.equals;
|
||||
if (options?.unobserved)
|
||||
this.x = options?.unobserved;
|
||||
}
|
||||
y() {
|
||||
if (this.s) this.l();
|
||||
if (!this.b || this.b.length) track(this);
|
||||
if (this.s)
|
||||
this.m();
|
||||
if (!this.b || this.b.length)
|
||||
track(this);
|
||||
newFlags |= this.i & ~currentMask;
|
||||
if (this.i & ERROR_BIT) {
|
||||
throw this.d;
|
||||
@@ -287,7 +307,7 @@ var Computation2 = class extends Owner {
|
||||
* before continuing
|
||||
*/
|
||||
wait() {
|
||||
if (this.loading()) {
|
||||
if (!syncResolve && this.loading()) {
|
||||
throw new NotReadyError();
|
||||
}
|
||||
return this.y();
|
||||
@@ -317,20 +337,17 @@ var Computation2 = class extends Owner {
|
||||
}
|
||||
/** Update the computation with a new value. */
|
||||
write(value, flags = 0, raw = false) {
|
||||
const newValue =
|
||||
!raw && typeof value === "function" ? value(this.d) : value;
|
||||
const valueChanged =
|
||||
newValue !== UNCHANGED &&
|
||||
(!!(flags & ERROR_BIT) || this.t === false || !this.t(this.d, newValue));
|
||||
if (valueChanged) this.d = newValue;
|
||||
const changedFlagsMask = this.i ^ flags,
|
||||
changedFlags = changedFlagsMask & flags;
|
||||
const newValue = !raw && typeof value === "function" ? value(this.d) : value;
|
||||
const valueChanged = newValue !== UNCHANGED && (!!(flags & ERROR_BIT) || this.t === false || !this.t(this.d, newValue));
|
||||
if (valueChanged)
|
||||
this.d = newValue;
|
||||
const changedFlagsMask = this.i ^ flags, changedFlags = changedFlagsMask & flags;
|
||||
this.i = flags;
|
||||
this.w = clock + 1;
|
||||
if (this.c) {
|
||||
for (let i = 0; i < this.c.length; i++) {
|
||||
if (valueChanged) {
|
||||
this.c[i].m(STATE_DIRTY);
|
||||
this.c[i].k(STATE_DIRTY);
|
||||
} else if (changedFlagsMask) {
|
||||
this.c[i].z(changedFlagsMask, changedFlags);
|
||||
}
|
||||
@@ -341,12 +358,13 @@ var Computation2 = class extends Owner {
|
||||
/**
|
||||
* Set the current node's state, and recursively mark all of this node's observers as STATE_CHECK
|
||||
*/
|
||||
m(state) {
|
||||
if (this.a >= state) return;
|
||||
k(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
this.a = state;
|
||||
if (this.c) {
|
||||
for (let i = 0; i < this.c.length; i++) {
|
||||
this.c[i].m(STATE_CHECK);
|
||||
this.c[i].k(STATE_CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,17 +375,18 @@ var Computation2 = class extends Owner {
|
||||
* @param newFlags The source's new flags, masked to just the changed ones.
|
||||
*/
|
||||
z(mask, newFlags2) {
|
||||
if (this.a >= STATE_DIRTY) return;
|
||||
if (this.a >= STATE_DIRTY)
|
||||
return;
|
||||
if (mask & this.p) {
|
||||
this.m(STATE_DIRTY);
|
||||
this.k(STATE_DIRTY);
|
||||
return;
|
||||
}
|
||||
if (this.a >= STATE_CHECK) return;
|
||||
if (this.a >= STATE_CHECK)
|
||||
return;
|
||||
const prevFlags = this.i & mask;
|
||||
const deltaFlags = prevFlags ^ newFlags2;
|
||||
if (newFlags2 === prevFlags);
|
||||
else if (deltaFlags & prevFlags & mask) {
|
||||
this.m(STATE_CHECK);
|
||||
if (newFlags2 === prevFlags) ; else if (deltaFlags & prevFlags & mask) {
|
||||
this.k(STATE_CHECK);
|
||||
} else {
|
||||
this.i ^= deltaFlags;
|
||||
if (this.c) {
|
||||
@@ -387,7 +406,7 @@ var Computation2 = class extends Owner {
|
||||
*
|
||||
* This function will ensure that the value and states we read from the computation are up to date
|
||||
*/
|
||||
l() {
|
||||
m() {
|
||||
if (this.a === STATE_DISPOSED) {
|
||||
throw new Error("Tried to read a disposed computation");
|
||||
}
|
||||
@@ -397,7 +416,7 @@ var Computation2 = class extends Owner {
|
||||
let observerFlags = 0;
|
||||
if (this.a === STATE_CHECK) {
|
||||
for (let i = 0; i < this.b.length; i++) {
|
||||
this.b[i].l();
|
||||
this.b[i].m();
|
||||
observerFlags |= this.b[i].i;
|
||||
if (this.a === STATE_DIRTY) {
|
||||
break;
|
||||
@@ -415,38 +434,40 @@ var Computation2 = class extends Owner {
|
||||
* Remove ourselves from the owner graph and the computation graph
|
||||
*/
|
||||
n() {
|
||||
if (this.a === STATE_DISPOSED) return;
|
||||
if (this.b) removeSourceObservers(this, 0);
|
||||
if (this.a === STATE_DISPOSED)
|
||||
return;
|
||||
if (this.b)
|
||||
removeSourceObservers(this, 0);
|
||||
super.n();
|
||||
}
|
||||
};
|
||||
function loadingState(node) {
|
||||
const prevOwner = setOwner(node.k);
|
||||
const prevOwner = setOwner(node.l);
|
||||
const options = void 0;
|
||||
const computation = new Computation2(
|
||||
void 0,
|
||||
() => {
|
||||
track(node);
|
||||
node.l();
|
||||
node.m();
|
||||
return !!(node.i & LOADING_BIT);
|
||||
},
|
||||
options,
|
||||
options
|
||||
);
|
||||
computation.p = ERROR_BIT | LOADING_BIT;
|
||||
setOwner(prevOwner);
|
||||
return computation;
|
||||
}
|
||||
function errorState(node) {
|
||||
const prevOwner = setOwner(node.k);
|
||||
const prevOwner = setOwner(node.l);
|
||||
const options = void 0;
|
||||
const computation = new Computation2(
|
||||
void 0,
|
||||
() => {
|
||||
track(node);
|
||||
node.l();
|
||||
node.m();
|
||||
return !!(node.i & ERROR_BIT);
|
||||
},
|
||||
options,
|
||||
options
|
||||
);
|
||||
computation.p = ERROR_BIT;
|
||||
setOwner(prevOwner);
|
||||
@@ -454,13 +475,10 @@ function errorState(node) {
|
||||
}
|
||||
function track(computation) {
|
||||
if (currentObserver) {
|
||||
if (
|
||||
!newSources &&
|
||||
currentObserver.b &&
|
||||
currentObserver.b[newSourcesIndex] === computation
|
||||
) {
|
||||
if (!newSources && currentObserver.b && currentObserver.b[newSourcesIndex] === computation) {
|
||||
newSourcesIndex++;
|
||||
} else if (!newSources) newSources = [computation];
|
||||
} else if (!newSources)
|
||||
newSources = [computation];
|
||||
else if (computation !== newSources[newSources.length - 1]) {
|
||||
newSources.push(computation);
|
||||
}
|
||||
@@ -470,9 +488,7 @@ function track(computation) {
|
||||
}
|
||||
}
|
||||
function update(node) {
|
||||
const prevSources = newSources,
|
||||
prevSourcesIndex = newSourcesIndex,
|
||||
prevFlags = newFlags;
|
||||
const prevSources = newSources, prevSourcesIndex = newSourcesIndex, prevFlags = newFlags;
|
||||
newSources = null;
|
||||
newSourcesIndex = 0;
|
||||
newFlags = 0;
|
||||
@@ -489,7 +505,8 @@ function update(node) {
|
||||
}
|
||||
} finally {
|
||||
if (newSources) {
|
||||
if (node.b) removeSourceObservers(node, newSourcesIndex);
|
||||
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++) {
|
||||
@@ -501,8 +518,10 @@ function update(node) {
|
||||
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);
|
||||
if (!source.c)
|
||||
source.c = [node];
|
||||
else
|
||||
source.c.push(node);
|
||||
}
|
||||
} else if (node.b && newSourcesIndex < node.b.length) {
|
||||
removeSourceObservers(node, newSourcesIndex);
|
||||
@@ -523,7 +542,8 @@ function removeSourceObservers(node, index) {
|
||||
swap = source.c.indexOf(node);
|
||||
source.c[swap] = source.c[source.c.length - 1];
|
||||
source.c.pop();
|
||||
if (!source.c.length) source.x?.();
|
||||
if (!source.c.length)
|
||||
source.x?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,7 +551,8 @@ function isEqual(a, b) {
|
||||
return a === b;
|
||||
}
|
||||
function untrack(fn) {
|
||||
if (currentObserver === null) return fn();
|
||||
if (currentObserver === null)
|
||||
return fn();
|
||||
return compute(getOwner(), fn, null);
|
||||
}
|
||||
function hasUpdated(fn) {
|
||||
@@ -544,10 +565,27 @@ function hasUpdated(fn) {
|
||||
updateCheck = current;
|
||||
}
|
||||
}
|
||||
function isPending(fn) {
|
||||
try {
|
||||
fn();
|
||||
return false;
|
||||
} catch (e) {
|
||||
return e instanceof NotReadyError;
|
||||
}
|
||||
}
|
||||
function latest(fn) {
|
||||
const prevFlags = newFlags;
|
||||
syncResolve = true;
|
||||
try {
|
||||
return fn();
|
||||
} catch {
|
||||
} finally {
|
||||
newFlags = prevFlags;
|
||||
syncResolve = false;
|
||||
}
|
||||
}
|
||||
function compute(owner, compute2, observer) {
|
||||
const prevOwner = setOwner(owner),
|
||||
prevObserver = currentObserver,
|
||||
prevMask = currentMask;
|
||||
const prevOwner = setOwner(owner), prevObserver = currentObserver, prevMask = currentMask;
|
||||
currentObserver = observer;
|
||||
currentMask = observer?.p ?? DEFAULT_FLAGS;
|
||||
try {
|
||||
@@ -561,20 +599,21 @@ function compute(owner, compute2, observer) {
|
||||
var EagerComputation = class extends Computation2 {
|
||||
constructor(initialValue, compute2, options) {
|
||||
super(initialValue, compute2, options);
|
||||
this.l();
|
||||
this.m();
|
||||
Computations.push(this);
|
||||
}
|
||||
m(state) {
|
||||
if (this.a >= state) return;
|
||||
k(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
if (this.a === STATE_CLEAN) {
|
||||
Computations.push(this);
|
||||
flushQueue();
|
||||
}
|
||||
this.a = state;
|
||||
super.k(state);
|
||||
}
|
||||
};
|
||||
|
||||
// src/effect.ts
|
||||
// src/core/effect.ts
|
||||
var BaseEffect = class extends Computation2 {
|
||||
r;
|
||||
q = false;
|
||||
@@ -585,7 +624,8 @@ var BaseEffect = class extends Computation2 {
|
||||
this.o = initialValue;
|
||||
}
|
||||
write(value) {
|
||||
if (value === UNCHANGED) return this.d;
|
||||
if (value === UNCHANGED)
|
||||
return this.d;
|
||||
this.d = value;
|
||||
this.q = true;
|
||||
return value;
|
||||
@@ -605,8 +645,9 @@ var Effect = class extends BaseEffect {
|
||||
Effects.push(this);
|
||||
flushQueue();
|
||||
}
|
||||
m(state) {
|
||||
if (this.a >= state) return;
|
||||
k(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
if (this.a === STATE_CLEAN) {
|
||||
Effects.push(this);
|
||||
flushQueue();
|
||||
@@ -617,11 +658,12 @@ var Effect = class extends BaseEffect {
|
||||
var RenderEffect = class extends BaseEffect {
|
||||
constructor(initialValue, compute2, effect, options) {
|
||||
super(initialValue, compute2, effect, options);
|
||||
this.l();
|
||||
this.m();
|
||||
RenderEffects.push(this);
|
||||
}
|
||||
m(state) {
|
||||
if (this.a >= state) return;
|
||||
k(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
if (this.a === STATE_CLEAN) {
|
||||
RenderEffects.push(this);
|
||||
flushQueue();
|
||||
@@ -637,7 +679,7 @@ function createSignal(first, second, third) {
|
||||
const node2 = new Computation2(
|
||||
first(p ? untrack(p[0]) : second),
|
||||
null,
|
||||
third,
|
||||
third
|
||||
);
|
||||
return [node2.read.bind(node2), node2.write.bind(node2)];
|
||||
});
|
||||
@@ -647,44 +689,52 @@ function createSignal(first, second, third) {
|
||||
return [node.read.bind(node), node.write.bind(node)];
|
||||
}
|
||||
function createAsync(fn, initial, options) {
|
||||
const lhs = new EagerComputation(void 0, () => {
|
||||
const source = fn(initial);
|
||||
const isPromise = source instanceof Promise;
|
||||
const iterator = source[Symbol.asyncIterator];
|
||||
if (!isPromise && !iterator) {
|
||||
return {
|
||||
wait() {
|
||||
return source;
|
||||
},
|
||||
};
|
||||
}
|
||||
const signal = new Computation2(initial, null, options);
|
||||
signal.write(UNCHANGED, LOADING_BIT);
|
||||
if (isPromise) {
|
||||
source.then(
|
||||
(value) => {
|
||||
signal.write(value, 0);
|
||||
},
|
||||
(error) => {
|
||||
signal.write(error, ERROR_BIT);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let abort = false;
|
||||
onCleanup(() => (abort = true));
|
||||
(async () => {
|
||||
try {
|
||||
for await (let value of source) {
|
||||
if (abort) return;
|
||||
signal.write(value, 0);
|
||||
const lhs = new EagerComputation(
|
||||
{
|
||||
d: initial
|
||||
},
|
||||
(p) => {
|
||||
const value = p?.d;
|
||||
const source = fn(value);
|
||||
const isPromise = source instanceof Promise;
|
||||
const iterator = source[Symbol.asyncIterator];
|
||||
if (!isPromise && !iterator) {
|
||||
return {
|
||||
wait() {
|
||||
return source;
|
||||
},
|
||||
d: source
|
||||
};
|
||||
}
|
||||
const signal = new Computation2(value, null, options);
|
||||
signal.write(UNCHANGED, LOADING_BIT);
|
||||
if (isPromise) {
|
||||
source.then(
|
||||
(value2) => {
|
||||
signal.write(value2, 0);
|
||||
},
|
||||
(error) => {
|
||||
signal.write(error, ERROR_BIT);
|
||||
}
|
||||
} catch (error) {
|
||||
signal.write(error, ERROR_BIT);
|
||||
}
|
||||
})();
|
||||
);
|
||||
} else {
|
||||
let abort = false;
|
||||
onCleanup(() => abort = true);
|
||||
(async () => {
|
||||
try {
|
||||
for await (let value2 of source) {
|
||||
if (abort)
|
||||
return;
|
||||
signal.write(value2, 0);
|
||||
}
|
||||
} catch (error) {
|
||||
signal.write(error, ERROR_BIT);
|
||||
}
|
||||
})();
|
||||
}
|
||||
return signal;
|
||||
}
|
||||
return signal;
|
||||
});
|
||||
);
|
||||
return () => lhs.wait().wait();
|
||||
}
|
||||
function createMemo(compute2, initialValue, options) {
|
||||
@@ -693,24 +743,31 @@ function createMemo(compute2, initialValue, options) {
|
||||
return () => {
|
||||
if (node) {
|
||||
value = node.wait();
|
||||
if (!node.b?.length) node = void 0;
|
||||
if (!node.b?.length)
|
||||
node = void 0;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
function createEffect(compute2, effect, initialValue, options) {
|
||||
void new Effect(initialValue, compute2, effect, void 0);
|
||||
void new Effect(
|
||||
initialValue,
|
||||
compute2,
|
||||
effect,
|
||||
void 0
|
||||
);
|
||||
}
|
||||
function createRenderEffect(compute2, effect, initialValue, options) {
|
||||
void new RenderEffect(initialValue, compute2, effect, void 0);
|
||||
void new RenderEffect(
|
||||
initialValue,
|
||||
compute2,
|
||||
effect,
|
||||
void 0
|
||||
);
|
||||
}
|
||||
function createRoot(init) {
|
||||
const owner = new Owner();
|
||||
return compute(
|
||||
owner,
|
||||
!init.length ? init : () => init(() => owner.dispose()),
|
||||
null,
|
||||
);
|
||||
return compute(owner, !init.length ? init : () => init(() => owner.dispose()), null);
|
||||
}
|
||||
function runWithOwner(owner, run) {
|
||||
try {
|
||||
@@ -730,33 +787,4 @@ function catchError(fn, handler) {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Computation2 as Computation,
|
||||
ContextNotFoundError,
|
||||
Effect,
|
||||
NoOwnerError,
|
||||
NotReadyError,
|
||||
Owner,
|
||||
RenderEffect,
|
||||
catchError,
|
||||
compute,
|
||||
createAsync,
|
||||
createContext,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createRenderEffect,
|
||||
createRoot,
|
||||
createSignal,
|
||||
flushSync,
|
||||
getContext,
|
||||
getObserver,
|
||||
getOwner,
|
||||
hasContext,
|
||||
hasUpdated,
|
||||
isEqual,
|
||||
onCleanup,
|
||||
runWithOwner,
|
||||
setContext,
|
||||
setOwner,
|
||||
untrack,
|
||||
};
|
||||
export { Computation2 as Computation, ContextNotFoundError, NoOwnerError, NotReadyError, Owner, catchError, createAsync, createContext, createEffect, createMemo, createRenderEffect, createRoot, createSignal, flushSync, getContext, getObserver, getOwner, hasContext, hasUpdated, isEqual, isPending, latest, onCleanup, runWithOwner, setContext, untrack };
|
||||
+14
-5
@@ -26,8 +26,8 @@
|
||||
* 3. updateIfNecessary() evaluates the computation if the node is dirty (the computations are
|
||||
* executed in root to leaf order)
|
||||
*/
|
||||
import { type Flags } from './flags';
|
||||
import { Owner } from './owner';
|
||||
import { type Flags } from "./flags.js";
|
||||
import { Owner } from "./owner.js";
|
||||
export interface SignalOptions<T> {
|
||||
name?: string;
|
||||
equals?: ((prev: T, next: T) => boolean) | false;
|
||||
@@ -58,7 +58,7 @@ export declare class Computation<T = any> extends Owner implements SourceType, O
|
||||
_sources: SourceType[] | null;
|
||||
_observers: ObserverType[] | null;
|
||||
_value: T | undefined;
|
||||
_compute: null | (() => T);
|
||||
_compute: null | ((p?: T) => T);
|
||||
_name: string | undefined;
|
||||
_equals: false | ((a: T, b: T) => boolean);
|
||||
_unobserved: (() => void) | undefined;
|
||||
@@ -69,7 +69,7 @@ export declare class Computation<T = any> extends Owner implements SourceType, O
|
||||
_error: Computation<boolean> | null;
|
||||
_loading: Computation<boolean> | null;
|
||||
_time: number;
|
||||
constructor(initialValue: T | undefined, compute: null | (() => T), options?: SignalOptions<T>);
|
||||
constructor(initialValue: T | undefined, compute: null | ((p?: T) => T), options?: SignalOptions<T>);
|
||||
_read(): T;
|
||||
/**
|
||||
* Return the current value of this computation
|
||||
@@ -138,7 +138,16 @@ export declare function isEqual<T>(a: T, b: T): boolean;
|
||||
* dependencies. Use `untrack` if you want to also disable owner tracking.
|
||||
*/
|
||||
export declare function untrack<T>(fn: () => T): T;
|
||||
export declare function hasUpdated(fn: () => any): Boolean;
|
||||
/**
|
||||
* Returns true if the given functinon contains signals that have been updated since the last time
|
||||
* the parent computation was run.
|
||||
*/
|
||||
export declare function hasUpdated(fn: () => any): boolean;
|
||||
/**
|
||||
* Returns true if the given function contains async signals that are not ready yet.
|
||||
*/
|
||||
export declare function isPending(fn: () => any): boolean;
|
||||
export declare function latest<T>(fn: () => T): T | undefined;
|
||||
/**
|
||||
* A convenient wrapper that calls `compute` with the `owner` and `observer` and is guaranteed
|
||||
* to reset the global context after the computation is finished even if an error is thrown.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Computation, type SignalOptions } from './core';
|
||||
import { Computation, type SignalOptions } from "./core.js";
|
||||
/**
|
||||
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
||||
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
||||
@@ -0,0 +1,6 @@
|
||||
export { ContextNotFoundError, NoOwnerError, NotReadyError, type ErrorHandler } from "./error.js";
|
||||
export { Owner, createContext, getContext, setContext, hasContext, getOwner, onCleanup, type Context, type ContextRecord, type Disposable } from "./owner.js";
|
||||
export { Computation, EagerComputation, getObserver, isEqual, untrack, hasUpdated, isPending, latest, UNCHANGED, compute, type SignalOptions } from "./core.js";
|
||||
export { Effect, RenderEffect } from "./effect.js";
|
||||
export { flushSync } from "./scheduler.js";
|
||||
export * from "./flags.js";
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
*
|
||||
* Note that the owner tree is largely orthogonal to the reactivity tree, and is much closer to the component tree.
|
||||
*/
|
||||
import { type ErrorHandler } from './error';
|
||||
import { type ErrorHandler } from "./error.js";
|
||||
export type ContextRecord = Record<string | symbol, unknown>;
|
||||
export interface Disposable {
|
||||
(): void;
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { Computation } from './core';
|
||||
import type { Effect, RenderEffect } from './effect';
|
||||
import type { Effect, RenderEffect } from "./effect.js";
|
||||
import { Computation } from "./core.js";
|
||||
export declare let Computations: Computation[], RenderEffects: RenderEffect[], Effects: Effect[];
|
||||
/**
|
||||
* By default, changes are batched on the microtask queue which is an async process. You can flush
|
||||
@@ -0,0 +1,4 @@
|
||||
export { Computation, ContextNotFoundError, NoOwnerError, NotReadyError, Owner, createContext, getContext, setContext, hasContext, getOwner, onCleanup, getObserver, isEqual, untrack, hasUpdated, isPending, latest } from "./core/index.js";
|
||||
export type { ErrorHandler, SignalOptions, Context, ContextRecord, Disposable } from "./core/index.js";
|
||||
export { flushSync } from "./core/scheduler.js";
|
||||
export * from "./signals.js";
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { Accessor } from './signals';
|
||||
import type { Accessor } from "./signals.js";
|
||||
export type Maybe<T> = T | void | null | undefined | false;
|
||||
/**
|
||||
* Reactive map helper that caches each list item by reference to reduce unnecessary mapping on
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import type { SignalOptions } from './core';
|
||||
import { Owner } from './owner';
|
||||
import type { SignalOptions } from "./core/index.js";
|
||||
import { Owner } from "./core/index.js";
|
||||
export interface Accessor<T> {
|
||||
(): T;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./store.js";
|
||||
@@ -0,0 +1,779 @@
|
||||
// src/core/error.ts
|
||||
var NotReadyError = class extends Error {
|
||||
};
|
||||
var NoOwnerError = class extends Error {
|
||||
constructor() {
|
||||
super(
|
||||
""
|
||||
);
|
||||
}
|
||||
};
|
||||
var ContextNotFoundError = class extends Error {
|
||||
constructor() {
|
||||
super(
|
||||
""
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// src/utils.ts
|
||||
function isUndefined(value) {
|
||||
return typeof value === "undefined";
|
||||
}
|
||||
|
||||
// src/core/constants.ts
|
||||
var STATE_CLEAN = 0;
|
||||
var STATE_CHECK = 1;
|
||||
var STATE_DIRTY = 2;
|
||||
var STATE_DISPOSED = 3;
|
||||
|
||||
// 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
|
||||
k = null;
|
||||
g = null;
|
||||
j = null;
|
||||
a = STATE_CLEAN;
|
||||
e = null;
|
||||
h = defaultContext;
|
||||
f = null;
|
||||
constructor(signal = false) {
|
||||
if (currentOwner && !signal)
|
||||
currentOwner.append(this);
|
||||
}
|
||||
append(child) {
|
||||
child.k = this;
|
||||
child.j = this;
|
||||
if (this.g)
|
||||
this.g.j = child;
|
||||
child.g = this.g;
|
||||
this.g = child;
|
||||
if (child.h !== this.h) {
|
||||
child.h = { ...this.h, ...child.h };
|
||||
}
|
||||
if (this.f) {
|
||||
child.f = !child.f ? this.f : [...child.f, ...this.f];
|
||||
}
|
||||
}
|
||||
dispose(self = true) {
|
||||
if (this.a === STATE_DISPOSED)
|
||||
return;
|
||||
let head = self ? this.j || this.k : this, current = this.g, next = null;
|
||||
while (current && current.k === this) {
|
||||
current.dispose(true);
|
||||
current.n();
|
||||
next = current.g;
|
||||
current.g = null;
|
||||
current = next;
|
||||
}
|
||||
if (self)
|
||||
this.n();
|
||||
if (current)
|
||||
current.j = !self ? this : this.j;
|
||||
if (head)
|
||||
head.g = current;
|
||||
}
|
||||
n() {
|
||||
if (this.j)
|
||||
this.j.g = null;
|
||||
this.k = null;
|
||||
this.j = null;
|
||||
this.h = defaultContext;
|
||||
this.f = null;
|
||||
this.a = STATE_DISPOSED;
|
||||
this.emptyDisposal();
|
||||
}
|
||||
emptyDisposal() {
|
||||
if (!this.e)
|
||||
return;
|
||||
if (Array.isArray(this.e)) {
|
||||
for (let i = 0; i < this.e.length; i++) {
|
||||
const callable = this.e[i];
|
||||
callable.call(callable);
|
||||
}
|
||||
} else {
|
||||
this.e.call(this.e);
|
||||
}
|
||||
this.e = null;
|
||||
}
|
||||
handleError(error) {
|
||||
if (!this.f)
|
||||
throw error;
|
||||
let i = 0, len = this.f.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
try {
|
||||
this.f[i](error);
|
||||
break;
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
if (i === len)
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
function createContext(defaultValue, description) {
|
||||
return { id: Symbol(description), defaultValue };
|
||||
}
|
||||
function getContext(context, owner = currentOwner) {
|
||||
if (!owner) {
|
||||
throw new NoOwnerError();
|
||||
}
|
||||
const value = hasContext(context, owner) ? owner.h[context.id] : context.defaultValue;
|
||||
if (isUndefined(value)) {
|
||||
throw new ContextNotFoundError();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function setContext(context, value, owner = currentOwner) {
|
||||
if (!owner) {
|
||||
throw new NoOwnerError();
|
||||
}
|
||||
owner.h = {
|
||||
...owner.h,
|
||||
[context.id]: isUndefined(value) ? context.defaultValue : value
|
||||
};
|
||||
}
|
||||
function hasContext(context, owner = currentOwner) {
|
||||
return !isUndefined(owner?.h[context.id]);
|
||||
}
|
||||
function onCleanup(disposable) {
|
||||
if (!currentOwner)
|
||||
return;
|
||||
const node = currentOwner;
|
||||
if (!node.e) {
|
||||
node.e = disposable;
|
||||
} else if (Array.isArray(node.e)) {
|
||||
node.e.push(disposable);
|
||||
} else {
|
||||
node.e = [node.e, disposable];
|
||||
}
|
||||
}
|
||||
|
||||
// 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 DEFAULT_FLAGS = ERROR_BIT;
|
||||
|
||||
// src/core/scheduler.ts
|
||||
var scheduled = false;
|
||||
var runningScheduled = false;
|
||||
var Computations = [];
|
||||
var RenderEffects = [];
|
||||
var Effects = [];
|
||||
function flushSync() {
|
||||
if (!runningScheduled)
|
||||
runScheduled();
|
||||
}
|
||||
function flushQueue() {
|
||||
if (scheduled)
|
||||
return;
|
||||
scheduled = true;
|
||||
queueMicrotask(runScheduled);
|
||||
}
|
||||
function runTop(node) {
|
||||
const ancestors = [];
|
||||
for (let current = node; current !== null; current = current.k) {
|
||||
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].l();
|
||||
}
|
||||
}
|
||||
function runScheduled() {
|
||||
if (!Effects.length && !RenderEffects.length && !Computations.length) {
|
||||
scheduled = false;
|
||||
return;
|
||||
}
|
||||
runningScheduled = true;
|
||||
try {
|
||||
runPureQueue(Computations);
|
||||
runPureQueue(RenderEffects);
|
||||
runPureQueue(Effects);
|
||||
} finally {
|
||||
const renderEffects = RenderEffects;
|
||||
const effects = Effects;
|
||||
Computations = [];
|
||||
Effects = [];
|
||||
RenderEffects = [];
|
||||
scheduled = false;
|
||||
runningScheduled = false;
|
||||
incrementClock();
|
||||
runEffectQueue(renderEffects);
|
||||
runEffectQueue(effects);
|
||||
}
|
||||
}
|
||||
function runPureQueue(queue) {
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
if (queue[i].a !== STATE_CLEAN)
|
||||
runTop(queue[i]);
|
||||
}
|
||||
}
|
||||
function runEffectQueue(queue) {
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
if (queue[i].q && queue[i].a !== STATE_DISPOSED) {
|
||||
queue[i].r(queue[i].d, queue[i].o);
|
||||
queue[i].q = false;
|
||||
queue[i].o = queue[i].d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// src/core/core.ts
|
||||
var currentObserver = null;
|
||||
var currentMask = DEFAULT_FLAGS;
|
||||
var newSources = null;
|
||||
var newSourcesIndex = 0;
|
||||
var newFlags = 0;
|
||||
var clock = 0;
|
||||
var syncResolve = false;
|
||||
var updateCheck = null;
|
||||
function getObserver() {
|
||||
return currentObserver;
|
||||
}
|
||||
function incrementClock() {
|
||||
clock++;
|
||||
}
|
||||
var UNCHANGED = Symbol(0);
|
||||
var Computation2 = class extends Owner {
|
||||
b = null;
|
||||
c = null;
|
||||
d;
|
||||
s;
|
||||
// Used in __DEV__ mode, hopefully removed in production
|
||||
C;
|
||||
// Using false is an optimization as an alternative to _equals: () => false
|
||||
// which could enable more efficient DIRTY notification
|
||||
t = isEqual;
|
||||
y;
|
||||
/** Whether the computation is an error or has ancestors that are unresolved */
|
||||
i = 0;
|
||||
/** Which flags raised by sources are handled, vs. being passed through. */
|
||||
p = DEFAULT_FLAGS;
|
||||
u = null;
|
||||
v = null;
|
||||
w = -1;
|
||||
constructor(initialValue, compute2, options) {
|
||||
super(compute2 === null);
|
||||
this.s = compute2;
|
||||
this.a = compute2 ? STATE_DIRTY : STATE_CLEAN;
|
||||
this.d = initialValue;
|
||||
if (options?.equals !== void 0)
|
||||
this.t = options.equals;
|
||||
if (options?.unobserved)
|
||||
this.y = options?.unobserved;
|
||||
}
|
||||
z() {
|
||||
if (this.s)
|
||||
this.l();
|
||||
if (!this.b || this.b.length)
|
||||
track(this);
|
||||
newFlags |= this.i & ~currentMask;
|
||||
if (this.i & ERROR_BIT) {
|
||||
throw this.d;
|
||||
} else {
|
||||
return this.d;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return the current value of this computation
|
||||
* Automatically re-executes the surrounding computation when the value changes
|
||||
*/
|
||||
read() {
|
||||
return this.z();
|
||||
}
|
||||
/**
|
||||
* 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 (!syncResolve && this.loading()) {
|
||||
throw new NotReadyError();
|
||||
}
|
||||
return this.z();
|
||||
}
|
||||
/**
|
||||
* Return true if the computation is the value is dependent on an unresolved promise
|
||||
* Triggers re-execution of the computation when the loading state changes
|
||||
*
|
||||
* This is useful especially when effects want to re-execute when a computation's
|
||||
* loading state changes
|
||||
*/
|
||||
loading() {
|
||||
if (this.v === null) {
|
||||
this.v = loadingState(this);
|
||||
}
|
||||
return this.v.read();
|
||||
}
|
||||
/**
|
||||
* Return true if the computation is the computation threw an error
|
||||
* Triggers re-execution of the computation when the error state changes
|
||||
*/
|
||||
error() {
|
||||
if (this.u === null) {
|
||||
this.u = errorState(this);
|
||||
}
|
||||
return this.u.read();
|
||||
}
|
||||
/** Update the computation with a new value. */
|
||||
write(value, flags = 0, raw = false) {
|
||||
const newValue = !raw && typeof value === "function" ? value(this.d) : value;
|
||||
const valueChanged = newValue !== UNCHANGED && (!!(flags & ERROR_BIT) || this.t === false || !this.t(this.d, newValue));
|
||||
if (valueChanged)
|
||||
this.d = newValue;
|
||||
const changedFlagsMask = this.i ^ flags, changedFlags = changedFlagsMask & flags;
|
||||
this.i = flags;
|
||||
this.w = clock + 1;
|
||||
if (this.c) {
|
||||
for (let i = 0; i < this.c.length; i++) {
|
||||
if (valueChanged) {
|
||||
this.c[i].m(STATE_DIRTY);
|
||||
} else if (changedFlagsMask) {
|
||||
this.c[i].A(changedFlagsMask, changedFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.d;
|
||||
}
|
||||
/**
|
||||
* Set the current node's state, and recursively mark all of this node's observers as STATE_CHECK
|
||||
*/
|
||||
m(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
this.a = state;
|
||||
if (this.c) {
|
||||
for (let i = 0; i < this.c.length; i++) {
|
||||
this.c[i].m(STATE_CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
A(mask, newFlags2) {
|
||||
if (this.a >= STATE_DIRTY)
|
||||
return;
|
||||
if (mask & this.p) {
|
||||
this.m(STATE_DIRTY);
|
||||
return;
|
||||
}
|
||||
if (this.a >= STATE_CHECK)
|
||||
return;
|
||||
const prevFlags = this.i & mask;
|
||||
const deltaFlags = prevFlags ^ newFlags2;
|
||||
if (newFlags2 === prevFlags) ; else if (deltaFlags & prevFlags & mask) {
|
||||
this.m(STATE_CHECK);
|
||||
} else {
|
||||
this.i ^= deltaFlags;
|
||||
if (this.c) {
|
||||
for (let i = 0; i < this.c.length; i++) {
|
||||
this.c[i].A(mask, newFlags2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
B(error) {
|
||||
this.write(error, this.i | ERROR_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
|
||||
*/
|
||||
l() {
|
||||
if (this.a === STATE_DISPOSED) {
|
||||
throw new Error("Tried to read a disposed computation");
|
||||
}
|
||||
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].l();
|
||||
observerFlags |= this.b[i].i;
|
||||
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
|
||||
*/
|
||||
n() {
|
||||
if (this.a === STATE_DISPOSED)
|
||||
return;
|
||||
if (this.b)
|
||||
removeSourceObservers(this, 0);
|
||||
super.n();
|
||||
}
|
||||
};
|
||||
function loadingState(node) {
|
||||
const prevOwner = setOwner(node.k);
|
||||
const options = void 0;
|
||||
const computation = new Computation2(
|
||||
void 0,
|
||||
() => {
|
||||
track(node);
|
||||
node.l();
|
||||
return !!(node.i & LOADING_BIT);
|
||||
},
|
||||
options
|
||||
);
|
||||
computation.p = ERROR_BIT | LOADING_BIT;
|
||||
setOwner(prevOwner);
|
||||
return computation;
|
||||
}
|
||||
function errorState(node) {
|
||||
const prevOwner = setOwner(node.k);
|
||||
const options = void 0;
|
||||
const computation = new Computation2(
|
||||
void 0,
|
||||
() => {
|
||||
track(node);
|
||||
node.l();
|
||||
return !!(node.i & ERROR_BIT);
|
||||
},
|
||||
options
|
||||
);
|
||||
computation.p = ERROR_BIT;
|
||||
setOwner(prevOwner);
|
||||
return computation;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (updateCheck) {
|
||||
updateCheck.d = computation.w > currentObserver.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
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.s, node);
|
||||
node.write(result, newFlags, true);
|
||||
} catch (error) {
|
||||
if (error instanceof NotReadyError) {
|
||||
node.write(UNCHANGED, newFlags | LOADING_BIT);
|
||||
} else {
|
||||
node.B(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.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.y?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
function isEqual(a, b) {
|
||||
return a === b;
|
||||
}
|
||||
function untrack(fn) {
|
||||
if (currentObserver === null)
|
||||
return fn();
|
||||
return compute(getOwner(), fn, null);
|
||||
}
|
||||
function hasUpdated(fn) {
|
||||
const current = updateCheck;
|
||||
updateCheck = { d: false };
|
||||
try {
|
||||
fn();
|
||||
return updateCheck.d;
|
||||
} finally {
|
||||
updateCheck = current;
|
||||
}
|
||||
}
|
||||
function isPending(fn) {
|
||||
try {
|
||||
fn();
|
||||
return false;
|
||||
} catch (e) {
|
||||
return e instanceof NotReadyError;
|
||||
}
|
||||
}
|
||||
function latest(fn) {
|
||||
const prevFlags = newFlags;
|
||||
syncResolve = true;
|
||||
try {
|
||||
return fn();
|
||||
} catch {
|
||||
} finally {
|
||||
newFlags = prevFlags;
|
||||
syncResolve = false;
|
||||
}
|
||||
}
|
||||
function compute(owner, compute2, observer) {
|
||||
const prevOwner = setOwner(owner), prevObserver = currentObserver, prevMask = currentMask;
|
||||
currentObserver = observer;
|
||||
currentMask = observer?.p ?? DEFAULT_FLAGS;
|
||||
try {
|
||||
return compute2(observer ? observer.d : void 0);
|
||||
} finally {
|
||||
setOwner(prevOwner);
|
||||
currentObserver = prevObserver;
|
||||
currentMask = prevMask;
|
||||
}
|
||||
}
|
||||
var EagerComputation = class extends Computation2 {
|
||||
constructor(initialValue, compute2, options) {
|
||||
super(initialValue, compute2, options);
|
||||
this.l();
|
||||
Computations.push(this);
|
||||
}
|
||||
m(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
if (this.a === STATE_CLEAN) {
|
||||
Computations.push(this);
|
||||
flushQueue();
|
||||
}
|
||||
super.m(state);
|
||||
}
|
||||
};
|
||||
|
||||
// src/core/effect.ts
|
||||
var USER_EFFECT = 0;
|
||||
var RENDER_EFFECT = 1;
|
||||
var Effect = class extends Computation2 {
|
||||
r;
|
||||
q = false;
|
||||
o;
|
||||
x;
|
||||
constructor(initialValue, compute2, effect, options) {
|
||||
super(initialValue, compute2, options);
|
||||
this.r = effect;
|
||||
this.o = initialValue;
|
||||
this.l();
|
||||
this.x = options?.render ? RENDER_EFFECT : USER_EFFECT;
|
||||
(this.x ? RenderEffects : Effects).push(this);
|
||||
}
|
||||
write(value) {
|
||||
if (value === UNCHANGED)
|
||||
return this.d;
|
||||
this.d = value;
|
||||
this.q = true;
|
||||
return value;
|
||||
}
|
||||
m(state) {
|
||||
if (this.a >= state)
|
||||
return;
|
||||
if (this.a === STATE_CLEAN) {
|
||||
(this.x ? RenderEffects : Effects).push(this);
|
||||
flushQueue();
|
||||
}
|
||||
this.a = state;
|
||||
}
|
||||
B(error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
n() {
|
||||
this.r = void 0;
|
||||
this.o = void 0;
|
||||
super.n();
|
||||
}
|
||||
};
|
||||
|
||||
// src/signals.ts
|
||||
function createSignal(first, second, third) {
|
||||
if (typeof first === "function") {
|
||||
const memo = createMemo((p) => {
|
||||
const node2 = new Computation2(
|
||||
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 node = new Computation2(first, null, second);
|
||||
return [node.read.bind(node), node.write.bind(node)];
|
||||
}
|
||||
function createAsync(fn, initial, options) {
|
||||
const lhs = new EagerComputation(
|
||||
{
|
||||
d: initial
|
||||
},
|
||||
(p) => {
|
||||
const value = p?.d;
|
||||
const source = fn(value);
|
||||
const isPromise = source instanceof Promise;
|
||||
const iterator = source[Symbol.asyncIterator];
|
||||
if (!isPromise && !iterator) {
|
||||
return {
|
||||
wait() {
|
||||
return source;
|
||||
},
|
||||
d: source
|
||||
};
|
||||
}
|
||||
const signal = new Computation2(value, null, options);
|
||||
signal.write(UNCHANGED, LOADING_BIT);
|
||||
if (isPromise) {
|
||||
source.then(
|
||||
(value2) => {
|
||||
signal.write(value2, 0);
|
||||
},
|
||||
(error) => {
|
||||
signal.write(error, ERROR_BIT);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
let abort = false;
|
||||
onCleanup(() => abort = true);
|
||||
(async () => {
|
||||
try {
|
||||
for await (let value2 of source) {
|
||||
if (abort)
|
||||
return;
|
||||
signal.write(value2, 0);
|
||||
}
|
||||
} catch (error) {
|
||||
signal.write(error, ERROR_BIT);
|
||||
}
|
||||
})();
|
||||
}
|
||||
return signal;
|
||||
}
|
||||
);
|
||||
return () => lhs.wait().wait();
|
||||
}
|
||||
function createMemo(compute2, initialValue, options) {
|
||||
let node = new Computation2(
|
||||
initialValue,
|
||||
compute2,
|
||||
options
|
||||
);
|
||||
let value;
|
||||
return () => {
|
||||
if (node) {
|
||||
value = node.wait();
|
||||
if (!node.b?.length)
|
||||
node = void 0;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
function createEffect(compute2, effect, initialValue, options) {
|
||||
void new Effect(
|
||||
initialValue,
|
||||
compute2,
|
||||
effect,
|
||||
void 0
|
||||
);
|
||||
}
|
||||
function createRenderEffect(compute2, effect, initialValue, options) {
|
||||
void new Effect(initialValue, compute2, effect, {
|
||||
render: true,
|
||||
...void 0
|
||||
});
|
||||
}
|
||||
function createRoot(init) {
|
||||
const owner = new Owner();
|
||||
return compute(
|
||||
owner,
|
||||
!init.length ? init : () => init(() => owner.dispose()),
|
||||
null
|
||||
);
|
||||
}
|
||||
function runWithOwner(owner, run) {
|
||||
try {
|
||||
return compute(owner, run, null);
|
||||
} catch (error) {
|
||||
owner?.handleError(error);
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
function catchError(fn, handler) {
|
||||
const owner = new Owner();
|
||||
owner.f = owner.f ? [handler, ...owner.f] : [handler];
|
||||
try {
|
||||
compute(owner, fn, null);
|
||||
} catch (error) {
|
||||
owner.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export { Computation2 as Computation, ContextNotFoundError, NoOwnerError, NotReadyError, Owner, catchError, createAsync, createContext, createEffect, createMemo, createRenderEffect, createRoot, createSignal, flushSync, getContext, getObserver, getOwner, hasContext, hasUpdated, isEqual, isPending, latest, onCleanup, runWithOwner, setContext, untrack };
|
||||
+25
-7
@@ -26,46 +26,50 @@
|
||||
* 3. updateIfNecessary() evaluates the computation if the node is dirty (the computations are
|
||||
* executed in root to leaf order)
|
||||
*/
|
||||
import { type Flags } from './flags';
|
||||
import { Owner } from './owner';
|
||||
import { type Flags } from "./flags.js";
|
||||
import { Owner } from "./owner.js";
|
||||
export interface SignalOptions<T> {
|
||||
name?: string;
|
||||
equals?: ((prev: T, next: T) => boolean) | false;
|
||||
}
|
||||
export interface MemoOptions<T> extends SignalOptions<T> {
|
||||
initial?: T;
|
||||
unobserved?: () => void;
|
||||
}
|
||||
interface SourceType {
|
||||
_observers: ObserverType[] | null;
|
||||
_unobserved?: () => void;
|
||||
_updateIfNecessary: () => void;
|
||||
_stateFlags: Flags;
|
||||
_time: number;
|
||||
}
|
||||
interface ObserverType {
|
||||
_sources: SourceType[] | null;
|
||||
_notify: (state: number) => void;
|
||||
_handlerMask: Flags;
|
||||
_notifyFlags: (mask: Flags, newFlags: Flags) => void;
|
||||
_time: number;
|
||||
}
|
||||
/**
|
||||
* Returns the current observer.
|
||||
*/
|
||||
export declare function getObserver(): ObserverType | null;
|
||||
export declare function incrementClock(): void;
|
||||
export declare const UNCHANGED: unique symbol;
|
||||
export type UNCHANGED = typeof UNCHANGED;
|
||||
export declare class Computation<T = any> extends Owner implements SourceType, ObserverType {
|
||||
_sources: SourceType[] | null;
|
||||
_observers: ObserverType[] | null;
|
||||
_value: T | undefined;
|
||||
_compute: null | (() => T);
|
||||
_compute: null | ((p?: T) => T);
|
||||
_name: string | undefined;
|
||||
_equals: false | ((a: T, b: T) => boolean);
|
||||
_unobserved: (() => void) | undefined;
|
||||
/** Whether the computation is an error or has ancestors that are unresolved */
|
||||
_stateFlags: number;
|
||||
/** Which flags raised by sources are handled, vs. being passed through. */
|
||||
_handlerMask: number;
|
||||
_error: Computation<boolean> | null;
|
||||
_loading: Computation<boolean> | null;
|
||||
constructor(initialValue: T | undefined, compute: null | (() => T), options?: MemoOptions<T>);
|
||||
_time: number;
|
||||
constructor(initialValue: T | undefined, compute: null | ((p?: T) => T), options?: SignalOptions<T>);
|
||||
_read(): T;
|
||||
/**
|
||||
* Return the current value of this computation
|
||||
@@ -134,10 +138,24 @@ export declare function isEqual<T>(a: T, b: T): boolean;
|
||||
* dependencies. Use `untrack` if you want to also disable owner tracking.
|
||||
*/
|
||||
export declare function untrack<T>(fn: () => T): T;
|
||||
/**
|
||||
* Returns true if the given functinon contains signals that have been updated since the last time
|
||||
* the parent computation was run.
|
||||
*/
|
||||
export declare function hasUpdated(fn: () => any): boolean;
|
||||
/**
|
||||
* Returns true if the given function contains async signals that are not ready yet.
|
||||
*/
|
||||
export declare function isPending(fn: () => any): boolean;
|
||||
export declare function latest<T>(fn: () => T): T | undefined;
|
||||
/**
|
||||
* A convenient wrapper that calls `compute` with the `owner` and `observer` and is guaranteed
|
||||
* to reset the global context after the computation is finished even if an error is thrown.
|
||||
*/
|
||||
export declare function compute<T>(owner: Owner | null, compute: (val: T) => T, observer: Computation<T>): T;
|
||||
export declare function compute<T>(owner: Owner | null, compute: (val: undefined) => T, observer: null): T;
|
||||
export declare class EagerComputation<T = any> extends Computation<T> {
|
||||
constructor(initialValue: T, compute: () => T, options?: SignalOptions<T>);
|
||||
_notify(state: number): void;
|
||||
}
|
||||
export {};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Computation, type SignalOptions } from "./core.js";
|
||||
/**
|
||||
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
||||
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
||||
* sources and recompute
|
||||
*/
|
||||
export declare class Effect<T = any> extends Computation<T> {
|
||||
_effect: (val: T, prev: T | undefined) => void;
|
||||
_modified: boolean;
|
||||
_prevValue: T | undefined;
|
||||
_type: 0 | 1;
|
||||
constructor(initialValue: T, compute: () => T, effect: (val: T, prev: T | undefined) => void, options?: SignalOptions<T> & {
|
||||
render?: boolean;
|
||||
});
|
||||
write(value: T): T;
|
||||
_notify(state: number): void;
|
||||
_setError(error: unknown): void;
|
||||
_disposeNode(): void;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export { ContextNotFoundError, NoOwnerError, NotReadyError, type ErrorHandler } from "./error.js";
|
||||
export { Owner, createContext, getContext, setContext, hasContext, getOwner, onCleanup, type Context, type ContextRecord, type Disposable } from "./owner.js";
|
||||
export { Computation, EagerComputation, getObserver, isEqual, untrack, hasUpdated, isPending, latest, UNCHANGED, compute, type SignalOptions } from "./core.js";
|
||||
export { Effect } from "./effect.js";
|
||||
export { flushSync } from "./scheduler.js";
|
||||
export * from "./flags.js";
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
*
|
||||
* Note that the owner tree is largely orthogonal to the reactivity tree, and is much closer to the component tree.
|
||||
*/
|
||||
import { type ErrorHandler } from './error';
|
||||
import { type ErrorHandler } from "./error.js";
|
||||
export type ContextRecord = Record<string | symbol, unknown>;
|
||||
export interface Disposable {
|
||||
(): void;
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Effect } from "./effect.js";
|
||||
import { Computation } from "./core.js";
|
||||
export declare let Computations: Computation[], RenderEffects: Effect[], Effects: Effect[];
|
||||
/**
|
||||
* By default, changes are batched on the microtask queue which is an async process. You can flush
|
||||
* the queue synchronously to get the latest updates by calling `flushSync()`.
|
||||
*/
|
||||
export declare function flushSync(): void;
|
||||
export declare function flushQueue(): void;
|
||||
@@ -0,0 +1,4 @@
|
||||
export { Computation, ContextNotFoundError, NoOwnerError, NotReadyError, Owner, createContext, getContext, setContext, hasContext, getOwner, onCleanup, getObserver, isEqual, untrack, hasUpdated, isPending, latest } from "./core/index.js";
|
||||
export type { ErrorHandler, SignalOptions, Context, ContextRecord, Disposable } from "./core/index.js";
|
||||
export { flushSync } from "./core/scheduler.js";
|
||||
export * from "./signals.js";
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { Accessor } from "./signals.js";
|
||||
export type Maybe<T> = T | void | null | undefined | false;
|
||||
/**
|
||||
* Reactive map helper that caches each list item by reference to reduce unnecessary mapping on
|
||||
* updates.
|
||||
*
|
||||
* @see {@link https://github.com/solidjs/x-reactivity#maparray}
|
||||
*/
|
||||
export declare function mapArray<Item, MappedItem>(list: Accessor<Maybe<readonly Item[]>>, map: (value: Accessor<Item>, index: Accessor<number>) => MappedItem, options?: {
|
||||
keyed?: boolean | ((item: Item) => any);
|
||||
name?: string;
|
||||
}): Accessor<MappedItem[]>;
|
||||
+8
-7
@@ -1,5 +1,5 @@
|
||||
import type { MemoOptions, SignalOptions } from './core';
|
||||
import { Owner } from './owner';
|
||||
import type { SignalOptions } from './core/index.js';
|
||||
import { Owner } from './core/index.js';
|
||||
export interface Accessor<T> {
|
||||
(): T;
|
||||
}
|
||||
@@ -15,26 +15,27 @@ export type Signal<T> = [read: Accessor<T>, write: Setter<T>];
|
||||
* `fn()`, and provide a simple write API via `write()`. The value can now be observed
|
||||
* when used inside other computations created with `computed` and `effect`.
|
||||
*/
|
||||
export declare function createSignal<T>(initialValue: T, options?: SignalOptions<T>): Signal<T>;
|
||||
export declare function createAsync<T>(fn: () => Promise<T>, initial?: T, options?: SignalOptions<T>): Accessor<T>;
|
||||
export declare function createSignal<T>(initialValue: Exclude<T, Function>, options?: SignalOptions<T>): Signal<T>;
|
||||
export declare function createSignal<T>(fn: (prev?: T) => T, initialValue?: T, options?: SignalOptions<T>): Signal<T>;
|
||||
export declare function createAsync<T>(fn: (prev?: T) => Promise<T> | AsyncIterable<T> | T, initial?: T, options?: SignalOptions<T>): Accessor<T>;
|
||||
/**
|
||||
* Creates a new computation whose value is computed and returned by the given function. The given
|
||||
* compute function is _only_ re-run when one of it's dependencies are updated. Dependencies are
|
||||
* are all signals that are read during execution.
|
||||
*/
|
||||
export declare function createMemo<T>(compute: () => T, initialValue?: T, options?: MemoOptions<T>): Accessor<T>;
|
||||
export declare function createMemo<T>(compute: (prev?: T) => T, initialValue?: T, options?: SignalOptions<T>): Accessor<T>;
|
||||
/**
|
||||
* Invokes the given function each time any of the signals that are read inside are updated
|
||||
* (i.e., their value changes). The effect is immediately invoked on initialization.
|
||||
*/
|
||||
export declare function createEffect<T>(effect: () => T, initialValue?: T, options?: {
|
||||
export declare function createEffect<T>(compute: () => T, effect: (v: T) => (() => void) | void, initialValue?: T, options?: {
|
||||
name?: string;
|
||||
}): void;
|
||||
/**
|
||||
* Invokes the given function each time any of the signals that are read inside are updated
|
||||
* (i.e., their value changes). The effect is immediately invoked on initialization.
|
||||
*/
|
||||
export declare function createRenderEffect<T>(compute: () => T, effect: (v: T) => T, initialValue?: T, options?: {
|
||||
export declare function createRenderEffect<T>(compute: () => T, effect: (v: T) => (() => void) | void, initialValue?: T, options?: {
|
||||
name?: string;
|
||||
}): void;
|
||||
/**
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./store.js";
|
||||
+9
@@ -1,5 +1,7 @@
|
||||
export type Store<T> = Readonly<T>;
|
||||
export type StoreSetter<T> = (fn: (state: T) => void) => void;
|
||||
declare const $TRACK: unique symbol, $PROXY: unique symbol;
|
||||
export { $PROXY, $TRACK };
|
||||
export type StoreNode = Record<PropertyKey, any>;
|
||||
export declare namespace SolidStore {
|
||||
interface Unwrappable {
|
||||
@@ -20,3 +22,10 @@ export declare function isWrappable<T>(obj: T | NotWrappable): obj is T;
|
||||
*/
|
||||
export declare function unwrap<T>(item: T, set?: Set<unknown>): T;
|
||||
export declare function createStore<T extends object = {}>(store: T | Store<T>): [get: Store<T>, set: StoreSetter<T>];
|
||||
export declare function createStore<T extends object = {}>(fn: (store: T) => void, store: T | Store<T>): [get: Store<T>, set: StoreSetter<T>];
|
||||
/**
|
||||
* Creates a mutable derived value
|
||||
*
|
||||
* @see {@link https://github.com/solidjs/x-reactivity#createprojection}
|
||||
*/
|
||||
export declare function createProjection<T extends Object>(fn: (draft: T) => void, initialValue: T): Readonly<T>;
|
||||
+20
-14
@@ -372,12 +372,11 @@ export function init({
|
||||
|
||||
elements.legend.prepend(div);
|
||||
|
||||
const { input, label, spanMain } = utils.dom.createComplexLabeledInput({
|
||||
const { input, label } = utils.dom.createLabeledInput({
|
||||
inputId: `legend-${series.title}`,
|
||||
inputName: `selected-${series.title}${name}`,
|
||||
inputValue: "value",
|
||||
labelTitle: "Click to toggle",
|
||||
name: series.title,
|
||||
onClick: (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@@ -385,6 +384,14 @@ export function init({
|
||||
series.active.set(input.checked);
|
||||
},
|
||||
});
|
||||
|
||||
const spanMain = window.document.createElement("span");
|
||||
spanMain.classList.add("main");
|
||||
label.append(spanMain);
|
||||
|
||||
const spanName = utils.dom.createSpanName(series.title);
|
||||
spanMain.append(spanName);
|
||||
|
||||
div.append(label);
|
||||
label.addEventListener("mouseover", () => {
|
||||
const hovered = hoveredLegend();
|
||||
@@ -492,8 +499,7 @@ export function init({
|
||||
|
||||
const anchor = window.document.createElement("a");
|
||||
anchor.href = series.dataset.url;
|
||||
anchor.innerHTML = `<svg viewBox="0 0 16 16"><path d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z" /><path d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z" /></svg>`;
|
||||
anchor.target = "_target";
|
||||
anchor.target = "_blank";
|
||||
anchor.rel = "noopener noreferrer";
|
||||
div.append(anchor);
|
||||
}
|
||||
@@ -894,17 +900,17 @@ export function init({
|
||||
});
|
||||
});
|
||||
}
|
||||
initGoToButtons(elements.timeScaleDateButtons);
|
||||
initGoToButtons(elements.timeScaleHeightButtons);
|
||||
// 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();
|
||||
// function createScaleButtonsToggleEffect() {
|
||||
// const isDate = signals.createMemo(() => scale() === "date");
|
||||
// signals.createEffect(isDate, (isDate) => {
|
||||
// elements.timeScaleDateButtons.hidden = !isDate;
|
||||
// elements.timeScaleHeightButtons.hidden = isDate;
|
||||
// });
|
||||
// }
|
||||
// createScaleButtonsToggleEffect();
|
||||
}
|
||||
initTimeScaleElement();
|
||||
|
||||
|
||||
+205
-886
File diff suppressed because it is too large
Load Diff
+370
-553
File diff suppressed because it is too large
Load Diff
@@ -17,9 +17,11 @@ self.addEventListener("install", (_event) => {
|
||||
"/scripts/options.js",
|
||||
"/scripts/chart.js",
|
||||
"/styles/chart.css",
|
||||
"/scripts/simulation.js",
|
||||
"/styles/simulation.css",
|
||||
"/packages/lean-qr/v2.3.4/script.js",
|
||||
"/packages/lightweight-charts/v4.2.0/script.js",
|
||||
"/packages/solid-signals/2024-11-01/script.js",
|
||||
"/packages/solid-signals/2024-11-02/script.js",
|
||||
"/packages/ufuzzy/v1.0.14/script.js",
|
||||
]);
|
||||
}),
|
||||
|
||||
Vendored
+22
-14
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
Accessor,
|
||||
Setter,
|
||||
} from "../../packages/solid-signals/2024-11-01/types/signals";
|
||||
} from "../../packages/solid-signals/2024-11-02/types/signals";
|
||||
import {
|
||||
DeepPartial,
|
||||
BaselineStyleOptions,
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
ISeriesApi,
|
||||
} from "../../packages/lightweight-charts/v4.2.0/types";
|
||||
import { DatePath, HeightPath, LastPath } from "./paths";
|
||||
import { Owner } from "../../packages/solid-signals/2024-11-01/types/core/owner";
|
||||
import { Owner } from "../../packages/solid-signals/2024-11-02/types/core/owner";
|
||||
import { AnyPossibleCohortId } from "../options";
|
||||
|
||||
type GrowToSize<T, N extends number, A extends T[]> = A["length"] extends N
|
||||
@@ -28,9 +28,6 @@ type FixedArray<T, N extends number> = GrowToSize<T, N, []>;
|
||||
|
||||
type Signal<T> = Accessor<T> & { set: Setter<T> };
|
||||
|
||||
type SettingsTheme = "system" | "dark" | "light";
|
||||
type FoldersFilter = "all" | "favorites" | "new";
|
||||
|
||||
type TimeScale = "date" | "height";
|
||||
|
||||
type TimeRange = Range<Time | number>;
|
||||
@@ -104,7 +101,7 @@ type Unit =
|
||||
| "Weight";
|
||||
|
||||
interface PartialOption {
|
||||
icon: string;
|
||||
// icon: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -135,7 +132,12 @@ interface PartialSimulationOption extends PartialOption {
|
||||
}
|
||||
|
||||
interface PartialPdfOption extends PartialOption {
|
||||
file: string;
|
||||
pdf: string;
|
||||
}
|
||||
|
||||
interface PartialUrlOption extends PartialOption {
|
||||
qrcode?: true;
|
||||
url: () => string;
|
||||
}
|
||||
|
||||
interface PartialOptionsGroup {
|
||||
@@ -147,7 +149,8 @@ type AnyPartialOption =
|
||||
| PartialHomeOption
|
||||
| PartialChartOption
|
||||
| PartialSimulationOption
|
||||
| PartialPdfOption;
|
||||
| PartialPdfOption
|
||||
| PartialUrlOption;
|
||||
|
||||
type PartialOptionsTree = (AnyPartialOption | PartialOptionsGroup)[];
|
||||
|
||||
@@ -155,8 +158,7 @@ interface ProcessedOptionAddons {
|
||||
id: string;
|
||||
path: OptionPath;
|
||||
serializedPath: string;
|
||||
isFavorite: Signal<boolean>;
|
||||
visited: Signal<boolean>;
|
||||
title: string;
|
||||
}
|
||||
|
||||
type OptionPath = {
|
||||
@@ -169,14 +171,22 @@ type SimulationOption = PartialSimulationOption & ProcessedOptionAddons;
|
||||
|
||||
interface PdfOption extends PartialPdfOption, ProcessedOptionAddons {
|
||||
kind: "pdf";
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface UrlOption extends PartialUrlOption, ProcessedOptionAddons {
|
||||
kind: "url";
|
||||
}
|
||||
|
||||
interface ChartOption extends PartialChartOption, ProcessedOptionAddons {
|
||||
kind: "chart";
|
||||
}
|
||||
|
||||
type Option = HomeOption | PdfOption | ChartOption | SimulationOption;
|
||||
type Option =
|
||||
| HomeOption
|
||||
| PdfOption
|
||||
| UrlOption
|
||||
| ChartOption
|
||||
| SimulationOption;
|
||||
|
||||
type OptionsTree = (Option | OptionsGroup)[];
|
||||
|
||||
@@ -185,8 +195,6 @@ interface OptionsGroup extends PartialOptionsGroup {
|
||||
tree: OptionsTree;
|
||||
}
|
||||
|
||||
type SerializedHistory = [string, number][];
|
||||
|
||||
interface OHLC {
|
||||
open: number;
|
||||
high: number;
|
||||
|
||||
+32
-45
@@ -8,8 +8,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
margin: -1rem -1.5rem;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: -1rem var(--negative-main-padding);
|
||||
padding: 1rem var(--main-padding);
|
||||
overflow-x: auto;
|
||||
min-width: 0;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
color: var(--off-color);
|
||||
|
||||
> span.main > span.name {
|
||||
text-decoration-style: wavy;
|
||||
text-decoration-thickness: 1.5px;
|
||||
text-decoration-color: var(--color);
|
||||
text-decoration-line: line-through;
|
||||
@@ -61,8 +60,8 @@
|
||||
> #chart-list {
|
||||
margin-top: 1rem;
|
||||
position: relative;
|
||||
margin-left: -1.5rem /* -24px */;
|
||||
margin-right: -2rem /* -32px */;
|
||||
margin-left: var(--negative-main-padding);
|
||||
margin-right: calc(var(--negative-main-padding) - 0.5rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
@@ -88,17 +87,38 @@
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 1.5rem /* 24px */;
|
||||
padding-right: 1.5rem /* 24px */;
|
||||
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);
|
||||
|
||||
> * + * {
|
||||
margin-left: 0.5rem; /* 8px */
|
||||
}
|
||||
> div.field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--font-size-xs);
|
||||
line-height: var(--line-height-xs);
|
||||
gap: 1rem;
|
||||
|
||||
> * + span {
|
||||
color: var(--off-color);
|
||||
> legend,
|
||||
> div {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
> hr {
|
||||
min-width: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 0.5rem;
|
||||
margin: -0.5rem;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,37 +128,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> hr {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
> #timescale {
|
||||
> #timescale-date-buttons,
|
||||
> #timescale-height-buttons {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
margin-bottom: -0.5rem;
|
||||
padding: 0.5rem 0.5rem;
|
||||
padding-top: 0.5rem;
|
||||
margin-left: -1.5rem;
|
||||
margin-right: -1.5rem;
|
||||
|
||||
> button {
|
||||
color: var(--off-color);
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
padding: 0.5rem;
|
||||
white-space: nowrap;
|
||||
min-width: 5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
margin-bottom: -1.5rem;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user