emberwick 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -14
- package/index.d.ts +150 -2
- package/index.js +484 -12
- package/index.js.map +1 -1
- package/package.json +1 -2
- package/umd/emberwick.umd.js +1 -1
- package/umd/emberwick.umd.js.map +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ point it at your own market data.
|
|
|
10
10
|
to new bounds, zoom is cursor-anchored and eased, panning has inertia.
|
|
11
11
|
- **Fast on big data.** One `requestAnimationFrame` loop, dirty-flag driven,
|
|
12
12
|
with visible-range culling — 500k bars loaded costs only the ~200 on screen.
|
|
13
|
+
- **Annotated.** Nine marker shapes, price lines and shaded zones, with
|
|
14
|
+
collision-aware stacking and hit-testing for hover and click.
|
|
13
15
|
|
|
14
16
|
---
|
|
15
17
|
|
|
@@ -25,10 +27,6 @@ npm install emberwick
|
|
|
25
27
|
import { createChart, RandomFeed } from 'emberwick'
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
> **Not published yet.** The package builds and packs (`npm run release`), but
|
|
29
|
-
> it has not been pushed to the registry, and the name has not been confirmed
|
|
30
|
-
> available — run `npm view emberwick` before relying on it.
|
|
31
|
-
|
|
32
30
|
### From a CDN, no build step
|
|
33
31
|
|
|
34
32
|
```html
|
|
@@ -213,7 +211,15 @@ const chart = createChart(el, {
|
|
|
213
211
|
| `append(bar)` | Open a new candle |
|
|
214
212
|
| `setFeed(feed)` | `async` — loads history, then subscribes. Detaches any previous feed |
|
|
215
213
|
| `detachFeed()` | Unsubscribe, keep the bars on screen |
|
|
216
|
-
| `subscribe(
|
|
214
|
+
| `subscribe(event, fn)` | Returns an unsubscribe fn. See events below |
|
|
215
|
+
| `setMarkers(markers)` | Replace every marker |
|
|
216
|
+
| `getMarkers()` | Current markers, normalised, each with its resolved bar index |
|
|
217
|
+
| `addMarker(marker)` | Append one marker |
|
|
218
|
+
| `removeMarker(id)` | Remove by id |
|
|
219
|
+
| `clearMarkers()` | Remove all markers |
|
|
220
|
+
| `setPriceLines(lines)` | Replace every horizontal price line |
|
|
221
|
+
| `setZones(zones)` | Replace every shaded region |
|
|
222
|
+
| `markerAt(x, y)` | Hit-test plot coordinates, returns a marker or `null` |
|
|
217
223
|
| `setTheme(partial)` | Merge theme keys and repaint |
|
|
218
224
|
| `setPriceMode(mode)` | `'linear'` or `'log'` |
|
|
219
225
|
| `setAnimate(bool)` | Toggle live-candle easing |
|
|
@@ -234,7 +240,137 @@ const off = chart.subscribe('crosshair', (payload) => {
|
|
|
234
240
|
off() // unsubscribe
|
|
235
241
|
```
|
|
236
242
|
|
|
237
|
-
|
|
243
|
+
| Event | Payload |
|
|
244
|
+
|---|---|
|
|
245
|
+
| Event | Payload |
|
|
246
|
+
|---|---|
|
|
247
|
+
| `'crosshair'` | `{ index, bar, price }`, or `null` when the pointer leaves the plot |
|
|
248
|
+
| `'markerHover'` | The marker under the pointer, or `null` when none is |
|
|
249
|
+
| `'markerClick'` | The clicked marker. Only fires on a hit, never with `null` |
|
|
250
|
+
| `'visibleRange'` | `{ from, to, fromTime, toTime, barCount, spacing, settled }` |
|
|
251
|
+
|
|
252
|
+
A drag that happens to end on top of a marker does not fire `'markerClick'` —
|
|
253
|
+
panning and clicking stay distinct.
|
|
254
|
+
|
|
255
|
+
### Tracking the visible range
|
|
256
|
+
|
|
257
|
+
`'visibleRange'` is a **state** event rather than a notification, which makes it
|
|
258
|
+
usable without any debouncing of your own:
|
|
259
|
+
|
|
260
|
+
- A new subscriber is called **immediately** with the current window, so it
|
|
261
|
+
never has to wait for the user to pan before it knows what is on screen.
|
|
262
|
+
- It then fires **only when the window actually changes**. Indices are
|
|
263
|
+
integers, so a slow pan at 9px/bar produces roughly one event every nine
|
|
264
|
+
frames, not one per frame.
|
|
265
|
+
- `spacing` is reported but is deliberately *not* part of the change test — it
|
|
266
|
+
is a float that moves every frame of an eased zoom, and keying on it would
|
|
267
|
+
turn this into a 60/sec firehose. Use it for level-of-detail decisions.
|
|
268
|
+
- `settled` *is* part of the change test, so the final event of a gesture
|
|
269
|
+
always arrives with `settled: true`. That makes "wait until the view stops
|
|
270
|
+
moving, then do the expensive thing" a safe pattern.
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
const off = chart.subscribe('visibleRange', async (r) => {
|
|
274
|
+
// paginate backwards when the user approaches the left edge
|
|
275
|
+
if (r.from < 50 && r.settled && r.fromTime) {
|
|
276
|
+
const older = await myApi.bars({ to: r.fromTime, limit: 500 })
|
|
277
|
+
chart.setData(older.concat(chart.bars))
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Syncing a second chart is the other common use — feed `fromTime`/`toTime`
|
|
283
|
+
straight into the other instance. And `chart.visibleRange()` returns the same
|
|
284
|
+
payload on demand if you would rather poll than subscribe.
|
|
285
|
+
|
|
286
|
+
> The chart also paginates backwards **on its own** through
|
|
287
|
+
> `feed.getBars({ to })` whenever you have attached a feed. This event is for
|
|
288
|
+
> when you want to drive that yourself, or to drive something other than data.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Annotations
|
|
293
|
+
|
|
294
|
+
Three independent collections, each replaced wholesale. Zones paint behind the
|
|
295
|
+
candles; price lines and markers paint in front.
|
|
296
|
+
|
|
297
|
+
```js
|
|
298
|
+
chart.setMarkers([
|
|
299
|
+
{ time: 1717070400000, shape: 'arrowUp', text: 'BUY 120',
|
|
300
|
+
data: { orderId: 'A-7741' } },
|
|
301
|
+
{ time: 1717074000000, shape: 'flag', text: 'Earnings', color: '#c084fc' },
|
|
302
|
+
{ time: 1717077600000, shape: 'arrowDown', text: 'SELL 160' },
|
|
303
|
+
])
|
|
304
|
+
|
|
305
|
+
chart.setPriceLines([
|
|
306
|
+
{ price: 148.20, title: 'target', color: '#26a69a' },
|
|
307
|
+
{ price: 141.05, title: 'stop', lineStyle: 'dotted' },
|
|
308
|
+
])
|
|
309
|
+
|
|
310
|
+
chart.setZones([
|
|
311
|
+
{ from: 143.5, to: 145.9, label: 'value area' },
|
|
312
|
+
])
|
|
313
|
+
|
|
314
|
+
chart.subscribe('markerClick', (m) => openTicket(m.data.orderId))
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Markers
|
|
318
|
+
|
|
319
|
+
A marker is pinned to a **timestamp**, not a bar index, and resolves to the
|
|
320
|
+
nearest bar. Load an older page of history and every marker re-resolves, so
|
|
321
|
+
nothing drifts off its candle.
|
|
322
|
+
|
|
323
|
+
| Field | Default | Notes |
|
|
324
|
+
|---|---|---|
|
|
325
|
+
| `time` | *required* | ms since epoch, snapped to the closest bar |
|
|
326
|
+
| `id` | generated | Needed for `removeMarker(id)` |
|
|
327
|
+
| `shape` | `'circle'` | See the list below |
|
|
328
|
+
| `position` | shape-dependent | `'aboveBar'`, `'belowBar'`, `'inBar'`, `'atPrice'` |
|
|
329
|
+
| `price` | — | Only used when `position` is `'atPrice'` |
|
|
330
|
+
| `color` | `theme.up` / `theme.down` | Down-pointing shapes default to the down colour |
|
|
331
|
+
| `text` | — | Short caption. For `'label'` it is drawn inside the pill |
|
|
332
|
+
| `textColor` | theme | Caption colour |
|
|
333
|
+
| `size` | `1` | Scale factor |
|
|
334
|
+
| `data` | — | Anything. Handed straight back on hover and click |
|
|
335
|
+
|
|
336
|
+
Shapes: `arrowUp`, `arrowDown`, `triangleUp`, `triangleDown`, `circle`,
|
|
337
|
+
`square`, `diamond`, `flag`, `label`.
|
|
338
|
+
|
|
339
|
+
Position defaults follow the trading convention: up-pointing shapes sit
|
|
340
|
+
*below* the bar, down-pointing shapes *above* it, everything else above.
|
|
341
|
+
|
|
342
|
+
**Overlap and density.** Markers sharing a bar are stacked rather than drawn on
|
|
343
|
+
top of each other. Below about 3px per bar, dense runs thin to roughly one
|
|
344
|
+
marker per 4px — a thousand trades stay legible and stay fast. A marker on the
|
|
345
|
+
forming candle anchors to the animated values, so it flows with the live bar.
|
|
346
|
+
|
|
347
|
+
### Price lines
|
|
348
|
+
|
|
349
|
+
| Field | Default | Notes |
|
|
350
|
+
|---|---|---|
|
|
351
|
+
| `price` | *required* | |
|
|
352
|
+
| `color` | `theme.textStrong` | |
|
|
353
|
+
| `lineStyle` | `'dashed'` | `'solid'`, `'dashed'`, `'dotted'` |
|
|
354
|
+
| `lineWidth` | `1` | |
|
|
355
|
+
| `title` | — | Pill drawn at the left end |
|
|
356
|
+
| `axisLabel` | `true` | Price tag on the axis |
|
|
357
|
+
|
|
358
|
+
### Zones
|
|
359
|
+
|
|
360
|
+
Supply `from`/`to` for a **price band** spanning the full width, or
|
|
361
|
+
`fromTime`/`toTime` for a **time band** spanning the full height.
|
|
362
|
+
|
|
363
|
+
```js
|
|
364
|
+
chart.setZones([
|
|
365
|
+
{ from: 143.5, to: 145.9, label: 'value area' },
|
|
366
|
+
{ fromTime: 1717070400000, toTime: 1717074000000,
|
|
367
|
+
color: 'rgba(239,83,80,0.07)', border: 'rgba(239,83,80,0.3)' },
|
|
368
|
+
])
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Autoscale fits the **bars**, not the annotations — a price line far outside the
|
|
372
|
+
data range is simply off-screen. Derive extreme values from the visible range
|
|
373
|
+
if you need them guaranteed visible.
|
|
238
374
|
|
|
239
375
|
---
|
|
240
376
|
|
|
@@ -475,9 +611,12 @@ npm run build:umd # minified UMD -> dist-lib/umd/emberwick.umd.js
|
|
|
475
611
|
npm run pack:lib # manifest + README + .d.ts into dist-lib/
|
|
476
612
|
npm run size # gzipped size budget check
|
|
477
613
|
npm run release # all four, in order
|
|
478
|
-
npm publish dist-lib
|
|
614
|
+
npm publish ./dist-lib # publish the assembled directory — note the ./
|
|
479
615
|
```
|
|
480
616
|
|
|
617
|
+
> The leading `./` is required. `npm publish dist-lib` makes npm look for a
|
|
618
|
+
> *registry package* named `dist-lib` and fail with `E404`.
|
|
619
|
+
|
|
481
620
|
Publishing from `dist-lib/` keeps the playground, the build configs and the
|
|
482
621
|
app's private `package.json` out of the artifact. `package.lib.json` is the
|
|
483
622
|
manifest that becomes the published `package.json`.
|
|
@@ -505,13 +644,10 @@ src/App.jsx, src/styles.css the playground (not published)
|
|
|
505
644
|
|
|
506
645
|
Honest list of what isn't there yet:
|
|
507
646
|
|
|
508
|
-
-
|
|
509
|
-
the
|
|
510
|
-
- **
|
|
511
|
-
|
|
512
|
-
- **The library builds are unverified.** `npm run build:lib` / `build:umd`
|
|
513
|
-
have not been executed here; this environment only runs the app build. Run
|
|
514
|
-
`npm run release` locally before publishing.
|
|
647
|
+
- **No OHLCV legend in the package.** `subscribe('crosshair', fn)` gives you
|
|
648
|
+
the hovered bar; rendering the readout is still yours to do.
|
|
649
|
+
- **Markers are not draggable.** They are hit-tested for hover and click, but
|
|
650
|
+
there is no drag-to-move or editing interaction.
|
|
515
651
|
- **No indicators or drawing tools.** SMA/EMA/RSI/MACD, multi-pane layout and
|
|
516
652
|
trendlines are planned but not implemented.
|
|
517
653
|
- **Candlesticks only.** No Heikin-Ashi, line, area or baseline series yet.
|
package/index.d.ts
CHANGED
|
@@ -40,6 +40,97 @@ export interface Theme {
|
|
|
40
40
|
timeAxisHeight: number
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/* --------------------------------------------------------------- markers -- */
|
|
44
|
+
|
|
45
|
+
export type MarkerShape =
|
|
46
|
+
| 'arrowUp'
|
|
47
|
+
| 'arrowDown'
|
|
48
|
+
| 'triangleUp'
|
|
49
|
+
| 'triangleDown'
|
|
50
|
+
| 'circle'
|
|
51
|
+
| 'square'
|
|
52
|
+
| 'diamond'
|
|
53
|
+
| 'flag'
|
|
54
|
+
| 'label'
|
|
55
|
+
|
|
56
|
+
export type MarkerPosition = 'aboveBar' | 'belowBar' | 'inBar' | 'atPrice'
|
|
57
|
+
|
|
58
|
+
/** A point annotation pinned to the bar nearest `time`. */
|
|
59
|
+
export interface Marker<T = unknown> {
|
|
60
|
+
/** Stable id. Generated when omitted; required for removeMarker(). */
|
|
61
|
+
id?: string
|
|
62
|
+
/** ms since epoch — snapped to the closest bar. */
|
|
63
|
+
time: number
|
|
64
|
+
/** Only used when position is 'atPrice'. */
|
|
65
|
+
price?: number
|
|
66
|
+
/** Default 'circle'. */
|
|
67
|
+
shape?: MarkerShape
|
|
68
|
+
/**
|
|
69
|
+
* Default depends on the shape: up shapes sit below the bar, down shapes
|
|
70
|
+
* above it, everything else above.
|
|
71
|
+
*/
|
|
72
|
+
position?: MarkerPosition
|
|
73
|
+
/** Defaults to theme.up, or theme.down for the down-pointing shapes. */
|
|
74
|
+
color?: string
|
|
75
|
+
textColor?: string
|
|
76
|
+
/** Short caption. For shape 'label' it is drawn inside the pill. */
|
|
77
|
+
text?: string
|
|
78
|
+
/** Scale factor. Default 1. */
|
|
79
|
+
size?: number
|
|
80
|
+
/** Passed straight back to you on hover/click. */
|
|
81
|
+
data?: T
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A marker after normalisation, as returned by getMarkers(). */
|
|
85
|
+
export interface ResolvedMarker<T = unknown> extends Marker<T> {
|
|
86
|
+
id: string
|
|
87
|
+
shape: MarkerShape
|
|
88
|
+
position: MarkerPosition
|
|
89
|
+
size: number
|
|
90
|
+
/** Bar index the marker resolved to, or -1 when there are no bars. */
|
|
91
|
+
index: number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type LineStyle = 'solid' | 'dashed' | 'dotted'
|
|
95
|
+
|
|
96
|
+
/** A horizontal line at a fixed price. */
|
|
97
|
+
export interface PriceLine {
|
|
98
|
+
id?: string
|
|
99
|
+
price: number
|
|
100
|
+
/** Defaults to theme.textStrong. */
|
|
101
|
+
color?: string
|
|
102
|
+
/** Default 'dashed'. */
|
|
103
|
+
lineStyle?: LineStyle
|
|
104
|
+
/** Default 1. */
|
|
105
|
+
lineWidth?: number
|
|
106
|
+
/** Optional pill drawn at the left end. */
|
|
107
|
+
title?: string
|
|
108
|
+
titleColor?: string
|
|
109
|
+
/** Price tag on the axis. Default true. */
|
|
110
|
+
axisLabel?: boolean
|
|
111
|
+
tagTextColor?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A shaded region: supply `from`/`to` for a price band spanning the full
|
|
116
|
+
* width, or `fromTime`/`toTime` for a time band spanning the full height.
|
|
117
|
+
*/
|
|
118
|
+
export interface Zone {
|
|
119
|
+
id?: string
|
|
120
|
+
from?: number
|
|
121
|
+
to?: number
|
|
122
|
+
fromTime?: number
|
|
123
|
+
toTime?: number
|
|
124
|
+
/** Fill. Use rgba(). Default a faint theme-green wash. */
|
|
125
|
+
color?: string
|
|
126
|
+
/** Optional 1px outline. */
|
|
127
|
+
border?: string
|
|
128
|
+
label?: string
|
|
129
|
+
labelColor?: string
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* --------------------------------------------------------------- options -- */
|
|
133
|
+
|
|
43
134
|
export interface TimeScaleOptions {
|
|
44
135
|
/** Pixels per bar. Default 9. */
|
|
45
136
|
spacing?: number
|
|
@@ -73,6 +164,10 @@ export interface ChartOptions {
|
|
|
73
164
|
animate?: boolean
|
|
74
165
|
/** Bars requested from the feed on setFeed(). Default 1500. */
|
|
75
166
|
initialBars?: number
|
|
167
|
+
/** Initial markers; equivalent to calling setMarkers() after construction. */
|
|
168
|
+
markers?: Marker[]
|
|
169
|
+
priceLines?: PriceLine[]
|
|
170
|
+
zones?: Zone[]
|
|
76
171
|
timeScale?: TimeScaleOptions
|
|
77
172
|
priceScale?: PriceScaleOptions
|
|
78
173
|
}
|
|
@@ -84,6 +179,30 @@ export interface CrosshairPayload {
|
|
|
84
179
|
price: number
|
|
85
180
|
}
|
|
86
181
|
|
|
182
|
+
/** Payload of the 'visibleRange' event, and the return of chart.visibleRange(). */
|
|
183
|
+
export interface VisibleRangePayload {
|
|
184
|
+
/** First visible bar index, clamped to the loaded data. */
|
|
185
|
+
from: number
|
|
186
|
+
/** Last visible bar index, clamped to the loaded data. */
|
|
187
|
+
to: number
|
|
188
|
+
/** `bars[from].time`, or null when no bars are loaded. */
|
|
189
|
+
fromTime: number | null
|
|
190
|
+
/** `bars[to].time`, or null when no bars are loaded. */
|
|
191
|
+
toTime: number | null
|
|
192
|
+
/** Total bars currently loaded. */
|
|
193
|
+
barCount: number
|
|
194
|
+
/** Current pixels per bar — useful for level-of-detail decisions. */
|
|
195
|
+
spacing: number
|
|
196
|
+
/**
|
|
197
|
+
* False while the view is still easing. The last event of a gesture always
|
|
198
|
+
* arrives with `settled: true`, so it is safe to defer expensive work
|
|
199
|
+
* (a fetch, a re-aggregation) until you see it.
|
|
200
|
+
*/
|
|
201
|
+
settled: boolean
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* ------------------------------------------------------------------ feed -- */
|
|
205
|
+
|
|
87
206
|
export interface GetBarsRequest {
|
|
88
207
|
symbol: string
|
|
89
208
|
timeframe: number
|
|
@@ -147,6 +266,8 @@ export declare class RandomFeed extends DataFeed {
|
|
|
147
266
|
stop(): void
|
|
148
267
|
}
|
|
149
268
|
|
|
269
|
+
/* ---------------------------------------------------------------- scales -- */
|
|
270
|
+
|
|
150
271
|
export declare class TimeScale {
|
|
151
272
|
constructor(opts?: TimeScaleOptions)
|
|
152
273
|
visibleRange(): { from: number; to: number }
|
|
@@ -168,6 +289,8 @@ export declare class PriceScale {
|
|
|
168
289
|
readonly hi: number
|
|
169
290
|
}
|
|
170
291
|
|
|
292
|
+
/* ----------------------------------------------------------------- chart -- */
|
|
293
|
+
|
|
171
294
|
export declare class Chart {
|
|
172
295
|
constructor(container: HTMLElement, options?: ChartOptions)
|
|
173
296
|
|
|
@@ -181,6 +304,19 @@ export declare class Chart {
|
|
|
181
304
|
setFeed(feed: Feed): Promise<void>
|
|
182
305
|
detachFeed(): void
|
|
183
306
|
|
|
307
|
+
/** Replace every marker. */
|
|
308
|
+
setMarkers(markers: Marker[]): void
|
|
309
|
+
/** Current markers, normalised, each with its resolved bar index. */
|
|
310
|
+
getMarkers(): ResolvedMarker[]
|
|
311
|
+
addMarker(marker: Marker): void
|
|
312
|
+
/** Removes by id — including ids generated for you. */
|
|
313
|
+
removeMarker(id: string): void
|
|
314
|
+
clearMarkers(): void
|
|
315
|
+
setPriceLines(lines: PriceLine[]): void
|
|
316
|
+
setZones(zones: Zone[]): void
|
|
317
|
+
/** Topmost marker under a plot-relative point, else null. */
|
|
318
|
+
markerAt(x: number, y: number): ResolvedMarker | null
|
|
319
|
+
|
|
184
320
|
setTheme(theme: Partial<Theme>): void
|
|
185
321
|
setPriceMode(mode: PriceMode): void
|
|
186
322
|
setAnimate(on: boolean): void
|
|
@@ -192,17 +328,27 @@ export declare class Chart {
|
|
|
192
328
|
/** Rolling frames-per-second of the render loop. */
|
|
193
329
|
readonly fps: number
|
|
194
330
|
|
|
331
|
+
/** The window currently on screen. Cheap enough to poll. */
|
|
332
|
+
visibleRange(): VisibleRangePayload
|
|
333
|
+
|
|
195
334
|
/**
|
|
196
335
|
* Subscribe to a chart event. Returns an unsubscribe function.
|
|
197
|
-
*
|
|
336
|
+
*
|
|
337
|
+
* 'visibleRange' is a state event rather than a notification: a new
|
|
338
|
+
* subscriber is called immediately with the current window, and then only
|
|
339
|
+
* when that window actually changes — so no debouncing is required.
|
|
198
340
|
*/
|
|
199
341
|
subscribe(event: 'crosshair', fn: (payload: CrosshairPayload | null) => void): () => void
|
|
200
|
-
subscribe(event: '
|
|
342
|
+
subscribe(event: 'markerClick', fn: (marker: ResolvedMarker) => void): () => void
|
|
343
|
+
subscribe(event: 'markerHover', fn: (marker: ResolvedMarker | null) => void): () => void
|
|
344
|
+
subscribe(event: 'visibleRange', fn: (range: VisibleRangePayload) => void): () => void
|
|
201
345
|
|
|
202
346
|
/** Removes listeners, canvases and the render loop. */
|
|
203
347
|
destroy(): void
|
|
204
348
|
|
|
205
349
|
readonly bars: Bar[]
|
|
350
|
+
readonly priceLines: PriceLine[]
|
|
351
|
+
readonly zones: Zone[]
|
|
206
352
|
readonly ts: TimeScale
|
|
207
353
|
readonly ps: PriceScale
|
|
208
354
|
}
|
|
@@ -214,6 +360,8 @@ export declare const defaultTheme: Theme
|
|
|
214
360
|
export declare const lightTheme: Theme
|
|
215
361
|
export declare const version: string
|
|
216
362
|
|
|
363
|
+
/* ------------------------------------------------------------- utilities -- */
|
|
364
|
+
|
|
217
365
|
/** Seeded PRNG used by RandomFeed. */
|
|
218
366
|
export declare function mulberry32(seed: number): () => number
|
|
219
367
|
|