diff --git a/.eslintrc.json b/.eslintrc.json
index 5892835d..84c5fc35 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -9,9 +9,7 @@
"sourceType": "module",
"ecmaVersion": "latest"
},
- "ignorePatterns": ["src/backend/*"],
"rules": {
- "no-dupe-keys": "off",
- "no-constant-condition": "off"
+ "no-cond-assign": "off"
}
}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..845d6286
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,21 @@
+name: Test
+
+on:
+ push:
+ branches: [next]
+ pull_request:
+ branches: [next]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+ - run: npm install -g pnpm
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm test
+ - run: pnpm prepublishOnly
+ - run: pnpm docs:build
diff --git a/.gitignore b/.gitignore
index bce47d4b..72e43463 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
node_modules
yarn.lock
package-lock.json
-**/target
-src/wasm
-dist
\ No newline at end of file
+dist
+cache
+docs/.vitepress/lib
+**-actual.svg
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
index 29d65edb..96dab5c0 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1 +1 @@
-src/backend/*
\ No newline at end of file
+test/output
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
index 963354f2..0edba09c 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,3 +1,4 @@
{
- "printWidth": 120
+ "printWidth": 120,
+ "bracketSpacing": false
}
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index ff0796fe..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2023-present Bairui SU https://github.com/pearmini
-
-Permission to use, copy, modify, and/or distribute this software for any purpose
-with or without fee is hereby granted, provided that the above copyright notice
-and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
-OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
-TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-THIS SOFTWARE.
diff --git a/License b/License
new file mode 100644
index 00000000..c88fea05
--- /dev/null
+++ b/License
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2025-Present Bairui SU https://github.com/pearmini
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 81aa8f37..a04200d0 100644
--- a/README.md
+++ b/README.md
@@ -1,2367 +1,51 @@
-# Charming: Charming Computing
+# Charming
-> Charming is still in testing; the APIs are not fully implemented and may not be stable.
+Charming is a JavaScript library for data-driven generative art that allows artists, designers, educators, and engineers to create expressive, accessible SVG graphics.
-**Charming**, short for _Charming Computing_, is a free, open-source creative coding language designed for computational and ASCII art, offering high performance. It has a declarative, concise, yet expressive API inspired by [G2.js](https://github.com/antvis/G2), [D3.js](https://github.com/d3) and [P5.js](https://p5js.org/).
+Charmingβs API is inspired by data visualization grammar β systems like [AntV G2](https://g2.antv.antgroup.com/), [Observable Plot](https://observablehq.com/plot/) and [Vega-Lite](https://vega.github.io/vega-lite/) β where visuals are built from meaningful, composable units. By combining declarative structure with the power of SVG, Charming encourages a more thoughtful, expressive, inspectable and accessible approach to generative art.
-Charming is built on the observation that both visualization and generative art are, to some extent, data-driven. Therefore, it provides a novel [flow-based](cm-flow) API for processing data, appending and transforming shapes. Charming also supports batch rendering of 2D primitives using a [WebGL renderer](https://observablehq.com/d/db16249bd7174a24), and defining some [GLSL functions](https://observablehq.com/d/86d2c1fe79fac300) to offload expensive computations to the GPU. Additionally, a [terminal renderer](https://observablehq.com/d/8152c4d46e22d446) for ASCII art, embedded in JavaScript, utilizes a software rasterizer written in Rust and compiled to WASM, aiming to achieve high performance. Charming also puts strong emphasis on extensible, composable, beginner-friendly and lightweight (29kb minified [core bundle](https://cdn.jsdelivr.net/npm/@charming-art/charming/dist/cm.core.umd.min.js)).
+
-If you are new to programming or JavaScript, P5 is still a good starting point, otherwise you should consider Charming. My hope with Charming is that you spend less time wrangling the machinery of programming and more time "using computing to tell stories". Or put more simply: **With Charming, you'll express more, and more easily.**
-
-> _Think of Charming as Lodash for graphics, or D3 for computational art._
-
-
-
-## Links
-
-If you are new to Charming, I highly recommend first reading following links to get started with:
-
-- [What is Charming](https://observablehq.com/d/477368f7e5423ff6) - a brief introduction
-- [Why Charming](https://observablehq.com/d/535654add5972bd2) - motivation
-- [How is Charming](https://observablehq.com/d/c5e87fb195e2166d) - features
-- [Roadmap](https://github.com/charming-art/charming/issues/216) - what's next
-
-And there are [a plenty of examples](https://observablehq.com/d/2f9bf9f52cb24090) to learn from, as well as [API reference](#api-reference) and core concepts:
-
-- [App](#app) - rendering app to DOM and animating it
-- [Flow](#flow) - binding data to shapes
-- [Process](#process) - preparing data to be rendered
-- [Shape](#shape) - appending geometric elements to canvas
-- [Transform](#transform) - deriving shape attribute values
-- [Scale](#scale) - mapping abstract data to visual representation
-- [Event](#event) - handling hooks and events
-- [Prop](#prop) - returning properties of the app
-- [Attribute](#attribute) - defining attributes for shapes
-
-If you want to have a comprehensive understanding of Charming, such as design choice, discussion and future work, you can skip links above and read [Charming: Charming Computing](https://observablehq.com/d/dd59b4c4f9f1d9d3) directly.
-
-## Installing
-
-Charming is typically installed via a package manager such as Yarn or NPM.
-
-```bash
-yarn add @charming-art/charming
-```
-
-```bash
-npm install @charming-art/charming
-```
-
-Charming can then imported as a namespace:
-
-```js
-import * as cm from "@charming-art/charming";
-```
-
-In vanilla HTML, Charming can be imported as an ES module, say from jsDelivr:
-
-```html
-
-```
-
-Charming is also available as a UMD bundle for legacy browsers.
-
-```html
-
-
-```
-
-## Quick Examples
-
-
-
-```js
-import * as cm from "@charming-art/charming";
-
-const app = cm.app({
- width: 640,
- height: 640,
-});
-
-app
- .data(cm.range(240))
- .process(cm.map, (_, i, data) => (i * Math.PI) / data.length)
- .append(cm.circle, {
- x: (t) => Math.cos(t) * Math.cos(t * 3),
- y: (t) => Math.sin(t) * Math.cos(t * 3),
- r: (_, i) => i,
- })
- .transform(cm.mapPosition, { padding: 15 })
- .transform(cm.mapAttrs, {
- r: { range: [8, 20] },
- });
-
-document.body.appendChild(app.render().node());
-```
-
-
-
-```js
-import * as cm from "@charming-art/charming";
-
-const app = cm.app({
- width: 1200,
- renderer: await cm.terminal(),
-});
-
-app.append(cm.text, {
- text: cm.figlet("hello world"),
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- fill: cm.gradientSineBowX(),
- textAlign: "center",
- textBaseline: "middle",
- fontFamily: cm.fontGhost(),
-});
-
-document.body.appendChild(app.render().node(()));
-```
-
-## API Reference
-
-The core modules of Charming, which are included in [core bundle](https://cdn.jsdelivr.net/npm/@charming-art/charming/dist/cm.core.umd.min.js):
-
-- [App](#app) - rendering app to DOM and animating it
-- [Flow](#flow) - binding data to shapes
-- [Process](#process) - preparing data to be rendered
-- [Shape](#shape) - appending geometric elements to canvas
-- [Transform](#transform) - deriving shape attribute values
-- [Scale](#scale) - mapping abstract data to visual representation
-- [Event](#event) - handling hooks and events
-- [Prop](#prop) - returning properties of the app
-- [Attribute](#attribute) - defining attributes for shapes
-
-The other modules are included in [full bundle](https://cdn.jsdelivr.net/npm/@charming-art/charming/dist/cm.umd.min.js). Different renderers and related modules will be placed in the following separate modules:
-
-- [WebGL](#webgl) - the WebGL renderer and related helpers
-- [Terminal](#terminal) - the terminal renderer and related helpers
-
-For common problems, Charming provides a series of optional modules encapsulate reusable solutions, increasing efficiency and alleviating the burden of common tasks:
-
-- [Array](#array) - array generation and manipulation
-- [Math](#math) - processing numbers, randomness, etc.
-- [Constant](#constant) - useful constants
-- [Vector](#vector) - basics for simulating physical laws
-- [Helper](#helper) - useful unities
-
-### App
-
-Rendering app to DOM and animating it.
-
-# _cm_.**app**(_[options]_)
-
-Constructs a new app with the specified _options_. If no argument is specified, constructs with default options.
-
-All the apps support the following options:
-
-- **width** - the outer width of the app, number in pixels
-- **height** - the outer height of the app, number in pixels
-- **frameRate** - the number of frames to draw per second
-- **renderer** - the [renderer](#renderer) to draw shapes and handle events, defaults to [canvas](cm-canvas) renderer
-
-```js
-const app = cm.app({
- width: 600,
- height: 400,
- renderer: cm.canvas(),
-});
-```
-
-Apps with [terminal](#cm-terminal) renderer support the extra options:
-
-- **cols** - the number of columns, with a priority level higher than the _width_
-- **rows** - the number of rows, with a priority level higher than the _height_
-- **fontSize** - the font size used to render text, see [CSS font-size](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size)
-- **fontWeight** - the font weight used to render text, see [CSS font-weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight)
-- **fontFamily** - the font family used to render text, see [CSS font-family](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family)
-- **mode** - the render mode, _single_ or _double_, defaults to _single_
-
-```js
-const app = cm.app({
- cols: 30,
- rows: 30,
- renderer: await cm.terminal(),
- fontSize: 20,
- fontWeight: "bold",
- fontFamily: "Georgia, serif",
- mode: "double",
-});
-```
-
-If _mode_ is _single_, a cell in terminal renders both single-width and double-width character once . If _mode_ is _double_, a cell in terminal renders single-width character twice and double-width character once.
-
-Single-width characters include characters like _A_, _a_, _1_, _@_, etc,. Double-width characters are characters include characters like _δΈ_, _π_ and strings made of two single-width characters like _AB_. Double mode aims to address the overlapping issues that arise from the inconsistent widths of single-width and double-width characters.
-
-# _app_.**data**(_data_)
-
-Appends a new flow with the specified array of _data_ to the root flow of app, returning the new flow.
-
-```js
-app.data([1, 2, 3]);
-```
-
-# _app_.**datum**(_[datum]_)
-
-Appends a new flow with an array containing the specified _datum_ to the root flow app, returning the new flow.
-
-```js
-app.datum(1);
-```
-
-The shorthand is thus equivalent to:
-
-```js
-app.data([1]);
-```
-
-If no argument is specified, return groups of this app:
-
-```js
-app.datum(); // [[1]]
-```
-
-# _app_.**append**(_shape[, options]_)
-
-Appends a shape with the specified options to this app, returning the new flow that contains the shape. Each shape has its own options, and different shape types support different options. See the respective [shape](#shape) type for details.
-
-```js
-app.append(cm.circle, { x: 100, y: 100, r: 50, fill: "orange" });
-```
-
-# _app_.**render**()
-
-Renders shapes in flows to canvas and removes existing flows, returning this app.
-
-```js
-app.append(cm.circle, {
- x: cm.random(50, 100),
- y: cm.random(50, 100),
- r: 25,
- fill: "orange",
-});
-
-app.render();
-
-app.append(cm.circle, {
- x: cm.random(50, 100),
- y: cm.random(50, 100),
- r: 25,
- fill: "steelblue",
-});
-
-app.render();
-```
-
-# _app_.**start**()
-
-Starts this app and returns it, firing [_update_](#event-update) event repeatedly until calling [app.stop](#app-stop). This allows this app to invoke the update callback every delay milliseconds, which is controlled by the [frameCount](#cm-app) option. Note that [app.render](#app-render) will be invoked automatically at the end of each frame, so there is no need to call it explicitly. For example, to draw a moving rect:
-
-```js
-let x = 0;
-
-function update() {
- app.append(cm.rect, {
- x: x++,
- y: 0,
- width: 100,
- height: 50,
- });
-}
-
-app.on("update", update);
-app.start();
-```
-
-# _app_.**stop**()
-
-Stops this app and returns it, cancelling firing [_update_](#event-update), thereby stops the animation.
-
-```js
-app.on("update", update);
-app.start();
-
-// Stops animation after 5 seconds.
-setTimeout(() => app.stop(), 5000);
-```
-
-# _app_.**dispose**()
-
-Disposes this app and returns it, stopping the timer to firing [_update_](#event-update) event and removing all event listeners.
-
-```js
-app.on("update", update);
-app.on("mouseMove", mouseMove);
-app.dispose();
-```
-
-# _app_.**node**()
-
-Returns the [canvas element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas) for drawing shapes.
-
-```js
-document.body.append(app.node());
-```
-
-# _app_.**prop**(_name_)
-
-Returns the property with the _specified_ name for this app. See the respective [property](#prop) name for details.
-
-```js
-app.prop("width"); // 640
-```
-
-# _app_.**on**(_type, listener_)
-
-Adds a _listener_ for the specified event _type_. Multiple listeners can be registered to receive the same event.
-
-```js
-function mouseMove() {}
-
-function mouseMove1() {}
-
-app.on("mouseMove", mouseMove).on("mouseMove", mouseMove1);
-```
-
-See the respective [event](#event) type for details.
-
-# _app_.**call**(_callback[, ...argument]_)
-
-Calls the specified _function_ on this app with any optional _arguments_ and returns this app. This is equivalent to calling the function by hand but avoids to break method chaining. For example, to draw two concentric circles in a reusable function:
-
-```js
-function ring(app, { x, y, r, r1, fill, fill1 }) {
- app.append(cm.circle, { x, y, r, fill });
- app.append(cm.circle, { x, y, r1, fill2 });
-}
-
-ring(app, {
- x: 100,
- y: 100,
- r: 25,
- r1: 50,
- fill: "orange",
- fill1: "steelblue",
-});
-```
-
-Instead of invoking this function directly on app, now say:
-
-```js
-app.call(ring, {
- x: 100,
- y: 100,
- r: 25,
- r1: 50,
- fill: "orange",
- fill2: "steelblue",
-});
-```
-
-# _app_.**textBBox**(_text_, _textOptions_)
-
-Computes the bounding box for the specified [_textOptions_](#cm-text). The returned bounding box has the following properties:
-
-- **x** - the x coordinate of the text
-- **y** - the y coordinate of the text
-- **width** - the width of the text
-- **height** - the height of the text
-
-```js
-const bbox = app.textBBox({
- text: "hello world",
- fontSize: 20,
- fontWeight: "bold",
-});
-```
-
-# _cm_.**canvas()**
-
-Constructs a canvas renderer, drawing shapes with [CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D). It is the default renderer for [app](#cm-app) and there is no need to specify it explicitly.
-
-```js
-const app = cm.app({
- height: 200,
- renderer: cm.canvas(), // not necessary
-});
-
-app.append(cm.circle, {
- x: 100,
- y: 100,
- r: 50,
- fill: "orange",
-});
-
-app.render();
-```
-
-
-
-### Flow
-
-Binding data to shapes.
-
-# _flow_.**data**(_[data]_)
-
-Returns a new flow that contains this specified _data_. The _data_ is specified for each group in this flow.
-
-If the specified _data_ is an array of arbitrary values(e.g. number of objects), sets _[data]_ as the group of this flow.
-
-```js
-const group = app.append(cm.group, {
- width: app.prop("width") / 3,
- height: app.prop("heigh") / 3,
-});
-
-group.data([1, 2, 3]);
-```
-
-If the flow has multiple groups(such as [flow.data](#flow-data) followed by [app.data](#app-data)), then _data_ should typically be specified as a function. The function will be evaluated for each group in order, being passed the group's parent datum(_d_), the group index(_i_), all the groups(_data_) and this flow(_flow_).
-
-```js
-app.data(matrix).data((d, i, data, flow) => {});
-```
-
-For example, to draw a matrix of characters in a terminal:
-
-```js
-const matrix = [
- [" +", "-", "+ "],
- [" |", cm.wch("π"), "| "],
- [" +", "-", "+ "],
-];
-
-app
- .data(matrix)
- .append(cm.group, { y: (_, i) => i })
- .data((d) => d)
- .append(cm.point, {
- y: 0,
- x: (_, i) => i,
- stroke: (d) => cm.cfb(d),
- });
-```
-
-If no argument is specified, return groups of this app:
+## A Quick Example
```js
-app.data(); // [...]
-```
-
-# _flow_.**datum**(_value_)
-
-Like [flow.data](#flow-data), except receives a _value_ instead of an array of data.
-
-```js
-flow.datum(1);
-
-// Equivalent
-flow.data([1]);
-```
-
-# _flow_.**process**(_process, options_)
-
-Processes the data of this flow with the specified _process_ function receiving the specified _options_, returning a flow with the processed data. It provides a convenient mechanism to manipulate data before calling [flow.append](#flow-append) to bind it with shapes.
-
-For example, to draw a particle system with two shape types:
-
-```js
-const groups = app
- .data(particles)
- .process(cm.push, createParticle)
- .process(cm.eachRight, removeDied)
- .process(cm.each, decay)
- .process(cm.each, move);
-
-groups.process(cm.filter, isCircle).append(cm.circle, {});
-groups.process(cm.filter, isSquare).append(cm.rect, {});
-```
-
-See the respective [process function](#process) for details.
-
-# _flow_.**append**(_shape[, options]_)
-
-Appends and binds shapes with the data of this flow, returning a flow with shapes. Shapes are created by the specified _shape_ function with the specified _options_.
-
-Shape function interprets attribute values and invokes the renderer of this flow to draw shapes. See the respective [shape function](#shape) for details. Each shape's options are typically specified as a object with corresponding attribute name.
-
-For each attribute, if the _value_ is constant, all the shapes are given the same attribute value; otherwise, if the _value_ is a function, it is evaluated for each datum, in order, being passed the current datum(_d_), the current index(_i_), the data(_data_) and this flow(_flow_). The function's return value is then used to set each shapes' attribute.
-
-```js
-const flow = app.data([1, 2, 3]);
-
-flow.append(cm.circle, {
- x: (d) => d * 100,
- y: (d) => d * 100,
- fill: "red",
-});
-```
-
-# _flow_.**transform**(_transform, options_)
-
-Transforms shapes' attribute values with the specified _transform_ function receiving the specified _options_, returning a flow with the transformed attribute values. It provides a convenient mechanism to manipulate attribute values after calling [flow.append](#flow-append) to binding data with shapes.
-
-For example, to map abstract values produced by [Math.sin](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin) into visual values, drawing a sine wave:
-
-```js
-app
- .data(cm.range(50, cm.TWO_PI))
- .append(cm.circle, {
- x: (d) => d,
- y: (d) => Math.sin(d),
- r: 20,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: "#000",
- strokeWidth: 1,
- })
- .transform(cm.mapPosition);
-```
-
-See the respective [transform function](#transform) for details.
-
-# _flow_.**call**(_callback[, ...argument]_)
+import * as cm from "charmingjs";
-Like [app.call](#app-call), except calls on this flow.
-
-```js
-function scaleRadius(flow) {
- flow.transform(cm.mapAttrs, {
- r: { range: [10, 15] },
- });
-}
-
-app
- .data([1, 2, 3, 4])
- .append(cm.circle, {
- x: 100,
- y: 100,
- r: (d) => d,
- fill: "steelblue",
- })
- .call(scaleRadius);
-```
-
-# _flow_.**app**()
-
-Returns this app. It helps define some pure functions relaying some properties of this app.
-
-```js
-function scale(d, i, data, flow) {
- const app = flow.app();
- const width = app.prop("width");
- return d * width;
+function circles(x, y, r, data = []) {
+ if (r < 16) return;
+ data.push({x, y, r});
+ circles(x - r / 2, y, r * 0.5, data);
+ circles(x + r / 2, y, r * 0.5, data);
+ circles(x, y - r / 2, r * 0.5, data);
+ circles(x, y + r / 2, r * 0.5, data);
+ return data;
}
-app.data([0.1, 0.2, 0.3]).process(cm.map, scale);
-```
-
-### Process
-
-Preparing data to be rendered.
-
-# _cm_.**each**
-
-Calls the specified _function_ on each datum of a flow, and returns a new flow that contains the data. The function is being passed the current datum(_d_), the current index(_i_), the current group(_data_) and the flow(_flow_).
-
-```js
-const data = [
- { name: "Locke", number: 4 },
- { name: "Reyes", number: 8 },
- { name: "Ford", number: 15 },
- { name: "Jarrah", number: 16 },
- { name: "Shephard", number: 23 },
- { name: "Kwon", number: 42 },
-];
-
-app.data(data).process(cm.each, (d) => d.number * 2);
-```
-
-# _cm_.**eachRight**
-
-Like [cm.each](#cm-each), except iterates from right to left.
-
-```js
-const data = [
- { name: "Locke", number: 4 },
- { name: "Reyes", number: 8 },
- { name: "Ford", number: 15 },
- { name: "Jarrah", number: 16 },
- { name: "Shephard", number: 23 },
- { name: "Kwon", number: 42 },
-];
-
-app.data(data).process(cm.eachRight, (d, i, data) => {
- if (d.number > 30) data.splice(i, 1);
+const svg = cm.render({
+ width: 480,
+ height: 480,
+ marks: [
+ cm.svg("circle", circles(240, 240, 200), {
+ cx: (d) => d.x,
+ cy: (d) => d.y,
+ r: (d) => d.r,
+ stroke: "black",
+ fill: "transparent",
+ }),
+ ],
});
-```
-# _cm_.**filter**
-
-Calls the specified _function_ on each datum of a flow, and returns a new flow that contains the data meeting true condition. The function is being passed the current datum(_d_), the current index(_i_), the current group(_data_) and the flow(_flow_).
-
-```js
-const data = [
- { name: "Locke", number: 4 },
- { name: "Reyes", number: 8 },
- { name: "Ford", number: 15 },
- { name: "Jarrah", number: 16 },
- { name: "Shephard", number: 23 },
- { name: "Kwon", number: 42 },
-];
-
-app.data(data).process(cm.filter, (d) => d.number % 2 === 0);
+document.body.appendChild(svg);
```
-# _cm_.**map**
+
-Calls the specified _function_ on each datum of a flow, and returns a new flow that contains the new data. The function is being passed the current datum(_d_), the current index(_i_), the current group(_data_) and the flow(_flow_).
-
-```js
-const data = [
- { name: "Locke", number: 4 },
- { name: "Reyes", number: 8 },
- { name: "Ford", number: 15 },
- { name: "Jarrah", number: 16 },
- { name: "Shephard", number: 23 },
- { name: "Kwon", number: 42 },
-];
-
-app.data(data).process(cm.map, (d) => ({ ...d, number: d.number * 2 }));
-```
+## Resources π
-# _cm_.**derive**
+- Documentation - https://charmingjs.org/
+- Features - https://charmingjs.org/what-is-charming
+- Examples - https://observablehq.com/d/18b3d6f3affff5bb
-Calls _value_ on each datum of a flow to derive a new field _key_ for each entries of the specified object, and returns a new flow that contains the new data. The value is being passed the current datum(_d_), the current index(_i_), the current group(_data_) and the flow(_flow_).
+## License π
-```js
-const data = [
- { name: "Locke", number: 4 },
- { name: "Reyes", number: 8 },
- { name: "Ford", number: 15 },
- { name: "Jarrah", number: 16 },
- { name: "Shephard", number: 23 },
- { name: "Kwon", number: 42 },
-];
-
-app.data(data).process(cm.derive, {
- double: (d) => d.name * 2,
- upper: (d) => d.name.toUpperCase(),
-});
-```
-
-# _cm_.**push**
-
-Appends the specified _datum_ to a flow, and returns a new flow that contains the new datum.
-
-If _datum_ is not a function, appends it to all groups.
-
-```js
-const data = [
- { name: "Locke", number: 4 },
- { name: "Reyes", number: 8 },
- { name: "Ford", number: 15 },
- { name: "Jarrah", number: 16 },
- { name: "Shephard", number: 23 },
- { name: "Kwon", number: 42 },
-];
-
-app.data(data).process(cm.push, { name: "Jim", number: 25 });
-```
-
-If the flow has multiple groups(such as [flow.data](#flow-data) followed by [app.data](#app-data)), then _datum_ should typically be specified as a function. The function will be evaluated for each group in order, being passed the group(_group_), the group index(_i_), all the groups(_data_) and this flow(_flow_).
-
-```js
-const matrix = [
- [" +", "-", "+ "],
- [" |", cm.wch("π"), "| "],
- [" +", "-", "+ "],
-];
-
-app
- .data(matrix)
- .append(cm.group, { y: (_, i) => i })
- .data((d) => d)
- .process(cm.push, (group, i, groups) => ` ${i}`);
-```
-
-### Shape
-
-Appending geometric elements to canvas, most of shapes support the following attributes:
-
-- **fill** - the fill color
-- **fillOpacity** - fill opacity (a number between 0 and 1)
-- **stroke** - stroke color
-- **strokeWidth** - stroke width (in pixels)
-- **strokeOpacity** - stroke opacity (a number between 0 and 1)
-
-# _cm_.**point**
-
-Appends dots positioned in _x_ and _y_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the horizontal position, in pixels or in cells
-- **y** - the vertical position, in pixels or in cells
-
-```js
-app.append(cm.point, { x: 10, y: 10 });
-```
-
-# _cm_.**link**
-
-Appends straight lines between two points _[x, y]_ and _[x1, y1]_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the starting horizontal position, in pixels or in cells
-- **y** - the starting vertical position, in pixels or in cells
-- **x1** - the ending horizontal position, in pixels or in cells
-- **y1** - the ending vertical position, in pixels or in cells
-- **rotate** - the rotation angle in degrees clockwise
-- **strokeCap** - the shape of end points, defaults to _butt_; _round_ and _square_
-- **transformOrigin** - the position of the origin point for rotation, defaults to _start_; _center_ and _end_
-
-```js
-app.append(cm.link, { x: 0, y: 0, x1: 100, y1: 100 });
-```
-
-# _cm_.**rect**
-
-Appends rectangles defined by _x_, _y_, _width_ and _height_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the horizontal position, in pixels or cells
-- **y** - the vertical position, in pixels or cells
-- **width** - the rectangle width, in pixels or cells
-- **height** - the rectangle height, in pixels or cells
-- **anchor** - how to position the rectangle, defaults to _start_; _center_
-- **rotate** - the rotation angle in degrees clockwise
-
-```js
-app.append(cm.rect, { x: 10, y: 10, width: 50, height: 40 });
-```
-
-# _cm_.**circle**
-
-Appends circles positioned in _x_ and _y_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the horizontal position, in pixels or cells
-- **y** - the vertical position, in pixels or cells
-- **r** - the circle radius, in pixels or cells
-
-```js
-app.append(cm.circle, { x: 50, y: 50, r: 30 });
-```
-
-# _cm_.**triangle**
-
-Appends triangles defined by _x_, _y_, _x1_, _y1_, _x2_, _y2_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the first horizontal position, in pixels or cells
-- **y** - the first vertical position, in pixels or cells
-- **x1** - the second horizontal position, in pixels or cells
-- **y1** - the second vertical position, in pixels or cells
-- **x2** - the third horizontal position, in pixels or cells
-- **y2** - the third vertical position, in pixels or cells
-
-```js
-app.append(cm.triangle, { x: 0, y: 0, x1: 10, y1: 0, x2: 10, y2: 10 });
-```
-
-# _cm_.**polygon**
-
-Appends polygons defined by _x_ and _y_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the horizontal positions of control points, in pixels or cells clockwise
-- **y** - the vertical positions of control points, in pixels or cells clockwise
-
-If appends one polygon defined by a column of _x_ and a of _y_, assigns columns to its x and y attribute respectively.
-
-```js
-app.append(cm.polygon, {
- x: [0, 10, 10],
- y: [0, 0, 10],
-});
-```
-
-If only appends one polygon defined by an array of points _[x, y]_ or objects, assigns accessors return number to its x and y attribute respectively.
-
-```js
-const polygon = [
- [0, 0],
- [10, 0],
- [10, 10],
-];
-
-app.data(polygon).append(cm.polygon, {
- d => d[0],
- d => d[1],
-})
-```
-
-If appends multiple polygons defined by an array of objects, assigns accessors returns an array of numbers to its x and y attribute respectively.
-
-```js
-const polygons = [
- { X: [0, 10, 10], Y: [0, 0, 10] },
- { X: [20, 30, 30], Y: [0, 0, 10] },
-];
-
-app.data(polygons).append(cm.polygon, {
- d => d.X,
- d => d.Y,
-})
-```
-
-# _cm_.**line**
-
-Appends draw two-dimensional lines defined by _x_ and _y_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the horizontal positions of control points, in pixels or cells
-- **y** - the vertical positions of control points, in pixels or cells
-
-If appends one line defined by a column of _x_ and a of _y_, assigns columns to its x and y attribute respectively.
-
-```js
-app.append(cm.line, {
- x: [0, 10, 20],
- y: [10, 5, 15],
-});
-```
-
-If only appends one line defined by an array of points _[x, y]_ or objects, assigns accessors return number to its x and y attribute respectively.
-
-```js
-const line = [
- [0, 10],
- [10, 5],
- [20, 15],
-];
-
-app.data(line).append(cm.line, {
- d => d[0],
- d => d[1],
-})
-```
-
-If appends multiple lines defined by an array of objects, assigns accessors returns an array of numbers to its x and y attribute respectively.
-
-```js
-const lines = [
- { X: [0, 10, 20], Y: [10, 5, 15] },
- { X: [20, 40, 35], Y: [10, 5, 15] },
-];
-
-app.data(lines).append(cm.line, {
- d => d.X,
- d => d.Y,
-})
-```
-
-# _cm_.**path**
-
-Appends path defined by _d_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **d**: an array or string of [path commands](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d)
-
-If appends one path, assigns the specified path commands to its d attribute.
-
-```js
-app.append(cm.path, {
- d: [["M", 0, 0], ["L", 10, 0], ["L", 10, 10], ["Z"]],
-});
-```
-
-If appends multiple paths, assigns accessors returns an array of path commands to its d attribute.
-
-```js
-const paths = [
- [["M", 0, 0], ["L", 10, 0], ["L", 10, 10], ["Z"]],
- [["M", 10, 0], ["L", 20, 10], ["L", 20, 10], ["Z"]],
-];
-
-app.data(paths).append(cm.path, { d: (d) => d });
-```
-
-# _cm_.**text**
-
-Appends texts at given position in _x_ and _y_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **x** - the horizontal position
-- **y** - the vertical position
-- **text** - the text contents (a string)
-- **fontSize** - the font size in pixels; defaults to 10
-- **fontFamily** - the font name
-- **fontWeight** - the [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight), defaults to normal
-- **textBaseline** - the line anchor for vertical position; _top_, _bottom_, or _middle_
-- **textAlign** - the [text align](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align) for horizontal position; _start_, _end_, or _middle_
-
-# _cm_.**group**
-
-Appends groups at given position in _x_ and _y_. In addition to the [standard shape attributes](#shape), the following attributes are supported:
-
-- **y** - the horizontal position
-- **x** - the vertical position
-- **rotate** - the rotation angle in degrees clockwise
-
-All the child shapes are applied translate(_x_, _y_) and rotate(_rotate_) transformations.
-
-```js
-const group = app.group({
- width: 100,
- height: 100,
-});
-
-group.append(cm.point, {
- x: 0,
- y: 0,
- r: 10,
-});
-```
-
-# _cm_.**clear**
-
-Appends clear shape to clear the canvas background with the _specified_ color. The following attributes are supported:
-
-- **fill** - the clear color
-
-# **function**(_flow_, _value_)
-
-Defines a composite shape by a _function_, passing the current flow(_flow_) and attribute value(_value_).
-
-```js
-// Defines a composite shape.
-function arrow(flow, { length, angle, x, y, rotate, ...options }) {
- const group = flow.append(cm.group, { x, y, rotate });
- const l1 = length.map((d) => d / 2);
- const l2 = length.map((d) => -d / 2);
- const a1 = angle.map((d) => d);
- const a2 = angle.map((d) => -d);
- group.append(cm.link, { x: l2, y: 0, x1: l1, y1: 0, ...options });
- group.append(cm.link, { x: 0, y: 0, x1: l1, y1: 0, rotate: a2, transformOrigin: "end", ...options });
- group.append(cm.link, { x: 0, y: 0, x1: l1, y1: 0, rotate: a1, transformOrigin: "end", ...options });
-}
-
-// Uses a composite shape.
-app
- .data(fields)
- .append(arrow, {
- x: (d) => d.x * size + size / 2,
- y: (d) => d.y * size + size / 2,
- length: size * 0.8,
- angle: Math.PI / 6,
- rotate: (d) => d.value,
- })
- .transform(cm.mapAttrs, {
- rotate: {
- domain: [0, 1],
- range: [0, cm.TWO_PI],
- },
- });
-```
-
-### Transform
-
-Deriving shape attribute values.
-
-# _cm_.**mapAttrs**
-
-Maps abstract attributes to visual attributes with scales. Each scale's options are specified as a nested options object with the corresponding attribute name.
-
-```js
-app
- .append(cm.circle, {
- x: (d) => d[0],
- y: (d) => d[1],
- })
- .transform(cm.mapAttrs, {
- x: {}, // scale for x attribute
- y: {}, // scale for y attribute
- });
-```
-
-A scale's domain is typically inferred automatically. It can be customized explicitly by these options:
-
-- **scale** - [scale](#scale), defaults to [scaleLinear](#cm-scaleLinear)
-- **domain** - abstract values, typically _[min, max]_
-- **range** - visual values, typically _[min, max]_
-
-```js
-app
- .append(cm.circle, {
- x: (d) => d[0],
- y: (d) => d[1],
- })
- .transform(cm.mapAttrs, {
- x: {
- scale: cm.scaleLog,
- range: [0, app.prop("height")],
- },
- });
-```
-
-# _cm_.**mapPosition**
-
-Map abstract position to visual position. Like [mapAttrs](#cm-mapAttrs), but only maps position attributes to corresponding dimension range.
-
-For x attributes, such as x and x1, the scale's range is _[0, app.prop("width")]_ by default. For y attributes, such as y and y1, the scale's range is _[0, app.prop("height")]_ by default.
-
-- **scaleX** - [scale](#scale) for x attributes, defaults to [scaleLinear](#cm-scaleLinear)
-- **scaleY** - [scale](#scale) for y attributes, defaults to [scaleLinear](#cm-scaleLinear)
-- **domainX** - abstract values for x attributes, typically _[min, max]_
-- **domainY** - abstract values for y attributes, typically _[min, max]_
-- **reverseX** - reverses range for x attributes, defaults to false
-- **reverseY** - reverses range for y attributes, defaults to false
-- **padding** - space between shapes and border, defaults to 0
-
-```js
-app
- .append(cm.line, {
- x: (d) => d[0],
- y: (d) => d[0],
- })
- .transform(cm.mapPosition, {
- scaleX: cm.scaleLog,
- reverseY: true,
- padding: 15,
- });
-```
-
-### Scale
-
-Mapping abstract data to visual representation.
-
-# _cm_.**scaleLinear**(_domain, range_)
-
-Constructs a new linear scale with the specified _domain_ and _range_. Linear scales map a continuous, quantitative to a continuous output using a linear transformation.
-
-```js
-const scale = cm.scaleLinear([0, 1], [0, 100]);
-scale(0); // 0;
-scale(0.5); // 50;
-scale(1); // 100;
-```
-
-# _cm_.**scaleSqrt**(_domain, range_)
-
-Constructs a new sqrt scale with the specified _domain_ and _range_. Sqrt scales are similar to [linear scale](#cm-scaleLinear), except a square root transform is applied to the input domain value before the output range is computed.
-
-```js
-const scale = cm.scaleSqrt([0, 1], [0, 100]);
-scale(0.09); // 30
-scale(0.64); // 80
-scale(0.81); // 90
-```
-
-# _cm_.**scaleLog**(_domain, range_)
-
-Constructs a new log scale with the specified _domain_ and _range_. Log scales are similar to [linear scale](#cm-scaleLinear), except a logarithmic transform transform is applied to the input domain value before the output range is computed.
-
-```js
-const scale = cm.scaleLog([1, 10], [0, 960]);
-```
-
-# _cm_.**scaleOrdinal**(_domain, range_)
-
-Constructs a new ordinal scale with the specified _domain_ and _range_. Unlike [linear scale](cm-scaleLinear), ordinal scales have a discrete domain and range. Given a _value_ in the input domain, returns the corresponding in the output range.
-
-```js
-const scale = cm.scaleOrdinal(["A", "B", "C"], ["steelblue", "yellow", "red"]);
-
-scale("A"); // "steelblue"
-scale("B"); // "yellow"
-scale("C"); // "red"
-```
-
-### Event
-
-Handling hooks and events.
-
-# _app_.**on**(_"update", callback_)
-
-The _update_ event is fired repeatedly until [app.stop](#app-stop) is called after calling [app.start](#app-start). For example, to draw a moving rect:
-
-```js
-let x = 0;
-
-function update() {
- app.append(cm.rect, {
- x: x++,
- y: 0,
- width: 100,
- height: 50,
- });
-}
-
-app.on("update", update);
-```
-
-# _app_.**on**(_"mouseDown", callback_)
-
-The _mouseDown_ event is fired when a mouse button is pressed. For example, to change the background color:
-
-```js
-let background = "red";
-
-function mouseDown() {
- background = "blue";
-}
-
-app.on("mouseDown", mouseDown);
-```
-
-# _app_.**on**(_"mouseUp", callback_)
-
-The _mouseUp_ event is fired when a mouse button is released. For example, to change the background color:
-
-```js
-let background = "red";
-
-function mouseUp() {
- background = "blue";
-}
-
-app.on("mouseUp", mouseUp);
-```
-
-# _app_.**on**(_"mouseClick", callback_)
-
-The _mouseClick_ event is fired when a mouse button is clicked. For example, to change the background color:
-
-```js
-let background = "red";
-
-function mouseClick() {
- background = "blue";
-}
-
-app.on("mouseClick", mouseClick);
-```
-
-# _app_.**on**(_"beforeEach", callback_)
-
-The _beforeEach_ event is fired before each [update event](#event-update) is fired. For example, to begin measuring frame rate by [stats.js](https://github.com/mrdoob/stats.js):
-
-```js
-function measure(app) {
- // ...
- app.on("beforeEach", () => {
- stats.begin();
- });
-}
-
-app.call(measure);
-```
-
-# _app_.**on**(_"afterEach", callback_)
-
-The _afterEach_ event is fired after each [update event](#event-update) is fired. For example, to end measuring frame rate by [stats.js](https://github.com/mrdoob/stats.js):
-
-```js
-function measure(app) {
- // ...
- app.on("afterEach", () => {
- stats.end();
- });
-}
-
-app.call(measure);
-```
-
-# _app_.**on**(_"beforeAll", callback_)
-
-The _beforeAll_ event is fired before calling [app.start](#app-start). For example, to construct a Stats instance from [stats.js](https://github.com/mrdoob/stats.js):
-
-```js
-function measure(app) {
- let stats;
-
- //...
- app.on("beforeAll", () => {
- const container = document.getElementById("tool");
- stats = new Stats();
- stats.dom.style.position = "inherit";
- stats.dom.style.marginLeft = "1em";
- container.appendChild(stats.dom);
- });
-}
-
-app.call(measure);
-```
-
-# _app_.**on**(_"afterAll", callback_)
-
-The _afterAll_ event is fired after calling [app.dispose](#app-dispose). For example, to remove the DOM of a Stats instance from [stats.js](https://github.com/mrdoob/stats.js):
-
-```js
-function measure(app) {
- let stats;
-
- //...
- app.on("afterAll", () => {
- stats.dom.remove();
- });
-}
-
-app.call(measure);
-```
-
-### Prop
-
-Returning properties of the app.
-
-# _app_.**prop**(_"width"_)
-
-If the renderer is not terminal, returns the width of this app in pixel.
-
-```js
-const app = cm.app();
-app.prop("width"); // 640;
-```
-
-If the renderer is [terminal](#cm-terminal), returns the width of this app in cell.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("width"); // 71
-```
-
-# _app_.**prop**(_"height"_)
-
-If the renderer is not [terminal](#cm-terminal), returns the height of this app in pixel.
-
-```js
-const app = cm.app();
-app.prop("height"); // 480;
-```
-
-If the renderer is [terminal](#cm-terminal), returns the height of this app in cell.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("height"); // 26
-```
-
-# _app_.**prop**(_"frameCount"_)
-
-Returns the number of frames that have been displayed since this app started. For example, to draw a moving rect:
-
-```js
-app.on("update", () =>
- app.append(cm.rect, {
- x: app.frameCount(),
- y: 0,
- width: 10,
- height: 10,
- }),
-);
-```
-
-# _app_.**prop**(_"frameCount"_)
-
-Returns the number of frames to be displayed per second.
-
-```js
-app.prop("frameRate"); // 60
-```
-
-# _app_.**prop**(_"mouseX"_)
-
-Returns the x coordinate of the mouse position.
-
-```js
-app.prop("mouseX"); // 0
-```
-
-# _app_.**prop**(_"mouseY"_)
-
-Returns the y coordinate of the mouse position.
-
-```js
-app.prop("mouseY"); // 0
-```
-
-# _app_.**prop**(_"mode"_)
-
-Returns the rendering mode of this app, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("mode"); // "single"
-```
-
-# _app_.**prop**(_"pixelWidth"_)
-
-Returns the computed width of this app in pixel, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("pixelWidth"); // 639
-```
-
-# _app_.**prop**(_"pixelHeight"_)
-
-Returns the computed height of this app in pixel, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("pixelHeight"); // 468;
-```
-
-# _app_.**prop**(_"cellWidth"_)
-
-Returns the computed width of the cells in pixel, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("cellWidth"); // 9
-```
-
-# _app_.**prop**(_"cellHeight"_)
-
-Returns the computed height of the cells in pixel, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("cellHeight"); // 18
-```
-
-# _app_.**prop**(_"fontSize"_)
-
-Returns the font size used to render text, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("fontSize"); // 15
-```
-
-# _app_.**prop**(_"fontFamily"_)
-
-Returns the font family used to render text, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("fontFamily"); // "courier-new, courier, monospace"
-```
-
-# _app_.**prop**(_"fontWeight"_)
-
-Returns the font weight used to render text, which is only for app with a [terminal](#cm-terminal) renderer.
-
-```js
-const app = cm.app({ renderer: await cm.terminal() });
-app.prop("fontWeight"); // "normal"
-```
-
-### Attribute
-
-Defining Attributes for shapes.
-
-# _cm_.**rgb**(_r[, g[, b]]_)
-
-Returns a string representing the RGB color according to the [CSS Object Model specification](https://drafts.csswg.org/cssom/#serialize-a-css-component-value).
-
-```js
-cm.rgb(234, 260, 180); // 'rgb(234, 260, 180)'
-```
-
-If only on argument is specified, sets all channels to the same _value_.
-
-```js
-cm.rgb(100); // 'rgb(100, 100, 100)'
-```
-
-# _cm_.**hsl**(_h[, s[, l]]_)
-
-Returns a string representing the HSL color according to the [CSS Object Model specification](https://drafts.csswg.org/cssom/#serialize-a-css-component-value).
-
-```js
-cm.hsl(234, 50, 50); // 'hsl(234, 50%, 50%)'
-```
-
-# _cm_.**constant**(_value_)
-
-Defines a attribute with the specified constant _value_, which is useful for defining some non-data-driven options for [composite shape](#cm-composite-shape).
-
-```js
-app.data(["A", "B", "C"]).append(bar, {
- x: (d) => d,
- y: (_, i) => i,
- axis: cm.constant(false),
- colors: cm.constant(["yellow", "red"]),
-});
-
-function bar(
- flow,
- {
- x, // ["A", "B", "C"]
- y, // [0, 1, 2]
- axis, // false
- colors, // ["yellow", "red"]
- },
-) {
- // ...
-}
-```
-
-### WebGL
-
-WebGL renderer and related helpers.
-
-# _cm_.**webgl()**
-
-Returns a WebGL renderer, rendering shapes by WebGL.
-
-```js
-const app = cm.app({
- renderer: cm.webgl(),
-});
-```
-
-# _cm_.**glsl**
-
-Defines a attribute with the specified [GLSL]() function through template literals. The name of the function should match the assigned attribute, being passed the current datum(_d_), returning value with corresponding type.
-
-```js
-const r = cm.glsl`float r(float theta) {
- float d = 0.2 + 0.12 * cos(theta * 9.0 - 2.0);
- return d * 300.0;
-}`;
-
-app.data(theta).append(cm.circle, { r });
-```
-
-Some numbers can also be interpolated:
-
-```js
-const scale = 300;
-const width = 640;
-const height = 480;
-const position = cm.glsl`vec2 position(float theta) {
- vec2 xy = vec2(
- cos(theta),
- sin(theta)) * (0.6 + 0.2 * cos(theta * 6.0 + cos(theta * 8.0 + ${time}))
- );
- return xy * ${scale} + vec2(${width / 2}, ${height / 2});
-}`;
-
-app.data(theta).append(cm.circle, { position });
-```
-
-### Terminal
-
-Terminal renderer and related helpers.
-
-# _cm_.**terminal()**
-
-Returns a promise resolved to a terminal renderer, drawing shapes in a terminal like context.
-
-```js
-const app = cm.app({
- renderer: await cm.terminal(),
-});
-```
-
-Shapes drawn by terminal renders are (typically) not positioned in literal pixels, or colored in literal colors, as in a conventional graphics system. Instead they are positioned in count of terminal's cell and colored by [characters](#cm-cfb).
-
-```js
-const app = cm.app({
- mode: "double",
- renderer: await cm.terminal(),
-});
-
-app
- .data(cm.range(240, 0, Math.PI * 2))
- .append(cm.group, {
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- })
- .append(cm.point, {
- x: (t) => 10 * Math.cos(t) * Math.cos(t * 3),
- y: (t) => 10 * Math.sin(t) * Math.cos(t * 3),
- stroke: cm.cfb(cm.wch("π")),
- });
-
-app.render();
-```
-
-
-
-Moreover, it draws ASCII text powered by [figlet.js](https://github.com/patorjk/figlet.js).
-
-```js
-const app = cm.app({
- width: 800,
- height: 200,
- mode: "double",
- renderer: await cm.terminal(),
-});
-
-app.append(cm.text, {
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- text: "charming",
- textBaseline: "middle",
- textAlign: "center",
-});
-
-app.render();
-```
-
-
-
-# _cm_.**cfb**(_ch[, f[, b]]_)
-
-Returns a terminal color object, which is only for app with a [terminal](#cm-terminal) renderer. A terminal color comprises the following three channels:
-
-- _ch_: character
-- _f_: CSS Color for the color of the character
-- _b_: CSS color for the background cell of the character
-
-If neither _f_ or _b_ are not specified, each defaults to null.
-
-```js
-app.append(cm.rect, {
- x: 0,
- y: 0,
- width: 10,
- height: 5,
- fill: cm.cfb("@", "steelblue", "orange"),
- stroke: cm.cfg("+"),
-});
-```
-
-# _cm_.**wch**(_ch_)
-
-Returns a character marked as a wide character, which is only for app with a [terminal](#cm-terminal) in double mode.
-
-```js
-const app = cm.app({
- terminal: await cm.terminal(),
- mode: "double",
-});
-
-app.append(cm.rect, {
- x: 0,
- y: 0,
- width: 10,
- height: 5,
- fill: cm.cfb(cm.wch("π")),
-});
-```
-
-# _cm_.**figlet**(_text_)
-
-Defines a figlet text with the specified _text_ for [terminal renderer](#cm-terminal).
-
-```js
-const app = cm.app({
- renderer: await cm.terminal(),
-});
-
-app.append(cm.text, {
- text: cm.figlet("Charming"),
-});
-```
-
-# _cm_.**fontStandard**()
-
-Parses and returns the standard font for the fontFamily attribute.
-
-```js
-app.append(cm.text, {
- // ...
- fontFamily: cm.fontStandard(),
-});
-```
-
-
-
-# _cm_.**fontGhost**()
-
-Parses and returns the ghost font for the fontFamily attribute.
-
-```js
-app.append(cm.text, {
- // ...
- fontFamily: cm.fontGhost(),
-});
-```
-
-
-
-# _cm_.**gradientRainBowX**()
-
-Returns the fill attribute with the vertical rainbow gradient.
-
-```js
-app.append(cm.text, {
- // ...
- fill: cm.gradientRainBowX(),
-});
-```
-
-
-
-# _cm_.**gradientSineBowX**()
-
-Returns the fill attribute with the vertical sinebox gradient.
-
-```js
-app.append(cm.text, {
- // ...
- fill: cm.gradientRainBowX(),
-});
-```
-
-
-
-### Array
-
-Array generation and manipulation.
-
-# _cm_.**range**(_count[, start[, end]]_)
-
-Returns an array of exactly _count_ uniformly-spaced values between _start_ and _end_. If _start_ is not specified, it defaults to 0. If _end_ is not specified, it defaults to 1.
-
-```js
-cm.range(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-cm.range(10, 5); // [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5]
-cm.range(10, 5, 55); // [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
-```
-
-# _cm_.**cross**(_...arrays_)
-
-Returns the [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) of the specified arrays.
-
-```js
-cm.cross([1, 2, 3], [1, 2]); // [[1, 1], [1, 2], [2, 1], [2, 2], [3, 1], [3, 2]]
-```
-
-# _cm_.**extent**(_array[, accessor]_)
-
-Returns the minium and maximum value in given _array_ using natural order.
-
-```js
-cm.extent([4, 3, 2, 2, 7, 3, 5]); // [2, 7]
-```
-
-If an optional accessor function is specified, the extent is computed after calling array.map function.
-
-```js
-cm.extent(people, (d) => d.age); // [10, 30]
-```
-
-### Math
-
-Processing numbers, randomness, etc.
-
-# _cm_.**clamp**(_value, min, max_)
-
-Constrains the input _value_ within the specified range _[min, max]_.
-
-```js
-const x = 10;
-cm.clamp(10, 2, 8); // 8
-cm.clamp(10, 2, 12); // 10
-cm.clamp(10, 12, 20); // 12
-```
-
-# _cm_.**random**(_[min[, max]]_)
-
-Generates random number with a uniform distribution, which is within range _\[min, max\)_. If _min_ is not specified, it defaults to 0; if _max_ is not specified, it defaults to 1.
-
-```js
-cm.random(); // 0.4418278691734798
-cm.random(10); // 3.747820060823679
-cm.random(2, 10); // 6.649642684087617
-```
-
-# _cm_.**randomInt**(_[min[, max]]_)
-
-Like `cm.random`, expect returns integers.
-
-```js
-cm.randomInt(0, 10); // 5
-```
-
-# _cm_.**randomNoise**(_min[, max[, options]]_)
-
-Returns a function with the specified _options_ for generating random numbers with a smooth, continuous random-like distribution, commonly referred to as [Perlin Noise](https://en.wikipedia.org/wiki/Perlin_noise), which is within range _\[min, max\)_. If _min_ is not specified, it defaults to 0; if _max_ is not specified, it defaults to 1.
-
-Supports following options:
-
-- **octaves** - layers of noise, default to 4.
-- **seed** - seed for generated sequence, a real number or as any integer, defaults to random number.
-- **falloff** - falloff factor for each octave, a real number or as any integer, defaults to 0.5.
-
-Increasing the number of octaves results in a more variable sequence, and two generators instanced with the same seed and octaves generate the same sequence.
-
-The returned function accept two parameters: _x_ is x coordinate in noise space; _y_ is y coordinate in noise space.
-
-```js
-cm.randomNoise()(0.2, 0.1); // 0.04076453205333332
-cm.randomNoise(6, 2)(0.2, 0.1); // -0.08489767172063487
-```
-
-
-
-# _cm_.**randomNormal**(_[mu[, sigma]]_)
-
-Returns a function for generating random numbers with a [normal(Gaussian) distribution](https://en.wikipedia.org/wiki/Normal_distribution). The expected value of the generated number is _mu_, with given standard deviation sigma. If _mu_ is not specified, it defaults to 0; if _sigma_ is not specified, it defaults to 1.
-
-```js
-cm.randomNormal()(); // -2.0897431210663022
-cm.randomNormal(30, 10)(); // 31.94829616303788
-```
-
-
-
-# _cm_.**randomChar**()
-
-Returns a random printable and non-empty character.
-
-```js
-cm.randomChar(); // 'A'
-```
-
-### Constant
-
-Useful constants.
-
-# _cm_.**TWO_PI**
-
-It is twice the ratio of the circumference of a circle to its diameter.
-
-```js
-Math.cos(cm.TOW_PI); // 1
-```
-
-### Vector
-
-Basics for simulating physical laws.
-
-# _cm_.**vec**(_[x[, y]]_)
-
-Constructs a vector with the specified _x_ and _y_ component. If either _x_ or _y_ are not specified, each defaults to 0. The returned vector has the following properties:
-
-- x - x component of the vector
-- y - y component of the vector
-
-```js
-cm.vec(); // { x: 0, y: 0 }
-cm.vec(1); // { x: 1, y: 0 }
-cm.vec(2, 3); // { x: 2, y: 3 }
-```
-
-# _cm_.**vecFromAngle**(_angle_)
-
-Constructs a vector from the specified _angle_ in radians.
-
-```js
-cm.vecFromAngle(Math.PI / 4); // { x: 1, y: 1 }
-```
-
-# _cm_.**vecAdd**(_a, b_)
-
-Adds the specified _vectors_ and returns a new vector.
-
-```js
-const a = cm.vec(1, 2);
-const b = cm.vec(2, 3);
-const c = cm.vecAdd(a, b);
-a; // { x: 1, y: 2 }
-b; // { x: 2, y: 3 }
-c; // { x: 3, y: 5 }
-```
-
-# _cm_.**vecAngle**(_a_)
-
-Computes the angle of the specified _vector_.
-
-```js
-const a = cm.vec(1, 1);
-cm.vecAngle(a); // Math.PI / 4
-```
-
-# _cm_.**vecClamp**(_a, min[, max]_)
-
-Constrains the magnitude of the specified _vector_ within the specified range _[min, max]_, and returns a new _vector_.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecClamp(a, 10, 15);
-a; // { x: 3, y: 4 }
-b; // { x: 6, y: 8 }
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum magnitude_, with the minium magnitude defaults to 0.
-
-```js
-const a = cm.vec(6, 8);
-cm.vecClamp(a, 5); // { a: 3, b: 4 }
-```
-
-# _cm_.**vecClampX**(_a, min[, max]_)
-
-Constrains the x component of the specified _vector_ within the specified range _[min, max]_, and returns a new _vector_.
-
-```js
-const a = cm.vec(6, 8);
-const b = cm.vecClampX(a, 10, 15);
-a; // { x: 6, y: 8 }
-b; // { x: 10, y: 8 }
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum value_, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(6, 8);
-const b = cm.vecClampX(a, 5);
-a; // { x: 6, y: 8 }
-b; // { x: 5, y: 8 }
-```
-
-# _cm_.**vecClampY**(_a, min[, max]_)
-
-Constrains the y component of the specified _vector_ within the specified range _[min, max]_, and returns a new _vector_.
-
-```js
-const a = cm.vec(6, 8);
-const b = cm.vecClampY(a, 10, 15);
-a; // { x: 6, y: 8 }
-b; // { x: 6, y: 10 }
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum value_, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(6, 8);
-const b = cm.vecClampY(a, 5);
-a; // { x: 6, y: 8 }
-b; // { x: 6, y: 5 }
-```
-
-# _cm_.**vecCross**(_a, b_)
-
-Computes the cross product of the specified _vectors_.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vec(1, 2);
-cm.vecCross(a, b); // 2
-```
-
-# _cm_.**vecDist**(_a, b_)
-
-Computes the distance of the specified _vectors_.
-
-```js
-const a = cm.vec(4, 6);
-const b = cm.vec(1, 2);
-cm.vecDist(a, b); // 5
-```
-
-# _cm_.**vecDist2**(_a, b_)
-
-Computes the square distance of the specified _vectors_.
-
-```js
-const a = cm.vec(4, 6);
-const b = cm.vec(1, 2);
-cm.vecDist2(a, b); // 25
-```
-
-# _cm_.**vecDiv**(_a, value_)
-
-Divides the specified _vector's_ x and y component by the specified _value_, and returns a new _vector_.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecDiv(a, 0.5);
-a; // { x: 3, y: 4 }
-b; // { x: 6, y: 8 }
-```
-
-# _cm_.**vecDot**(_a, b_)
-
-Computes the dot product of the specified _vectors_.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vec(1, 2);
-cm.vecDot(a, b); // 11
-```
-
-# _cm_.**vecInX**(_a, min[, max]_)
-
-Returns true if the specified _vector's_ x component is within the specified range _[min, max]_.
-
-```js
-const a = cm.vec(3, 4);
-cm.vecInX(a, 1, 2); // false
-cm.vecInX(a, 1, 3); // true
-cm.vecInX(a, 1, 4); // true
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum value_, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(3, 4);
-cm.vecInX(a, 2); // false
-cm.vecInX(a, 3); // true
-cm.vecInX(a, 4); // true
-```
-
-# _cm_.**vecInY**(_a, x[, x1]_)
-
-Returns true if the specified _vector's_ y component is within the specified range _[min, max]_.
-
-```js
-const a = cm.vec(3, 4);
-cm.vecInY(a, 1, 3); // false
-cm.vecInY(a, 1, 4); // true
-cm.vecInY(a, 1, 5); // true
-```
-
-If two arguments are specified, the second one is interpreted as the maximum value, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(3, 4);
-cm.vecInY(a, 3); // false
-cm.vecInY(a, 4); // true
-cm.vecInY(a, 5); // true
-```
-
-# _cm_.**vecMag**(_a[, value]_)
-
-If only one argument is specified, computes the magnitude of the specified _vector_.
-
-```js
-const a = cm.vec(3, 4);
-cm.vecMag(a); // 5
-```
-
-If two arguments are specified, sets the magnitude of the specified _vector_ to the specified _value_, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecMag(a, 10);
-a; // { x: 3, y: 4 }
-b; // { x: 6, y: 8 }
-```
-
-# _cm_.**vecMult**(_a, value_)
-
-Multiplies the specified _vector's_ x and y component by the specified _value_, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecMult(a, 2);
-a; // { x: 3, y: 4 }
-b; // { x: 6, y: 8 }
-```
-
-# _cm_.**vecNeg**(_a_)
-
-Negates the specified _vector's_ x and y component, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecNeg(a);
-a; // { x: 3, y: 4 }
-b; // { x: -3, y: -4 }
-```
-
-# _cm_.**vecNegX**(_a_)
-
-Negates the specified _vector's_ x component, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecNegX(a);
-a; // { x: 3, y: 4 }
-b; // { x: -3, y: 4 }
-```
-
-# _cm_.**vecNegY**(_a_)
-
-Negates the specified _vector's_ y component, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecNegY(a);
-a; // { x: 3, y: 4 }
-b; // { x: 3, y: -4 }
-```
-
-# _cm_.**vecNorm**(_a_)
-
-Normalizes the specified _vector_, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecNorm(a);
-a; // { x: 3, y: 4 }
-b; // { x: 0.6, y: 0.8 }
-```
-
-# _cm_.**vecRandom**()
-
-Returns a unit vector with a random heading, following a uniform distribution.
-
-```js
-cm.vecRandom(); // { x: 0.9239434883837478, y: 0.688605153583981 }
-```
-
-# _cm_.**vecSub**(_a, b_)
-
-Subtracts the specified _vectors_ and returns a new vector.
-
-```js
-const a = cm.vec(1, 2);
-const b = cm.vec(2, 4);
-const c = cm.vecSub(a, b);
-a; // { x: 1, y: 2 }
-b; // { x: 2, y: 4 }
-c; // { x: -1, y: -2 }
-```
-
-# _vec_.**clone**()
-
-Clones the _vector_ and returns a new vector.
-
-```js
-const a = cm.vec(1, 2);
-const b = a.clone();
-a === b; // false
-b; // { x: 1, y: 2 }
-```
-
-# _vec_.**add**(_a_)
-
-Adds the specified _vector_ to the target vector, and returns the target vector.
-
-```js
-const a = cm.vec(1, 2);
-const b = cm.vec(3, 4);
-a.add(b); // a
-a; // { x: 4, y: 6 }
-```
-
-# _vec_.**angle**()
-
-Computes the angle of the target vector.
-
-```js
-const a = cm.vec(1, 1);
-a.angle(); // Math.PI / 4
-```
-
-# _vec_.**clamp**(_min[, max]_)
-
-Constrains the magnitude of the target vector within the specified range _[min, max]_, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.clamp(10, 15); // a
-a; // { x: 6, y: 8 }
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum magnitude_, with the minium magnitude defaults to 0.
-
-```js
-const a = cm.vec(6, 8);
-a.clamp(5); // a
-a; // { a: 3, b: 4 }
-```
-
-# _vec_.**clampX**(_min[, max]_)
-
-Constrains the x component of the target _vector_ within the specified range _[min, max]_, and returns it.
-
-```js
-const a = cm.vec(6, 8);
-a.clampX(10, 15); // a
-a; // { x: 10, y: 8 }
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum value_, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(6, 8);
-a.clampX(5); // a
-a; // { x: 5, y: 8 }
-```
-
-# _vec_.**clampY**(_min[, max]_)
-
-Constrains the y component of the target _vector_ within the specified range _[min, max]_, and returns it.
-
-```js
-const a = cm.vec(6, 8);
-a.clampY(10, 15); // a
-a; // { x: 6, y: 10 }
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum value_, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(6, 8);
-a.clampY(5);
-a; // { x: 6, y: 5 }
-```
-
-# _vec_.**cross**(_a_)
-
-Computes the cross product of the specified _vector_ and the target vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vec(1, 2);
-a.cross(b); // 2
-```
-
-# _vec_.**dist**(_a_)
-
-Computes the distance of the specified _vector_ and the target vector.
-
-```js
-const a = cm.vec(4, 6);
-const b = cm.vec(1, 2);
-a.dist(b); // 5
-```
-
-# _vec_.**dist2**(_a_)
-
-Computes the square distance of the specified _vector_ and the target vector.
-
-```js
-const a = cm.vec(4, 6);
-const b = cm.vec(1, 2);
-a.dist(b); // 25
-```
-
-# _vec_.**div**(_value_)
-
-Divides the target vector' x and y component by the specified _value_, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.div(0.5); // a
-a; // { x: 6, y: 8 }
-```
-
-# _vec_.**dot**(_a_)
-
-Computes the dot product of the specified _vector_ and the target vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vec(1, 2);
-a.dot(b); // 11
-```
-
-# _vec_.**inX**(_min[, max]_)
-
-Returns true if the target vector's x component is within the specified range _[min, max]_.
-
-```js
-const a = cm.vec(3, 4);
-a.inX(1, 2); // false
-a.inX(1, 3); // true
-a.inX(1, 4); // true
-```
-
-If two arguments are specified, the second one is interpreted as the _maximum value_, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(3, 4);
-a.inX(2); // false
-a.inX(3); // true
-a.inX(4); // true
-```
-
-# _vec_.**inY**(_min[, max]_)
-
-Returns true if the target vector's y component is within the specified range _[min, max]_.
-
-```js
-const a = cm.vec(3, 4);
-a.inY(1, 3); // false
-a.inY(1, 4); // true
-a.inY(1, 5); // true
-```
-
-If two arguments are specified, the second one is interpreted as the maximum value, with the minium value defaults to 0.
-
-```js
-const a = cm.vec(3, 4);
-a.inY(3); // false
-a.inY(4); // true
-a.inY(5); // true
-```
-
-# _vec_.**mag**(_[value]_)
-
-If no argument is specified, computes the magnitude of the target vector.
-
-```js
-const a = cm.vec(3, 4);
-cm.mag(); // 5
-```
-
-If one argument is specified, sets the magnitude of the target to the specified _value_, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.mag(10); // a
-a; // { x: 6, y: 8 }
-```
-
-# _vec_.**mult**(_m_)
-
-Multiplies the specified component's x and y by the specified _value_, and returns a new vector.
-
-```js
-const a = cm.vec(3, 4);
-const b = cm.vecMult(a, 2);
-a; // { x: 3, y: 4 }
-b; // { x: 6, y: 8 }
-```
-
-# _vec_.**neg**()
-
-Negates the target vector's x and y component, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.neg(); // a
-a; // { x: -3, y: -4 }
-```
-
-# _vec_.**negX**()
-
-Negates the target vector's x component, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.negX(); // a
-a; // { x: -3, y: 4 }
-```
-
-# _vec_.**negY**()
-
-Negates the target vector's y component, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.negY(); // a
-a; // { x: 3, y: -4 }
-```
-
-# _vec_.**norm**()
-
-Normalizes the target vector, and returns it.
-
-```js
-const a = cm.vec(3, 4);
-a.norm(); // a
-a; // { x: 0.6, y: 0.8 }
-```
-
-# _vec_.**set**(_x[, y]_)
-
-If only one argument is specified and it is a vector instance, sets the target vector's x and y component with the source _vector's_ x and y component, and returns the target vector.
-
-```js
-const a = cm.vec(1, 2);
-const b = cm.vec(3, 4);
-a.set(b); // a
-a; // { x: 3, y: 4 }
-```
-
-If two arguments are specified, sets the target vector's x and y component with the specified _x_ and _y_, and returns it.
-
-```js
-const a = cm.vec(1, 2);
-a.set(3, 4); // a
-a; // { x: 3, y: 4 }
-```
-
-# _vec_.**setX**(_x_)
-
-Sets the target vector's x component with the specified _x_, and returns it.
-
-```js
-const a = cm.vec(1, 2);
-a.setX(3); // a
-a; // { x: 3, y: 2 }
-```
-
-# _vec_.**setY**(_y_)
-
-Sets the target vector's y component with the specified _y_, and returns it.
-
-```js
-const a = cm.vec(1, 2);
-a.setY(3); // a
-a; // { x: 1, y: 3 }
-```
-
-# _vec_.**sub**(_a_)
-
-Subtracts the target vector with the specified vector and returns the target vector.
-
-```js
-const a = cm.vec(1, 2);
-const b = cm.vec(2, 4);
-a.sub(b); // a
-a; // { x: -1, y: -2 }
-b; // { x: 2, y: 4 }
-```
-
-### Helper
-
-Useful unities.
-
-# _cm_.**pathContext**()
-
-Constructs a new path generator like [d3-path](https://d3js.org/d3-path#path) serializer, expect returns an array of path commands instead of a path string. Useful for charming to render the path generated by [d3-shape](https://d3js.org/d3-shape) or [d3-geo](https://d3js.org/d3-geo/path) without parsing it, which is good for performance.
-
-```js
-const circle = d3.geoCircle()();
-const projection = d3.geoOrthographic().translate([0, 0]).scale(10);
-const path = d3.geoPath(projection);
-
-app.append(cm.path, {
- d: () => {
- const context = cm.pathContext();
- path.context(context)(circle);
- return context.toArray();
- },
-});
-```
-
-See [d3-path](https://d3js.org/d3-path#path) for more [CanvasPathMethods](https://html.spec.whatwg.org/multipage/#canvaspathmethods).
-
-# _pathContext_.**toArray**()
-
-Returns the array of path commands.
-
-```js
-const context = cm.pathContext();
-context.moveTo(0, 0);
-context.lineTo(10, 0);
-context.lineTo(10, 10);
-context.closePath();
-context.toArray(); // [["M", 0, 0], ["L", 10, 0], ["L", 10, 10], ["Z"]]
-```
+MIT@Bairui SU
diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs
new file mode 100644
index 00000000..46b8228f
--- /dev/null
+++ b/docs/.vitepress/config.mjs
@@ -0,0 +1,43 @@
+import {defineConfig} from "vitepress";
+import config from "genji-theme-vitepress/config";
+
+// https://vitepress.dev/reference/site-config
+export default defineConfig({
+ title: "Charming",
+ description: "The JavaScript library for expressive creative coding",
+ cleanUrls: true,
+ extends: config,
+ head: [["link", {rel: "icon", type: "image/png", href: "/logo.png"}]],
+ appearance: false,
+ themeConfig: {
+ // https://vitepress.dev/reference/default-theme-config
+ nav: [
+ {text: "Docs", link: "/docs/what-is-charming"},
+ {
+ text: "Examples",
+ link: "https://observablehq.com/d/18b3d6f3affff5bb",
+ },
+ ],
+ sidebar: {
+ "/": [
+ {
+ text: "Introduction",
+ items: [
+ {text: "What is Charming?", link: "/docs/what-is-charming"},
+ {text: "Getting started", link: "/docs/getting-started"},
+ {text: "API Index", link: "/docs/api-index"},
+ ],
+ },
+ // {
+ // text: "Reference",
+ // items: [
+ // {text: "Charming Mark", link: "/docs/charming-mark"},
+ // {text: "Charming Vector", link: "/docs/charming-vector"},
+ // ],
+ // },
+ ],
+ },
+ socialLinks: [{icon: "github", link: "https://github.com/charming-art/charming"}],
+ logo: "/logo.svg",
+ },
+});
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
new file mode 100644
index 00000000..9b56c6e2
--- /dev/null
+++ b/docs/.vitepress/theme/custom.css
@@ -0,0 +1,10 @@
+:root {
+ --vp-home-hero-name-background: -webkit-linear-gradient(315deg, #00adb5 25%, #393e46);
+ --vp-home-hero-name-color: transparent;
+}
+
+:root {
+ --vp-c-indigo-1: #005559;
+ --vp-c-indigo-2: #00797f;
+ --vp-c-indigo-3: #00adb5;
+}
diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js
new file mode 100644
index 00000000..8118ffcf
--- /dev/null
+++ b/docs/.vitepress/theme/index.js
@@ -0,0 +1,27 @@
+import DefaultTheme from "vitepress/theme";
+import Layout from "genji-theme-vitepress";
+import {h} from "vue";
+import {selectAll} from "d3-selection";
+import "d3-transition";
+import * as cm from "../../../src/index.js";
+import "./custom.css";
+
+// More props: https://genji-md.dev/reference/props
+const props = {
+ Theme: DefaultTheme,
+ library: {cm},
+ transform: {
+ module(code) {
+ const newCode = code.replace(`cm.render`, `return cm.render`);
+ const result = `(() => {
+ ${newCode}
+ })()`;
+ return result;
+ },
+ },
+};
+
+export default {
+ extends: DefaultTheme,
+ Layout: () => h(Layout, props),
+};
diff --git a/docs/docs/api-index.md b/docs/docs/api-index.md
new file mode 100644
index 00000000..ab287bb4
--- /dev/null
+++ b/docs/docs/api-index.md
@@ -0,0 +1,115 @@
+# API Reference
+
+- [_cm_.**render**](/docs/api-index#cm-render) - render a SVG element.
+- [_cm_.**renderMark**](/docs/api-index#cm-renderMark) - render a mark.
+- [_cm_.**svg**](/docs/api-index#cm-svg) - create a SVG mark.
+- [_cm_.**html**](/docs/api-index#cm-html) - create a HTML mark.
+- [_cm_.**tag**](/docs/api-index#cm-tag) - create a new mark factory.
+- [_mark_.**with**](/docs/api-index#mark-with) - append children to mark.
+
+## _cm_.render(_options_) {#cm-render}
+
+```js eval t=module
+cm.render({
+ width: 200,
+ height: 100,
+ style_background: "black",
+ marks: [
+ cm.svg("circle", {
+ cx: 100,
+ cy: 50,
+ r: 40,
+ fill: "white",
+ }),
+ ],
+});
+```
+
+## _cm_.renderMark(_mark_) {#cm-renderMark}
+
+```js eval t=module
+cm.renderMark(
+ cm.html("span", {
+ textContent: "Hello Charming.js",
+ style_color: "red",
+ }),
+);
+```
+
+## _cm_.svg(_tag[, data[, options]]_) {#cm-svg}
+
+```js eval t=module
+cm.render({
+ width: 100,
+ height: 50,
+ marks: [
+ cm.svg("circle", [1, 2, 3], {
+ cx: (d) => d * 30,
+ cy: 25,
+ r: 10,
+ }),
+ ],
+});
+```
+
+## _cm_.html(_tag[, data[, options]]_) {#cm-html}
+
+```js eval t=module
+const table = [
+ [11975, 5871, 8916, 2868],
+ [1951, 10048, 2060, 6171],
+ [8010, 16145, 8090, 8045],
+ [1013, 990, 940, 6907],
+];
+
+cm.renderMark(
+ cm.html("table").with([
+ cm.html("tr", table).with([
+ cm.html("td", (row) => row, {
+ textContent: (d) => d,
+ }),
+ ]),
+ ]),
+);
+```
+
+## _cm_.tag(_namespace_) {#cm-tag}
+
+```js eval t=module
+const math = cm.tag("http://www.w3.org/1998/Math/MathML");
+
+cm.renderMark(
+ math("math").with([
+ math("mrow").with([
+ math("mrow").with([
+ math("mi", {textContent: "x"}),
+ math("mo", {textContent: "β"}),
+ math("mn", {textContent: "2"}),
+ ]),
+ math("mo", {textContent: "+"}),
+ math("mi", {textContent: "y"}),
+ ]),
+ ]),
+);
+```
+
+## _mark_.with(_children_) {#mark-with}
+
+```js eval t=module
+const svg = cm.svg;
+
+cm.render({
+ width: 100,
+ height: 50,
+ marks: [
+ svg("g", [1, 2, 3], {
+ transform: (d) => `translate(${d * 30},${25})`,
+ fill: "steelblue",
+ }).with([
+ svg("circle", [1, 2, 3], {
+ r: 10,
+ }),
+ ]),
+ ],
+});
+```
diff --git a/docs/docs/charming-mark.md b/docs/docs/charming-mark.md
new file mode 100644
index 00000000..56fe3191
--- /dev/null
+++ b/docs/docs/charming-mark.md
@@ -0,0 +1,5 @@
+# Charming Mark
+
+## _cm_.**render** {#render}
+
+## _cm_.**svg** {#svg}
diff --git a/docs/docs/charming-transition.md b/docs/docs/charming-transition.md
new file mode 100644
index 00000000..78aee722
--- /dev/null
+++ b/docs/docs/charming-transition.md
@@ -0,0 +1,3 @@
+# Charming Transition
+
+> WIP
diff --git a/docs/docs/charming-vector.md b/docs/docs/charming-vector.md
new file mode 100644
index 00000000..24f77dac
--- /dev/null
+++ b/docs/docs/charming-vector.md
@@ -0,0 +1,5 @@
+# Charming Vector
+
+> WIP
+
+- [Github](https://github.com/charming-art/charming-vector)
diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md
new file mode 100644
index 00000000..63449542
--- /dev/null
+++ b/docs/docs/getting-started.md
@@ -0,0 +1,74 @@
+# Getting Started
+
+```bash
+npm install charmingjs
+```
+
+There are several way to using Charming.
+
+## Try Charming Online
+
+The fastest way to get started with Charming is on [p5.js Web Editor](https://editor.p5js.org/Charming.js/sketches/aR6--CqLp)! Open this sketch and play around. Then go to File > Duplicate to fork it!
+
+
+
+## Installing from Package Manager
+
+Charming is typically installed via a package manager such as Yarn or NPM.
+
+::: code-group
+
+```sh [npm]
+$ npm add -S charmingjs
+```
+
+```sh [pnpm]
+$ pnpm add -S charmingjs
+```
+
+```sh [yarn]
+$ yarn add -S charmingjs
+```
+
+```sh [bun]
+$ bun add -S charmingjs
+```
+
+:::
+
+Charming can then imported as a namespace:
+
+```js
+import * as cm from "charmingjs";
+```
+
+## Imported as an ES module
+
+In vanilla HTML, Charming can be imported as an ES module, say from jsDelivr:
+
+```html
+
+```
+
+## UMD Bundle
+
+Charming is also available as a UMD bundle for legacy browsers.
+
+```html
+
+
+```
diff --git a/docs/docs/what-is-charming.md b/docs/docs/what-is-charming.md
new file mode 100644
index 00000000..66be1f3d
--- /dev/null
+++ b/docs/docs/what-is-charming.md
@@ -0,0 +1,43 @@
+# What is Charming?
+
+**Charming** is a JavaScript library for data-driven generative art that allows artists, designers, educators, and engineers to create expressive, accessible SVG graphics.
+
+Charmingβs API is inspired by data visualization grammar β systems like [AntV G2](https://g2.antv.antgroup.com/), [Observable Plot](https://observablehq.com/plot/) and [Vega-Lite](https://vega.github.io/vega-lite/) β where visuals are built from meaningful, composable units. By combining declarative structure with the power of SVG, Charming encourages a more thoughtful, expressive, inspectable and accessible approach to generative art.
+
+For example, let's first define a function to generate circles recursively:
+
+```js eval inspector=false
+function circles(x, y, r, data = []) {
+ if (r < 16) return;
+ data.push({x, y, r});
+ circles(x - r / 2, y, r * 0.5, data);
+ circles(x + r / 2, y, r * 0.5, data);
+ circles(x, y - r / 2, r * 0.5, data);
+ circles(x, y + r / 2, r * 0.5, data);
+ return data;
+}
+```
+
+Then call the function to generate circles:
+
+```js eval
+const data = circles(240, 240, 200);
+```
+
+Finally draw or visualize the data with Charming's declarative API:
+
+```js eval
+cm.render({
+ width: 480,
+ height: 480,
+ marks: [
+ cm.svg("circle", data, {
+ cx: (d) => d.x,
+ cy: (d) => d.y,
+ r: (d) => d.r,
+ stroke: "black",
+ fill: "transparent",
+ }),
+ ],
+});
+```
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..18ff9cb6
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,26 @@
+---
+layout: home
+
+hero:
+ name: Charming
+ text: A JavaScript library for data-driven generative art
+ tagline: Create expressive, accessible SVG graphics with declarative code and creative flexibility
+ image:
+ src: /logo.svg
+ alt: Charming
+ actions:
+ - theme: brand
+ text: Get Started
+ link: /docs/getting-started
+ - theme: alt
+ text: What is Charming
+ link: /docs/what-is-charming
+
+features:
+ - title: Declarative API
+ details: Inspired by visualization grammar, such as AntV G2, Vega and Observable Plot, where visuals are built from meaningful, composable units.
+ - title: Native SVG Power
+ details: "Leverage the full potential of SVG: crisp rendering, accessible structure, and smooth integration with design tools."
+ - title: Lightweight and Modular
+ details: Charming is a collection of lightweight tools focused on modularity, which can be used independently or work with D3 and p5.js.
+---
diff --git a/docs/public/logo.png b/docs/public/logo.png
new file mode 100644
index 00000000..6d3d8064
Binary files /dev/null and b/docs/public/logo.png differ
diff --git a/docs/public/logo.svg b/docs/public/logo.svg
new file mode 100644
index 00000000..b892ad03
--- /dev/null
+++ b/docs/public/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/public/p5.js-editor-example.png b/docs/public/p5.js-editor-example.png
new file mode 100644
index 00000000..b3870ae5
Binary files /dev/null and b/docs/public/p5.js-editor-example.png differ
diff --git a/img/cm-canvas.png b/img/cm-canvas.png
deleted file mode 100644
index 9d8296f2..00000000
Binary files a/img/cm-canvas.png and /dev/null differ
diff --git a/img/cm-fontGhost.png b/img/cm-fontGhost.png
deleted file mode 100644
index c76ff31d..00000000
Binary files a/img/cm-fontGhost.png and /dev/null differ
diff --git a/img/cm-fontStandard.png b/img/cm-fontStandard.png
deleted file mode 100644
index b7202375..00000000
Binary files a/img/cm-fontStandard.png and /dev/null differ
diff --git a/img/cm-gradientRainBowX.png b/img/cm-gradientRainBowX.png
deleted file mode 100644
index ea46dc38..00000000
Binary files a/img/cm-gradientRainBowX.png and /dev/null differ
diff --git a/img/cm-gradientSineBowX.png b/img/cm-gradientSineBowX.png
deleted file mode 100644
index 88c74fd3..00000000
Binary files a/img/cm-gradientSineBowX.png and /dev/null differ
diff --git a/img/cm-random.png b/img/cm-random.png
deleted file mode 100644
index 06d7c171..00000000
Binary files a/img/cm-random.png and /dev/null differ
diff --git a/img/cm-randomNoise.png b/img/cm-randomNoise.png
deleted file mode 100644
index 7734cdfa..00000000
Binary files a/img/cm-randomNoise.png and /dev/null differ
diff --git a/img/cm-randomNormal.png b/img/cm-randomNormal.png
deleted file mode 100644
index af3127ea..00000000
Binary files a/img/cm-randomNormal.png and /dev/null differ
diff --git a/img/cm-terminal-shape.png b/img/cm-terminal-shape.png
deleted file mode 100644
index aed6103b..00000000
Binary files a/img/cm-terminal-shape.png and /dev/null differ
diff --git a/img/cm-terminal-text.png b/img/cm-terminal-text.png
deleted file mode 100644
index b7202375..00000000
Binary files a/img/cm-terminal-text.png and /dev/null differ
diff --git a/img/example-ascii-text.png b/img/example-ascii-text.png
deleted file mode 100644
index 3ad170fe..00000000
Binary files a/img/example-ascii-text.png and /dev/null differ
diff --git a/img/example-bar-chart.png b/img/example-bar-chart.png
deleted file mode 100644
index cac15b0c..00000000
Binary files a/img/example-bar-chart.png and /dev/null differ
diff --git a/img/example-clover.png b/img/example-clover.png
deleted file mode 100644
index 221a0301..00000000
Binary files a/img/example-clover.png and /dev/null differ
diff --git a/img/example-noise.png b/img/example-noise.png
deleted file mode 100644
index aed3501f..00000000
Binary files a/img/example-noise.png and /dev/null differ
diff --git a/img/example.png b/img/example.png
new file mode 100644
index 00000000..79373f62
Binary files /dev/null and b/img/example.png differ
diff --git a/img/examples.png b/img/examples.png
index 72c30f0a..2c3e8531 100644
Binary files a/img/examples.png and b/img/examples.png differ
diff --git a/img/logo.svg b/img/logo.svg
new file mode 100644
index 00000000..b892ad03
--- /dev/null
+++ b/img/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/index.html b/index.html
deleted file mode 100644
index 1d250af1..00000000
--- a/index.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
- Preview
-
-
-
-
-
-
diff --git a/package.json b/package.json
index 5525de95..fbf7f576 100644
--- a/package.json
+++ b/package.json
@@ -1,71 +1,52 @@
{
- "name": "@charming-art/charming",
- "version": "0.0.9",
- "description": "The declarative language for computational art with high performance.",
+ "name": "charmingjs",
+ "version": "0.0.18",
+ "description": "The JavaScript library for generative art based on SVG",
"author": {
"name": "Bairui SU",
- "url": "https://github.com/pearmini"
+ "url": "https://charmingjs.org/"
},
"license": "ISC",
"type": "module",
- "main": "dist/es/index.js",
- "module": "dist/es/index.js",
- "jsdelivr": "dist/cm.umd.min.js",
- "unpkg": "dist/cm.umd.min.js",
- "exports": {
- "./umd": "./dist/cm.umd.min.js"
- },
+ "main": "src/index.js",
+ "module": "src/index.js",
+ "jsdelivr": "dist/charming.umd.min.js",
+ "unpkg": "dist/charming.umd.min.js",
"repository": {
"type": "git",
- "url": "git+https://github.com/charming-art/charming.git"
+ "url": "https://github.com/charming-art/charming.git"
},
"files": [
- "dist/**/*.wasm",
- "dist/**/*.js"
+ "dist",
+ "src"
],
"scripts": {
- "dev": "npm run build:rust && vite dev",
- "test": "npm run test:lint && npm run test:format && npm run test:rust && npm run test:js",
+ "dev": "vite",
+ "test:update": "vitest --update",
+ "test": "npm run test:js && npm run test:lint && npm run test:format",
"test:js": "vitest",
- "test:rust": "cargo test --manifest-path ./rust/Cargo.toml",
"test:lint": "eslint src test",
- "test:format": "prettier --check src test && cargo fmt --manifest-path ./rust/Cargo.toml --check",
- "build": "npm run build:rust && npm run build:js",
- "build:js": "rm -rf dist && rollup -c",
- "build:rust": "rm -rf ./src/backend && wasm-pack build ./rust --out-dir ../src/backend --out-name index --target web",
- "preview": "npm run build && vite preview",
- "prepublishOnly": "npm run build"
+ "test:format": "prettier --check src test",
+ "docs:dev": "vitepress dev docs",
+ "docs:build": "vitepress build docs",
+ "docs:preview": "vitepress preview docs",
+ "prepublishOnly": "rm -rf dist && rollup -c"
},
- "sideEffect": false,
"devDependencies": {
- "@rollup/plugin-commonjs": "^25.0.7",
- "@rollup/plugin-node-resolve": "^15.2.3",
- "@rollup/plugin-terser": "^0.4.3",
- "@rollup/plugin-wasm": "^6.1.3",
- "d3-geo": "^3.1.0",
- "eslint": "^8.55.0",
+ "@rollup/plugin-node-resolve": "^15.3.1",
+ "@rollup/plugin-terser": "^0.4.4",
+ "d3-selection": "^3.0.0",
+ "d3-transition": "^3.0.1",
+ "eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
- "matter-js": "^0.19.0",
- "pixelmatch": "^5.3.0",
- "playwright": "^1.41.2",
- "pngjs": "^7.0.0",
- "prettier": "^3.1.0",
- "rollup": "^3.29.4",
- "rollup-plugin-polyfill-node": "^0.12.0",
- "stats.js": "^0.17.0",
- "toxiclibsjs": "^0.3.3",
- "vite": "^4.4.9",
- "vitest": "^0.34.6"
- },
- "dependencies": {
- "d3-color": "^3.1.0",
- "d3-path": "^3.1.0",
- "d3-scale-chromatic": "^3.0.0",
- "d3-timer": "^3.0.1",
- "figlet": "^1.6.0"
- },
- "engines": {
- "node": ">=14.18"
+ "genji-theme-vitepress": "^0.2.8",
+ "js-beautify": "^1.15.4",
+ "jsdom": "^26.1.0",
+ "prettier": "^3.5.3",
+ "rollup": "^4.43.0",
+ "vite": "^6.3.5",
+ "vitepress": "^1.6.3",
+ "vitest": "^3.2.3"
},
"publishConfig": {
"access": "public"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 00000000..63610364
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,3930 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ '@rollup/plugin-node-resolve':
+ specifier: ^15.3.1
+ version: 15.3.1(rollup@4.43.0)
+ '@rollup/plugin-terser':
+ specifier: ^0.4.4
+ version: 0.4.4(rollup@4.43.0)
+ d3-selection:
+ specifier: ^3.0.0
+ version: 3.0.0
+ d3-transition:
+ specifier: ^3.0.1
+ version: 3.0.1(d3-selection@3.0.0)
+ eslint:
+ specifier: ^8.57.1
+ version: 8.57.1
+ eslint-config-prettier:
+ specifier: ^9.1.0
+ version: 9.1.0(eslint@8.57.1)
+ genji-theme-vitepress:
+ specifier: ^0.2.8
+ version: 0.2.8(vitepress@1.6.3(@algolia/client-search@5.27.0)(postcss@8.5.5)(search-insights@2.17.2)(terser@5.42.0)(typescript@5.8.3))(vue@3.5.16(typescript@5.8.3))
+ js-beautify:
+ specifier: ^1.15.4
+ version: 1.15.4
+ jsdom:
+ specifier: ^26.1.0
+ version: 26.1.0
+ prettier:
+ specifier: ^3.5.3
+ version: 3.5.3
+ rollup:
+ specifier: ^4.43.0
+ version: 4.43.0
+ vite:
+ specifier: ^6.3.5
+ version: 6.3.5(terser@5.42.0)(yaml@2.8.0)
+ vitepress:
+ specifier: ^1.6.3
+ version: 1.6.3(@algolia/client-search@5.27.0)(postcss@8.5.5)(search-insights@2.17.2)(terser@5.42.0)(typescript@5.8.3)
+ vitest:
+ specifier: ^3.2.3
+ version: 3.2.3(@types/debug@4.1.12)(jsdom@26.1.0)(terser@5.42.0)(yaml@2.8.0)
+
+packages:
+
+ '@algolia/autocomplete-core@1.17.7':
+ resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==}
+
+ '@algolia/autocomplete-plugin-algolia-insights@1.17.7':
+ resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==}
+ peerDependencies:
+ search-insights: '>= 1 < 3'
+
+ '@algolia/autocomplete-preset-algolia@1.17.7':
+ resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+
+ '@algolia/autocomplete-shared@1.17.7':
+ resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+
+ '@algolia/client-abtesting@5.27.0':
+ resolution: {integrity: sha512-SITU5umoknxETtw67TxJu9njyMkWiH8pM+Bvw4dzfuIrIAT6Y1rmwV4y0A0didWoT+6xVuammIykbtBMolBcmg==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-analytics@5.27.0':
+ resolution: {integrity: sha512-go1b9qIZK5vYEQ7jD2bsfhhhVsoh9cFxQ5xF8TzTsg2WOCZR3O92oXCkq15SOK0ngJfqDU6a/k0oZ4KuEnih1Q==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-common@5.27.0':
+ resolution: {integrity: sha512-tnFOzdNuMzsz93kOClj3fKfuYoF3oYaEB5bggULSj075GJ7HUNedBEm7a6ScrjtnOaOtipbnT7veUpHA4o4wEQ==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-insights@5.27.0':
+ resolution: {integrity: sha512-y1qgw39qZijjQBXrqZTiwK1cWgWGRiLpJNWBv9w36nVMKfl9kInrfsYmdBAfmlhVgF/+Woe0y1jQ7pa4HyShAw==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-personalization@5.27.0':
+ resolution: {integrity: sha512-XluG9qPZKEbiLoIfXTKbABsWDNOMPx0t6T2ImJTTeuX+U/zBdmfcqqgcgkqXp+vbXof/XX/4of9Eqo1JaqEmKw==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-query-suggestions@5.27.0':
+ resolution: {integrity: sha512-V8/To+SsAl2sdw2AAjeLJuCW1L+xpz+LAGerJK7HKqHzE5yQhWmIWZTzqYQcojkii4iBMYn0y3+uReWqT8XVSQ==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-search@5.27.0':
+ resolution: {integrity: sha512-EJJ7WmvmUXZdchueKFCK8UZFyLqy4Hz64snNp0cTc7c0MKaSeDGYEDxVsIJKp15r7ORaoGxSyS4y6BGZMXYuCg==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/ingestion@1.27.0':
+ resolution: {integrity: sha512-xNCyWeqpmEo4EdmpG57Fs1fJIQcPwt5NnJ6MBdXnUdMVXF4f5PHgza+HQWQQcYpCsune96jfmR0v7us6gRIlCw==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/monitoring@1.27.0':
+ resolution: {integrity: sha512-P0NDiEFyt9UYQLBI0IQocIT7xHpjMpoFN3UDeerbztlkH9HdqT0GGh1SHYmNWpbMWIGWhSJTtz6kSIWvFu4+pw==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/recommend@5.27.0':
+ resolution: {integrity: sha512-cqfTMF1d1cc7hg0vITNAFxJZas7MJ4Obc36WwkKpY23NOtGb+4tH9X7UKlQa2PmTgbXIANoJ/DAQTeiVlD2I4Q==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/requester-browser-xhr@5.27.0':
+ resolution: {integrity: sha512-ErenYTcXl16wYXtf0pxLl9KLVxIztuehqXHfW9nNsD8mz9OX42HbXuPzT7y6JcPiWJpc/UU/LY5wBTB65vsEUg==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/requester-fetch@5.27.0':
+ resolution: {integrity: sha512-CNOvmXsVi+IvT7z1d+6X7FveVkgEQwTNgipjQCHTIbF9KSMfZR7tUsJC+NpELrm10ALdOMauah84ybs9rw1cKQ==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/requester-node-http@5.27.0':
+ resolution: {integrity: sha512-Nx9EdLYZDsaYFTthqmc0XcVvsx6jqeEX8fNiYOB5i2HboQwl8pJPj1jFhGqoGd0KG7KFR+sdPO5/e0EDDAru2Q==}
+ engines: {node: '>= 14.0.0'}
+
+ '@asamuzakjp/css-color@3.2.0':
+ resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.27.5':
+ resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.27.6':
+ resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@csstools/color-helpers@5.0.2':
+ resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.4':
+ resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-color-parser@3.0.10':
+ resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5':
+ resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-tokenizer@3.0.4':
+ resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
+ engines: {node: '>=18'}
+
+ '@docsearch/css@3.8.2':
+ resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==}
+
+ '@docsearch/js@3.8.2':
+ resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==}
+
+ '@docsearch/react@3.8.2':
+ resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==}
+ peerDependencies:
+ '@types/react': '>= 16.8.0 < 19.0.0'
+ react: '>= 16.8.0 < 19.0.0'
+ react-dom: '>= 16.8.0 < 19.0.0'
+ search-insights: '>= 1 < 3'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ search-insights:
+ optional: true
+
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.25.5':
+ resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.25.5':
+ resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.5':
+ resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.5':
+ resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.25.5':
+ resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.5':
+ resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.25.5':
+ resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.5':
+ resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.25.5':
+ resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.5':
+ resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.5':
+ resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.5':
+ resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.5':
+ resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.5':
+ resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.5':
+ resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.5':
+ resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.5':
+ resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.5':
+ resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.5':
+ resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.5':
+ resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.5':
+ resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.25.5':
+ resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.25.5':
+ resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.5':
+ resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.5':
+ resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.7.0':
+ resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/eslintrc@2.1.4':
+ resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ '@eslint/js@8.57.1':
+ resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ '@humanwhocodes/config-array@0.13.0':
+ resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
+ engines: {node: '>=10.10.0'}
+ deprecated: Use @eslint/config-array instead
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/object-schema@2.0.3':
+ resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
+ deprecated: Use @eslint/object-schema instead
+
+ '@iconify-json/simple-icons@1.2.38':
+ resolution: {integrity: sha512-mvMeFQgVjoHanQE9Q7ihmriEXAorjLZW+crUgQspDjFpzWuQp2RZMTppl1MN6TQztMVTsNFgF6LDKsp+v1RYRg==}
+
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@jridgewell/gen-mapping@0.3.8':
+ resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/source-map@0.3.6':
+ resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@observablehq/inputs@0.10.6':
+ resolution: {integrity: sha512-fOcpJvyBwPqr9I1QdW55J5x36nxRbfyqRQXVT3li9AvMpy6m14WPo5K0m4cPCxr4IlLIDtM/lq6z1GL3ElA14g==}
+ engines: {node: '>=14.5.0'}
+
+ '@observablehq/inspector@5.0.1':
+ resolution: {integrity: sha512-euwWxwDa6KccU4G3D2JBD7GI/2McJh/z7HHEzJKbj2TDa7zhI37eTbTxiwE9rgTWBagvVBel+hAmnJRYBYOv2Q==}
+
+ '@one-ini/wasm@0.1.1':
+ resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@rollup/plugin-node-resolve@15.3.1':
+ resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^2.78.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/plugin-terser@0.4.4':
+ resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/pluginutils@5.1.4':
+ resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.43.0':
+ resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.43.0':
+ resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.43.0':
+ resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.43.0':
+ resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.43.0':
+ resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.43.0':
+ resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.43.0':
+ resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.43.0':
+ resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.43.0':
+ resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.43.0':
+ resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.43.0':
+ resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.43.0':
+ resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.43.0':
+ resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.43.0':
+ resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.43.0':
+ resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.43.0':
+ resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.43.0':
+ resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-win32-arm64-msvc@4.43.0':
+ resolution: {integrity: sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.43.0':
+ resolution: {integrity: sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.43.0':
+ resolution: {integrity: sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@shikijs/core@2.5.0':
+ resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==}
+
+ '@shikijs/engine-javascript@2.5.0':
+ resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==}
+
+ '@shikijs/engine-oniguruma@2.5.0':
+ resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==}
+
+ '@shikijs/langs@2.5.0':
+ resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==}
+
+ '@shikijs/themes@2.5.0':
+ resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==}
+
+ '@shikijs/transformers@2.5.0':
+ resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==}
+
+ '@shikijs/types@2.5.0':
+ resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==}
+
+ '@shikijs/vscode-textmate@10.0.2':
+ resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
+ '@types/chai@5.2.2':
+ resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
+
+ '@types/debug@4.1.12':
+ resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+ '@types/estree@1.0.7':
+ resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/hast@3.0.4':
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
+ '@types/linkify-it@5.0.0':
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+
+ '@types/markdown-it@14.1.2':
+ resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/mdurl@2.0.0':
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/resolve@1.20.2':
+ resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/web-bluetooth@0.0.21':
+ resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+
+ '@vitejs/plugin-vue@5.2.4':
+ resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0
+ vue: ^3.2.25
+
+ '@vitest/expect@3.2.3':
+ resolution: {integrity: sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==}
+
+ '@vitest/mocker@3.2.3':
+ resolution: {integrity: sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@3.2.3':
+ resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==}
+
+ '@vitest/runner@3.2.3':
+ resolution: {integrity: sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==}
+
+ '@vitest/snapshot@3.2.3':
+ resolution: {integrity: sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==}
+
+ '@vitest/spy@3.2.3':
+ resolution: {integrity: sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==}
+
+ '@vitest/utils@3.2.3':
+ resolution: {integrity: sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==}
+
+ '@vue/compiler-core@3.5.16':
+ resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==}
+
+ '@vue/compiler-dom@3.5.16':
+ resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==}
+
+ '@vue/compiler-sfc@3.5.16':
+ resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==}
+
+ '@vue/compiler-ssr@3.5.16':
+ resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==}
+
+ '@vue/devtools-api@7.7.6':
+ resolution: {integrity: sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==}
+
+ '@vue/devtools-kit@7.7.6':
+ resolution: {integrity: sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==}
+
+ '@vue/devtools-shared@7.7.6':
+ resolution: {integrity: sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==}
+
+ '@vue/reactivity@3.5.16':
+ resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==}
+
+ '@vue/runtime-core@3.5.16':
+ resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==}
+
+ '@vue/runtime-dom@3.5.16':
+ resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==}
+
+ '@vue/server-renderer@3.5.16':
+ resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==}
+ peerDependencies:
+ vue: 3.5.16
+
+ '@vue/shared@3.5.16':
+ resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==}
+
+ '@vueuse/core@12.8.2':
+ resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
+
+ '@vueuse/integrations@12.8.2':
+ resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==}
+ peerDependencies:
+ async-validator: ^4
+ axios: ^1
+ change-case: ^5
+ drauu: ^0.4
+ focus-trap: ^7
+ fuse.js: ^7
+ idb-keyval: ^6
+ jwt-decode: ^4
+ nprogress: ^0.2
+ qrcode: ^1.5
+ sortablejs: ^1
+ universal-cookie: ^7
+ peerDependenciesMeta:
+ async-validator:
+ optional: true
+ axios:
+ optional: true
+ change-case:
+ optional: true
+ drauu:
+ optional: true
+ focus-trap:
+ optional: true
+ fuse.js:
+ optional: true
+ idb-keyval:
+ optional: true
+ jwt-decode:
+ optional: true
+ nprogress:
+ optional: true
+ qrcode:
+ optional: true
+ sortablejs:
+ optional: true
+ universal-cookie:
+ optional: true
+
+ '@vueuse/metadata@12.8.2':
+ resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
+
+ '@vueuse/shared@12.8.2':
+ resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
+
+ abbrev@2.0.0:
+ resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@7.1.3:
+ resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+ engines: {node: '>= 14'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ algoliasearch@5.27.0:
+ resolution: {integrity: sha512-2PvAgvxxJzA3+dB+ERfS2JPdvUsxNf89Cc2GF5iCcFupTULOwmbfinvqrC4Qj9nHJJDNf494NqEN/1f9177ZTQ==}
+ engines: {node: '>= 14.0.0'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ birpc@2.3.0:
+ resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ chai@5.2.0:
+ resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
+ engines: {node: '>=12'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+ character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ commander@10.0.1:
+ resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+ engines: {node: '>=14'}
+
+ commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ config-chain@1.1.13:
+ resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
+ copy-anything@3.0.5:
+ resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+ engines: {node: '>=12.13'}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ cssstyle@4.4.0:
+ resolution: {integrity: sha512-W0Y2HOXlPkb2yaKrCVRjinYKciu/qSLEmK0K9mcfDei3zwlnHFEHAs/Du3cIRwPqY+J4JsiBzUjoHyc8RsJ03A==}
+ engines: {node: '>=18'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ d3-transition@3.0.1:
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
+ debug@4.4.1:
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js@10.5.0:
+ resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
+
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ doctrine@3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ editorconfig@1.0.4:
+ resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ emoji-regex-xs@1.0.0:
+ resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ es-module-lexer@1.7.0:
+ resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.25.5:
+ resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-prettier@9.1.0:
+ resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+
+ eslint-scope@7.2.2:
+ resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint@8.57.1:
+ resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
+ hasBin: true
+
+ espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ expect-type@1.2.1:
+ resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
+ engines: {node: '>=12.0.0'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ fdir@6.4.6:
+ resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@6.0.1:
+ resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@3.2.0:
+ resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ focus-trap@7.6.5:
+ resolution: {integrity: sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==}
+
+ foreground-child@3.3.1:
+ resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+ engines: {node: '>=14'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ genji-runtime@0.2.8:
+ resolution: {integrity: sha512-PQtQGtDOnY9kkUw6WxfYKinly6VqTP4KwYp1LDeSCuskAI1Monl4S5OtkftsoPJiCHvKxWYmTazeq3MZ+vEssg==}
+
+ genji-theme-vitepress@0.2.8:
+ resolution: {integrity: sha512-+O44QFhhPB92o1aSMsHwZkGqT+J+nIsUU9U/+WDN+qyrbTqcZi3RqR5J6qW4s4uH2V1sEONsn95/gjCQhBmmJw==}
+ peerDependencies:
+ vitepress: '>=1.0.0'
+ vue: '>=3.0.0'
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ globals@13.24.0:
+ resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+ engines: {node: '>=8'}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ hast-util-to-html@9.0.5:
+ resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+ htl@0.3.1:
+ resolution: {integrity: sha512-1LBtd+XhSc+++jpOOt0lCcEycXs/zTQSupOISnVAUmvGBpV7DH+C2M6hwV7zWYfpTMMg9ch4NO0lHiOTAMHdVA==}
+ engines: {node: '>=12'}
+
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-module@1.0.0:
+ resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
+
+ is-path-inside@3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ is-what@4.1.16:
+ resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+ engines: {node: '>=12.13'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ isoformat@0.2.1:
+ resolution: {integrity: sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ js-beautify@1.15.4:
+ resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ js-cookie@3.0.5:
+ resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+ engines: {node: '>=14'}
+
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsdom@26.1.0:
+ resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ loupe@3.1.3:
+ resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+ mark.js@8.11.1:
+ resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
+
+ mdast-util-to-hast@13.2.0:
+ resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.1:
+ resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minisearch@7.1.2:
+ resolution: {integrity: sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==}
+
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ nopt@7.2.1:
+ resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ hasBin: true
+
+ nwsapi@2.2.20:
+ resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ oniguruma-to-es@3.1.1:
+ resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse5@7.3.0:
+ resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
+
+ perfect-debounce@1.0.0:
+ resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.5:
+ resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ preact@10.26.9:
+ resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier@3.5.3:
+ resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ property-information@7.1.0:
+ resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
+
+ proto-list@1.2.4:
+ resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+
+ regex-recursion@6.0.2:
+ resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
+
+ regex-utilities@2.3.0:
+ resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+
+ regex@6.0.1:
+ resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve@1.22.10:
+ resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
+ rollup@4.43.0:
+ resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ rrweb-cssom@0.8.0:
+ resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ search-insights@2.17.2:
+ resolution: {integrity: sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==}
+
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ serialize-javascript@6.0.2:
+ resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shiki@2.5.0:
+ resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==}
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ smob@1.5.0:
+ resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.9.0:
+ resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ stringify-entities@4.0.4:
+ resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ strip-literal@3.0.0:
+ resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
+
+ superjson@2.2.2:
+ resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
+ engines: {node: '>=16'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ tabbable@6.2.0:
+ resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+
+ terser@5.42.0:
+ resolution: {integrity: sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ text-table@0.2.0:
+ resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
+ tinyglobby@0.2.14:
+ resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+ engines: {node: '>=12.0.0'}
+
+ tinypool@1.1.0:
+ resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@2.0.0:
+ resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@4.0.3:
+ resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@6.1.86:
+ resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
+
+ tldts@6.1.86:
+ resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
+ hasBin: true
+
+ tough-cookie@5.1.2:
+ resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
+ engines: {node: '>=16'}
+
+ tr46@5.1.1:
+ resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
+ engines: {node: '>=18'}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ type-fest@0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+
+ typescript@5.8.3:
+ resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ unist-util-is@6.0.0:
+ resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+
+ unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.1:
+ resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+
+ unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ vfile-message@4.0.2:
+ resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite-node@3.2.3:
+ resolution: {integrity: sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+
+ vite@5.4.19:
+ resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ vite@6.3.5:
+ resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitepress@1.6.3:
+ resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==}
+ hasBin: true
+ peerDependencies:
+ markdown-it-mathjax3: ^4
+ postcss: ^8
+ peerDependenciesMeta:
+ markdown-it-mathjax3:
+ optional: true
+ postcss:
+ optional: true
+
+ vitest@3.2.3:
+ resolution: {integrity: sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/debug': ^4.1.12
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@vitest/browser': 3.2.3
+ '@vitest/ui': 3.2.3
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/debug':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ vue@3.5.16:
+ resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@14.2.0:
+ resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
+ engines: {node: '>=18'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ ws@8.18.2:
+ resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yaml@2.8.0:
+ resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)(search-insights@2.17.2)':
+ dependencies:
+ '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)(search-insights@2.17.2)
+ '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+ - search-insights
+
+ '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)(search-insights@2.17.2)':
+ dependencies:
+ '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)
+ search-insights: 2.17.2
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+
+ '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)':
+ dependencies:
+ '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)
+ '@algolia/client-search': 5.27.0
+ algoliasearch: 5.27.0
+
+ '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)':
+ dependencies:
+ '@algolia/client-search': 5.27.0
+ algoliasearch: 5.27.0
+
+ '@algolia/client-abtesting@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/client-analytics@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/client-common@5.27.0': {}
+
+ '@algolia/client-insights@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/client-personalization@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/client-query-suggestions@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/client-search@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/ingestion@1.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/monitoring@1.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/recommend@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ '@algolia/requester-browser-xhr@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+
+ '@algolia/requester-fetch@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+
+ '@algolia/requester-node-http@5.27.0':
+ dependencies:
+ '@algolia/client-common': 5.27.0
+
+ '@asamuzakjp/css-color@3.2.0':
+ dependencies:
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ lru-cache: 10.4.3
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/parser@7.27.5':
+ dependencies:
+ '@babel/types': 7.27.6
+
+ '@babel/types@7.27.6':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@csstools/color-helpers@5.0.2': {}
+
+ '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/color-helpers': 5.0.2
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-tokenizer@3.0.4': {}
+
+ '@docsearch/css@3.8.2': {}
+
+ '@docsearch/js@3.8.2(@algolia/client-search@5.27.0)(search-insights@2.17.2)':
+ dependencies:
+ '@docsearch/react': 3.8.2(@algolia/client-search@5.27.0)(search-insights@2.17.2)
+ preact: 10.26.9
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/react'
+ - react
+ - react-dom
+ - search-insights
+
+ '@docsearch/react@3.8.2(@algolia/client-search@5.27.0)(search-insights@2.17.2)':
+ dependencies:
+ '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)(search-insights@2.17.2)
+ '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.27.0)(algoliasearch@5.27.0)
+ '@docsearch/css': 3.8.2
+ algoliasearch: 5.27.0
+ optionalDependencies:
+ search-insights: 2.17.2
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.25.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm@0.25.5':
+ optional: true
+
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
+ '@esbuild/android-x64@0.25.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.5':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.5':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.5':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)':
+ dependencies:
+ eslint: 8.57.1
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/eslintrc@2.1.4':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.1
+ espree: 9.6.1
+ globals: 13.24.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@8.57.1': {}
+
+ '@humanwhocodes/config-array@0.13.0':
+ dependencies:
+ '@humanwhocodes/object-schema': 2.0.3
+ debug: 4.4.1
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/object-schema@2.0.3': {}
+
+ '@iconify-json/simple-icons@1.2.38':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify/types@2.0.0': {}
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@jridgewell/gen-mapping@0.3.8':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/source-map@0.3.6':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@observablehq/inputs@0.10.6':
+ dependencies:
+ htl: 0.3.1
+ isoformat: 0.2.1
+
+ '@observablehq/inspector@5.0.1':
+ dependencies:
+ isoformat: 0.2.1
+
+ '@one-ini/wasm@0.1.1': {}
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@rollup/plugin-node-resolve@15.3.1(rollup@4.43.0)':
+ dependencies:
+ '@rollup/pluginutils': 5.1.4(rollup@4.43.0)
+ '@types/resolve': 1.20.2
+ deepmerge: 4.3.1
+ is-module: 1.0.0
+ resolve: 1.22.10
+ optionalDependencies:
+ rollup: 4.43.0
+
+ '@rollup/plugin-terser@0.4.4(rollup@4.43.0)':
+ dependencies:
+ serialize-javascript: 6.0.2
+ smob: 1.5.0
+ terser: 5.42.0
+ optionalDependencies:
+ rollup: 4.43.0
+
+ '@rollup/pluginutils@5.1.4(rollup@4.43.0)':
+ dependencies:
+ '@types/estree': 1.0.8
+ estree-walker: 2.0.2
+ picomatch: 4.0.2
+ optionalDependencies:
+ rollup: 4.43.0
+
+ '@rollup/rollup-android-arm-eabi@4.43.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.43.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.43.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.43.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.43.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.43.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.43.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.43.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.43.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.43.0':
+ optional: true
+
+ '@shikijs/core@2.5.0':
+ dependencies:
+ '@shikijs/engine-javascript': 2.5.0
+ '@shikijs/engine-oniguruma': 2.5.0
+ '@shikijs/types': 2.5.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/engine-javascript@2.5.0':
+ dependencies:
+ '@shikijs/types': 2.5.0
+ '@shikijs/vscode-textmate': 10.0.2
+ oniguruma-to-es: 3.1.1
+
+ '@shikijs/engine-oniguruma@2.5.0':
+ dependencies:
+ '@shikijs/types': 2.5.0
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/langs@2.5.0':
+ dependencies:
+ '@shikijs/types': 2.5.0
+
+ '@shikijs/themes@2.5.0':
+ dependencies:
+ '@shikijs/types': 2.5.0
+
+ '@shikijs/transformers@2.5.0':
+ dependencies:
+ '@shikijs/core': 2.5.0
+ '@shikijs/types': 2.5.0
+
+ '@shikijs/types@2.5.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/vscode-textmate@10.0.2': {}
+
+ '@types/chai@5.2.2':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+
+ '@types/debug@4.1.12':
+ dependencies:
+ '@types/ms': 2.1.0
+ optional: true
+
+ '@types/deep-eql@4.0.2': {}
+
+ '@types/estree@1.0.7': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/hast@3.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/linkify-it@5.0.0': {}
+
+ '@types/markdown-it@14.1.2':
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/mdurl@2.0.0': {}
+
+ '@types/ms@2.1.0':
+ optional: true
+
+ '@types/resolve@1.20.2': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@ungap/structured-clone@1.3.0': {}
+
+ '@vitejs/plugin-vue@5.2.4(vite@5.4.19(terser@5.42.0))(vue@3.5.16(typescript@5.8.3))':
+ dependencies:
+ vite: 5.4.19(terser@5.42.0)
+ vue: 3.5.16(typescript@5.8.3)
+
+ '@vitest/expect@3.2.3':
+ dependencies:
+ '@types/chai': 5.2.2
+ '@vitest/spy': 3.2.3
+ '@vitest/utils': 3.2.3
+ chai: 5.2.0
+ tinyrainbow: 2.0.0
+
+ '@vitest/mocker@3.2.3(vite@6.3.5(terser@5.42.0)(yaml@2.8.0))':
+ dependencies:
+ '@vitest/spy': 3.2.3
+ estree-walker: 3.0.3
+ magic-string: 0.30.17
+ optionalDependencies:
+ vite: 6.3.5(terser@5.42.0)(yaml@2.8.0)
+
+ '@vitest/pretty-format@3.2.3':
+ dependencies:
+ tinyrainbow: 2.0.0
+
+ '@vitest/runner@3.2.3':
+ dependencies:
+ '@vitest/utils': 3.2.3
+ pathe: 2.0.3
+ strip-literal: 3.0.0
+
+ '@vitest/snapshot@3.2.3':
+ dependencies:
+ '@vitest/pretty-format': 3.2.3
+ magic-string: 0.30.17
+ pathe: 2.0.3
+
+ '@vitest/spy@3.2.3':
+ dependencies:
+ tinyspy: 4.0.3
+
+ '@vitest/utils@3.2.3':
+ dependencies:
+ '@vitest/pretty-format': 3.2.3
+ loupe: 3.1.3
+ tinyrainbow: 2.0.0
+
+ '@vue/compiler-core@3.5.16':
+ dependencies:
+ '@babel/parser': 7.27.5
+ '@vue/shared': 3.5.16
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.16':
+ dependencies:
+ '@vue/compiler-core': 3.5.16
+ '@vue/shared': 3.5.16
+
+ '@vue/compiler-sfc@3.5.16':
+ dependencies:
+ '@babel/parser': 7.27.5
+ '@vue/compiler-core': 3.5.16
+ '@vue/compiler-dom': 3.5.16
+ '@vue/compiler-ssr': 3.5.16
+ '@vue/shared': 3.5.16
+ estree-walker: 2.0.2
+ magic-string: 0.30.17
+ postcss: 8.5.5
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.16':
+ dependencies:
+ '@vue/compiler-dom': 3.5.16
+ '@vue/shared': 3.5.16
+
+ '@vue/devtools-api@7.7.6':
+ dependencies:
+ '@vue/devtools-kit': 7.7.6
+
+ '@vue/devtools-kit@7.7.6':
+ dependencies:
+ '@vue/devtools-shared': 7.7.6
+ birpc: 2.3.0
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 1.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.2
+
+ '@vue/devtools-shared@7.7.6':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/reactivity@3.5.16':
+ dependencies:
+ '@vue/shared': 3.5.16
+
+ '@vue/runtime-core@3.5.16':
+ dependencies:
+ '@vue/reactivity': 3.5.16
+ '@vue/shared': 3.5.16
+
+ '@vue/runtime-dom@3.5.16':
+ dependencies:
+ '@vue/reactivity': 3.5.16
+ '@vue/runtime-core': 3.5.16
+ '@vue/shared': 3.5.16
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.16
+ '@vue/shared': 3.5.16
+ vue: 3.5.16(typescript@5.8.3)
+
+ '@vue/shared@3.5.16': {}
+
+ '@vueuse/core@12.8.2(typescript@5.8.3)':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 12.8.2
+ '@vueuse/shared': 12.8.2(typescript@5.8.3)
+ vue: 3.5.16(typescript@5.8.3)
+ transitivePeerDependencies:
+ - typescript
+
+ '@vueuse/integrations@12.8.2(focus-trap@7.6.5)(typescript@5.8.3)':
+ dependencies:
+ '@vueuse/core': 12.8.2(typescript@5.8.3)
+ '@vueuse/shared': 12.8.2(typescript@5.8.3)
+ vue: 3.5.16(typescript@5.8.3)
+ optionalDependencies:
+ focus-trap: 7.6.5
+ transitivePeerDependencies:
+ - typescript
+
+ '@vueuse/metadata@12.8.2': {}
+
+ '@vueuse/shared@12.8.2(typescript@5.8.3)':
+ dependencies:
+ vue: 3.5.16(typescript@5.8.3)
+ transitivePeerDependencies:
+ - typescript
+
+ abbrev@2.0.0: {}
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ agent-base@7.1.3: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ algoliasearch@5.27.0:
+ dependencies:
+ '@algolia/client-abtesting': 5.27.0
+ '@algolia/client-analytics': 5.27.0
+ '@algolia/client-common': 5.27.0
+ '@algolia/client-insights': 5.27.0
+ '@algolia/client-personalization': 5.27.0
+ '@algolia/client-query-suggestions': 5.27.0
+ '@algolia/client-search': 5.27.0
+ '@algolia/ingestion': 1.27.0
+ '@algolia/monitoring': 1.27.0
+ '@algolia/recommend': 5.27.0
+ '@algolia/requester-browser-xhr': 5.27.0
+ '@algolia/requester-fetch': 5.27.0
+ '@algolia/requester-node-http': 5.27.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.1.0: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.1: {}
+
+ argparse@2.0.1: {}
+
+ assertion-error@2.0.1: {}
+
+ balanced-match@1.0.2: {}
+
+ birpc@2.3.0: {}
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ buffer-from@1.1.2: {}
+
+ cac@6.7.14: {}
+
+ callsites@3.1.0: {}
+
+ ccount@2.0.1: {}
+
+ chai@5.2.0:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.3
+ pathval: 2.0.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ check-error@2.1.1: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ comma-separated-tokens@2.0.3: {}
+
+ commander@10.0.1: {}
+
+ commander@2.20.3: {}
+
+ concat-map@0.0.1: {}
+
+ config-chain@1.1.13:
+ dependencies:
+ ini: 1.3.8
+ proto-list: 1.2.4
+
+ copy-anything@3.0.5:
+ dependencies:
+ is-what: 4.1.16
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cssstyle@4.4.0:
+ dependencies:
+ '@asamuzakjp/css-color': 3.2.0
+ rrweb-cssom: 0.8.0
+
+ csstype@3.1.3: {}
+
+ d3-color@3.1.0: {}
+
+ d3-dispatch@3.0.1: {}
+
+ d3-ease@3.0.1: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-timer@3.0.1: {}
+
+ d3-transition@3.0.1(d3-selection@3.0.0):
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.2.0
+
+ debug@4.4.1:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js@10.5.0: {}
+
+ deep-eql@5.0.2: {}
+
+ deep-is@0.1.4: {}
+
+ deepmerge@4.3.1: {}
+
+ dequal@2.0.3: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ doctrine@3.0.0:
+ dependencies:
+ esutils: 2.0.3
+
+ eastasianwidth@0.2.0: {}
+
+ editorconfig@1.0.4:
+ dependencies:
+ '@one-ini/wasm': 0.1.1
+ commander: 10.0.1
+ minimatch: 9.0.1
+ semver: 7.7.2
+
+ emoji-regex-xs@1.0.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ entities@4.5.0: {}
+
+ entities@6.0.1: {}
+
+ es-module-lexer@1.7.0: {}
+
+ esbuild@0.21.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+
+ esbuild@0.25.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.5
+ '@esbuild/android-arm': 0.25.5
+ '@esbuild/android-arm64': 0.25.5
+ '@esbuild/android-x64': 0.25.5
+ '@esbuild/darwin-arm64': 0.25.5
+ '@esbuild/darwin-x64': 0.25.5
+ '@esbuild/freebsd-arm64': 0.25.5
+ '@esbuild/freebsd-x64': 0.25.5
+ '@esbuild/linux-arm': 0.25.5
+ '@esbuild/linux-arm64': 0.25.5
+ '@esbuild/linux-ia32': 0.25.5
+ '@esbuild/linux-loong64': 0.25.5
+ '@esbuild/linux-mips64el': 0.25.5
+ '@esbuild/linux-ppc64': 0.25.5
+ '@esbuild/linux-riscv64': 0.25.5
+ '@esbuild/linux-s390x': 0.25.5
+ '@esbuild/linux-x64': 0.25.5
+ '@esbuild/netbsd-arm64': 0.25.5
+ '@esbuild/netbsd-x64': 0.25.5
+ '@esbuild/openbsd-arm64': 0.25.5
+ '@esbuild/openbsd-x64': 0.25.5
+ '@esbuild/sunos-x64': 0.25.5
+ '@esbuild/win32-arm64': 0.25.5
+ '@esbuild/win32-ia32': 0.25.5
+ '@esbuild/win32-x64': 0.25.5
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-prettier@9.1.0(eslint@8.57.1):
+ dependencies:
+ eslint: 8.57.1
+
+ eslint-scope@7.2.2:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint@8.57.1:
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1)
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/eslintrc': 2.1.4
+ '@eslint/js': 8.57.1
+ '@humanwhocodes/config-array': 0.13.0
+ '@humanwhocodes/module-importer': 1.0.1
+ '@nodelib/fs.walk': 1.2.8
+ '@ungap/structured-clone': 1.3.0
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.1
+ doctrine: 3.0.0
+ escape-string-regexp: 4.0.0
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 6.0.1
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ globals: 13.24.0
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ js-yaml: 4.1.0
+ json-stable-stringify-without-jsonify: 1.0.1
+ levn: 0.4.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ strip-ansi: 6.0.1
+ text-table: 0.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@9.6.1:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 3.4.3
+
+ esprima@4.0.1: {}
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ esutils@2.0.3: {}
+
+ expect-type@1.2.1: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fdir@6.4.6(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ file-entry-cache@6.0.1:
+ dependencies:
+ flat-cache: 3.2.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@3.2.0:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+ rimraf: 3.0.2
+
+ flatted@3.3.3: {}
+
+ focus-trap@7.6.5:
+ dependencies:
+ tabbable: 6.2.0
+
+ foreground-child@3.3.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ genji-runtime@0.2.8:
+ dependencies:
+ '@observablehq/inputs': 0.10.6
+ '@observablehq/inspector': 5.0.1
+ esprima: 4.0.1
+ estraverse: 5.3.0
+
+ genji-theme-vitepress@0.2.8(vitepress@1.6.3(@algolia/client-search@5.27.0)(postcss@8.5.5)(search-insights@2.17.2)(terser@5.42.0)(typescript@5.8.3))(vue@3.5.16(typescript@5.8.3)):
+ dependencies:
+ genji-runtime: 0.2.8
+ vitepress: 1.6.3(@algolia/client-search@5.27.0)(postcss@8.5.5)(search-insights@2.17.2)(terser@5.42.0)(typescript@5.8.3)
+ vue: 3.5.16(typescript@5.8.3)
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.4.5:
+ dependencies:
+ foreground-child: 3.3.1
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ globals@13.24.0:
+ dependencies:
+ type-fest: 0.20.2
+
+ graphemer@1.4.0: {}
+
+ has-flag@4.0.0: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ hast-util-to-html@9.0.5:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.0
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
+ hookable@5.5.3: {}
+
+ htl@0.3.1: {}
+
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ html-void-elements@3.0.0: {}
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ignore@5.3.2: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-module@1.0.0: {}
+
+ is-path-inside@3.0.3: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ is-what@4.1.16: {}
+
+ isexe@2.0.0: {}
+
+ isoformat@0.2.1: {}
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ js-beautify@1.15.4:
+ dependencies:
+ config-chain: 1.1.13
+ editorconfig: 1.0.4
+ glob: 10.4.5
+ js-cookie: 3.0.5
+ nopt: 7.2.1
+
+ js-cookie@3.0.5: {}
+
+ js-tokens@9.0.1: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsdom@26.1.0:
+ dependencies:
+ cssstyle: 4.4.0
+ data-urls: 5.0.0
+ decimal.js: 10.5.0
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.20
+ parse5: 7.3.0
+ rrweb-cssom: 0.8.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 5.1.2
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.2.0
+ ws: 8.18.2
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ loupe@3.1.3: {}
+
+ lru-cache@10.4.3: {}
+
+ magic-string@0.30.17:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ mark.js@8.11.1: {}
+
+ mdast-util-to-hast@13.2.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@ungap/structured-clone': 1.3.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.1
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ vfile: 6.0.3
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@9.0.1:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minipass@7.1.2: {}
+
+ minisearch@7.1.2: {}
+
+ mitt@3.0.1: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ natural-compare@1.4.0: {}
+
+ nopt@7.2.1:
+ dependencies:
+ abbrev: 2.0.0
+
+ nwsapi@2.2.20: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ oniguruma-to-es@3.1.1:
+ dependencies:
+ emoji-regex-xs: 1.0.0
+ regex: 6.0.1
+ regex-recursion: 6.0.2
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ package-json-from-dist@1.0.1: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse5@7.3.0:
+ dependencies:
+ entities: 6.0.1
+
+ path-exists@4.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ pathe@2.0.3: {}
+
+ pathval@2.0.0: {}
+
+ perfect-debounce@1.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.2: {}
+
+ postcss@8.5.5:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ preact@10.26.9: {}
+
+ prelude-ls@1.2.1: {}
+
+ prettier@3.5.3: {}
+
+ property-information@7.1.0: {}
+
+ proto-list@1.2.4: {}
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ randombytes@2.1.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ regex-recursion@6.0.2:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex-utilities@2.3.0: {}
+
+ regex@6.0.1:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ resolve-from@4.0.0: {}
+
+ resolve@1.22.10:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.1.0: {}
+
+ rfdc@1.4.1: {}
+
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
+ rollup@4.43.0:
+ dependencies:
+ '@types/estree': 1.0.7
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.43.0
+ '@rollup/rollup-android-arm64': 4.43.0
+ '@rollup/rollup-darwin-arm64': 4.43.0
+ '@rollup/rollup-darwin-x64': 4.43.0
+ '@rollup/rollup-freebsd-arm64': 4.43.0
+ '@rollup/rollup-freebsd-x64': 4.43.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.43.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.43.0
+ '@rollup/rollup-linux-arm64-gnu': 4.43.0
+ '@rollup/rollup-linux-arm64-musl': 4.43.0
+ '@rollup/rollup-linux-loongarch64-gnu': 4.43.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.43.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.43.0
+ '@rollup/rollup-linux-riscv64-musl': 4.43.0
+ '@rollup/rollup-linux-s390x-gnu': 4.43.0
+ '@rollup/rollup-linux-x64-gnu': 4.43.0
+ '@rollup/rollup-linux-x64-musl': 4.43.0
+ '@rollup/rollup-win32-arm64-msvc': 4.43.0
+ '@rollup/rollup-win32-ia32-msvc': 4.43.0
+ '@rollup/rollup-win32-x64-msvc': 4.43.0
+ fsevents: 2.3.3
+
+ rrweb-cssom@0.8.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ search-insights@2.17.2: {}
+
+ semver@7.7.2: {}
+
+ serialize-javascript@6.0.2:
+ dependencies:
+ randombytes: 2.1.0
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shiki@2.5.0:
+ dependencies:
+ '@shikijs/core': 2.5.0
+ '@shikijs/engine-javascript': 2.5.0
+ '@shikijs/engine-oniguruma': 2.5.0
+ '@shikijs/langs': 2.5.0
+ '@shikijs/themes': 2.5.0
+ '@shikijs/types': 2.5.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ siginfo@2.0.0: {}
+
+ signal-exit@4.1.0: {}
+
+ smob@1.5.0: {}
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.6.1: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ speakingurl@14.0.1: {}
+
+ stackback@0.0.2: {}
+
+ std-env@3.9.0: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ stringify-entities@4.0.4:
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.0:
+ dependencies:
+ ansi-regex: 6.1.0
+
+ strip-json-comments@3.1.1: {}
+
+ strip-literal@3.0.0:
+ dependencies:
+ js-tokens: 9.0.1
+
+ superjson@2.2.2:
+ dependencies:
+ copy-anything: 3.0.5
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ symbol-tree@3.2.4: {}
+
+ tabbable@6.2.0: {}
+
+ terser@5.42.0:
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.15.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
+ text-table@0.2.0: {}
+
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.2: {}
+
+ tinyglobby@0.2.14:
+ dependencies:
+ fdir: 6.4.6(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tinypool@1.1.0: {}
+
+ tinyrainbow@2.0.0: {}
+
+ tinyspy@4.0.3: {}
+
+ tldts-core@6.1.86: {}
+
+ tldts@6.1.86:
+ dependencies:
+ tldts-core: 6.1.86
+
+ tough-cookie@5.1.2:
+ dependencies:
+ tldts: 6.1.86
+
+ tr46@5.1.1:
+ dependencies:
+ punycode: 2.3.1
+
+ trim-lines@3.0.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-fest@0.20.2: {}
+
+ typescript@5.8.3:
+ optional: true
+
+ unist-util-is@6.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+
+ unist-util-visit@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ vfile-message@4.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.2
+
+ vite-node@3.2.3(terser@5.42.0)(yaml@2.8.0):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.1
+ es-module-lexer: 1.7.0
+ pathe: 2.0.3
+ vite: 6.3.5(terser@5.42.0)(yaml@2.8.0)
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ vite@5.4.19(terser@5.42.0):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.5.5
+ rollup: 4.43.0
+ optionalDependencies:
+ fsevents: 2.3.3
+ terser: 5.42.0
+
+ vite@6.3.5(terser@5.42.0)(yaml@2.8.0):
+ dependencies:
+ esbuild: 0.25.5
+ fdir: 6.4.6(picomatch@4.0.2)
+ picomatch: 4.0.2
+ postcss: 8.5.5
+ rollup: 4.43.0
+ tinyglobby: 0.2.14
+ optionalDependencies:
+ fsevents: 2.3.3
+ terser: 5.42.0
+ yaml: 2.8.0
+
+ vitepress@1.6.3(@algolia/client-search@5.27.0)(postcss@8.5.5)(search-insights@2.17.2)(terser@5.42.0)(typescript@5.8.3):
+ dependencies:
+ '@docsearch/css': 3.8.2
+ '@docsearch/js': 3.8.2(@algolia/client-search@5.27.0)(search-insights@2.17.2)
+ '@iconify-json/simple-icons': 1.2.38
+ '@shikijs/core': 2.5.0
+ '@shikijs/transformers': 2.5.0
+ '@shikijs/types': 2.5.0
+ '@types/markdown-it': 14.1.2
+ '@vitejs/plugin-vue': 5.2.4(vite@5.4.19(terser@5.42.0))(vue@3.5.16(typescript@5.8.3))
+ '@vue/devtools-api': 7.7.6
+ '@vue/shared': 3.5.16
+ '@vueuse/core': 12.8.2(typescript@5.8.3)
+ '@vueuse/integrations': 12.8.2(focus-trap@7.6.5)(typescript@5.8.3)
+ focus-trap: 7.6.5
+ mark.js: 8.11.1
+ minisearch: 7.1.2
+ shiki: 2.5.0
+ vite: 5.4.19(terser@5.42.0)
+ vue: 3.5.16(typescript@5.8.3)
+ optionalDependencies:
+ postcss: 8.5.5
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/node'
+ - '@types/react'
+ - async-validator
+ - axios
+ - change-case
+ - drauu
+ - fuse.js
+ - idb-keyval
+ - jwt-decode
+ - less
+ - lightningcss
+ - nprogress
+ - qrcode
+ - react
+ - react-dom
+ - sass
+ - sass-embedded
+ - search-insights
+ - sortablejs
+ - stylus
+ - sugarss
+ - terser
+ - typescript
+ - universal-cookie
+
+ vitest@3.2.3(@types/debug@4.1.12)(jsdom@26.1.0)(terser@5.42.0)(yaml@2.8.0):
+ dependencies:
+ '@types/chai': 5.2.2
+ '@vitest/expect': 3.2.3
+ '@vitest/mocker': 3.2.3(vite@6.3.5(terser@5.42.0)(yaml@2.8.0))
+ '@vitest/pretty-format': 3.2.3
+ '@vitest/runner': 3.2.3
+ '@vitest/snapshot': 3.2.3
+ '@vitest/spy': 3.2.3
+ '@vitest/utils': 3.2.3
+ chai: 5.2.0
+ debug: 4.4.1
+ expect-type: 1.2.1
+ magic-string: 0.30.17
+ pathe: 2.0.3
+ picomatch: 4.0.2
+ std-env: 3.9.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.14
+ tinypool: 1.1.0
+ tinyrainbow: 2.0.0
+ vite: 6.3.5(terser@5.42.0)(yaml@2.8.0)
+ vite-node: 3.2.3(terser@5.42.0)(yaml@2.8.0)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/debug': 4.1.12
+ jsdom: 26.1.0
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ vue@3.5.16(typescript@5.8.3):
+ dependencies:
+ '@vue/compiler-dom': 3.5.16
+ '@vue/compiler-sfc': 3.5.16
+ '@vue/runtime-dom': 3.5.16
+ '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.8.3))
+ '@vue/shared': 3.5.16
+ optionalDependencies:
+ typescript: 5.8.3
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@7.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@14.2.0:
+ dependencies:
+ tr46: 5.1.1
+ webidl-conversions: 7.0.0
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ wrappy@1.0.2: {}
+
+ ws@8.18.2: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yaml@2.8.0:
+ optional: true
+
+ yocto-queue@0.1.0: {}
+
+ zwitch@2.0.4: {}
diff --git a/rollup.config.js b/rollup.config.js
index e02ebdd1..3cb96219 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,13 +1,5 @@
import node from "@rollup/plugin-node-resolve";
-import { wasm } from "@rollup/plugin-wasm";
import terser from "@rollup/plugin-terser";
-import commonjs from "@rollup/plugin-commonjs";
-import polyfill from "rollup-plugin-polyfill-node";
-
-const onwarn = (message, warn) => {
- if (message.code === "CIRCULAR_DEPENDENCY") return;
- warn(message);
-};
const umd = {
input: "src/index.js",
@@ -15,60 +7,27 @@ const umd = {
format: "umd",
name: "cm",
},
- plugins: [wasm({ targetEnv: "auto-inline" }), commonjs(), polyfill(), node()],
- onwarn,
-};
-
-const core = {
- input: "src/core.js",
- output: {
- format: "umd",
- name: "cm",
+ plugins: [node()],
+ onwarn(message, warn) {
+ if (message.code === "CIRCULAR_DEPENDENCY") return;
+ warn(message);
},
- plugins: [commonjs(), polyfill(), node()],
- onwarn,
};
export default [
- {
- input: "src/index.js",
- output: {
- format: "es",
- dir: "dist/es",
- preserveModules: true,
- },
- external: [/node_modules/],
- plugins: [wasm({ targetEnv: "auto-inline" }), commonjs(), node()],
- onwarn,
- },
{
...umd,
output: {
...umd.output,
- file: "dist/cm.umd.js",
+ file: "dist/charming.umd.js",
},
},
{
...umd,
output: {
...umd.output,
- file: "dist/cm.umd.min.js",
+ file: "dist/charming.umd.min.js",
},
plugins: [...umd.plugins, terser()],
},
- {
- ...core,
- output: {
- ...umd.output,
- file: "dist/cm.core.umd.js",
- },
- },
- {
- ...core,
- output: {
- ...umd.output,
- file: "dist/cm.core.umd.min.js",
- },
- plugins: [...core.plugins, terser()],
- },
];
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
deleted file mode 100644
index 7d50bd5c..00000000
--- a/rust/Cargo.lock
+++ /dev/null
@@ -1,143 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "bumpalo"
-version = "3.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "js-sys"
-version = "0.3.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "log"
-version = "0.4.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
-
-[[package]]
-name = "once_cell"
-version = "1.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "rust"
-version = "0.1.0"
-dependencies = [
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.87"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
-dependencies = [
- "cfg-if",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.87"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
-dependencies = [
- "bumpalo",
- "log",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.87"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.87"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.87"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
-
-[[package]]
-name = "web-sys"
-version = "0.3.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
deleted file mode 100644
index 17fab849..00000000
--- a/rust/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "rust"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-wasm-bindgen = "0.2"
-
-[dependencies.web-sys]
-version = "0.3.4"
-features = [
- "console",
-]
\ No newline at end of file
diff --git a/rust/src/attributes.rs b/rust/src/attributes.rs
deleted file mode 100644
index f5eb3f60..00000000
--- a/rust/src/attributes.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-extern crate wasm_bindgen;
-use crate::backend::Backend;
-use wasm_bindgen::prelude::*;
-
-#[wasm_bindgen]
-impl Backend {
- pub fn stroke(&mut self, ch: u32, ch1: u32, fg: u32, bg: u32) {
- self.has_stroke = true;
- self.stroke_color = [ch, ch1, fg, bg];
- }
- pub fn fill(&mut self, ch: u32, ch1: u32, fg: u32, bg: u32) {
- self.has_fill = true;
- self.fill_color = [ch, ch1, fg, bg];
- }
- pub fn background(&mut self, ch: u32, ch1: u32, fg: u32, bg: u32) {
- self.has_background = true;
- self.background_color = [ch, ch1, fg, bg];
- }
- #[wasm_bindgen(js_name = "noStroke")]
- pub fn no_stroke(&mut self) {
- self.has_stroke = false;
- }
- #[wasm_bindgen(js_name = "noFill")]
- pub fn no_fill(&mut self) {
- self.has_fill = false;
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::Backend;
-
- #[test]
- fn should_set_stroke() {
- let mut backend: Backend = Backend::new(10, 10);
- backend.stroke(0, 0, 0, 0);
- assert_eq!(backend.stroke_color[0], 0);
- assert_eq!(backend.stroke_color[1], 0);
- assert_eq!(backend.stroke_color[2], 0);
- assert_eq!(backend.stroke_color[3], 0);
- }
-}
diff --git a/rust/src/backend.rs b/rust/src/backend.rs
deleted file mode 100644
index 61e84970..00000000
--- a/rust/src/backend.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-extern crate wasm_bindgen;
-use crate::{
- globals::{Color, Matrix3, Shape, Vector3, CELL_SIZE, NULL_VALUE},
- matrix3::matrix3_identity,
-};
-use std::vec;
-use wasm_bindgen::prelude::*;
-
-#[wasm_bindgen]
-pub struct Backend {
- pub(crate) cols: usize,
- pub(crate) rows: usize,
- pub(crate) stroke_color: Color,
- pub(crate) fill_color: Color,
- pub(crate) has_stroke: bool,
- pub(crate) has_fill: bool,
- pub(crate) has_background: bool,
- pub(crate) background_color: Color,
- pub(crate) buffer: Vec,
- pub(crate) shapes: Vec,
- pub(crate) mode_view: Matrix3,
- pub(crate) out: Vector3,
- pub(crate) stacks: Vec,
-}
-
-#[wasm_bindgen]
-impl Backend {
- pub fn new(cols: usize, rows: usize) -> Backend {
- let buffer: Vec = vec![NULL_VALUE; cols * rows * CELL_SIZE];
- Backend {
- cols,
- rows,
- buffer,
- stroke_color: [NULL_VALUE, NULL_VALUE, NULL_VALUE, NULL_VALUE],
- fill_color: [NULL_VALUE, NULL_VALUE, NULL_VALUE, NULL_VALUE],
- has_fill: false,
- has_stroke: true,
- has_background: false,
- background_color: [NULL_VALUE, NULL_VALUE, NULL_VALUE, NULL_VALUE],
- mode_view: matrix3_identity(),
- stacks: vec![],
- shapes: vec![],
- out: [0.0, 0.0, 0.0],
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::Backend;
-
- #[test]
- fn should_have_expected_defaults() {
- let backend: Backend = Backend::new(10, 10);
- assert_eq!(backend.cols, 10);
- assert_eq!(backend.rows, 10);
- assert_eq!(backend.buffer.len(), 400);
- assert_eq!(backend.shapes.len(), 0);
- assert_eq!(backend.stacks.len(), 0);
- assert_eq!(backend.has_stroke, true);
- assert_eq!(backend.has_background, false);
- assert_eq!(backend.has_fill, false);
- }
-}
diff --git a/rust/src/globals.rs b/rust/src/globals.rs
deleted file mode 100644
index c985f32f..00000000
--- a/rust/src/globals.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-pub const CELL_SIZE: usize = 4;
-
-pub const NULL_VALUE: u32 = 0xFFFFFFFF;
-
-pub struct Vertex {
- pub(crate) color: Color,
- pub(crate) x: f64,
- pub(crate) y: f64,
-}
-
-pub struct Point {
- pub(crate) color: Color,
- pub(crate) x: isize,
- pub(crate) y: isize,
-}
-
-pub type Edge<'a> = [&'a Point; 2];
-
-pub type Color = [u32; CELL_SIZE];
-
-pub struct Shape {
- pub(crate) vertices: Vec,
- pub(crate) matrix: Matrix3,
- pub(crate) is_closed: bool,
- pub(crate) has_stroke: bool,
- pub(crate) has_fill: bool,
- pub(crate) fill_color: Color,
-}
-
-pub type Matrix3 = [f64; 9];
-
-pub type Vector3 = [f64; 3];
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
deleted file mode 100644
index 3c1473ae..00000000
--- a/rust/src/lib.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-mod attributes;
-pub mod backend;
-mod globals;
-mod matrix3;
-mod pipeline;
-mod primitives;
-mod transform;
diff --git a/rust/src/matrix3.rs b/rust/src/matrix3.rs
deleted file mode 100644
index f98ff266..00000000
--- a/rust/src/matrix3.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use crate::globals::{Matrix3, Vector3};
-
-pub fn matrix3_identity() -> Matrix3 {
- [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
-}
-
-pub fn matrix3_zero() -> Matrix3 {
- [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
-}
-
-// x' a b c x
-// y' = d e f * y
-// z' g h i z
-pub fn matrix3_transform(v: &Vector3, m: &Matrix3) -> Vector3 {
- let mut out: Vector3 = [0.0, 0.0, 0.0];
- let x: f64 = v[0];
- let y: f64 = v[1];
- let z: f64 = v[2];
- out[0] = x * m[0] + y * m[1] + z * m[2];
- out[1] = x * m[3] + y * m[4] + z * m[5];
- out[2] = x * m[6] + y * m[7] + z * m[8];
- out
-}
-
-// x' a b c 1 0 x x
-// y' = d e f * 0 1 y * y
-// 1 g h i 0 0 1 1
-pub fn matrix3_translate(a: &Matrix3, x: f64, y: f64) -> Matrix3 {
- let mut out: Matrix3 = matrix3_zero();
- out[0] = a[0];
- out[1] = a[1];
- out[2] = a[0] * x + a[1] * y + a[2];
- out[3] = a[3];
- out[4] = a[4];
- out[5] = a[3] * x + a[4] * y + a[5];
- out[6] = a[6];
- out[7] = a[7];
- out[8] = a[6] * x + a[7] * y + a[8];
- out
-}
-
-// x' a b c x 0 0 x0
-// y' = d e f * 0 y 0 * y0
-// 1 g h i 0 0 1 1
-pub fn matrix3_scale(a: &Matrix3, x: f64, y: f64) -> Matrix3 {
- let mut out: Matrix3 = matrix3_zero();
- out[0] = x * a[0];
- out[1] = y * a[1];
- out[2] = a[2];
- out[3] = x * a[3];
- out[4] = y * a[4];
- out[5] = a[5];
- out[6] = x * a[6];
- out[7] = y * a[7];
- out[8] = a[8];
- out
-}
-
-// x' a b c cosx -sinx 0 x
-// y'= d e f * sinx cosx 0 * y
-// 1 g h i 0 0 1 1
-pub fn matrix3_rotate(a: &Matrix3, rad: f64) -> Matrix3 {
- let mut out: Matrix3 = matrix3_zero();
- let cos: f64 = rad.cos();
- let sin: f64 = rad.sin();
- out[0] = a[0] * cos + a[1] * sin;
- out[1] = -a[0] * sin + a[1] * cos;
- out[2] = a[2];
- out[3] = a[3] * cos + a[4] * sin;
- out[4] = -a[3] * sin + a[4] * cos;
- out[5] = a[5];
- out[6] = a[6] * cos + a[7] * sin;
- out[7] = -a[6] * sin + a[7] * cos;
- out[8] = a[8];
- out
-}
diff --git a/rust/src/pipeline.rs b/rust/src/pipeline.rs
deleted file mode 100644
index 62004997..00000000
--- a/rust/src/pipeline.rs
+++ /dev/null
@@ -1,263 +0,0 @@
-extern crate wasm_bindgen;
-use crate::{
- backend::Backend,
- globals::{Color, Edge, Matrix3, Point, Vector3, Vertex, CELL_SIZE, NULL_VALUE},
- matrix3::{matrix3_identity, matrix3_transform},
-};
-use std::{cmp, ptr};
-use wasm_bindgen::prelude::*;
-
-fn vertex_processing(vertices: &Vec, m: &Matrix3) -> Vec {
- let mut transformed: Vec = vec![];
- for vertex in vertices {
- let v: Vector3 = [vertex.x, vertex.y, 1.0];
- let out: Vector3 = matrix3_transform(&v, &m);
- transformed.push(Point {
- color: vertex.color,
- x: out[0].round() as isize,
- y: out[1].round() as isize,
- })
- }
- transformed
-}
-
-fn primitive_assembly(vertices: &Vec, is_closed: bool) -> Vec {
- if vertices.len() == 0 {
- vec![]
- } else if vertices.len() == 1 {
- let edge: Edge = [&vertices[0], &vertices[0]];
- vec![edge]
- } else {
- let mut edges: Vec = vec![];
- let len: usize = vertices.len();
- let end: usize = if is_closed { len + 1 } else { len };
- for i in 1..end {
- let from: &Point = &vertices[i - 1];
- let to: &Point = &vertices[i % len];
- let edge: Edge = [from, to];
- edges.push(edge);
- }
- edges
- }
-}
-
-fn rasterization(
- edges: &Vec,
- fill_color: Color,
- has_stroke: bool,
- has_fill: bool,
-) -> Vec {
- let mut vertices: Vec = vec![];
- if has_fill {
- rasterization_stroke(&mut vertices, edges, fill_color, true);
- rasterization_fill(&mut vertices, fill_color);
- }
- if has_stroke {
- rasterization_stroke(&mut vertices, edges, fill_color, false);
- }
- vertices
-}
-
-fn rasterization_stroke(
- vertices: &mut Vec,
- edges: &Vec,
- fill_color: Color,
- use_fill: bool,
-) {
- for i in 0..edges.len() {
- let edge: Edge = edges[i];
- let from: &Point = edge[0];
- let to: &Point = edge[1];
- if ptr::eq(from, to) {
- vertices.push(Point {
- color: from.color,
- x: from.x,
- y: from.y,
- })
- } else {
- let next: Edge = edges[(i + 1) % edges.len()];
- let next_from: &Point = next[0];
- let color: Color = if use_fill { fill_color } else { from.color };
- let line: &mut Vec = &mut rasterization_line(from, to, color);
- if next_from.x == to.x && next_from.y == to.y && line.len() >= 2 {
- line.pop();
- vertices.append(line);
- } else {
- vertices.append(line);
- }
- }
- }
-}
-
-fn rasterization_fill(vertices: &mut Vec, color: Color) {
- let mut y0: isize = isize::MAX;
- let mut y1: isize = isize::MIN;
- for vertex in &mut *vertices {
- y0 = cmp::min(y0, vertex.y);
- y1 = cmp::max(y1, vertex.y);
- }
-
- let mut lookup: Vec> = vec![vec![]; (y1 - y0) as usize + 1];
- for vertex in &mut *vertices {
- let index: usize = (vertex.y - y0) as usize;
- lookup[index].push(vertex.x as isize)
- }
-
- for i in 0..lookup.len() {
- let line: &mut Vec = &mut lookup[i];
- line.sort();
- let y: isize = (i as isize) + y0;
- let mut in_polygon: bool = false;
- for i in 0..line.len() {
- if in_polygon {
- let x0: isize = line[i - 1];
- let x1: isize = line[i];
- for x in (x0 + 1)..x1 {
- vertices.push(Point { color, x, y })
- }
- }
- in_polygon = !in_polygon;
- }
- }
-}
-
-// @see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
-fn rasterization_line(from: &Point, to: &Point, color: Color) -> Vec {
- let dx: isize = (to.x - from.x).abs();
- let dy: isize = -(to.y - from.y).abs();
- let sx: isize = if from.x < to.x { 1 } else { -1 };
- let sy: isize = if from.y < to.y { 1 } else { -1 };
- let mut vertices: Vec = vec![];
- let mut x: isize = from.x;
- let mut y: isize = from.y;
- let mut error: isize = dx + dy;
-
- loop {
- vertices.push(Point { color, x, y });
- if x == to.x && y == to.y {
- break;
- };
- let error2: isize = error * 2;
- if error2 >= dy {
- if x == to.x {
- break;
- }
- error = error + dy;
- x = x + sx;
- }
- if error2 <= dx {
- if y == to.y {
- break;
- }
- error = error + dx;
- y = y + sy;
- }
- }
-
- vertices
-}
-
-fn clipping(vertices: &Vec, cols: isize, rows: isize) -> Vec {
- let mut clipped: Vec = vec![];
- for i in 0..vertices.len() {
- let vertex: &Point = &vertices[i];
- let x: isize = vertex.x;
- let y: isize = vertex.y;
- if x >= 0 && x < cols && y >= 0 && y < rows {
- clipped.push(i);
- }
- }
- clipped
-}
-
-fn fragment_processing(
- vertices: &Vec,
- clipped: &Vec,
- buffer: &mut Vec,
- cols: isize,
-) {
- for i in clipped {
- let vertex: &Point = &vertices[*i];
- let x: isize = vertex.x;
- let y: isize = vertex.y;
- let index: usize = ((x + y * cols) as usize) * CELL_SIZE;
- buffer[index] = vertex.color[0];
- buffer[index + 1] = vertex.color[1];
- buffer[index + 2] = vertex.color[2];
- buffer[index + 3] = vertex.color[3];
- }
-}
-
-fn background_processing(
- has_background: bool,
- color: Color,
- buffer: &mut Vec,
- rows: isize,
- cols: isize,
-) {
- if has_background {
- for x in 0..cols {
- for y in 0..rows {
- let index: usize = ((x + y * cols) as usize) * CELL_SIZE;
- buffer[index] = color[0];
- buffer[index + 1] = color[1];
- buffer[index + 2] = color[2];
- buffer[index + 3] = color[3];
- }
- }
- } else {
- buffer.fill(NULL_VALUE);
- }
-}
-
-#[wasm_bindgen]
-impl Backend {
- pub fn render(&mut self) -> *const u32 {
- background_processing(
- self.has_background,
- self.background_color,
- &mut self.buffer,
- self.rows as isize,
- self.cols as isize,
- );
- for shape in &self.shapes {
- let transformed: Vec = vertex_processing(&shape.vertices, &shape.matrix);
- let primitive: Vec = primitive_assembly(&transformed, shape.is_closed);
- let fragment: Vec = rasterization(
- &primitive,
- shape.fill_color,
- shape.has_stroke,
- shape.has_fill,
- );
- let clipped: Vec = clipping(&fragment, self.cols as isize, self.rows as isize);
- fragment_processing(&fragment, &clipped, &mut self.buffer, self.cols as isize);
- }
- self.has_background = false;
- self.has_stroke = true;
- self.has_fill = false;
- self.shapes.clear();
- self.stacks.clear();
- self.mode_view = matrix3_identity();
- self.buffer.as_ptr()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::Backend;
-
- #[test]
- fn should_clear_reset() {
- let mut backend: Backend = Backend::new(10, 10);
- backend.point(0.0, 0.0);
- backend.render();
- assert_eq!(backend.shapes.len(), 0);
- assert_eq!(backend.stacks.len(), 0);
- assert_eq!(backend.mode_view[0], 1.0);
- assert_eq!(backend.mode_view[4], 1.0);
- assert_eq!(backend.mode_view[8], 1.0);
- assert_eq!(backend.has_background, false);
- assert_eq!(backend.has_stroke, true);
- assert_eq!(backend.has_fill, false);
- }
-}
diff --git a/rust/src/primitives.rs b/rust/src/primitives.rs
deleted file mode 100644
index ba354be1..00000000
--- a/rust/src/primitives.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-extern crate wasm_bindgen;
-use crate::{
- backend::Backend,
- globals::{Shape, Vertex},
-};
-use wasm_bindgen::prelude::*;
-
-#[wasm_bindgen]
-impl Backend {
- pub fn point(&mut self, x: f64, y: f64) {
- let v: Vertex = Vertex {
- color: self.stroke_color,
- x,
- y,
- };
- self.shapes.push(Shape {
- vertices: vec![v],
- matrix: self.mode_view,
- is_closed: false,
- has_fill: false,
- has_stroke: self.has_stroke,
- fill_color: self.fill_color,
- });
- }
- pub fn line(&mut self, x: f64, y: f64, x1: f64, y1: f64) {
- let v: Vertex = Vertex {
- color: self.stroke_color,
- x,
- y,
- };
- let v1: Vertex = Vertex {
- color: self.stroke_color,
- x: x1,
- y: y1,
- };
- self.shapes.push(Shape {
- vertices: vec![v, v1],
- matrix: self.mode_view,
- is_closed: false,
- has_fill: false,
- has_stroke: self.has_stroke,
- fill_color: self.fill_color,
- })
- }
- pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
- if width == 0.0 || height == 0.0 {
- return;
- }
- let dx: f64 = width - 1.0;
- let dy: f64 = height - 1.0;
- let v: Vertex = Vertex {
- color: self.stroke_color,
- x,
- y,
- };
- let v1: Vertex = Vertex {
- color: self.stroke_color,
- x: x + dx,
- y,
- };
- let v2: Vertex = Vertex {
- color: self.stroke_color,
- x: x + dx,
- y: y + dy,
- };
- let v3: Vertex = Vertex {
- color: self.stroke_color,
- x,
- y: y + dy,
- };
- self.shapes.push(Shape {
- vertices: vec![v, v1, v2, v3],
- matrix: self.mode_view,
- is_closed: true,
- has_fill: self.has_fill,
- has_stroke: self.has_stroke,
- fill_color: self.fill_color,
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
-
- use super::Backend;
-
- #[test]
- fn should_add_point() {
- let mut backend: Backend = Backend::new(10, 10);
- backend.point(0.0, 0.0);
- assert_eq!(backend.shapes[0].vertices[0].x, 0.0);
- assert_eq!(backend.shapes[0].vertices[0].y, 0.0);
- }
-}
diff --git a/rust/src/transform.rs b/rust/src/transform.rs
deleted file mode 100644
index 441d1546..00000000
--- a/rust/src/transform.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-extern crate wasm_bindgen;
-use crate::{
- backend::Backend,
- matrix3::{matrix3_rotate, matrix3_scale, matrix3_transform, matrix3_translate},
-};
-use wasm_bindgen::prelude::*;
-
-#[wasm_bindgen]
-impl Backend {
- pub fn translate(&mut self, x: f64, y: f64) {
- self.mode_view = matrix3_translate(&self.mode_view, x, y);
- }
-
- pub fn scale(&mut self, x: f64, y: f64) {
- self.mode_view = matrix3_scale(&self.mode_view, x, y);
- }
-
- pub fn rotate(&mut self, rad: f64) {
- self.mode_view = matrix3_rotate(&self.mode_view, rad);
- }
-
- #[wasm_bindgen(js_name = "pushMatrix")]
- pub fn push_matrix(&mut self) {
- self.stacks.push(self.mode_view);
- }
-
- #[wasm_bindgen(js_name = "popMatrix")]
- pub fn pop_matrix(&mut self) {
- if let Some(last) = self.stacks.pop() {
- self.mode_view = last;
- }
- }
-
- pub fn transform(&mut self, x: f64, y: f64) -> *const f64 {
- self.out = matrix3_transform(&[x, y, 1.0], &self.mode_view);
- self.out.as_ptr()
- }
-}
diff --git a/server.js b/server.js
deleted file mode 100644
index 0a5eaeb1..00000000
--- a/server.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { createServer } from "vite";
-import config from "./vite.config";
-
-const PORT = 3001;
-
-export default async function () {
- const server = await createServer({
- ...config,
- server: {
- ...config.server,
- open: false,
- port: PORT,
- strictPort: true,
- },
- mode: "test",
- });
- await server.listen();
- server.printUrls();
-
- process.env.TEST_PORT = PORT;
-
- return () => {
- server.close();
- };
-}
diff --git a/src/app/app.js b/src/app/app.js
deleted file mode 100644
index b1609bdd..00000000
--- a/src/app/app.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function app$app() {
- return this._flow.app();
-}
diff --git a/src/app/append.js b/src/app/append.js
deleted file mode 100644
index 56366177..00000000
--- a/src/app/append.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function app$append(render, options) {
- return this._flow.append(render, options);
-}
diff --git a/src/app/call.js b/src/app/call.js
deleted file mode 100644
index 9d5106ba..00000000
--- a/src/app/call.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function app$call(callback, ...params) {
- callback(this, ...params);
- return this;
-}
diff --git a/src/app/data.js b/src/app/data.js
deleted file mode 100644
index 0e230207..00000000
--- a/src/app/data.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function app$data(data) {
- return this._flow.data(data);
-}
diff --git a/src/app/datum.js b/src/app/datum.js
deleted file mode 100644
index 98e9a867..00000000
--- a/src/app/datum.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function app$datum(datum) {
- return this.data([datum]);
-}
diff --git a/src/app/dispose.js b/src/app/dispose.js
deleted file mode 100644
index 15b08140..00000000
--- a/src/app/dispose.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { maybe } from "./maybe.js";
-
-export function app$dispose() {
- maybe(this._renderer, "mousemove");
- maybe(this._renderer, "mouseup");
- maybe(this._renderer, "mousedown");
- maybe(this._renderer, "mouseclick");
- if (this._timer) this._timer.stop();
- this._emitter.emit("afterAll", this);
- this._emitter.off();
- return this;
-}
diff --git a/src/app/index.js b/src/app/index.js
deleted file mode 100644
index b1a1ad23..00000000
--- a/src/app/index.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { app$data } from "./data.js";
-import { app$app } from "./app.js";
-import { app$datum } from "./datum.js";
-import { app$node } from "./node.js";
-import { app$render } from "./render.js";
-import { app$start } from "./start.js";
-import { app$stop } from "./stop.js";
-import { app$append } from "./append.js";
-import { app$call } from "./call.js";
-import { app$dispose } from "./dispose.js";
-import { app$on } from "./on.js";
-import { app$prop } from "./prop.js";
-import { app$textBBox } from "./textBBox.js";
-import { maybe } from "./maybe.js";
-import { Emitter } from "../emitter.js";
-import { canvas } from "../canvas/index.js";
-import { Node } from "../node.js";
-import { Flow } from "../flow/index.js";
-
-function App({ width = 640, height = 480, renderer = canvas(), frameRate = 60, dpi = null, ...rest } = {}) {
- const root = new Node();
- const flow = new Flow([[0]], [0], root, this);
- const emitter = new Emitter();
- const props = {
- frameRate,
- frameCount: 0,
- mouseX: 0,
- mouseY: 0,
- };
- Object.defineProperties(this, {
- _renderer: { value: renderer },
- _stop: { value: false, writable: true },
- _reschedule: { value: true, writable: true },
- _timer: { value: null, writable: true },
- _dispose: { value: () => {}, writable: true },
- _root: { value: root },
- _flow: { value: flow },
- _emitter: { value: emitter },
- _props: { value: props, writable: false },
- });
-
- maybe(this._renderer, "init", { width, height, dpi, ...rest });
- maybe(this._renderer, "mousemove", (e) => {
- const { x, y } = e;
- this._props.mouseX = x;
- this._props.mouseY = y;
- });
- maybe(this._renderer, "mousedown", () => emitter.emit("mouseDown", this));
- maybe(this._renderer, "mouseup", () => emitter.emit("mouseUp", this));
- maybe(this._renderer, "mouseclick", () => emitter.emit("mouseClick", this));
-}
-
-Object.defineProperties(App.prototype, {
- data: { value: app$data },
- datum: { value: app$datum },
- node: { value: app$node },
- render: { value: app$render },
- start: { value: app$start },
- stop: { value: app$stop },
- append: { value: app$append },
- call: { value: app$call },
- dispose: { value: app$dispose },
- prop: { value: app$prop },
- on: { value: app$on },
- textBBox: { value: app$textBBox },
- app: { value: app$app },
-});
-
-export function app(options) {
- return new App(options);
-}
diff --git a/src/app/maybe.js b/src/app/maybe.js
deleted file mode 100644
index 4692bdd2..00000000
--- a/src/app/maybe.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export function maybe(object, method, ...params) {
- if (object[method]) {
- const value = object[method](...params);
- return value;
- } else {
- console.warn(`Unimplemented renderer method: ${method}.`);
- }
-}
diff --git a/src/app/node.js b/src/app/node.js
deleted file mode 100644
index f169c9d2..00000000
--- a/src/app/node.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { maybe } from "./maybe.js";
-
-export function app$node() {
- return maybe(this._renderer, "node");
-}
diff --git a/src/app/on.js b/src/app/on.js
deleted file mode 100644
index 09e13c2e..00000000
--- a/src/app/on.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function app$on(typename, callback) {
- this._emitter.on(typename, callback);
- return this;
-}
diff --git a/src/app/prop.js b/src/app/prop.js
deleted file mode 100644
index 812c0fc7..00000000
--- a/src/app/prop.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export function app$prop(name) {
- const props = this._props;
- const rendererProps = this._renderer._props || {};
- if (arguments.length === 0) return { ...rendererProps, ...props };
- return props[name] ?? rendererProps[name];
-}
diff --git a/src/app/render.js b/src/app/render.js
deleted file mode 100644
index 5a56a5ff..00000000
--- a/src/app/render.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Flow } from "../flow/index.js";
-
-function renderNode(renderer, node, dimension, index, app) {
- const data = node._data[index];
- const { render, I, value, options, group } = data;
- const { builtin } = render;
- const children = node._children;
- if (!builtin) {
- node._children = [];
- const flow = new Flow([I], I, node, app);
- render(flow, value);
- const children = node._children;
- if (!children.length) return;
- for (const child of children) renderNode(renderer, child, dimension, 0, app);
- }
- if (builtin) render(renderer, I, value, options, group);
- if (!children.length) return;
- for (let i = 0; i < I.length; i++) {
- if (value) {
- const { x: X = [], y: Y = [], rotate: R = [] } = value;
- const x = X[i] ?? 0;
- const y = Y[i] ?? 0;
- const r = R[i];
- renderer.save();
- renderer.translate(x, y);
- if (r !== undefined) renderer.rotate(r);
- }
- for (const child of children) renderNode(renderer, child, dimension, i, app);
- if (value) renderer.restore();
- }
-}
-
-export function app$render() {
- const renderer = this._renderer;
- const dimension = { width: this.prop("width"), height: this.prop("height") };
- this._root._children.forEach((d) => renderNode(renderer, d, dimension, 0, this));
- this._root._children = [];
- if (this._renderer.render) this._renderer.render();
- return this;
-}
diff --git a/src/app/start.js b/src/app/start.js
deleted file mode 100644
index 5f698de8..00000000
--- a/src/app/start.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { interval } from "d3-timer";
-
-function schedule() {
- // Need to stop?
- if (this._stop) {
- this._timer.stop();
- this._timer = null;
- this._reschedule = true;
- this._stop = false;
- return;
- }
-
- // Need to change frameRate?
- if (this._reschedule) {
- if (this._timer) this._timer.stop();
- const delay = (1000 / this._props.frameRate) | 0;
- this._timer = interval(() => {
- tick.call(this);
- }, delay);
- this._reschedule = false;
- }
-}
-
-function tick() {
- const emitter = this._emitter;
- emitter.emit("beforeEach", this);
- emitter.emit("update", this);
- this.render();
- this._props.frameCount++;
- emitter.emit("afterEach", this);
- schedule.call(this); // Schedule at the end of every tick.
-}
-
-export function app$start() {
- this._emitter.emit("beforeAll", this);
- if (!this._stop) tick.call(this);
- this._stop = false;
- return this;
-}
diff --git a/src/app/stop.js b/src/app/stop.js
deleted file mode 100644
index c249c3c2..00000000
--- a/src/app/stop.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function app$stop() {
- this._stop = true;
- return this;
-}
diff --git a/src/app/textBBox.js b/src/app/textBBox.js
deleted file mode 100644
index 9f26cffd..00000000
--- a/src/app/textBBox.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { maybe } from "./maybe.js";
-
-export function app$textBBox(options) {
- return maybe(this._renderer, "textBBox", options);
-}
diff --git a/src/array/cross.js b/src/array/cross.js
deleted file mode 100644
index 95b16069..00000000
--- a/src/array/cross.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export function cross(...values) {
- const product = [];
- const lengths = values.map((d) => d.length);
- const n = values.length - 1;
- const index = new Array(n + 1).fill(0);
- while (true) {
- product.push(index.map((i, j) => values[j][i]));
- let m = n;
- while (++index[m] === lengths[m]) {
- if (m === 0) return product;
- index[m--] = 0;
- }
- }
-}
diff --git a/src/array/extent.js b/src/array/extent.js
deleted file mode 100644
index b601f964..00000000
--- a/src/array/extent.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export function extent(values, valueof = (d) => d) {
- const min = Math.min(...values.map(valueof));
- const max = Math.max(...values.map(valueof));
- return [min, max];
-}
diff --git a/src/array/range.js b/src/array/range.js
deleted file mode 100644
index a1da2640..00000000
--- a/src/array/range.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export function range(length, start, end) {
- if (arguments.length === 1) return Array.from({ length }, (_, i) => i);
- if (arguments.length === 2) [start, end] = [0, start];
- const step = (end - start) / length;
- return Array.from({ length }, (_, i) => start + i * step);
-}
diff --git a/src/attribute/cfb.js b/src/attribute/cfb.js
deleted file mode 100644
index 9ebe7fa8..00000000
--- a/src/attribute/cfb.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function cfb(ch, fg, bg) {
- return { ch, fg, bg };
-}
diff --git a/src/attribute/constant.js b/src/attribute/constant.js
deleted file mode 100644
index e3fb770a..00000000
--- a/src/attribute/constant.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export function constant(value) {
- return {
- constant: true,
- value,
- };
-}
diff --git a/src/attribute/figlet.js b/src/attribute/figlet.js
deleted file mode 100644
index df2ac140..00000000
--- a/src/attribute/figlet.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function figlet(text) {
- return { type: "figlet", text };
-}
diff --git a/src/attribute/glsl.js b/src/attribute/glsl.js
deleted file mode 100644
index 89ab009c..00000000
--- a/src/attribute/glsl.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function glsl(strings, ...params) {
- return {
- preserve: true,
- strings,
- params,
- };
-}
diff --git a/src/attribute/hsl.js b/src/attribute/hsl.js
deleted file mode 100644
index 271d40c2..00000000
--- a/src/attribute/hsl.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export function hsl(h, s, l) {
- h = h | 0;
- s = s | 0;
- l = l | 0;
- return `hsl(${h}, ${s}%, ${l}%)`;
-}
diff --git a/src/attribute/rgb.js b/src/attribute/rgb.js
deleted file mode 100644
index 46888be0..00000000
--- a/src/attribute/rgb.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function rgb(r, g, b) {
- r = r | 0;
- g = g | 0;
- b = b | 0;
- if (arguments.length === 1) return `rgb(${r}, ${r}, ${r})`;
- return `rgb(${r}, ${g}, ${b})`;
-}
diff --git a/src/attribute/wch.js b/src/attribute/wch.js
deleted file mode 100644
index 1596dc3e..00000000
--- a/src/attribute/wch.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { NULL_VALUE } from "../terminal/constant.js";
-
-export function wch(string) {
- const code = string.codePointAt(0);
- return [code + 0xf0000000, NULL_VALUE];
-}
diff --git a/src/canvas/circle.js b/src/canvas/circle.js
deleted file mode 100644
index 104fab0d..00000000
--- a/src/canvas/circle.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { normalizeColorString } from "../color.js";
-
-export function canvas$circle({ x = 0, y = 0, r = 0, stroke, strokeWidth, fill, fillOpacity, strokeOpacity }) {
- const context = this._context;
- stroke = normalizeColorString(stroke, strokeOpacity);
- fill = normalizeColorString(fill, fillOpacity);
- context.save();
- context.beginPath();
- if (stroke) context.strokeStyle = stroke;
- if (strokeWidth) context.lineWidth = strokeWidth;
- context.fillStyle = fill;
- context.arc(x, y, r, 0, Math.PI * 2);
- context.fill();
- if (stroke) context.stroke();
- context.closePath();
- context.restore();
- return this;
-}
diff --git a/src/canvas/clear.js b/src/canvas/clear.js
deleted file mode 100644
index ac1a5233..00000000
--- a/src/canvas/clear.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export function canvas$clear({ fill }) {
- this._context.fillStyle = fill;
- this._context.fillRect(0, 0, this._props.width, this._props.height);
- return this;
-}
diff --git a/src/canvas/index.js b/src/canvas/index.js
deleted file mode 100644
index 6ce62e0f..00000000
--- a/src/canvas/index.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { canvas$init } from "./init.js";
-import { canvas$node } from "./node.js";
-import { canvas$circle } from "./circle.js";
-import { canvas$point } from "./point.js";
-import { canvas$rect } from "./rect.js";
-import { canvas$line } from "./line.js";
-import { canvas$triangle } from "./triangle";
-import { canvas$translate } from "./translate.js";
-import { canvas$path } from "./path.js";
-import { canvas$polygon } from "./polygon.js";
-import { canvas$text } from "./text.js";
-import { canvas$clear } from "./clear.js";
-import { canvas$mousemove } from "./mousemove.js";
-import { canvas$mouseup } from "./mouseup.js";
-import { canvas$mousedown } from "./mousedown.js";
-import { canvas$mouseclick } from "./mouseclick.js";
-import { canvas$rotate } from "./rotate.js";
-import { canvas$save } from "./save.js";
-import { canvas$restore } from "./restore.js";
-import { canvas$textBBox } from "./textBBox.js";
-
-function Canvas() {
- Object.defineProperties(this, {
- _context: { value: null, writable: true },
- _props: { value: {}, writable: true },
- _mousemove: { value: null, writable: true },
- _mousedown: { value: null, writable: true },
- _mouseup: { value: null, writable: true },
- });
-}
-
-Object.defineProperties(Canvas.prototype, {
- init: { value: canvas$init },
- node: { value: canvas$node },
- point: { value: canvas$point },
- circle: { value: canvas$circle },
- rect: { value: canvas$rect },
- line: { value: canvas$line },
- triangle: { value: canvas$triangle },
- path: { value: canvas$path },
- polygon: { value: canvas$polygon },
- text: { value: canvas$text },
- clear: { value: canvas$clear },
- mousemove: { value: canvas$mousemove },
- mouseup: { value: canvas$mouseup },
- mousedown: { value: canvas$mousedown },
- mouseclick: { value: canvas$mouseclick },
- translate: { value: canvas$translate },
- rotate: { value: canvas$rotate },
- save: { value: canvas$save },
- restore: { value: canvas$restore },
- textBBox: { value: canvas$textBBox },
-});
-
-export function canvas(options) {
- return new Canvas(options);
-}
diff --git a/src/canvas/init.js b/src/canvas/init.js
deleted file mode 100644
index 7a34f43a..00000000
--- a/src/canvas/init.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { context2d } from "../context.js";
-
-export function canvas$init({ width, height, dpi }) {
- const context = context2d(width, height, dpi);
- Object.assign(this, { _context: context });
- Object.assign(this._props, { width, height });
- return this;
-}
diff --git a/src/canvas/line.js b/src/canvas/line.js
deleted file mode 100644
index 959948d3..00000000
--- a/src/canvas/line.js
+++ /dev/null
@@ -1,23 +0,0 @@
-export function canvas$line({ stroke, strokeWidth, strokeCap, x, y, x1, y1, rotate, transformOrigin = "start" }) {
- const context = this._context;
- context.save();
- context.beginPath();
-
- if (rotate) {
- const [mx, my] =
- transformOrigin === "center" ? [(x + x1) / 2, (y + y1) / 2] : transformOrigin === "end" ? [x1, y1] : [x, y];
- context.translate(mx, my);
- context.rotate(rotate);
- context.translate(-mx, -my);
- }
-
- if (stroke) context.strokeStyle = stroke;
- if (strokeWidth) context.lineWidth = strokeWidth;
- if (strokeCap) context.lineCap = strokeCap;
- context.moveTo(x, y);
- context.lineTo(x1, y1);
- if (stroke) context.stroke();
- context.closePath();
- context.restore();
- return this;
-}
diff --git a/src/canvas/mouseclick.js b/src/canvas/mouseclick.js
deleted file mode 100644
index 5193270a..00000000
--- a/src/canvas/mouseclick.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export function canvas$mouseclick(listener) {
- const node = this.node();
- if (arguments.length === 0) {
- node.removeEventListener("click", this._mouseclick);
- }
- this._mouseclick = (e) => listener(e);
- node.addEventListener("click", this._mouseclick);
- return this;
-}
diff --git a/src/canvas/mousedown.js b/src/canvas/mousedown.js
deleted file mode 100644
index 7626c17e..00000000
--- a/src/canvas/mousedown.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export function canvas$mousedown(listener) {
- const node = this.node();
- if (arguments.length === 0) {
- node.removeEventListener("mousedown", this._mousedown);
- }
- this._mousedown = (e) => listener(e);
- node.addEventListener("mousedown", this._mousedown);
- return this;
-}
diff --git a/src/canvas/mousemove.js b/src/canvas/mousemove.js
deleted file mode 100644
index efb5816e..00000000
--- a/src/canvas/mousemove.js
+++ /dev/null
@@ -1,15 +0,0 @@
-export function canvas$mousemove(listener) {
- const node = this.node();
- if (arguments.length === 0) {
- node.removeEventListener("mousemove", this._mousemove);
- }
- this._mousemove = (e) => {
- const { x, y } = node.getBoundingClientRect();
- const { clientX, clientY } = e;
- const dx = clientX - x;
- const dy = clientY - y;
- listener({ x: dx, y: dy });
- };
- node.addEventListener("mousemove", this._mousemove);
- return this;
-}
diff --git a/src/canvas/mouseup.js b/src/canvas/mouseup.js
deleted file mode 100644
index dd6ebc0d..00000000
--- a/src/canvas/mouseup.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export function canvas$mouseup(listener) {
- const node = this.node();
- if (arguments.length === 0) {
- node.removeEventListener("mouseup", this._mouseup);
- }
- this._mouseup = (e) => listener(e);
- node.addEventListener("mouseup", this._mouseup);
- return this;
-}
diff --git a/src/canvas/node.js b/src/canvas/node.js
deleted file mode 100644
index 2b7caad6..00000000
--- a/src/canvas/node.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function canvas$node() {
- return this._context.canvas;
-}
diff --git a/src/canvas/path.js b/src/canvas/path.js
deleted file mode 100644
index 1db3bf11..00000000
--- a/src/canvas/path.js
+++ /dev/null
@@ -1,162 +0,0 @@
-import { normalizeColorString } from "../color.js";
-
-function drawArc(context, x1, y1, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x2, y2) {
- // Ensure radii are valid.
- if (rx === 0 || ry === 0) {
- // Draw a straight line if one of the radii is zero.
- context.moveTo(x1, y1);
- context.lineTo(x2, y2);
- return;
- }
-
- // Step 1: Compute (x1', y1')
- let dx2 = (x1 - x2) / 2.0;
- let dy2 = (y1 - y2) / 2.0;
- let x1p = Math.cos(xAxisRotation) * dx2 + Math.sin(xAxisRotation) * dy2;
- let y1p = -Math.sin(xAxisRotation) * dx2 + Math.cos(xAxisRotation) * dy2;
-
- // Ensure radii are large enough.
- let rxs = rx * rx;
- let rys = ry * ry;
- let x1ps = x1p * x1p;
- let y1ps = y1p * y1p;
-
- // Correct radii.
- let cr = x1ps / rxs + y1ps / rys;
- if (cr > 1) {
- let s = Math.sqrt(cr);
- rx = s * rx;
- ry = s * ry;
- rxs = rx * rx;
- rys = ry * ry;
- }
-
- // Step 2: Compute (cx', cy').
- let sign = largeArcFlag === sweepFlag ? -1 : 1;
- let sq = (rxs * rys - rxs * y1ps - rys * x1ps) / (rxs * y1ps + rys * x1ps);
- sq = sq < 0 ? 0 : sq;
- let coef = sign * Math.sqrt(sq);
- let cxp = (coef * (rx * y1p)) / ry;
- let cyp = (coef * -(ry * x1p)) / rx;
-
- // Step 3: Compute (cx, cy) from (cx', cy').
- let cx = Math.cos(xAxisRotation) * cxp - Math.sin(xAxisRotation) * cyp + (x1 + x2) / 2;
- let cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (y1 + y2) / 2;
-
- // Step 4: Compute ΞΈ1 and ΞΞΈ.
- let ux = (x1p - cxp) / rx;
- let uy = (y1p - cyp) / ry;
- let vx = (-x1p - cxp) / rx;
- let vy = (-y1p - cyp) / ry;
- let n = Math.sqrt(ux * ux + uy * uy);
- let p = ux; // Initially angle start.
- sign = uy < 0 ? -1 : 1;
- let angleStart = sign * Math.acos(p / n);
-
- n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
- p = ux * vx + uy * vy;
- sign = ux * vy - uy * vx < 0 ? -1 : 1;
- let angleExtent = sign * Math.acos(p / n);
- if (!sweepFlag && angleExtent > 0) {
- angleExtent -= 2 * Math.PI;
- } else if (sweepFlag && angleExtent < 0) {
- angleExtent += 2 * Math.PI;
- }
-
- // Some browsers require the angles to be modulated by 2Ο.
- angleStart = angleStart % (2 * Math.PI);
- angleExtent = angleExtent % (2 * Math.PI);
-
- // Draw the arc.
- context.ellipse(cx, cy, rx, ry, xAxisRotation, angleStart, angleStart + angleExtent, !sweepFlag);
-}
-
-function drawPath(context, d) {
- let px, py;
- for (const cmd of d) {
- const [c, ...args] = cmd;
- if (c === "M") {
- context.moveTo(...args);
- [px, py] = args;
- } else if (c === "L") {
- context.lineTo(...args);
- [px, py] = args;
- } else if (c === "C") {
- context.bezierCurveTo(...args);
- [px, py] = args.slice(-2);
- } else if (c === "Q") {
- context.quadraticCurveTo(...args);
- [px, py] = args.slice(-2);
- } else if (c === "Z") {
- context.closePath();
- } else if (c === "A") {
- const [rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y] = args;
- drawArc(context, px, py, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
- [px, py] = [x, y];
- } else if (c === "H") {
- context.lineTo(args[0], py);
- px = args[0];
- } else if (c === "V") {
- context.lineTo(px, args[0]);
- py = args[0];
- } else if (c === "v") {
- context.lineTo(px, py + args[0]);
- py += args[0];
- } else if (c === "h") {
- context.lineTo(px + args[0], py);
- px += args[0];
- } else if (c === "l") {
- context.lineTo(px + args[0], py + args[1]);
- px += args[0];
- py += args[1];
- } else if (c === "c") {
- context.bezierCurveTo(px + args[0], py + args[1], px + args[2], py + args[3], px + args[4], py + args[5]);
- px += args[4];
- py += args[5];
- } else if (c === "s") {
- context.bezierCurveTo(px + args[0], py + args[1], px + args[2], py + args[3], px + args[4], py + args[5]);
- px += args[2];
- py += args[3];
- } else if (c === "q") {
- context.quadraticCurveTo(px + args[0], py + args[1], px + args[2], py + args[3]);
- px += args[2];
- py += args[3];
- } else if (c === "t") {
- context.quadraticCurveTo(px + args[0], py + args[1], px + args[0], py + args[1]);
- px += args[0];
- py += args[1];
- } else if (c === "a") {
- const [rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y] = args;
- drawArc(context, px, py, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
- px += x;
- py += y;
- } else if (c === "z") {
- context.closePath();
- } else {
- console.warn(`Unknown command: ${c}`);
- }
- }
-}
-
-export function canvas$path({ x = 0, y = 0, d, stroke, strokeOpacity, fill, fillOpacity, strokeWidth }) {
- stroke = normalizeColorString(stroke, strokeOpacity);
- fill = normalizeColorString(fill, fillOpacity);
- const context = this._context;
- context.save();
- context.translate(x, y);
- if (strokeWidth) context.lineWidth = strokeWidth;
- if (fill) context.fillStyle = fill;
- if (stroke) context.strokeStyle = stroke;
- context.beginPath();
- if (typeof d === "string") {
- const path = new Path2D(d);
- if (fill) context.fill(path);
- if (stroke) context.stroke(path);
- } else if (Array.isArray(d)) {
- drawPath(context, d);
- if (fill) context.fill();
- if (stroke) context.stroke();
- }
- context.restore();
- return this;
-}
diff --git a/src/canvas/point.js b/src/canvas/point.js
deleted file mode 100644
index 8213c2f9..00000000
--- a/src/canvas/point.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { canvas$circle } from "./circle";
-
-export function canvas$point({ stroke = "#000", ...rest }) {
- return canvas$circle.call(this, { ...rest, stroke, r: 1 });
-}
diff --git a/src/canvas/polygon.js b/src/canvas/polygon.js
deleted file mode 100644
index fb52a8e8..00000000
--- a/src/canvas/polygon.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { normalizeColorString } from "../color.js";
-
-export function canvas$polygon({ x: X, y: Y, fill, stroke, strokeWidth, fillOpacity, strokeOpacity }) {
- stroke = normalizeColorString(stroke, strokeOpacity);
- fill = normalizeColorString(fill, fillOpacity);
- const context = this._context;
- context.save();
- context.beginPath();
- if (stroke) context.strokeStyle = stroke;
- if (strokeWidth) context.lineWidth = strokeWidth;
- context.fillStyle = fill;
- const x0 = X[0];
- const y0 = Y[0];
- context.moveTo(x0, y0);
- for (let i = 1; i < X.length; i++) {
- const x = X[i];
- const y = Y[i];
- context.lineTo(x, y);
- }
- context.closePath();
- context.fill();
- if (stroke) context.stroke();
- context.restore();
- return this;
-}
diff --git a/src/canvas/rect.js b/src/canvas/rect.js
deleted file mode 100644
index 5b645a90..00000000
--- a/src/canvas/rect.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { normalizeColorString } from "../color.js";
-
-export function canvas$rect({
- stroke,
- fill,
- x,
- y,
- width,
- height,
- rotate,
- anchor,
- fillOpacity,
- strokeOpacity,
- strokeWidth,
-}) {
- if (anchor === "center") {
- x = x - width / 2;
- y = y - height / 2;
- }
- stroke = normalizeColorString(stroke, strokeOpacity);
- fill = normalizeColorString(fill, fillOpacity);
- const context = this._context;
- context.save();
- context.translate(x + width / 2, y + height / 2);
- if (rotate) context.rotate(rotate);
- context.beginPath();
- if (stroke) context.strokeStyle = stroke;
- if (strokeWidth) context.lineWidth = strokeWidth;
- context.fillStyle = fill;
- context.rect(-width / 2, -height / 2, width, height);
- context.fill();
- if (stroke) context.stroke();
- context.closePath();
- context.restore();
- return this;
-}
diff --git a/src/canvas/restore.js b/src/canvas/restore.js
deleted file mode 100644
index 370aee30..00000000
--- a/src/canvas/restore.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function canvas$restore() {
- this._context.restore();
-}
diff --git a/src/canvas/rotate.js b/src/canvas/rotate.js
deleted file mode 100644
index f1faea6d..00000000
--- a/src/canvas/rotate.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function canvas$rotate(angle) {
- this._context.rotate(angle);
-}
diff --git a/src/canvas/save.js b/src/canvas/save.js
deleted file mode 100644
index 86afed87..00000000
--- a/src/canvas/save.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function canvas$save() {
- this._context.save();
-}
diff --git a/src/canvas/text.js b/src/canvas/text.js
deleted file mode 100644
index 7458a20d..00000000
--- a/src/canvas/text.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { normalizeColorString } from "../color.js";
-
-export function canvas$text({
- text,
- x,
- y,
- fill,
- stroke,
- strokeWidth,
- fillOpacity,
- strokeOpacity,
- fontFamily,
- textAlign,
- textBaseline,
- fontSize = 14,
- fontWeight = "normal",
-}) {
- stroke = normalizeColorString(stroke, strokeOpacity);
- fill = normalizeColorString(fill, fillOpacity);
- const context = this._context;
- context.save();
- if (stroke) context.strokeStyle = stroke;
- if (strokeWidth) context.lineWidth = strokeWidth;
- if (fill) context.fillStyle = fill;
- if (textAlign) context.textAlign = textAlign;
- if (textBaseline) context.textBaseline = textBaseline;
- context.font = `${fontWeight} ${fontSize}px ${fontFamily}`.trim();
- context.fillText(text, x, y);
- context.restore();
-}
diff --git a/src/canvas/textBBox.js b/src/canvas/textBBox.js
deleted file mode 100644
index cf193aed..00000000
--- a/src/canvas/textBBox.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { measureText } from "../measure";
-
-export function canvas$textBBox({ text, fontSize, fontFamily, textAlign, textBaseline, x, y }) {
- const { width, height } = measureText(text, { fontSize, fontFamily });
- const startX = textAlign === "end" ? x - width : textAlign === "center" ? x - width / 2 : x;
- const startY = textBaseline === "bottom" ? y - height : textBaseline === "middle" ? y - height / 2 : y;
- return { x: startX, y: startY, width, height };
-}
diff --git a/src/canvas/translate.js b/src/canvas/translate.js
deleted file mode 100644
index 173106d7..00000000
--- a/src/canvas/translate.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function canvas$translate(x, y) {
- this._context.translate(x, y);
-}
diff --git a/src/canvas/triangle.js b/src/canvas/triangle.js
deleted file mode 100644
index 9a371dd7..00000000
--- a/src/canvas/triangle.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export function canvas$triangle({ x, y, x1, y1, x2, y2, fill, stroke, strokeWidth, rotate }) {
- const context = this._context;
- context.save();
- if (rotate) context.rotate(rotate);
- context.beginPath();
- if (stroke) context.strokeStyle = stroke;
- if (strokeWidth) context.lineWidth = strokeWidth;
- context.fillStyle = fill;
- context.moveTo(x, y);
- context.lineTo(x1, y1);
- context.lineTo(x2, y2);
- context.closePath();
- context.fill();
- if (stroke) context.stroke();
- context.restore();
- return this;
-}
diff --git a/src/color.js b/src/color.js
deleted file mode 100644
index fb8b1ef0..00000000
--- a/src/color.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { color as d3Color } from "d3-color";
-
-const colorByValue = new Map();
-
-export function rgba(value) {
- if (colorByValue.has(value)) return colorByValue.get(value);
- const color = d3Color(value).rgb();
- colorByValue.set(value, color);
- return color;
-}
-
-export function normalizeColor(color, opacity) {
- if (color === undefined) return color;
- if (color === "transparent") return [0, 0, 0, 0];
- const { r, g, b, opacity: a } = rgba(color);
- return [r, g, b, ((opacity ?? a ?? 1) * 255) | 0];
-}
-
-export function normalizeColorString(color, opacity) {
- if (typeof color !== "string") return color;
- const [r, g, b, a] = normalizeColor(color, opacity);
- return `rgba(${r},${g},${b},${a / 255})`;
-}
diff --git a/src/constant/TWO_PI.js b/src/constant/TWO_PI.js
deleted file mode 100644
index 7e778729..00000000
--- a/src/constant/TWO_PI.js
+++ /dev/null
@@ -1 +0,0 @@
-export const TWO_PI = Math.PI * 2;
diff --git a/src/context.js b/src/context.js
deleted file mode 100644
index ab531fbe..00000000
--- a/src/context.js
+++ /dev/null
@@ -1,23 +0,0 @@
-export function context2d(width = 640, height = 480, dpr = null) {
- if (dpr == null) dpr = devicePixelRatio;
- const canvas = document.createElement("canvas");
- canvas.width = width * dpr;
- canvas.height = height * dpr;
- canvas.style.width = width + "px";
- canvas.style.height = height + "px";
- const context = canvas.getContext("2d");
- context.scale(dpr, dpr);
- return context;
-}
-
-export function contextGL(width, height, dpr) {
- if (dpr == null) dpr = devicePixelRatio;
- const canvas = document.createElement("canvas");
- const gl = canvas.getContext("webgl");
- canvas.width = width * dpr;
- canvas.height = height * dpr;
- canvas.style.width = width + "px";
- canvas.style.height = height + "px";
- gl.viewport(0, 0, width * dpr, height * dpr);
- return gl;
-}
diff --git a/src/core.js b/src/core.js
deleted file mode 100644
index ab4c3932..00000000
--- a/src/core.js
+++ /dev/null
@@ -1,28 +0,0 @@
-export { app } from "./app/index.js";
-export { circle } from "./shape/circle.js";
-export { link } from "./shape/link.js";
-export { clear } from "./shape/clear.js";
-export { rect } from "./shape/rect.js";
-export { group } from "./shape/group.js";
-export { triangle } from "./shape/triangle.js";
-export { line } from "./shape/line.js";
-export { polygon } from "./shape/polygon.js";
-export { point } from "./shape/point.js";
-export { path } from "./shape/path.js";
-export { text } from "./shape/text.js";
-export { linear as scaleLinear } from "./scale/linear.js";
-export { sqrt as scaleSqrt } from "./scale/sqrt.js";
-export { log as scaleLog } from "./scale/log.js";
-export { ordinal as scaleOrdinal } from "./scale/ordinal.js";
-export { canvas } from "./canvas/index.js";
-export { each } from "./process/each.js";
-export { eachRight } from "./process/eachRight.js";
-export { filter } from "./process/filter.js";
-export { map } from "./process/map.js";
-export { push } from "./process/push.js";
-export { derive } from "./process/derive.js";
-export { mapAttrs } from "./transform/mapAttrs.js";
-export { mapPosition } from "./transform/mapPosition.js";
-export { rgb } from "./attribute/rgb.js";
-export { hsl } from "./attribute/hsl.js";
-export { constant } from "./attribute/constant.js";
diff --git a/src/dom.js b/src/dom.js
new file mode 100644
index 00000000..94a9b72a
--- /dev/null
+++ b/src/dom.js
@@ -0,0 +1,113 @@
+const propSetterCache = {};
+
+const protoOf = Object.getPrototypeOf;
+
+const isFunc = (x) => typeof x === "function";
+
+const isStr = (x) => typeof x === "string";
+
+const isObjectLiteral = (x) => Object.prototype.toString.call(x) === "[object Object]";
+
+const isMark = (x) => x instanceof Mark;
+
+const isTruthy = (x) => x != null && x !== false;
+
+function preprocess(options) {
+ if (options.marks) {
+ options.children = options.marks;
+ delete options.marks;
+ }
+ return options;
+}
+
+function postprocess(nodes) {
+ if (!nodes) return null;
+ if (nodes.length === 1) return nodes[0];
+ const fragment = document.createDocumentFragment();
+ fragment.append(...nodes);
+ return fragment;
+}
+
+function snake2kebab(str) {
+ return str.replace(/_/g, "-");
+}
+
+// Ref: https://github.com/vanjs-org/van/blob/d09cfd1e1e3b5ea7cf8d0a9b5deacca4c0946fb4/src/van.js#L99
+function set(dom, k, v) {
+ k = snake2kebab(k);
+ if (k.startsWith("on")) return dom.addEventListener(k.slice(2), v);
+ if (k.startsWith("style-")) return dom.style.setProperty(k.slice(6), v);
+ const get = (proto) => (proto ? (Object.getOwnPropertyDescriptor(proto, k) ?? get(protoOf(proto))) : undefined);
+ const cacheKey = dom.nodeName + "," + k;
+ const propSetter = (propSetterCache[cacheKey] ??= get(protoOf(dom))?.set ?? 0);
+ const setter = propSetter ? propSetter.bind(dom) : dom.setAttribute.bind(dom, k);
+ setter(v);
+}
+
+class Mark {
+ constructor(ns, tag, data, options) {
+ if (isObjectLiteral(data)) (options = data), (data = undefined);
+ this._ns = ns;
+ this._tag = tag;
+ this._data = data;
+ this._options = options;
+ }
+ clone() {
+ return new Mark(this._ns, this._tag, this._data, this._options);
+ }
+ with(children) {
+ this._options = {...this._options, children};
+ return this;
+ }
+}
+
+function renderNodes(mark) {
+ const {_ns: ns, _tag: tag, _data: data = [undefined], _options: options = {}} = mark;
+ if (!isStr(tag)) return null;
+ const {children = [], ...attrs} = options;
+ const nodes = data.map((d, i, array) => {
+ const dom = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
+ for (const [k, v] of Object.entries(attrs)) {
+ const val = k.startsWith("on") ? (e) => v(e, d, i, array) : isFunc(v) ? v(d, i, array) : v;
+ set(dom, k, val);
+ }
+ return dom;
+ });
+ for (const child of children.filter(isTruthy).flat(Infinity)) {
+ const n = nodes.length;
+ if (!isMark(child)) {
+ for (let i = 0; i < n; i++) {
+ const node = nodes[i];
+ const datum = data[i];
+ const text = isFunc(child) ? child(datum, i, data) : child;
+ node.append(document.createTextNode(text));
+ }
+ } else if (child._data) {
+ for (let i = 0; i < n; i++) {
+ const node = nodes[i];
+ const datum = data[i];
+ const childData = child._data;
+ const clonedChild = child.clone();
+ clonedChild._data = isFunc(childData) ? childData(datum, i, data) : childData;
+ const childNodes = renderNodes(clonedChild);
+ node.append(...childNodes);
+ }
+ } else {
+ const clonedChild = child.clone();
+ clonedChild._data = data;
+ const childNodes = renderNodes(clonedChild);
+ for (let i = 0; i < n; i++) nodes[i].append(childNodes[i]);
+ }
+ }
+ return nodes;
+}
+
+export const renderMark = (mark) => postprocess(renderNodes(mark));
+
+export const render = (options) => renderMark(svg("svg", preprocess(options)));
+
+export const tag = (ns) => (tag, data, options) => new Mark(ns, tag, data, options);
+
+export const svg = tag("http://www.w3.org/2000/svg");
+
+export const html = tag(null);
diff --git a/src/emitter.js b/src/emitter.js
deleted file mode 100644
index 7197dc11..00000000
--- a/src/emitter.js
+++ /dev/null
@@ -1,34 +0,0 @@
-function emitter$off(typename, callback) {
- if (arguments.length === 0) this._ = {};
- else if (arguments.length === 1) this._[typename] = [];
- else {
- const { [typename]: callbacks } = this._;
- const index = callbacks.indexOf(callback);
- if (index === -1) return;
- callbacks.splice(index, 1);
- }
-}
-
-function emitter$emit(typename, ...params) {
- const { [typename]: callbacks } = this._;
- if (!callbacks) return;
- for (const callback of callbacks) callback(...params);
-}
-
-function emitter$on(typename, callback) {
- const { [typename]: callbacks = [] } = this._;
- callbacks.push(callback);
- this._[typename] = callbacks;
-}
-
-export function Emitter() {
- Object.defineProperties(this, {
- _: { value: {}, writable: true },
- });
-}
-
-Object.defineProperties(Emitter.prototype, {
- emit: { value: emitter$emit },
- on: { value: emitter$on },
- off: { value: emitter$off },
-});
diff --git a/src/flow/app.js b/src/flow/app.js
deleted file mode 100644
index e4d2baaa..00000000
--- a/src/flow/app.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function flow$app() {
- return this._app;
-}
diff --git a/src/flow/append.js b/src/flow/append.js
deleted file mode 100644
index 81330008..00000000
--- a/src/flow/append.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Flow } from "./index.js";
-import { Node } from "../node.js";
-
-export function valueOf(data, options) {
- const values = Object.entries(options).map(([key, value]) => {
- if (typeof value === "object" && value.constant) return [key, value.value];
- if (typeof value === "object" && value.preserve) return [key, value];
- if (Array.isArray(value)) return [key, data.map((d, i) => value[+d || i])];
- const v = typeof value === "function" ? value : () => value;
- const V = data.map(v);
- return [key, V];
- });
- return Object.fromEntries(values);
-}
-
-export function flow$append(render, options) {
- const flow = this._data ? this : this.data((d) => [d]);
- const groups = flow._groups;
- const parent = this._parent;
- const values = groups.map((group) => ({
- I: group.map((_, i) => i),
- render,
- options,
- value: valueOf(group, options),
- group,
- }));
- const node = new Node(values, [], parent);
- parent._children.push(node);
- return new Flow(groups, null, node, this._app);
-}
diff --git a/src/flow/call.js b/src/flow/call.js
deleted file mode 100644
index 58e7d0fb..00000000
--- a/src/flow/call.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function flow$call(callback, ...params) {
- callback(this, ...params);
- return this;
-}
diff --git a/src/flow/data.js b/src/flow/data.js
deleted file mode 100644
index 6be5a8e5..00000000
--- a/src/flow/data.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Flow } from "./index.js";
-
-export function flow$data(data) {
- if (arguments.length === 0) return this._groups;
-
- if (typeof data !== "function") {
- return new Flow([data], data, this._parent, this._app);
- }
-
- const groups = this._groups;
- const app = this._app;
- const m = groups.length;
- const newGroups = [];
-
- for (let j = 0; j < m; j++) {
- const group = groups[j];
- const n = group.length;
- for (let i = 0; i < n; i++) {
- const newGroup = data.call(this, group[i], i, group, this);
- newGroups.push(newGroup);
- }
- }
-
- return new Flow(newGroups, data, this._parent, app);
-}
diff --git a/src/flow/datum.js b/src/flow/datum.js
deleted file mode 100644
index c437690f..00000000
--- a/src/flow/datum.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function flow$datum(d) {
- if (arguments.length === 0) return this._groups;
- return this.data([d]);
-}
diff --git a/src/flow/index.js b/src/flow/index.js
deleted file mode 100644
index 9fa59c1d..00000000
--- a/src/flow/index.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { flow$append } from "./append.js";
-import { flow$call } from "./call.js";
-import { flow$data } from "./data.js";
-import { flow$datum } from "./datum.js";
-import { flow$app } from "./app.js";
-import { flow$process } from "./process.js";
-import { flow$transform } from "./transform.js";
-
-export function Flow(groups = null, data = null, parent = null, app = null) {
- Object.defineProperties(this, {
- _groups: { value: groups },
- _parent: { value: parent },
- _app: { value: app },
- _data: { value: data },
- });
-}
-
-Object.defineProperties(Flow.prototype, {
- call: { value: flow$call },
- append: { value: flow$append },
- data: { value: flow$data },
- datum: { value: flow$datum },
- app: { value: flow$app },
- process: { value: flow$process },
- transform: { value: flow$transform },
-});
diff --git a/src/flow/process.js b/src/flow/process.js
deleted file mode 100644
index cba0c63c..00000000
--- a/src/flow/process.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { Flow } from "./index.js";
-
-export function flow$process(process, ...params) {
- const processed = process(this, this._groups, ...params);
- return new Flow(processed, this._data, this._parent, this._app);
-}
diff --git a/src/flow/transform.js b/src/flow/transform.js
deleted file mode 100644
index a01a8f10..00000000
--- a/src/flow/transform.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Flow } from "./index.js";
-
-export function flow$transform(transform, ...params) {
- const flow = new Flow(this._groups, this._data, this._parent, this._app);
- if (!this._parent) return flow;
- const data = this._parent._data;
- this._parent._data = transform(this, data, ...params);
- return flow;
-}
diff --git a/src/font/define.js b/src/font/define.js
deleted file mode 100644
index 2dcb45fa..00000000
--- a/src/font/define.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { parseFont } from "figlet";
-
-export function define(name, font) {
- let parsed = false;
- return function () {
- if (!parsed) parseFont(name, font), (parsed = true);
- return name;
- };
-}
diff --git a/src/font/ghost.js b/src/font/ghost.js
deleted file mode 100644
index ec8155fe..00000000
--- a/src/font/ghost.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import font from "figlet/importable-fonts/Ghost.js";
-import { define } from "./define.js";
-
-export const ghost = define("ghost", font);
diff --git a/src/font/standard.js b/src/font/standard.js
deleted file mode 100644
index c174bcd5..00000000
--- a/src/font/standard.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import font from "figlet/importable-fonts/Standard.js";
-import { define } from "./define.js";
-
-export const standard = define("standard", font);
diff --git a/src/gradient/define.js b/src/gradient/define.js
deleted file mode 100644
index 3d05e7d9..00000000
--- a/src/gradient/define.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { linear as scaleLinear } from "../scale/linear.js";
-
-export function defineX(interpolator) {
- return () => {
- return () => {
- return (width) => {
- const scale = scaleLinear([0, width], [0, 1]);
- return (i) => interpolator(scale(i));
- };
- };
- };
-}
diff --git a/src/gradient/rainbowX.js b/src/gradient/rainbowX.js
deleted file mode 100644
index 729b0531..00000000
--- a/src/gradient/rainbowX.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import { interpolateRainbow } from "d3-scale-chromatic";
-import { defineX } from "./define.js";
-
-export const rainbowX = defineX(interpolateRainbow);
diff --git a/src/gradient/sinebowX.js b/src/gradient/sinebowX.js
deleted file mode 100644
index 7f5328a0..00000000
--- a/src/gradient/sinebowX.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import { interpolateSinebow } from "d3-scale-chromatic";
-import { defineX } from "./define.js";
-
-export const sineBowX = defineX(interpolateSinebow);
diff --git a/src/helper/pathContext.js b/src/helper/pathContext.js
deleted file mode 100644
index bd8962f0..00000000
--- a/src/helper/pathContext.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Path } from "d3-path";
-
-function appendArray(strings) {
- let command = [strings[0]];
- for (let i = 1, n = strings.length; i < n; i++) {
- command.push(arguments[i]);
- const string = strings[i];
- if (string !== "," && string !== "") {
- if (string.startsWith(",")) {
- const numbers = string
- .split(",")
- .filter((d) => d !== "")
- .map((d) => +d);
- command.push(...numbers);
- } else {
- this._.push(command);
- command = [string];
- }
- }
- }
- this._.push(command);
-}
-
-class PathContext extends Path {
- constructor() {
- super();
- this._ = [];
- this._append = appendArray;
- }
- toArray() {
- return this._;
- }
-}
-
-export function pathContext() {
- return new PathContext();
-}
diff --git a/src/index.js b/src/index.js
index 24792b2d..8f623a73 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,30 +1 @@
-export * from "./core.js";
-export * from "./webgl.js";
-export * from "./terminal.js";
-export { clamp } from "./math/clamp.js";
-export { random } from "./math/random.js";
-export { randomChar } from "./math/randomChar.js";
-export { randomInt } from "./math/randomInt.js";
-export { randomNoise } from "./math/randomNoise.js";
-export { randomNormal } from "./math/randomNormal.js";
-export { range } from "./array/range.js";
-export { cross } from "./array/cross.js";
-export { extent } from "./array/extent.js";
-export { TWO_PI } from "./constant/TWO_PI.js";
-export { vec } from "./vector/vec.js";
-export { vecAdd } from "./vector/add.js";
-export { vecSub } from "./vector/sub.js";
-export { vecDist, vecDist2 } from "./vector/dist.js";
-export { vecMult } from "./vector/mult.js";
-export { vecDiv } from "./vector/div.js";
-export { vecMag } from "./vector/mag.js";
-export { vecNorm } from "./vector/norm.js";
-export { vecClamp, vecClampX, vecClampY } from "./vector/clamp.js";
-export { vecRandom } from "./vector/random.js";
-export { vecDot } from "./vector/dot.js";
-export { vecCross } from "./vector/cross.js";
-export { vecNeg, vecNegX, vecNegY } from "./vector/neg.js";
-export { vecInX, vecInY } from "./vector/in.js";
-export { vecAngle } from "./vector/angle.js";
-export { vecFromAngle } from "./vector/fromAngle.js";
-export { pathContext } from "./helper/pathContext.js";
+export {svg, html, tag, render, renderMark} from "./dom.js";
diff --git a/src/math/clamp.js b/src/math/clamp.js
deleted file mode 100644
index 46decbc9..00000000
--- a/src/math/clamp.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function clamp(x, min, max) {
- return Math.max(Math.min(max, x), min);
-}
diff --git a/src/math/random.js b/src/math/random.js
deleted file mode 100644
index d56844ff..00000000
--- a/src/math/random.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function random(min, max) {
- [min = 0, max = 1] = arguments.length === 1 ? [0, min] : [min, max];
- return min + (max - min) * Math.random();
-}
diff --git a/src/math/randomChar.js b/src/math/randomChar.js
deleted file mode 100644
index 65360b19..00000000
--- a/src/math/randomChar.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { random } from "./random.js";
-
-export function randomChar() {
- const seed = random(32, 127);
- return String.fromCharCode(seed);
-}
diff --git a/src/math/randomInt.js b/src/math/randomInt.js
deleted file mode 100644
index 5a325fb8..00000000
--- a/src/math/randomInt.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export function randomInt(min, max) {
- [min = 0, max = 1] = arguments.length === 1 ? [0, min] : [min, max];
- min = Math.floor(min);
- max = Math.floor(max) - min;
- return Math.floor(Math.random() * max + min);
-}
diff --git a/src/math/randomNoise.js b/src/math/randomNoise.js
deleted file mode 100644
index 9e8de3fd..00000000
--- a/src/math/randomNoise.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import { linear } from "../scale/linear.js";
-import { random } from "./random.js";
-
-const PERLIN_YWRAPB = 4;
-const PERLIN_YWRAP = 1 << PERLIN_YWRAPB;
-const PERLIN_ZWRAPB = 8;
-const PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB;
-const PERLIN_SIZE = 4095;
-
-function scaledCosine(i) {
- return 0.5 * (1.0 - Math.cos(i * Math.PI));
-}
-
-function randomLcg(seed) {
- const m = 4294967296;
- const a = 1664525;
- const c = 1013904223;
- let z = (seed == null ? Math.random() * m : seed) >>> 0;
- return () => {
- z = (a * z + c) % m;
- return z / m;
- };
-}
-
-function noiseSeed(seed) {
- const random = randomLcg(seed);
- const perlin = new Array(PERLIN_SIZE + 1);
- for (let i = 0; i < PERLIN_SIZE + 1; i++) {
- perlin[i] = random();
- }
- return perlin;
-}
-
-// Adapting from P5.js
-// @see https://github.com/processing/p5.js/blob/1e6b0caa1e8a8dff3280917d2fd9a84d2e7126ba/src/math/noise.js#L200
-export function randomNoise(lo = 0, hi = 1, { octaves = 4, seed = random(100000), falloff = 0.5 } = {}) {
- const map = linear([0, 1], [lo, hi]);
- const perlin = noiseSeed(seed);
-
- const noise = (x, y = 0, z = 0) => {
- if (x < 0) x = -x;
- if (y < 0) y = -y;
- if (z < 0) z = -z;
-
- let xi = Math.floor(x),
- yi = Math.floor(y),
- zi = Math.floor(z);
- let xf = x - xi;
- let yf = y - yi;
- let zf = z - zi;
- let rxf, ryf;
-
- let r = 0;
- let ampl = 0.5;
-
- let n1, n2, n3;
-
- for (let o = 0; o < octaves; o++) {
- let of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB);
-
- rxf = scaledCosine(xf);
- ryf = scaledCosine(yf);
-
- n1 = perlin[of & PERLIN_SIZE];
- n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1);
- n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
- n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2);
- n1 += ryf * (n2 - n1);
-
- of += PERLIN_ZWRAP;
- n2 = perlin[of & PERLIN_SIZE];
- n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2);
- n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
- n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3);
- n2 += ryf * (n3 - n2);
-
- n1 += scaledCosine(zf) * (n2 - n1);
-
- r += n1 * ampl;
- ampl *= falloff;
- xi <<= 1;
- xf *= 2;
- yi <<= 1;
- yf *= 2;
- zi <<= 1;
- zf *= 2;
-
- if (xf >= 1.0) {
- xi++;
- xf--;
- }
- if (yf >= 1.0) {
- yi++;
- yf--;
- }
- if (zf >= 1.0) {
- zi++;
- zf--;
- }
- }
- return r;
- };
-
- return (x0 = 0, y0 = 0, z0 = 0) => map(noise(x0, y0, z0));
-}
diff --git a/src/math/randomNormal.js b/src/math/randomNormal.js
deleted file mode 100644
index d6942621..00000000
--- a/src/math/randomNormal.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// @see https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
-// @see https://github.com/d3/d3-random/blob/main/src/normal.js
-export function randomNormal(mu = 0, sigma = 1) {
- let x = null;
- let r = null;
- return () => {
- let y;
- if (x !== null) (y = x), (x = null);
- else
- do {
- x = Math.random() * 2 - 1;
- y = Math.random() * 2 - 1;
- r = x * x + y * y;
- } while (!r || r > 1);
- return mu + sigma * y * Math.sqrt((-2 * Math.log(r)) / r);
- };
-}
diff --git a/src/measure.js b/src/measure.js
deleted file mode 100644
index 4642e15f..00000000
--- a/src/measure.js
+++ /dev/null
@@ -1,22 +0,0 @@
-export function measureText(text, styles) {
- const span = document.createElement("span");
-
- // Hide span.
- span.style.visibility = "hidden";
- span.style.position = "absolute";
- span.style.display = "inline-block";
- span.style.left = "-9999em";
- span.style.top = "0";
- span.style.lineHeight = "normal";
- span.setAttribute("aria-hidden", true);
-
- // Font attributes.
- span.style.fontSize = `${styles.fontSize}px`;
- span.style.fontFamily = styles.fontFamily;
-
- span.innerHTML = text;
- document.body.appendChild(span);
-
- const bbox = span.getBoundingClientRect();
- return { width: bbox.width, height: Math.ceil(bbox.height) };
-}
diff --git a/src/node.js b/src/node.js
deleted file mode 100644
index 8ee96bd8..00000000
--- a/src/node.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function Node(data = null, children = [], parent = null) {
- Object.defineProperties(this, {
- _data: { value: data, writable: true },
- _children: { value: children, writable: true },
- _parent: { value: parent },
- });
-}
diff --git a/src/process/derive.js b/src/process/derive.js
deleted file mode 100644
index 8c02c8ea..00000000
--- a/src/process/derive.js
+++ /dev/null
@@ -1,15 +0,0 @@
-export function derive(flow, groups, options) {
- const m = groups.length;
- const newGroups = new Array(m);
- const callback = (d, i, data) => {
- const obj = typeof d === "object" ? { ...d } : {};
- for (const [key, value] of Object.entries(options)) {
- obj[key] = typeof value === "function" ? value.call(flow, d, i, data) : value;
- }
- return obj;
- };
- for (let j = 0; j < m; j++) {
- newGroups[j] = groups[j].map((d, i, data) => callback.call(flow, d, i, data));
- }
- return newGroups;
-}
diff --git a/src/process/each.js b/src/process/each.js
deleted file mode 100644
index 1390a895..00000000
--- a/src/process/each.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export function each(flow, groups, callback) {
- const m = groups.length;
- for (let j = 0; j < m; j++) {
- const group = groups[j];
- for (let i = 0; i < group.length; i++) {
- const datum = group[i];
- callback.call(flow, datum, i, group, flow);
- }
- }
- return groups;
-}
diff --git a/src/process/eachRight.js b/src/process/eachRight.js
deleted file mode 100644
index 1d5df1e3..00000000
--- a/src/process/eachRight.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export function eachRight(flow, groups, callback) {
- const m = groups.length;
- for (let j = 0; j < m; j++) {
- const group = groups[j];
- const n = group.length;
- for (let i = n - 1; i >= 0; i--) {
- const datum = group[i];
- callback.call(flow, datum, i, group, flow);
- }
- }
- return groups;
-}
diff --git a/src/process/filter.js b/src/process/filter.js
deleted file mode 100644
index c1acca1c..00000000
--- a/src/process/filter.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export function filter(flow, groups, match) {
- const m = groups.length;
- const subgroups = new Array(m);
- for (let j = 0; j < m; j++) {
- const subgroup = (subgroups[j] = []);
- const group = groups[j];
- const n = group.length;
- for (let i = 0; i < n; i++) {
- const d = group[i];
- if (match.call(flow, d, i, group)) subgroup.push(d);
- }
- }
- return subgroups;
-}
diff --git a/src/process/map.js b/src/process/map.js
deleted file mode 100644
index aa87d834..00000000
--- a/src/process/map.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export function map(flow, groups, callback) {
- const m = groups.length;
- const newGroups = new Array(m);
- for (let j = 0; j < m; j++) {
- newGroups[j] = groups[j].map((d, i, data) => callback.call(flow, d, i, data));
- }
- return newGroups;
-}
diff --git a/src/process/push.js b/src/process/push.js
deleted file mode 100644
index ba53f331..00000000
--- a/src/process/push.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export function push(flow, groups, datum) {
- const m = groups.length;
- const datumOf = typeof datum === "function" ? datum : () => datum;
- for (let i = 0; i < m; i++) {
- const group = groups[i];
- const datum = datumOf.call(flow, group, i, groups, flow);
- group.push(datum);
- }
- return groups;
-}
diff --git a/src/scale/continuous.js b/src/scale/continuous.js
deleted file mode 100644
index 62eabfd2..00000000
--- a/src/scale/continuous.js
+++ /dev/null
@@ -1,16 +0,0 @@
-function interpolateNumber(a, b) {
- return (t) => a * (1 - t) + t * b;
-}
-
-function normalizeNumber(a, b) {
- return (n) => (b - a === 0 ? 0.5 : (n - a) / (b - a));
-}
-
-export function continuous(domain, [r, r1] = [0, 1], { transform, unknown, interpolate = interpolateNumber(r, r1) }) {
- const [d, d1] = domain.map(transform);
- const normalize = normalizeNumber(d, d1);
- return (x) => {
- if (x === null || Number.isNaN(x)) return unknown;
- return interpolate(normalize(transform(x)));
- };
-}
diff --git a/src/scale/linear.js b/src/scale/linear.js
deleted file mode 100644
index 34209513..00000000
--- a/src/scale/linear.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { continuous } from "./continuous";
-
-function transform(x) {
- return x;
-}
-
-export function linear(domain, range, options) {
- return continuous(domain, range, { ...options, transform });
-}
diff --git a/src/scale/log.js b/src/scale/log.js
deleted file mode 100644
index 1a25ad97..00000000
--- a/src/scale/log.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { continuous } from "./continuous";
-
-function transform(x) {
- return Math.log(x);
-}
-
-export function log(domain, range, options = {}) {
- return continuous(domain, range, { ...options, transform });
-}
diff --git a/src/scale/ordinal.js b/src/scale/ordinal.js
deleted file mode 100644
index 190c3dc0..00000000
--- a/src/scale/ordinal.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export function ordinal(domain, range, { unknown } = {}) {
- const map = new Map(domain.map((d, i) => [d, i]));
- return (x) => {
- const i = map.get(x);
- if (i === undefined) return unknown;
- return range[i % range.length];
- };
-}
-
-ordinal.ordinal = true;
diff --git a/src/scale/sqrt.js b/src/scale/sqrt.js
deleted file mode 100644
index 14bfbfc3..00000000
--- a/src/scale/sqrt.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { continuous } from "./continuous";
-
-function transform(x) {
- return Math.sign(x) * Math.sqrt(Math.abs(x));
-}
-
-export function sqrt(domain, range, options = {}) {
- return continuous(domain, range, { ...options, transform });
-}
diff --git a/src/shape/circle.js b/src/shape/circle.js
deleted file mode 100644
index 0d716288..00000000
--- a/src/shape/circle.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { define } from "./define.js";
-
-export const circle = define((renderer, I, value, options, group) => {
- if (renderer.circles) return renderer.circles(I, value, group);
- const {
- x: X = [],
- y: Y = [],
- r: R = [],
- fill: F = [],
- stroke: S = [],
- strokeWidth: SW = [],
- fillOpacity: FO = [],
- strokeOpacity: SO = [],
- } = value;
- for (const i of I) {
- renderer.circle({
- x: X[i],
- y: Y[i],
- r: R[i],
- fill: F[i],
- stroke: S[i],
- strokeWidth: SW[i],
- fillOpacity: FO[i],
- strokeOpacity: SO[i],
- });
- }
-});
diff --git a/src/shape/clear.js b/src/shape/clear.js
deleted file mode 100644
index 6eb58458..00000000
--- a/src/shape/clear.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { define } from "./define.js";
-
-export const clear = define((renderer, I, value) => {
- const { fill: F = [] } = value;
- for (const i of I) renderer.clear({ fill: F[i] });
-});
diff --git a/src/shape/define.js b/src/shape/define.js
deleted file mode 100644
index 71205819..00000000
--- a/src/shape/define.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function define(shape) {
- shape.builtin = true;
- return shape;
-}
diff --git a/src/shape/group.js b/src/shape/group.js
deleted file mode 100644
index 06238b28..00000000
--- a/src/shape/group.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { define } from "./define.js";
-
-export const group = define(() => {});
diff --git a/src/shape/line.js b/src/shape/line.js
deleted file mode 100644
index 8811a840..00000000
--- a/src/shape/line.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { define } from "./define.js";
-
-export const line = define((renderer, I, value, options) => {
- const { close = false } = options;
- const { x: X, y: Y, stroke: S = [], strokeWidth: SW = [] } = value;
- const first = I[0];
- const D = [["M", X[first], Y[first]]];
- const k = I.length;
- for (let m = 1, i = I[m]; m < k; m++, i = I[m]) D.push(["L", X[i], Y[i]]);
- if (close) D.push(["Z"]);
- renderer.path({
- d: D,
- stroke: S[first],
- strokeWidth: SW[first],
- });
-});
diff --git a/src/shape/link.js b/src/shape/link.js
deleted file mode 100644
index 67b43c82..00000000
--- a/src/shape/link.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { define } from "./define.js";
-
-const defaults = {
- stroke: "#000",
- strokeWidth: 1,
-};
-
-export const link = define((renderer, I, value) => {
- const {
- x: X,
- y: Y,
- x1: X1,
- y1: Y1,
- stroke: S = [],
- strokeWidth: SW = [],
- strokeCap: SC = [],
- rotate: R = [],
- transformOrigin: TO = [],
- } = value;
- for (const i of I) {
- renderer.line({
- ...defaults,
- x: X[i],
- y: Y[i],
- x1: X1[i],
- y1: Y1[i],
- rotate: R[i],
- transformOrigin: TO[i],
- ...(S[i] && { stroke: S[i] }),
- ...(SW[i] && { strokeWidth: SW[i] }),
- ...(SC[i] && { strokeCap: SC[i] }),
- });
- }
-});
diff --git a/src/shape/path.js b/src/shape/path.js
deleted file mode 100644
index 7a6e7fc1..00000000
--- a/src/shape/path.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { define } from "./define.js";
-
-export const path = define((renderer, I, value) => {
- const {
- x: X = [],
- y: Y = [],
- fill: F = [],
- stroke: S = [],
- fillOpacity: FO = [],
- strokeOpacity: SO = [],
- strokeWidth: SW = [],
- d: D = [],
- } = value;
- for (const i of I) {
- renderer.path({
- x: X[i],
- y: Y[i],
- d: D[i],
- stroke: S[i],
- fill: F[i],
- fillOpacity: FO[i],
- strokeOpacity: SO[i],
- strokeWidth: SW[i],
- });
- }
-});
diff --git a/src/shape/point.js b/src/shape/point.js
deleted file mode 100644
index 866e94a5..00000000
--- a/src/shape/point.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { define } from "./define.js";
-
-export const point = define((renderer, I, value) => {
- const {
- x: X = [],
- y: Y = [],
- stroke: S = [],
- strokeWidth: SW = [],
- stroke0: SC = [],
- stroke1: SF = [],
- stroke2: SB = [],
- } = value;
- for (const i of I) {
- renderer.point({
- x: X[i],
- y: Y[i],
- stroke: S[i],
- stroke0: SC[i],
- stroke1: SF[i],
- stroke2: SB[i],
- strokeWidth: SW[i],
- });
- }
-});
diff --git a/src/shape/polygon.js b/src/shape/polygon.js
deleted file mode 100644
index fe20ff3c..00000000
--- a/src/shape/polygon.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { define } from "./define.js";
-
-function isFlatArray(value) {
- return Array.isArray(value) && !Array.isArray(value[0]);
-}
-
-export const polygon = define((renderer, I, value) => {
- const {
- x: X = [],
- y: Y = [],
- fill: F = [],
- stroke: S = [],
- fillOpacity: FO = [],
- strokeOpacity: SO = [],
- strokeWidth: SW = [],
- } = value;
-
- if (isFlatArray(X)) {
- return renderer.polygon({
- x: X,
- y: Y,
- stroke: S[0],
- fill: F[0],
- fillOpacity: FO[0],
- strokeOpacity: SO[0],
- strokeWidth: SW[0],
- });
- }
-
- for (const i of I) {
- renderer.polygon({
- x: X[i],
- y: Y[i],
- stroke: S[i],
- fill: F[i],
- fillOpacity: FO[i],
- strokeOpacity: SO[i],
- strokeWidth: SW[i],
- });
- }
-});
diff --git a/src/shape/rect.js b/src/shape/rect.js
deleted file mode 100644
index c0a45685..00000000
--- a/src/shape/rect.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { define } from "./define.js";
-
-export const rect = define((renderer, I, value) => {
- if (renderer.rects) return renderer.rects(I, value);
- const {
- x: X = [],
- y: Y = [],
- width: W = [],
- height: H = [],
- fill: F = [],
- stroke: S = [],
- rotate: R = [],
- anchor: A = [],
- fillOpacity: FO = [],
- strokeOpacity: SO = [],
- strokeWidth: SW = [],
- } = value;
- for (const i of I) {
- renderer.rect({
- x: X[i],
- y: Y[i],
- width: W[i],
- height: H[i],
- stroke: S[i],
- fill: F[i],
- fillOpacity: FO[i],
- strokeOpacity: SO[i],
- strokeWidth: SW[i],
- ...(A[i] && { anchor: A[i] }),
- ...(R[i] && { rotate: R[i] }),
- });
- }
-});
diff --git a/src/shape/text.js b/src/shape/text.js
deleted file mode 100644
index 724a89aa..00000000
--- a/src/shape/text.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { define } from "./define.js";
-
-export const text = define((renderer, I, value) => {
- const {
- text: T = [],
- x: X = [],
- y: Y = [],
- fill: F = [],
- stroke: S = [],
- fillOpacity: FO = [],
- strokeOpacity: SO = [],
- strokeWidth: SW = [],
- textBaseline: TB = [],
- textAlign: TA = [],
- fontFamily: FF = [],
- fontSize: FS = [],
- } = value;
- for (const i of I) {
- renderer.text({
- x: X[i],
- y: Y[i],
- text: T[i],
- stroke: S[i],
- fill: F[i],
- fillOpacity: FO[i],
- strokeOpacity: SO[i],
- strokeWidth: SW[i],
- textBaseline: TB[i],
- textAlign: TA[i],
- fontFamily: FF[i],
- fontSize: FS[i],
- });
- }
-});
diff --git a/src/shape/triangle.js b/src/shape/triangle.js
deleted file mode 100644
index ef79009d..00000000
--- a/src/shape/triangle.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { define } from "./define.js";
-
-export const triangle = define((renderer, I, value) => {
- if (renderer.triangles) return renderer.triangles(I, value);
- const {
- x: X,
- y: Y,
- x1: X1,
- y1: Y1,
- x2: X2,
- y2: Y2,
- fill: F = [],
- stroke: S = [],
- strokeWidth: SW = [],
- rotate: R = [],
- } = value;
- for (const i of I) {
- renderer.triangle({
- x: X[i],
- y: Y[i],
- x1: X1[i],
- y1: Y1[i],
- x2: X2[i],
- y2: Y2[i],
- fill: F[i],
- stroke: S[i],
- strokeWidth: SW[i],
- rotate: R[i],
- });
- }
-});
diff --git a/src/terminal.js b/src/terminal.js
deleted file mode 100644
index 062d3cf0..00000000
--- a/src/terminal.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export { cfb } from "./attribute/cfb.js";
-export { wch } from "./attribute/wch.js";
-export { figlet } from "./attribute/figlet.js";
-export { terminal } from "./terminal/index.js";
-export { ghost as fontGhost } from "./font/ghost.js";
-export { standard as fontStandard } from "./font/standard.js";
-export { rainbowX as gradientRainBowX } from "./gradient/rainbowX.js";
-export { sineBowX as gradientSineBowX } from "./gradient/sinebowX.js";
diff --git a/src/terminal/cfb.js b/src/terminal/cfb.js
deleted file mode 100644
index d20695be..00000000
--- a/src/terminal/cfb.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { NULL_VALUE } from "./constant.js";
-import { normalizeColor } from "../color.js";
-
-function encodeColor(color) {
- if (color === NULL_VALUE || color === null) return NULL_VALUE;
- const [r, g, b] = normalizeColor(color);
- return b + (g << 8) + (r << 16);
-}
-
-function encodeChar(ch) {
- if (Array.isArray(ch)) return ch;
- return Array.from(ch)
- .slice(0, 2)
- .map((ch) => ch.codePointAt(0));
-}
-
-export function cfb(color) {
- const { ch = " ", fg = "#fff", bg = null } = color;
- const [n, n1 = NULL_VALUE] = encodeChar(ch);
- return [n, n1, encodeColor(fg), encodeColor(bg)];
-}
diff --git a/src/terminal/char.js b/src/terminal/char.js
deleted file mode 100644
index c368d8e0..00000000
--- a/src/terminal/char.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { CELL_SIZE, TEXT_BASELINE } from "./constant.js";
-
-export function terminal$char(char, i, j, fg, bg, wide = false) {
- const { width: cols, mode, cellWidth, cellHeight, fontWeight, fontSize, fontFamily } = this._props;
-
- const x = cellWidth * i;
- const y = cellHeight * j;
- const index = (cols * j + i) * CELL_SIZE;
-
- if (bg) {
- this._context.fillStyle = bg;
- this._context.fillRect(x, y, cellWidth, cellHeight);
- this._buffer[index + 2] = bg;
- }
-
- if (fg) {
- this._context.fillStyle = fg;
- this._buffer[index + 1] = fg;
- }
-
- if (!char) return;
- this._context.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
- this._context.textBaseline = TEXT_BASELINE;
- this._context.fillText(char, x, y + cellHeight);
- this._buffer[index] = char;
-
- if (mode !== "double" || wide) return;
- this._context.fillText(char, x + cellWidth / 2, y + cellHeight);
- this._buffer[index] += char;
-
- return this;
-}
diff --git a/src/terminal/clear.js b/src/terminal/clear.js
deleted file mode 100644
index e2e33493..00000000
--- a/src/terminal/clear.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export function terminal$clear({ fill = "#000" }) {
- const { pixelWidth, pixelHeight } = this._props;
- this._context.fillStyle = fill;
- this._context.fillRect(0, 0, pixelWidth, pixelHeight);
-}
diff --git a/src/terminal/color.js b/src/terminal/color.js
deleted file mode 100644
index de62f772..00000000
--- a/src/terminal/color.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export function strokeOf(options) {
- const { stroke, stroke0, stroke1, stroke2 } = options;
- if (!(stroke || stroke0 || stroke1 || stroke2)) return;
- return {
- ...stroke,
- ...(stroke0 && { ch: stroke0 }),
- ...(stroke1 && { fg: stroke1 }),
- ...(stroke2 && { bg: stroke2 }),
- };
-}
diff --git a/src/terminal/constant.js b/src/terminal/constant.js
deleted file mode 100644
index 4ea8a908..00000000
--- a/src/terminal/constant.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// For vite env.
-const isNode = typeof navigator === "undefined" ? true : false;
-
-const userAgent = isNode ? "node" : navigator.userAgent;
-
-const isFireFox = userAgent.includes("Firefox");
-
-const isLegacyEdge = userAgent.includes("Edge");
-
-export const NULL_VALUE = 0xffffffff;
-
-export const CELL_SIZE = 4;
-
-// https://github.com/xtermjs/xterm.js/blob/096fe171356fc9519e0a6b737a98ca82d0587e91/src/browser/renderer/shared/Constants.ts#LL14C1-L14C1
-export const TEXT_BASELINE = isFireFox || isLegacyEdge ? "bottom" : "ideographic";
diff --git a/src/terminal/index.js b/src/terminal/index.js
deleted file mode 100644
index 77516adb..00000000
--- a/src/terminal/index.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import init from "../backend/index.js";
-import wasm from "../backend/index_bg.wasm";
-import { terminal$init } from "./init.js";
-import { terminal$node } from "./node.js";
-import { terminal$char } from "./char.js";
-import { terminal$render } from "./render.js";
-import { terminal$point } from "./point.js";
-import { terminal$text } from "./text.js";
-import { terminal$rect } from "./rect.js";
-import { terminal$line } from "./line.js";
-import { terminal$clear } from "./clear.js";
-import { terminal$save } from "./save.js";
-import { terminal$restore } from "./restore.js";
-import { terminal$rotate } from "./rotate.js";
-import { terminal$scale } from "./scale.js";
-import { terminal$translate } from "./translate.js";
-import { terminal$toString } from "./toString.js";
-import { terminal$textBBox } from "./textBBox.js";
-
-function Terminal(memory) {
- Object.defineProperties(this, {
- _memory: { value: memory },
- _props: { value: {}, writable: true },
- _buffer: { value: null, writable: true },
- _context: { value: null, writable: true },
- _backend: { value: null, writable: true },
- });
-}
-
-Object.defineProperties(Terminal.prototype, {
- init: { value: terminal$init },
- node: { value: terminal$node },
- char: { value: terminal$char },
- render: { value: terminal$render },
- point: { value: terminal$point },
- rect: { value: terminal$rect },
- line: { value: terminal$line },
- text: { value: terminal$text },
- clear: { value: terminal$clear },
- save: { value: terminal$save },
- restore: { value: terminal$restore },
- rotate: { value: terminal$rotate },
- scale: { value: terminal$scale },
- translate: { value: terminal$translate },
- toString: { value: terminal$toString },
- textBBox: { value: terminal$textBBox },
-});
-
-export async function terminal() {
- const module = await init(typeof wasm === "function" ? await wasm() : undefined);
- const { memory } = module;
- return new Terminal(memory);
-}
diff --git a/src/terminal/init.js b/src/terminal/init.js
deleted file mode 100644
index a0760830..00000000
--- a/src/terminal/init.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { context2d } from "../context.js";
-import { Backend } from "../backend/index.js";
-import { measureText } from "../measure.js";
-
-export const TERMINAL_CLASS = "charming-terminal";
-
-function dimensionOf(count, pixel, unit) {
- if (count === undefined) return (pixel / unit) | 0;
- return count;
-}
-
-// Default options from: https://github.com/xtermjs/xterm.js/blob/ac0207bf2e8a923d0cff95cc383f6f3e36a2e923/src/common/services/OptionsService.ts#LL12C1-L12C1
-export function terminal$init({
- width,
- height,
- cols: _cols,
- rows: _rows,
- mode = "single",
- fontFamily = "courier-new, courier, monospace",
- fontSize = 15,
- fontWeight = "normal",
-} = {}) {
- const { width: tw, height: th } = measureText("W", {
- fontSize,
- fontFamily,
- fontWeight,
- });
- const cellWidth = mode === "double" ? tw * 2 : tw;
- const cellHeight = th;
- const cols = _cols ?? undefined;
- const rows = _rows ?? undefined;
- const computedCols = dimensionOf(cols, width, cellWidth);
- const computedRows = dimensionOf(rows, height, cellHeight);
- const computedWidth = computedCols * cellWidth;
- const computedHeight = computedRows * cellHeight;
- const context = context2d(computedWidth, computedHeight);
- const buffer = Array.from({ length: computedCols * computedRows }, () => null);
- context.canvas.classList.add(TERMINAL_CLASS);
- const backend = Backend.new(computedCols, computedRows);
- Object.assign(this._props, {
- mode,
- cellWidth,
- cellHeight,
- fontSize,
- fontFamily,
- fontWeight,
- width: computedCols,
- height: computedRows,
- pixelWidth: computedWidth,
- pixelHeight: computedHeight,
- });
- Object.assign(this, {
- _buffer: buffer,
- _context: context,
- _backend: backend,
- });
- this.clear({ fill: "#000" });
- return this;
-}
diff --git a/src/terminal/line.js b/src/terminal/line.js
deleted file mode 100644
index 1724b1b0..00000000
--- a/src/terminal/line.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { cfb } from "./cfb.js";
-
-export function terminal$line({ x, y, x1, y1, stroke }) {
- if (!stroke) this._backend.noStroke();
- else this._backend.stroke(...cfb(stroke));
- this._backend.noFill();
- this._backend.line(x, y, x1, y1);
-}
diff --git a/src/terminal/node.js b/src/terminal/node.js
deleted file mode 100644
index d52b3113..00000000
--- a/src/terminal/node.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function terminal$node() {
- return this._context.canvas;
-}
diff --git a/src/terminal/point.js b/src/terminal/point.js
deleted file mode 100644
index d8fd743f..00000000
--- a/src/terminal/point.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { cfb } from "./cfb.js";
-import { strokeOf } from "./color.js";
-
-export function terminal$point({ x, y, ...rest }) {
- const stroke = strokeOf(rest);
- if (!stroke) this._backend.noStroke();
- this._backend.stroke(...cfb(stroke));
- this._backend.point(x, y);
-}
diff --git a/src/terminal/rect.js b/src/terminal/rect.js
deleted file mode 100644
index 461e42d7..00000000
--- a/src/terminal/rect.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { cfb } from "./cfb.js";
-import { strokeOf } from "./color.js";
-
-export function terminal$rect({ x, y, width, height, fill, ...rest }) {
- const stroke = strokeOf(rest);
-
- if (!stroke) this._backend.noStroke();
- else this._backend.stroke(...cfb(stroke));
-
- if (!fill) this._backend.noFill();
- else this._backend.fill(...cfb(fill));
-
- this._backend.rect(x, y, width, height);
-}
diff --git a/src/terminal/render.js b/src/terminal/render.js
deleted file mode 100644
index 5eafc941..00000000
--- a/src/terminal/render.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { CELL_SIZE, NULL_VALUE } from "./constant.js";
-
-function decodeColor(color) {
- if (color === NULL_VALUE) return undefined;
- const r = (color & 0xff0000) >> 16;
- const g = (color & 0x00ff00) >> 8;
- const b = color & 0x0000ff;
- return `rgb(${r}, ${g}, ${b})`;
-}
-
-function decodeChar(n) {
- if (n === NULL_VALUE) return ["", 0];
- const first = n >> 31;
- const n1 = n & 0x0fffffff;
- const w = first === 0 ? 1 : 2;
- return [String.fromCodePoint(n1), w];
-}
-
-export function terminal$render() {
- const bufferPtr = this._backend.render();
- const { width: cols, height: rows } = this._props;
- const buffer = new Uint32Array(this._memory.buffer, bufferPtr, cols * rows * CELL_SIZE);
- for (let i = 0; i < rows; i++) {
- for (let j = 0; j < cols; j++) {
- const index = (cols * i + j) * CELL_SIZE;
- const [ch, wch] = decodeChar(buffer[index]);
- const [ch1, wch1] = decodeChar(buffer[index + 1]);
- const fg = decodeColor(buffer[index + 2]);
- const bg = decodeColor(buffer[index + 3]);
- const wide = wch + wch1 >= 2;
- const ch2 = ch + ch1;
- if (ch2 || fg) this.char(ch2, j, i, fg, bg, wide);
- }
- }
- return this;
-}
diff --git a/src/terminal/restore.js b/src/terminal/restore.js
deleted file mode 100644
index fe6abf6a..00000000
--- a/src/terminal/restore.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function terminal$restore() {
- this._backend.popMatrix();
-}
diff --git a/src/terminal/rotate.js b/src/terminal/rotate.js
deleted file mode 100644
index 06b98b55..00000000
--- a/src/terminal/rotate.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function terminal$rotate(angle) {
- this._context.rotate(angle);
-}
diff --git a/src/terminal/save.js b/src/terminal/save.js
deleted file mode 100644
index 5870b974..00000000
--- a/src/terminal/save.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function terminal$save() {
- this._backend.pushMatrix();
-}
diff --git a/src/terminal/scale.js b/src/terminal/scale.js
deleted file mode 100644
index 69e6cec5..00000000
--- a/src/terminal/scale.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export function terminal$scale(x, y) {
- this._backend.scale(x, y);
- return this;
-}
diff --git a/src/terminal/text.js b/src/terminal/text.js
deleted file mode 100644
index 01f7c16d..00000000
--- a/src/terminal/text.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { standard as fontStandard } from "../font/standard.js";
-import { bboxOf, textOf } from "./textBBox.js";
-
-function normalizeColor(color, width, height) {
- if (typeof color === "function") return color(width, height);
- return () => color;
-}
-
-export function terminal$text({
- x,
- y,
- text,
- fill,
- textAlign = "start",
- textBaseline = "top",
- fontFamily = fontStandard(),
-}) {
- const matrix = textOf(text, fontFamily);
- const {
- x: startX,
- y: startY,
- width: textWidth,
- height: textHeight,
- lines,
- } = bboxOf(matrix, {
- x,
- y,
- textAlign,
- textBaseline,
- });
- fill = normalizeColor(fill, textWidth, textHeight);
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i];
- for (let j = 0; j < line.length; j++) {
- const ch = line[j];
- this.point({
- x: startX + j,
- y: startY + i,
- stroke: { ch, fg: fill(j, i) },
- });
- }
- }
-}
diff --git a/src/terminal/textBBox.js b/src/terminal/textBBox.js
deleted file mode 100644
index 177135d8..00000000
--- a/src/terminal/textBBox.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { textSync } from "figlet";
-import { standard as fontStandard } from "../font/standard.js";
-
-export function bboxOf(matrix, { x, y, textAlign, textBaseline }) {
- const lines = matrix.split("\n");
- const height = lines.length;
- const width = Math.max(...lines.map((l) => l.length));
- const startX = textAlign === "end" ? x - width : textAlign === "center" ? x - width / 2 : x;
- const startY = textBaseline === "bottom" ? y - height : textBaseline === "middle" ? y - height / 2 : y;
- return { lines, x: startX, y: startY, width, height };
-}
-
-export function textOf(text, font) {
- if (typeof text === "object" && text.type === "figlet") return textSync(text.text + "", { font });
- return text + "";
-}
-
-export function terminal$textBBox({
- text,
- fontFamily = fontStandard(),
- textAlign = "start",
- textBaseline = "top",
- x = 0,
- y = 0,
-} = {}) {
- const matrix = textOf(text, fontFamily);
- const bbox = bboxOf(matrix, { textAlign, textBaseline, x, y });
- return { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height };
-}
diff --git a/src/terminal/toString.js b/src/terminal/toString.js
deleted file mode 100644
index 35964ec2..00000000
--- a/src/terminal/toString.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { CELL_SIZE } from "./constant.js";
-
-export function terminal$toString() {
- let string = "";
- const { width: cols, height: rows } = this._props;
- const { _mode: mode, _buffer: buffer } = this;
- for (let j = 0; j < rows; j++) {
- if (j !== 0) string += "\n";
- for (let i = 0; i < cols; i++) {
- const index = (cols * j + i) * CELL_SIZE;
- const empty = mode === "double" ? "Β·Β·" : "Β·";
- const char = buffer[index] || empty;
- string += char;
- }
- }
- return string;
-}
diff --git a/src/terminal/translate.js b/src/terminal/translate.js
deleted file mode 100644
index b331f5de..00000000
--- a/src/terminal/translate.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function terminal$translate(x, y) {
- this._backend.translate(x, y);
-}
diff --git a/src/transform/mapAttrs.js b/src/transform/mapAttrs.js
deleted file mode 100644
index 28ecb70a..00000000
--- a/src/transform/mapAttrs.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { linear } from "../scale/linear.js";
-
-function extentQ(array) {
- return [Math.min(...array), Math.max(...array)];
-}
-
-function extentO(array) {
- return Array.from(new Set(array));
-}
-
-function extent(scale, array) {
- return scale.ordinal ? extentO(array) : extentQ(array);
-}
-
-function applyScale(value, options) {
- return Object.fromEntries(
- Object.entries(value).map(([key, V]) => {
- const { [key]: descriptor } = options;
- if (!descriptor) return [key, V];
- const { range, scale = linear, domain = extent(scale, V), ...rest } = descriptor;
- const map = scale(domain, range, rest);
- const scaled = V.map(map);
- return [key, scaled];
- }),
- );
-}
-
-export function mapAttrs(_, data, options) {
- const m = data.length;
- for (let i = 0; i < m; i++) {
- const context = data[i];
- context.value = applyScale(context.value, options);
- }
- return data;
-}
diff --git a/src/transform/mapPosition.js b/src/transform/mapPosition.js
deleted file mode 100644
index a1a82523..00000000
--- a/src/transform/mapPosition.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { mapAttrs } from "./mapAttrs.js";
-
-export function mapPosition(
- flow,
- data,
- {
- scaleX,
- scaleY,
- domainX,
- domainY,
- reverseY = false,
- reverseX = false,
- padding = 0,
- keysX = ["x", "x1"],
- keysY = ["y", "y1"],
- } = {},
-) {
- const app = flow.app();
- const width = app.prop("width");
- const height = app.prop("height");
- const rangeX = [padding, width - padding];
- const rangeY = [padding, height - padding];
-
- if (reverseX) rangeX.reverse();
- if (reverseY) rangeY.reverse();
-
- const X = keysX.map((key) => [
- key,
- {
- domain: domainX,
- range: rangeX,
- scale: scaleX,
- },
- ]);
-
- const Y = keysY.map((key) => [
- key,
- {
- domain: domainY,
- range: rangeY,
- scale: scaleY,
- },
- ]);
-
- return mapAttrs(flow, data, Object.fromEntries([...X, ...Y]));
-}
diff --git a/src/vector/add.js b/src/vector/add.js
deleted file mode 100644
index 3ce088bb..00000000
--- a/src/vector/add.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { staticize } from "./staticize.js";
-import { memberize } from "./memberize.js";
-
-function add(out, a, b) {
- out.x = a.x + b.x;
- out.y = a.y + b.y;
-}
-
-export const vec$add = memberize(add);
-
-export const vecAdd = staticize(add);
diff --git a/src/vector/angle.js b/src/vector/angle.js
deleted file mode 100644
index 167cba5d..00000000
--- a/src/vector/angle.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function vec$angle() {
- return Math.atan2(this.y, this.x);
-}
-
-export function vecAngle(a) {
- return vec$angle.call(a);
-}
diff --git a/src/vector/clamp.js b/src/vector/clamp.js
deleted file mode 100644
index f472470c..00000000
--- a/src/vector/clamp.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { memberize } from "./memberize.js";
-import { staticize } from "./staticize.js";
-
-function clamp(out, a, m, m1) {
- const [min, max] = arguments.length === 3 ? [0, m] : [m, m1];
- const m0 = out.set(a).mag();
- if (m0 < min) out.mag(min);
- if (m0 > max) out.mag(max);
-}
-
-function clampX(out, a, x, x1) {
- const [min, max] = arguments.length === 3 ? [0, x] : [x, x1];
- const x0 = Math.max(min, Math.min(max, a.x));
- out.x = x0;
- out.y = a.y;
-}
-
-function clampY(out, a, y, y1) {
- const [min, max] = arguments.length === 3 ? [0, y] : [y, y1];
- const y0 = Math.max(min, Math.min(max, a.y));
- out.x = a.x;
- out.y = y0;
-}
-
-export const vec$clampX = memberize(clampX);
-export const vec$clampY = memberize(clampY);
-export const vec$clamp = memberize(clamp);
-
-export const vecClampX = staticize(clampX);
-export const vecClampY = staticize(clampY);
-export const vecClamp = staticize(clamp);
diff --git a/src/vector/clone.js b/src/vector/clone.js
deleted file mode 100644
index 629d8c38..00000000
--- a/src/vector/clone.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Vec } from "./vec.js";
-
-export function vec$clone() {
- return new Vec(this.x, this.y);
-}
diff --git a/src/vector/cross.js b/src/vector/cross.js
deleted file mode 100644
index 6569ef59..00000000
--- a/src/vector/cross.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function vec$cross(a) {
- return vecCross(this, a);
-}
-
-export function vecCross(a, b) {
- return a.x * b.y - a.y * b.x;
-}
diff --git a/src/vector/dist.js b/src/vector/dist.js
deleted file mode 100644
index 993cd88c..00000000
--- a/src/vector/dist.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export function vec$dist(a) {
- return vecDist(this, a);
-}
-
-export function vec$dist2(a) {
- return vecDist2(this, a);
-}
-
-export function vecDist(a, b) {
- return Math.sqrt(vecDist2(a, b));
-}
-
-export function vecDist2(a, b) {
- const dx = a.x - b.x;
- const dy = a.y - b.y;
- return dx * dx + dy * dy;
-}
diff --git a/src/vector/div.js b/src/vector/div.js
deleted file mode 100644
index 5ae2f4d1..00000000
--- a/src/vector/div.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { memberize } from "./memberize.js";
-import { staticize } from "./staticize.js";
-
-function div(out, a, s) {
- out.x = a.x / s;
- out.y = a.y / s;
-}
-
-export const vec$div = memberize(div);
-
-export const vecDiv = staticize(div);
diff --git a/src/vector/dot.js b/src/vector/dot.js
deleted file mode 100644
index 27b14a8b..00000000
--- a/src/vector/dot.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export function vec$dot(a) {
- return vecDot(this, a);
-}
-
-export function vecDot(a, b) {
- return a.x * b.x + a.y * b.y;
-}
diff --git a/src/vector/fromAngle.js b/src/vector/fromAngle.js
deleted file mode 100644
index 25f0569f..00000000
--- a/src/vector/fromAngle.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Vec } from "./vec";
-
-export function vecFromAngle(angle) {
- return new Vec(Math.cos(angle), Math.sin(angle));
-}
diff --git a/src/vector/in.js b/src/vector/in.js
deleted file mode 100644
index 63b13506..00000000
--- a/src/vector/in.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export function vec$inX(x, x1) {
- const [min, max] = arguments.length == 1 ? [0, x] : [x, x1];
- return this.x >= min && this.x <= max;
-}
-
-export function vec$inY(y, y1) {
- const [min, max] = arguments.length == 1 ? [0, y] : [y, y1];
- return this.y >= min && this.y <= max;
-}
-
-export function vecInX(...params) {
- return vec$inX.call(...params);
-}
-
-export function vecInY(...params) {
- return vec$inY.call(...params);
-}
diff --git a/src/vector/index.js b/src/vector/index.js
deleted file mode 100644
index 951656c7..00000000
--- a/src/vector/index.js
+++ /dev/null
@@ -1,16 +0,0 @@
-export { vec } from "./vec.js";
-export { vecAdd } from "./add.js";
-export { vecSub } from "./sub.js";
-export { vecDist, vecDist2 } from "./dist.js";
-export { vecMult } from "./mult.js";
-export { vecDiv } from "./div.js";
-export { vecMag } from "./mag.js";
-export { vecNorm } from "./norm.js";
-export { vecClamp, vecClampX, vecClampY } from "./clamp.js";
-export { vecRandom } from "./random.js";
-export { vecDot } from "./dot.js";
-export { vecCross } from "./cross.js";
-export { vecNeg, vecNegX, vecNegY } from "./neg.js";
-export { vecInX, vecInY } from "./in.js";
-export { vecAngle } from "./angle.js";
-export { vecFromAngle } from "./fromAngle.js";
diff --git a/src/vector/mag.js b/src/vector/mag.js
deleted file mode 100644
index f5e228ac..00000000
--- a/src/vector/mag.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Vec } from "./vec.js";
-
-function mag(out, s, mag) {
- if (mag !== 0) {
- const t = s / mag;
- out.mult(t);
- }
-}
-
-export function vec$mag(s) {
- const m = Math.sqrt(this.x * this.x + this.y * this.y);
- if (arguments.length === 0) return m;
- mag(this, s, m);
- return this;
-}
-
-export function vecMag(a, s) {
- const m = Math.sqrt(a.x * a.x + a.y * a.y);
- if (arguments.length === 1) return m;
- const out = new Vec().set(a);
- mag(out, s, m);
- return out;
-}
diff --git a/src/vector/memberize.js b/src/vector/memberize.js
deleted file mode 100644
index 6829a202..00000000
--- a/src/vector/memberize.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export function memberize(compute) {
- return function (...params) {
- compute(this, this, ...params);
- return this;
- };
-}
diff --git a/src/vector/mult.js b/src/vector/mult.js
deleted file mode 100644
index eafe372f..00000000
--- a/src/vector/mult.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { memberize } from "./memberize.js";
-import { staticize } from "./staticize.js";
-
-function mult(out, a, s) {
- out.x = a.x * s;
- out.y = a.y * s;
-}
-
-export const vec$mult = memberize(mult);
-
-export const vecMult = staticize(mult);
diff --git a/src/vector/neg.js b/src/vector/neg.js
deleted file mode 100644
index 89c4c627..00000000
--- a/src/vector/neg.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { memberize } from "./memberize.js";
-import { staticize } from "./staticize.js";
-
-export function neg(out, a) {
- out.x = a.x * -1;
- out.y = a.y * -1;
-}
-
-export function negX(out, a) {
- out.x = a.x * -1;
- out.y = a.y;
-}
-
-export function negY(out, a) {
- out.x = a.x;
- out.y = a.y * -1;
-}
-
-export const vec$neg = memberize(neg);
-export const vec$negX = memberize(negX);
-export const vec$negY = memberize(negY);
-
-export const vecNeg = staticize(neg);
-export const vecNegX = staticize(negX);
-export const vecNegY = staticize(negY);
diff --git a/src/vector/norm.js b/src/vector/norm.js
deleted file mode 100644
index 78da50cc..00000000
--- a/src/vector/norm.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { memberize } from "./memberize.js";
-import { staticize } from "./staticize.js";
-
-function norm(out, a) {
- const mag = a.mag();
- if (mag !== 0) out.set(a).div(mag);
-}
-
-export const vec$norm = memberize(norm);
-
-export const vecNorm = staticize(norm);
diff --git a/src/vector/random.js b/src/vector/random.js
deleted file mode 100644
index d173b64e..00000000
--- a/src/vector/random.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { memberize } from "./memberize.js";
-import { staticize } from "./staticize.js";
-
-function random(out) {
- out.x = Math.random();
- out.y = Math.random();
-}
-
-export const vec$random = memberize(random);
-
-export const vecRandom = staticize(random);
diff --git a/src/vector/set.js b/src/vector/set.js
deleted file mode 100644
index 79a1c62b..00000000
--- a/src/vector/set.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Vec } from "./vec.js";
-
-export function vec$set(a, b) {
- if (a instanceof Vec) {
- this.x = a.x;
- this.y = a.y;
- return this;
- }
- this.x = a;
- this.y = b;
- return this;
-}
-
-export function vec$setX(x) {
- this.x = x;
- return this;
-}
-
-export function vec$setY(y) {
- this.y = y;
- return this;
-}
diff --git a/src/vector/staticize.js b/src/vector/staticize.js
deleted file mode 100644
index 2e9f1455..00000000
--- a/src/vector/staticize.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Vec } from "./vec.js";
-
-export function staticize(compute) {
- return function (...params) {
- const out = new Vec();
- compute(out, ...params);
- return out;
- };
-}
diff --git a/src/vector/sub.js b/src/vector/sub.js
deleted file mode 100644
index b5e10b90..00000000
--- a/src/vector/sub.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { staticize } from "./staticize.js";
-import { memberize } from "./memberize.js";
-
-function sub(out, a, b) {
- out.x = a.x - b.x;
- out.y = a.y - b.y;
-}
-
-export const vec$sub = memberize(sub);
-
-export const vecSub = staticize(sub);
diff --git a/src/vector/vec.js b/src/vector/vec.js
deleted file mode 100644
index 4c7447cd..00000000
--- a/src/vector/vec.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import { vec$add } from "./add.js";
-import { vec$set, vec$setX, vec$setY } from "./set.js";
-import { vec$sub } from "./sub.js";
-import { vec$dist, vec$dist2 } from "./dist.js";
-import { vec$mult } from "./mult.js";
-import { vec$mag } from "./mag.js";
-import { vec$div } from "./div.js";
-import { vec$norm } from "./norm.js";
-import { vec$clamp, vec$clampX, vec$clampY } from "./clamp.js";
-import { vec$random } from "./random.js";
-import { vec$dot } from "./dot.js";
-import { vec$cross } from "./cross.js";
-import { vec$neg, vec$negX, vec$negY } from "./neg.js";
-import { vec$inX, vec$inY } from "./in.js";
-import { vec$angle } from "./angle.js";
-import { vec$clone } from "./clone.js";
-
-export function Vec(x = 0, y = 0) {
- Object.defineProperties(this, {
- x: { value: x, writable: true },
- y: { value: y, writable: true },
- });
-}
-
-Object.defineProperties(Vec.prototype, {
- set: { value: vec$set },
- setX: { value: vec$setX },
- setY: { value: vec$setY },
- add: { value: vec$add },
- sub: { value: vec$sub },
- dist: { value: vec$dist },
- dist2: { value: vec$dist2 },
- mult: { value: vec$mult },
- div: { value: vec$div },
- mag: { value: vec$mag },
- norm: { value: vec$norm },
- clamp: { value: vec$clamp },
- clampX: { value: vec$clampX },
- clampY: { value: vec$clampY },
- random: { value: vec$random },
- dot: { value: vec$dot },
- cross: { value: vec$cross },
- random: { value: vec$random },
- neg: { value: vec$neg },
- negX: { value: vec$negX },
- negY: { value: vec$negY },
- inX: { value: vec$inX },
- inY: { value: vec$inY },
- angle: { value: vec$angle },
- clone: { value: vec$clone },
-});
-
-export function vec(...params) {
- return new Vec(...params);
-}
diff --git a/src/webgl.js b/src/webgl.js
deleted file mode 100644
index d07d8577..00000000
--- a/src/webgl.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export { glsl } from "./attribute/glsl.js";
-export { webgl } from "./webgl/index.js";
diff --git a/src/webgl/circles.js b/src/webgl/circles.js
deleted file mode 100644
index c39888a6..00000000
--- a/src/webgl/circles.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import { range } from "../array/range.js";
-import { normalizeColor } from "../color.js";
-import {
- getProgram,
- hasGLSLAttribute,
- bindAttribute,
- bindUniform,
- defineGlobalAttributes,
- defineLocalAttributes,
- bindVariables,
-} from "./program.js";
-
-function createVertexShaderSource(descriptors, value) {
- const { position, strokeOpacity } = value;
- const xy = position ? "_position + a_vertex * _r" : "vec2(_x, _y) + a_vertex * _r";
- const so = strokeOpacity ? "_strokeOpacity" : "1.0";
- return `
- ${defineGlobalAttributes(descriptors, value)}
- attribute vec2 a_vertex;
- attribute float a_datum;
- uniform vec2 u_resolution;
- varying vec4 v_stroke;
- void main() {
- ${defineLocalAttributes(descriptors, value)}
- // Use computed attributes
- vec2 xy = ${xy};
- vec2 scale = xy / u_resolution;
- vec2 clipSpace = scale * 2.0 - 1.0;
- gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
- v_stroke = vec4(_stroke.r, _stroke.g, _stroke.b, ${so});
- }
- `;
-}
-
-function createFragmentShaderSource() {
- return `
- precision mediump float;
- varying vec4 v_stroke;
- void main() {
- gl_FragColor = v_stroke;
- }
- `;
-}
-
-const attributes = {
- x: { type: "float", size: 1, glType: "FLOAT", normalize: false, map: (value) => new Float32Array(value) },
- y: { type: "float", size: 1, glType: "FLOAT", normalize: false, map: (value) => new Float32Array(value) },
- position: { type: "vec2", size: 2, glType: "FLOAT", map: (value) => new Float32Array(value) },
- stroke: {
- type: "vec4",
- size: 4,
- glType: "UNSIGNED_BYTE",
- normalize: true,
- map: (value) => new Uint8Array(value.flatMap((d) => normalizeColor(d))),
- },
- strokeOpacity: {
- type: "float",
- glType: "UNSIGNED_BYTE",
- normalize: true,
- map: (value) => new Uint8Array(value.map((d) => (d * 255) | 0)),
- },
- r: { type: "float", size: 1, glType: "FLOAT", map: (value) => new Float32Array(value), normalize: false },
-};
-
-export function webgl$circles(I, value, data) {
- const { _gl: gl, _circle: map } = this;
- const { count = 100, ...rest } = value;
- const vertex = createVertexShaderSource(attributes, rest);
- const fragment = createFragmentShaderSource();
- const program = getProgram(gl, map, vertex, fragment);
-
- gl.useProgram(program);
- gl.enable(gl.BLEND);
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE);
- gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
-
- const ext = gl.getExtension("ANGLE_instanced_arrays");
-
- if (hasGLSLAttribute(value, attributes)) {
- bindAttribute(gl, program, ext, {
- name: "a_datum",
- data: new Float32Array(data),
- divisor: 1,
- });
- }
-
- bindAttribute(gl, program, ext, {
- name: "a_vertex",
- size: 2,
- divisor: 0,
- data: new Float32Array(range(count, 0, Math.PI * 2).flatMap((i) => [Math.cos(i), Math.sin(i)])),
- });
-
- bindUniform(gl, program, {
- name: "u_resolution",
- data: [gl.canvas.width / devicePixelRatio, gl.canvas.height / devicePixelRatio],
- });
-
- bindVariables(gl, program, ext, attributes, rest);
-
- ext.drawArraysInstancedANGLE(gl.LINE_LOOP, 0, count, I.length);
-}
diff --git a/src/webgl/clear.js b/src/webgl/clear.js
deleted file mode 100644
index 4b0dbb8e..00000000
--- a/src/webgl/clear.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { normalizeColor } from "../color.js";
-import { linear as scaleLinear } from "../scale/linear.js";
-
-export function webgl$clear({ fill }) {
- const gl = this._gl;
- const [r, g, b, a] = normalizeColor(fill);
- const scale = scaleLinear([0, 255], [0, 1]);
- gl.clearColor(scale(r), scale(g), scale(b), scale(a));
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-}
diff --git a/src/webgl/index.js b/src/webgl/index.js
deleted file mode 100644
index 3f769ff4..00000000
--- a/src/webgl/index.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { webgl$node } from "./node.js";
-import { webgl$init } from "./init.js";
-import { webgl$clear } from "./clear.js";
-import { webgl$triangles } from "./triangles.js";
-import { webgl$circles } from "./circles.js";
-import { webgl$rects } from "./rects.js";
-
-function WebGL() {
- Object.defineProperties(this, {
- _gl: { value: null, writable: true },
- _triangle: { value: new Map() },
- _circle: { value: new Map() },
- _props: { value: {}, writable: true },
- });
-}
-
-Object.defineProperties(WebGL.prototype, {
- node: { value: webgl$node },
- init: { value: webgl$init },
- triangles: { value: webgl$triangles },
- circles: { value: webgl$circles },
- rects: { value: webgl$rects },
- clear: { value: webgl$clear },
-});
-
-export function webgl(options) {
- return new WebGL(options);
-}
diff --git a/src/webgl/init.js b/src/webgl/init.js
deleted file mode 100644
index d3c20395..00000000
--- a/src/webgl/init.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { contextGL } from "../context.js";
-
-export function webgl$init({ width, height, dpi = null }) {
- const gl = contextGL(width, height, dpi);
- Object.assign(this._props, { width, height });
- Object.assign(this, { _gl: gl });
- return this;
-}
diff --git a/src/webgl/node.js b/src/webgl/node.js
deleted file mode 100644
index ee996088..00000000
--- a/src/webgl/node.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function webgl$node() {
- return this._gl.canvas;
-}
diff --git a/src/webgl/program.js b/src/webgl/program.js
deleted file mode 100644
index 9dc162d7..00000000
--- a/src/webgl/program.js
+++ /dev/null
@@ -1,146 +0,0 @@
-function createShader(gl, type, source) {
- const shader = gl.createShader(type);
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
- const succuss = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
- if (succuss) return shader;
- console.log(gl.getShaderInfoLog(shader));
- gl.deleteShader(shader);
-}
-
-function createProgram(gl, vertexShader, fragmentShader) {
- const program = gl.createProgram();
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
- gl.linkProgram(program);
- const success = gl.getProgramParameter(program, gl.LINK_STATUS);
- if (success) return program;
- console.log(gl.getProgramInfoLog(program));
- gl.deleteProgram(program);
-}
-
-export function getProgram(gl, map, vertex, fragment) {
- const key = vertex + fragment;
- if (map.has(key)) return map.get(key);
- const program = compileProgram(gl, vertex, fragment);
- map.set(key, program);
- return program;
-}
-
-export function compileProgram(gl, vertexSource, fragmentSource) {
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
- const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
- return createProgram(gl, vertexShader, fragmentShader);
-}
-
-export function uniformName(key, j) {
- return `_${key}_${j}`;
-}
-
-export function attributeName(key) {
- return `_${key}`;
-}
-
-export function bindAttribute(
- gl,
- program,
- ext,
- { name, data, size = 1, divisor = 0, normalize = false, stride = 0, offset = 0, type = gl.FLOAT },
-) {
- const loc = gl.getAttribLocation(program, name);
- const radiusBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, radiusBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
- gl.vertexAttribPointer(loc, size, type, normalize, stride, offset);
- gl.enableVertexAttribArray(loc);
- ext.vertexAttribDivisorANGLE(loc, divisor);
-}
-
-export function bindUniform(gl, program, { name, data }) {
- const resolutionLoc = gl.getUniformLocation(program, name);
- if (data.length === 1) gl.uniform1f(resolutionLoc, ...data);
- if (data.length === 2) gl.uniform2f(resolutionLoc, ...data);
-}
-
-export function bindVariables(gl, program, ext, descriptors, values) {
- for (const [key, value] of Object.entries(values)) {
- if (Array.isArray(value)) {
- const { size, glType, normalize, map } = descriptors[key];
- const data = map(value);
- const name = attributeName(key);
- bindAttribute(gl, program, ext, { name, data, size, type: gl[glType], normalize, divisor: 1 });
- } else {
- const { params } = value;
- const uniforms = params
- .map((d, j) => ({ name: uniformName(key, j), data: [d] }))
- .filter((d) => typeof d.data[0] === "number"); // Filter functions.
- for (const { name, data } of uniforms) bindUniform(gl, program, { name, data });
- }
- }
-}
-
-export function hasGLSLAttribute(value, descriptors) {
- return Object.entries(value).some(([key, value]) => !Array.isArray(value) && descriptors[key].onlyUniform !== true);
-}
-
-// TODO Defined nested functions.
-// TODO Params for nested functions.
-export function defineFunctionAttribute(descriptors, attribute) {
- const [key, value] = attribute;
- const { onlyUniform = false } = descriptors[key];
- const { strings, params } = value;
-
- const uniforms = params
- .map((d, j) => [d, `uniform float ${uniformName(key, j)};`])
- .filter(([d]) => typeof d === "number") // Filter function.
- .map((d) => d[1])
- .join("\n");
-
- const regex = /(float|bool|int|void|vec[234])\s+(\w+)\s*\(/g;
- const functions = params.filter((d) => typeof d !== "number").map((d) => d.strings.join("-"));
- const functionNames = functions.map((d) => regex.exec(d)[2]);
-
- // Define function in fragment shader.
- if (onlyUniform) return uniforms;
-
- let _ = uniforms + "\n" + functions + "\n" + strings[0];
- for (let i = 1, n = strings.length; i < n; ++i) {
- const param = params[i - 1];
- const name = typeof param === "number" ? uniformName(key, i - 1) : functionNames.shift();
- _ += name + strings[i];
- }
- return _;
-}
-
-export function defineArrayAttribute(descriptors, attribute) {
- const [key] = attribute;
- const descriptor = descriptors[key];
- if (!descriptor) throw new Error(`Unknown attribute: ${key}`);
- return `attribute ${descriptor.type} ${attributeName(key)};`;
-}
-
-export function defineComputeAttribute(descriptors, attribute) {
- const [key] = attribute;
- const descriptor = descriptors[key];
- if (!descriptor) throw new Error(`Unknown attribute: ${key}`);
- return `${descriptor.type} ${attributeName(key)} = ${key}(a_datum);`;
-}
-
-export function defineAttribute(descriptors, attribute) {
- const [, value] = attribute;
- if (Array.isArray(value)) return defineArrayAttribute(descriptors, attribute);
- else return defineFunctionAttribute(descriptors, attribute);
-}
-
-export function defineGlobalAttributes(descriptors, value) {
- const values = Object.entries(value).map((d) => defineAttribute(descriptors, d));
- return values.join("\n");
-}
-
-export function defineLocalAttributes(descriptors, value) {
- const computes = Object.entries(value)
- .filter(([, value]) => !Array.isArray(value))
- .filter(([key]) => descriptors[key].onlyUniform !== true)
- .map((d) => defineComputeAttribute(descriptors, d));
- return computes.join("\n");
-}
diff --git a/src/webgl/rects.js b/src/webgl/rects.js
deleted file mode 100644
index 7ba03997..00000000
--- a/src/webgl/rects.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import { normalizeColor } from "../color.js";
-import {
- getProgram,
- hasGLSLAttribute,
- bindAttribute,
- bindUniform,
- defineGlobalAttributes,
- defineLocalAttributes,
- bindVariables,
- defineFunctionAttribute,
-} from "./program.js";
-
-function createVertexShaderSource(descriptors, value) {
- const { fill } = value;
- const [fillVarying, fillAssignment] = Array.isArray(fill) ? ["varying vec4 v_fill;", " v_fill = _fill;"] : ["", ""];
- return `
- ${defineGlobalAttributes(descriptors, value)}
- attribute vec2 a_vertex;
- attribute float a_datum;
- uniform vec2 u_resolution;
- ${fillVarying}
- void main() {
- ${defineLocalAttributes(descriptors, value)}
- // Use computed attributes
- vec2 xy = vec2(_x, _y) + a_vertex * vec2(_width, _height);
- vec2 scale = xy / u_resolution;
- vec2 clipSpace = scale * 2.0 - 1.0;
- gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
- ${fillAssignment}
- }
- `;
-}
-
-function createFragmentShaderSource(descriptors, value) {
- const newDescriptors = {
- ...descriptors,
- fill: { ...descriptors.fill, onlyUniform: false },
- };
- const { fill } = value;
- const globalFill = fill ? defineFunctionAttribute(newDescriptors, ["fill", fill]) : "varying vec4 v_fill;";
- const fragColor = fill ? `fill(gl_FragCoord.xy, gl_FragColor)` : "v_fill";
- return `
- precision mediump float;
- ${globalFill}
- void main() {
- gl_FragColor = ${fragColor};
- }
- `;
-}
-
-const attributes = {
- x: { type: "float", size: 1, glType: "FLOAT", normalize: false, map: (value) => new Float32Array(value) },
- y: { type: "float", size: 1, glType: "FLOAT", normalize: false, map: (value) => new Float32Array(value) },
- width: { type: "float", size: 1, glType: "FLOAT", normalize: false, map: (value) => new Float32Array(value) },
- height: { type: "float", size: 1, glType: "FLOAT", normalize: false, map: (value) => new Float32Array(value) },
- fill: {
- type: "vec4",
- size: 4,
- glType: "UNSIGNED_BYTE",
- normalize: true,
- map: (value) => new Uint8Array(value.flatMap((d) => normalizeColor(d))),
- onlyUniform: true,
- },
-};
-
-export function webgl$rects(I, value, data) {
- const { _gl: gl, _circle: map } = this;
- const { x, y, width, height, fill } = value;
- const vertexVariables = { x, y, width, height, fill };
- const fragmentVariables = { ...(!Array.isArray(fill) && { fill }) };
- const vertex = createVertexShaderSource(attributes, vertexVariables);
- const fragment = createFragmentShaderSource(attributes, fragmentVariables);
- const program = getProgram(gl, map, vertex, fragment);
-
- gl.useProgram(program);
- gl.enable(gl.BLEND);
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE);
- gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
-
- const ext = gl.getExtension("ANGLE_instanced_arrays");
-
- if (hasGLSLAttribute(value, attributes)) {
- bindAttribute(gl, program, ext, {
- name: "a_datum",
- data: new Float32Array(data),
- divisor: 1,
- });
- }
-
- bindAttribute(gl, program, ext, {
- name: "a_vertex",
- size: 2,
- divisor: 0,
- data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]),
- });
-
- bindUniform(gl, program, {
- name: "u_resolution",
- data: [gl.canvas.width / devicePixelRatio, gl.canvas.height / devicePixelRatio],
- });
-
- bindVariables(gl, program, ext, attributes, vertexVariables);
-
- ext.drawArraysInstancedANGLE(gl.TRIANGLE_STRIP, 0, 4, I.length);
-}
diff --git a/src/webgl/triangles.js b/src/webgl/triangles.js
deleted file mode 100644
index 4d7179d2..00000000
--- a/src/webgl/triangles.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { color as d3Color } from "d3-color";
-import { getProgram } from "./program";
-
-function createVertexShaderSource() {
- return `
- attribute vec2 a_position;
- attribute vec4 a_color;
- uniform vec2 u_resolution;
- varying vec4 v_color;
- void main() {
- vec2 scale = a_position / u_resolution;
- vec2 clipSpace = scale * 2.0 - 1.0;
- gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
- v_color = a_color;
- }
- `;
-}
-
-function createFragmentShaderSource() {
- return `
- precision mediump float;
- varying vec4 v_color;
- void main() {
- gl_FragColor = v_color;
- }
- `;
-}
-
-export function webgl$triangles(I, value) {
- const { _gl: gl, _triangle: map } = this;
-
- const program = getProgram(gl, map, createVertexShaderSource(), createFragmentShaderSource());
-
- gl.useProgram(program);
-
- const { x: X, y: Y, x1: X1, y1: Y1, x2: X2, y2: Y2, fill: F = [] } = value;
-
- // Pass vertexes to position buffer.
- const positions = I.flatMap((i) => [X[i], Y[i], X1[i], Y1[i], X2[i], Y2[i]]);
- const positionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
-
- // Pass colors to color buffer.
- const colors = I.flatMap((i) => {
- const fill = F[i];
- const { r, g, b, opacity } = d3Color(fill).rgb();
- const o = 255 * opacity;
- return [r, g, b, o, r, g, b, o, r, g, b, o];
- });
- const colorBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(colors), gl.STATIC_DRAW);
-
- // Extract data from position buffer to attribute position.
- const positionLocation = gl.getAttribLocation(program, "a_position");
- gl.enableVertexAttribArray(positionLocation);
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
-
- // Extract data from color buffer to attribute color.
- const colorLocation = gl.getAttribLocation(program, "a_color");
- gl.enableVertexAttribArray(colorLocation);
- gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
- gl.vertexAttribPointer(colorLocation, 4, gl.UNSIGNED_BYTE, true, 0, 0);
-
- // Set the resolution.
- const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
- gl.uniform2f(resolutionLocation, gl.canvas.width / devicePixelRatio, gl.canvas.height / devicePixelRatio);
-
- // Render.
- gl.drawArrays(gl.TRIANGLES, 0, I.length * 3);
-}
diff --git a/test/apps/a2-arriving.js b/test/apps/a2-arriving.js
deleted file mode 100644
index d3890091..00000000
--- a/test/apps/a2-arriving.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { force, object, location } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-function update(app, context) {
- const { target, a2 } = context;
-
- const update = location();
-
- const arrive = force((d) => {
- const desired = cm.vecSub(target.location, a2.location);
-
- // The closer, the slower.
- const distance = desired.mag();
- const scale = cm.scaleLinear([0, 100], [0, d.maxSpeed]);
- if (distance > 100) desired.mag(d.maxSpeed);
- else desired.mag(scale(distance));
-
- const steer = cm.vecSub(desired, d.velocity);
- steer.clamp(d.maxForce);
- return steer;
- });
-
- app.append(cm.clear, { fill: cm.rgb(255) });
-
- app
- .datum(target)
- .process(cm.each, (d) => d.location.set(app.prop("mouseX"), app.prop("mouseY")))
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: 24,
- stroke: "black",
- fill: cm.rgb(175),
- strokeWidth: 2,
- });
-
- app
- .datum(a2)
- .process(cm.each, arrive)
- .process(cm.each, update)
- .append(cm.group, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- rotate: (d) => d.velocity.angle(),
- })
- .append(cm.triangle, {
- x: (d) => d.r * 2,
- y: 0,
- x1: (d) => -d.r * 2,
- y1: (d) => -d.r,
- x2: (d) => -d.r * 2,
- y2: (d) => d.r,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- strokeWidth: 2,
- })
- .append(cm.circle, {
- x: 0,
- y: 0,
- r: 5,
- fill: "red",
- });
-}
-
-export function a2Arriving() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const context = {
- target: object({
- location: cm.vec(app.prop("mouseX"), app.prop("mouseY")),
- }),
- a2: object({
- location: cm.vec(app.prop("width") / 2, app.prop("height") / 2),
- velocity: cm.vec(),
- acceleration: cm.vec(),
- maxSpeed: 4,
- maxForce: 0.1,
- r: 6,
- }),
- };
-
- return app
- .on("update", () => update(app, context))
- .call(dispose)
- .call(stats)
- .call(frame)
- .start()
- .node();
-}
diff --git a/test/apps/a2-flow-noise.js b/test/apps/a2-flow-noise.js
deleted file mode 100644
index 3ce56648..00000000
--- a/test/apps/a2-flow-noise.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { force, location, object } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function a2FlowNoise() {
- const app = cm.app({
- width: 640,
- height: 240,
- });
-
- const resolution = 20;
- const cols = Math.floor(app.prop("width") / resolution);
- const rows = Math.floor(app.prop("height") / resolution);
- const fields = cm.range(cols * rows);
-
- const cellWidth = app.prop("width") / cols;
- const cellHeight = app.prop("height") / rows;
- const x = (i) => (i % cols) * cellWidth + cellWidth / 2;
- const y = (i) => ((i / cols) | 0) * cellHeight + cellHeight / 2;
-
- const vertices = cm.range(120).map(() =>
- object({
- location: cm.vec(cm.random(app.prop("width")), cm.random(app.prop("height"))),
- velocity: cm.vec(),
- r: 4,
- maxSpeed: cm.random(2, 5),
- maxForce: cm.random(0.1, 0.5),
- }),
- );
-
- const initFields = () => {
- const noise = cm.randomNoise(0, cm.TWO_PI);
- for (let m = 0; m < fields.length; m++) {
- const i = m % cols;
- const j = (m / cols) | 0;
- fields[m] = noise(j * 0.1, i * 0.1);
- }
- };
-
- const lookup = (x, y) => {
- const i = cm.clamp((x / cellWidth) | 0, 0, cols - 1);
- const j = cm.clamp((y / cellHeight) | 0, 0, rows - 1);
- const angle = fields[j * cols + i];
- return cm.vecFromAngle(angle);
- };
-
- const updateLocation = location();
-
- const constrainX = (d) => {
- const width = app.prop("width");
- if (d.location.x < 0) d.location.x += width;
- if (d.location.x > width) d.location.x -= width;
- };
-
- const constrainY = (d) => {
- const height = app.prop("height");
- if (d.location.y < 0) d.location.y += height;
- if (d.location.y > height) d.location.y -= height;
- };
-
- const applyAuto = force((d) => {
- const desired = lookup(d.location.x, d.location.y);
- desired.mag(d.maxSpeed); // !!!Important
- const steer = cm.vecSub(desired, d.velocity);
- steer.clamp(d.maxForce); // !!!Important
- return steer;
- });
-
- initFields();
-
- function update() {
- app.append(cm.clear, { fill: "white" });
-
- app
- .data(fields)
- .append(cm.group, {
- x: (_, i) => x(i),
- y: (_, i) => y(i),
- rotate: (d) => d,
- })
- .append(cm.link, {
- x: (-cellWidth / 2) * 0.5,
- y: 0,
- x1: (cellWidth / 2) * 0.5,
- y1: 0,
- });
-
- app
- .data(vertices)
- .process(cm.each, applyAuto)
- .process(cm.each, updateLocation)
- .process(cm.each, constrainX)
- .process(cm.each, constrainY)
- .append(cm.group, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- rotate: (d) => d.velocity.angle(),
- })
- .append(cm.triangle, {
- x: (d) => d.r * 2,
- y: 0,
- x1: (d) => -d.r * 2,
- y1: (d) => -d.r,
- x2: (d) => -d.r * 2,
- y2: (d) => d.r,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- strokeWidth: 2,
- });
- }
-
- function mouseClick() {
- initFields();
- }
-
- return app.on("update", update).on("mouseClick", mouseClick).call(dispose).call(frame).call(stats).start().node();
-}
diff --git a/test/apps/a2-flow-random.js b/test/apps/a2-flow-random.js
deleted file mode 100644
index 24a05cf6..00000000
--- a/test/apps/a2-flow-random.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-
-function multiple(d, x) {
- if (typeof d === "function") return (...params) => d(...params) * x;
- else return d * x;
-}
-
-function arrow(flow, { length, angle, ...options }) {
- const group = flow.append(cm.group, options);
- const halfLength = multiple(length, 0.5);
-
- group.append(cm.link, {
- x: multiple(halfLength, -1),
- y: 0,
- x1: halfLength,
- y1: 0,
- });
-
- group.append(cm.link, {
- x: 0,
- y: 0,
- x1: halfLength,
- y1: 0,
- rotate: multiple(angle, -1),
- transformOrigin: "end",
- });
-
- group.append(cm.link, {
- x: 0,
- y: 0,
- x1: halfLength,
- y1: 0,
- rotate: angle,
- transformOrigin: "end",
- });
-}
-
-export function a2FlowRandom() {
- const app = cm.app({
- width: 640,
- height: 240,
- });
-
- const resolution = 16;
- const cols = Math.floor(app.prop("width") / resolution);
- const rows = Math.floor(app.prop("height") / resolution);
-
- const noise = cm.randomNoise(0, cm.TWO_PI);
- const fields = cm.range(cols * rows).map((_, d) => {
- const i = d % cols;
- const j = (d / cols) | 0;
- return noise(j * 0.1, i * 0.1);
- });
-
- const cellWidth = app.prop("width") / cols;
- const cellHeight = app.prop("height") / rows;
- const x = (i) => (i % cols) * cellWidth;
- const y = (i) => ((i / cols) | 0) * cellHeight;
-
- app.data(fields).call(arrow, {
- x: (_, i) => x(i) + cellWidth / 2,
- y: (_, i) => y(i) + cellHeight / 2,
- length: cellWidth,
- rotate: (d) => d,
- angle: Math.PI / 6,
- });
-
- return app.call(dispose).call(frame).render().node();
-}
diff --git a/test/apps/a2-seeking.js b/test/apps/a2-seeking.js
deleted file mode 100644
index eb95749d..00000000
--- a/test/apps/a2-seeking.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { force, object, location } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-function update(app, context) {
- const { target, a2 } = context;
-
- const update = location();
-
- const seek = force((d) => {
- const desired = cm.vecSub(target.location, a2.location).mag(d.maxSpeed);
- const steer = cm.vecSub(desired, d.velocity);
- steer.clamp(d.maxForce);
- return steer;
- });
-
- app.append(cm.clear, { fill: cm.rgb(255) });
-
- app
- .datum(target)
- .process(cm.each, (d) => d.location.set(app.prop("mouseX"), app.prop("mouseY")))
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: 24,
- stroke: "black",
- fill: cm.rgb(175),
- strokeWidth: 2,
- });
-
- app
- .datum(a2)
- .process(cm.each, seek)
- .process(cm.each, update)
- .append(cm.group, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- rotate: (d) => d.velocity.angle(),
- })
- .append(cm.triangle, {
- x: (d) => d.r * 2,
- y: 0,
- x1: (d) => -d.r * 2,
- y1: (d) => -d.r,
- x2: (d) => -d.r * 2,
- y2: (d) => d.r,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- strokeWidth: 2,
- });
-}
-
-export function a2Seeking() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const context = {
- target: object({
- location: cm.vec(app.prop("mouseX"), app.prop("mouseY")),
- }),
- a2: object({
- location: cm.vec(app.prop("width") / 2, app.prop("height") / 2),
- velocity: cm.vec(),
- acceleration: cm.vec(),
- maxSpeed: 8,
- maxForce: 0.2,
- r: 6,
- }),
- };
-
- return app
- .on("update", () => update(app, context))
- .call(dispose)
- .call(stats)
- .call(frame)
- .start()
- .node();
-}
diff --git a/test/apps/a2-wandering.js b/test/apps/a2-wandering.js
deleted file mode 100644
index d7ef3917..00000000
--- a/test/apps/a2-wandering.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { force, object, location } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-function update(app, context) {
- const { a2 } = context;
-
- const update = location();
-
- const constrainX = (d) => {
- const width = app.prop("width");
- if (d.location.x < 0) d.location.x += width;
- if (d.location.x > width) d.location.x -= width;
- };
-
- const constrainY = (d) => {
- const height = app.prop("height");
- if (d.location.y < 0) d.location.y += height;
- if (d.location.y > height) d.location.y -= height;
- };
-
- const seek = force((d) => {
- const desired = cm.vecSub(d.target, a2.location).mag(d.maxSpeed);
- const steer = cm.vecSub(desired, d.velocity);
- steer.clamp(d.maxForce);
- return steer;
- });
-
- const target = (d) => {
- const circle = d.velocity.clone();
- circle.mag(d.wanderD).add(d.location);
- const heading = d.velocity.angle();
- const theta = (d.wanderT += cm.random(-d.wanderC, d.wanderC)) + heading;
- const r = cm.vec(d.wanderR * Math.cos(theta), d.wanderR * Math.sin(theta));
- d.target = cm.vecAdd(circle, r);
- d.circle = circle;
- };
-
- app.append(cm.clear, { fill: cm.rgb(255) });
-
- const groups = app
- .datum(a2)
- .process(cm.each, target)
- .process(cm.each, seek)
- .process(cm.each, constrainX)
- .process(cm.each, constrainY)
- .process(cm.each, update);
-
- groups.append(cm.circle, {
- x: (d) => d.circle.x,
- y: (d) => d.circle.y,
- r: (d) => d.wanderR,
- fill: cm.rgb(255),
- stroke: cm.rgb(0),
- });
-
- groups.append(cm.link, {
- x: (d) => d.circle.x,
- y: (d) => d.circle.y,
- x1: (d) => d.location.x,
- y1: (d) => d.location.y,
- });
-
- groups.append(cm.circle, {
- x: (d) => d.target.x,
- y: (d) => d.target.y,
- r: 2,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- });
-
- groups.append(cm.link, {
- x: (d) => d.circle.x,
- y: (d) => d.circle.y,
- x1: (d) => d.target.x,
- y1: (d) => d.target.y,
- });
-
- groups
- .append(cm.group, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- rotate: (d) => d.velocity.angle(),
- })
- .append(cm.triangle, {
- x: (d) => d.r * 2,
- y: 0,
- x1: (d) => -d.r * 2,
- y1: (d) => -d.r,
- x2: (d) => -d.r * 2,
- y2: (d) => d.r,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- strokeWidth: 2,
- });
-}
-
-export function a2Wandering() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const a2 = object({
- location: cm.vec(app.prop("width") / 2, app.prop("height") / 2),
- maxSpeed: 2,
- maxForce: 0.05,
- r: 6,
- wanderR: 25,
- wanderD: 80,
- wanderT: 0,
- wanderC: 0.3,
- });
-
- return app
- .on("update", () => update(app, { a2 }))
- .call(dispose)
- .call(stats)
- .call(frame)
- .start()
- .node();
-}
diff --git a/test/apps/force-attract.js b/test/apps/force-attract.js
deleted file mode 100644
index 5b5a878a..00000000
--- a/test/apps/force-attract.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { attraction, object, location } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function forceAttract() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const centerX = app.prop("width") / 2;
- const centerY = app.prop("height") / 2;
-
- const attractor = object({
- mass: 20,
- G: 1,
- location: cm.vec(centerX, centerY),
- });
-
- const mover = object({
- location: cm.vec(centerX, centerY - 50),
- velocity: cm.vec(1, 0),
- acceleration: cm.vec(),
- mass: 5,
- });
-
- const update = location();
- const applyAttraction = attraction(attractor);
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .datum(mover)
- .process(cm.each, applyAttraction)
- .process(cm.each, update)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: (d) => d.mass,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 2,
- });
- })
- .on("update", () => {
- app
- .datum(attractor) // Convert to an array.
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: (d) => d.mass,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 5,
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/force-dancing.js b/test/apps/force-dancing.js
deleted file mode 100644
index e5ef2482..00000000
--- a/test/apps/force-dancing.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { force, location, collision, object } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function forceDancing() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const movers = cm.range(25).map(() =>
- object({
- location: cm.vec(),
- velocity: cm.vec(),
- acceleration: cm.vec(),
- mass: cm.random(0.1, 5),
- }),
- );
-
- const applyGravity = force(cm.vec(0, 0.2));
- const applyWind = force(cm.vec(0.02, 0));
- const update = location();
- const detect = collision();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, applyGravity)
- .process(cm.each, applyWind)
- .process(cm.each, update)
- .process(cm.each, detect)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: cm.rgb(0),
- r: (d) => d.mass,
- })
- .transform(cm.mapAttrs, {
- r: { scale: cm.scaleSqrt, range: [2, 20] },
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/force-everything.js b/test/apps/force-everything.js
deleted file mode 100644
index 2d2ad8d5..00000000
--- a/test/apps/force-everything.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { location, attraction, object } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function forceEverything() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const movers = Array.from({ length: 20 }, () =>
- object({
- location: cm.vec(cm.random(app.prop("width")), cm.random(app.prop("height"))),
- velocity: cm.vec(cm.random(-1, 1), cm.random(-1, 1)),
- acceleration: cm.vec(),
- mass: cm.random(2, 5),
- G: 0.4,
- }),
- );
-
- const update = location();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, (i) =>
- app
- .data(movers)
- .process(cm.filter, (j) => i !== j)
- .process(cm.each, (j) => attraction(j)(i)),
- )
- .process(cm.each, update)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: (d) => d.mass * 2,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 2,
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/force-falling.js b/test/apps/force-falling.js
deleted file mode 100644
index 7d96acc0..00000000
--- a/test/apps/force-falling.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { force, location, collision, object } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function forceFalling() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const movers = Array.from({ length: 25 }, () =>
- object({
- location: cm.vec(cm.random(0, app.prop("width")), 0),
- velocity: cm.vec(),
- acceleration: cm.vec(),
- mass: cm.random(1, 5),
- }),
- );
-
- const applyGravity = force((d) => cm.vec(0, 0.1).mult(d.mass));
- const applyFriction = force((d) => cm.vecNeg(d.velocity).mag(0.05));
- const applyWind = force(cm.vec(0.001, 0));
- const update = location();
- const detect = collision();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, applyFriction)
- .process(cm.each, applyGravity)
- .process(cm.each, applyWind)
- .process(cm.each, update)
- .process(cm.each, detect)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: cm.rgb(0),
- r: (d) => d.mass,
- })
- .transform(cm.mapAttrs, {
- r: { scale: cm.scaleSqrt, range: [2, 20] },
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/force-liquid.js b/test/apps/force-liquid.js
deleted file mode 100644
index 04cffe1e..00000000
--- a/test/apps/force-liquid.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { force, location, collision, object } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function forceLiquid() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const movers = cm.range(25).map(() =>
- object({
- location: cm.vec(cm.random(0, app.prop("width")), 0),
- velocity: cm.vec(),
- acceleration: cm.vec(),
- mass: cm.random(1, 5),
- }),
- );
-
- const liquid = {
- x: 0,
- y: app.prop("height") / 2,
- width: app.prop("width"),
- height: app.prop("height") / 2,
- c: 0.1,
- };
-
- const applyGravity = force((d) => cm.vec(0, 0.1).mult(d.mass));
- const applyDrag = force((d) => {
- const { x, y, height, width, c } = liquid;
- if (!d.location.inX(x, x + width)) return;
- if (!d.location.inY(y, y + height)) return;
- const v = d.velocity.mag();
- const mag = c * v * v;
- return cm.vecNeg(d.velocity).mag(mag);
- });
- const update = location();
- const detect = collision();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => app.append(cm.rect, { ...liquid, fill: cm.rgb(175) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, applyGravity)
- .process(cm.each, applyDrag)
- .process(cm.each, update)
- .process(cm.each, detect)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: cm.rgb(0),
- r: (d) => d.mass,
- })
- .transform(cm.mapAttrs, {
- r: { scale: cm.scaleSqrt, range: [2, 20] },
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/force-multiple.js b/test/apps/force-multiple.js
deleted file mode 100644
index 50b72a15..00000000
--- a/test/apps/force-multiple.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { location, object, attraction } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function forceMultiple() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const centerX = app.prop("width") / 2;
- const centerY = app.prop("height") / 2;
-
- const attractor = object({
- mass: 10,
- location: cm.vec(centerX, centerY),
- G: 1,
- });
-
- const movers = cm.range(20).map(() =>
- object({
- location: cm.vec(cm.random(app.prop("width")), cm.random(app.prop("height"))),
- velocity: cm.vec(cm.random(), cm.random()),
- mass: cm.random(2, 5),
- }),
- );
-
- const applyAttraction = attraction(attractor);
- const update = location();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, applyAttraction)
- .process(cm.each, update)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: (d) => d.mass * 2,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 2,
- });
- })
- .on("update", () => {
- app
- .datum(attractor) // Convert to an array.
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: (d) => d.mass * 2,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 5,
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/general-broken-heart.js b/test/apps/general-broken-heart.js
deleted file mode 100644
index 297946f7..00000000
--- a/test/apps/general-broken-heart.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-function noiseRadius(theta, radius, time, noise) {
- const s = noise(Math.cos(theta) + 1, Math.sin(theta) + 1, time) + 1;
- return radius * (1 + Math.cos(theta - Math.PI / 2)) * s;
-}
-
-function noiseAngle(theta, radius, time, noise) {
- const offset = 0.06;
- const theta1 = theta - offset;
- const theta2 = theta + offset;
- const a = noiseRadius(theta1, radius, time, noise);
- const b = noiseRadius(theta2, radius, time, noise);
- return Math.atan2(a * Math.sin(theta1) - b * Math.sin(theta2), a * Math.cos(theta1) - b * Math.cos(theta2)) + Math.PI;
-}
-
-function drawHeart(flow, { x, y, count, radius, noise, time }) {
- flow
- .append(cm.group, { x, y })
- .data(cm.range(count, 0, cm.TWO_PI))
- .process(cm.derive, {
- t: (d) => d,
- r: (d) => noiseRadius(d, radius, time, noise),
- })
- .append(cm.polygon, {
- x: (d) => Math.cos(d.t) * d.r,
- y: (d) => Math.sin(d.t) * d.r,
- fill: "red",
- });
-}
-
-function drawEyes(flow, { x, y, r, radius }) {
- const app = flow.app();
- const mouseX = app.prop("mouseX");
- const mouseY = app.prop("mouseY");
- const height = app.prop("height");
-
- const group = flow
- .data([-1, 1])
- .process(cm.derive, {
- x: (d) => x + (d * radius) / 1.8,
- y: y + height / 6,
- })
- .process(cm.derive, {
- rotate: (d) => Math.atan2(mouseY - d.y, mouseX - d.x),
- })
- .append(cm.group, {
- x: (d) => d.x,
- y: (d) => d.y,
- });
-
- group.append(cm.circle, {
- x: 0,
- y: 0,
- r: r,
- fill: "white",
- });
-
- group
- .append(cm.group, {
- rotate: (d) => d.rotate,
- })
- .append(cm.circle, {
- x: r / 3,
- y: 0,
- r: r / 2,
- fill: "black",
- });
-}
-
-function drawTexts(flow, { chars, x, y, radius, time, noise, ...font }) {
- const app = flow.app();
- const mouseX = app.prop("mouseX");
- const mouseY = app.prop("mouseY");
- const start = Math.atan2(mouseY - y, mouseX - x);
-
- flow
- .append(cm.group, { x, y })
- .data(chars)
- .process(cm.derive, {
- t: (d) => d.t + start,
- })
- .process(cm.derive, {
- r: (d) => noiseRadius(d.t, radius, time, noise),
- a: (d) => noiseAngle(d.t, radius, time, noise),
- })
- .append(cm.group, {
- x: (d) => Math.cos(d.t) * d.r,
- y: (d) => Math.sin(d.t) * d.r,
- rotate: (d) => d.a,
- })
- .append(cm.text, {
- x: 0,
- y: 0,
- text: (d) => d.c,
- fill: "#7214E3",
- ...font,
- });
-}
-
-export function generalBrokenHeart() {
- let width = 640,
- height = 640,
- count = 200,
- radius = 100,
- noise = cm.randomNoise(),
- string = "Sad News!!!",
- chars = null,
- font = { fontSize: 100 };
-
- function step(app) {
- let prev = 0;
- chars = string.split("").map((d) => {
- const { width } = app.textBBox({ text: d, ...font });
- const step = width / (radius * 1.8);
- const theta = prev + step / 2;
- prev += step;
- return { t: theta, c: d };
- });
- }
-
- function update(app) {
- const time = app.prop("frameCount") / 100;
- const x = width / 2;
- const y = height / 3;
-
- app.append(cm.clear, { fill: "black" });
-
- app
- .call(drawHeart, { x, y, count, radius, noise, time })
- .call(drawEyes, { x, y, r: radius / 2, radius })
- .call(drawTexts, { x, y, chars, radius, noise, time, ...font });
- }
-
- return cm
- .app({ width, height })
- .on("beforeAll", step)
- .on("update", update)
- .call(dispose)
- .call(stats)
- .call(frame)
- .start()
- .node();
-}
diff --git a/test/apps/general-circle-clover.js b/test/apps/general-circle-clover.js
deleted file mode 100644
index 7f680e88..00000000
--- a/test/apps/general-circle-clover.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import * as cm from "../../src/index.js";
-import { interpolateRainbow } from "d3-scale-chromatic";
-import { dispose } from "../utils/dispose.js";
-
-export function generalCircleClover() {
- const app = cm.app({
- width: 640,
- height: 640,
- });
-
- app
- .data(cm.range(120))
- .process(cm.map, (_, i, a) => i * (Math.PI / a.length))
- .append(cm.circle, {
- x: (t) => Math.cos(t) * Math.cos(t * 3),
- y: (t) => Math.sin(t) * Math.cos(t * 3),
- r: (t) => t,
- fill: (t) => t,
- })
- .transform(cm.mapPosition, { padding: 20 })
- .transform(cm.mapAttrs, {
- r: { range: [8, 20] },
- fill: { interpolate: interpolateRainbow },
- });
-
- return app.call(dispose).render().node();
-}
diff --git a/test/apps/general-circles-glsl.webgl.js b/test/apps/general-circles-glsl.webgl.js
deleted file mode 100644
index a038705e..00000000
--- a/test/apps/general-circles-glsl.webgl.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function generalCirclesGLSLWebGL() {
- const width = 700;
- const height = 700;
- const count = 22000;
- const theta = cm.range(count, 0, cm.TWO_PI);
-
- function update(app) {
- const time = app.prop("frameCount") / 50;
- const scale = 300;
-
- app.append(cm.clear, { fill: "black" });
-
- app
- .data(theta) // Bind Data.
- .append(cm.circle, {
- position: cm.glsl`vec2 position(float theta) {
- vec2 xy = vec2(cos(theta), sin(theta)) * (0.6 + 0.2 * cos(theta * 6.0 + cos(theta * 8.0 + ${time})));
- return xy * ${scale} + vec2(${width / 2}, ${height / 2});
- }`,
- r: cm.glsl`float r(float theta) {
- float d = 0.2 + 0.12 * cos(theta * 9.0 - ${time} * 2.0);
- return d * ${scale};
- }`,
- stroke: cm.glsl`vec4 stroke(float theta) {
- float th = 8.0 * theta + ${time} * 2.0;
- vec3 rgb = 0.6 + 0.4 * vec3(
- cos(th),
- cos(th - ${Math.PI} / 3.0),
- cos(th - ${Math.PI} * 2.0 / 3.0)
- );
- return vec4(rgb, 0.0);
- }`,
- strokeOpacity: cm.glsl`float strokeOpacity(float theta) {
- return 0.15 * 2000.0 / ${count};
- }`,
- });
- }
-
- return cm.app({ width, height, renderer: cm.webgl() }).on("update", update).call(dispose).call(stats).start().node();
-}
diff --git a/test/apps/general-circles.js b/test/apps/general-circles.js
deleted file mode 100644
index 076c6c38..00000000
--- a/test/apps/general-circles.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function generalCircles() {
- const width = 700;
- const height = 700;
- const count = 5500;
- const theta = cm.range(count, 0, cm.TWO_PI);
-
- function update(app) {
- const time = app.prop("frameCount") / 50;
- const dist = (d) => 0.6 + 0.2 * Math.cos(d * 6.0 + Math.cos(d * 8.0 + time));
-
- app.append(cm.clear, { fill: "black" });
-
- app
- .data(theta)
- .append(cm.circle, {
- x: (d) => Math.cos(d) * dist(d),
- y: (d) => Math.sin(d) * dist(d),
- r: (d) => 0.2 + 0.12 * Math.cos(d * 9.0 - time * 2.0),
- stroke: (d) => {
- const th = 8.0 * d + time * 2.0;
- const r = 0.6 + 0.4 * Math.cos(th);
- const g = 0.6 + 0.4 * Math.cos(th - Math.PI / 3);
- const b = 0.6 + 0.4 * Math.cos(th - (Math.PI * 2.0) / 3.0);
- return cm.rgb(r * 255, g * 255, b * 255);
- },
- fill: "transparent",
- strokeOpacity: (0.15 * 2000) / count,
- })
- .transform(cm.mapPosition, { padding: 100 })
- .transform(cm.mapAttrs, { r: { range: [24, 96] } });
- }
-
- return cm.app({ width, height }).on("update", update).call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/general-circles.webgl.js b/test/apps/general-circles.webgl.js
deleted file mode 100644
index 67056423..00000000
--- a/test/apps/general-circles.webgl.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function generalCirclesWebGL() {
- const width = 700;
- const height = 700;
- const count = 22000;
- const theta = cm.range(count, 0, cm.TWO_PI);
-
- function update(app) {
- const time = app.prop("frameCount") / 50;
- const dist = (d) => 0.6 + 0.2 * Math.cos(d * 6.0 + Math.cos(d * 8.0 + time));
-
- app.append(cm.clear, { fill: "black" });
-
- app
- .data(theta)
- .append(cm.circle, {
- x: (d) => Math.cos(d) * dist(d),
- y: (d) => Math.sin(d) * dist(d),
- r: (d) => 0.2 + 0.12 * Math.cos(d * 9.0 - time * 2.0),
- stroke: (d) => {
- const th = 8.0 * d + time * 2.0;
- const r = 0.6 + 0.4 * Math.cos(th);
- const g = 0.6 + 0.4 * Math.cos(th - Math.PI / 3);
- const b = 0.6 + 0.4 * Math.cos(th - (Math.PI * 2.0) / 3.0);
- return cm.rgb(r * 255, g * 255, b * 255);
- },
- strokeOpacity: (0.15 * 2000) / count,
- })
- .transform(cm.mapPosition, { padding: 100 })
- .transform(cm.mapAttrs, { r: { range: [24, 96] } });
- }
-
- return cm
- .app({
- width,
- height,
- renderer: cm.webgl(),
- })
- .on("update", update)
- .call(dispose)
- .call(stats)
- .start()
- .node();
-}
diff --git a/test/apps/general-line-maurer-rose.js b/test/apps/general-line-maurer-rose.js
deleted file mode 100644
index 29aa5ba5..00000000
--- a/test/apps/general-line-maurer-rose.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-// @see https://observablehq.com/@recifs/a-rose-is-a-rose
-export function generalLineMaurerRose() {
- const n = 10;
- const d = 17;
- const angles = cm.range(360, 0, 360).map((d) => (d * Math.PI) / 180);
-
- const app = cm.app({
- width: 450,
- height: 450,
- });
-
- app
- .data(angles)
- .append(cm.line, {
- x: (i) => Math.sin(n * d * i) * Math.cos(d * i),
- y: (i) => Math.sin(n * d * i) * Math.sin(d * i),
- stroke: "black",
- strokeWidth: 0.33,
- close: true,
- })
- .transform(cm.mapPosition, {
- domainX: [-1, 1],
- domainY: [-1, 1],
- });
-
- app
- .data(angles)
- .append(cm.line, {
- x: (i) => Math.sin(n * i) * Math.cos(i),
- y: (i) => Math.sin(n * i) * Math.sin(i),
- stroke: "red",
- strokeWidth: 2.5,
- close: true,
- })
- .transform(cm.mapPosition, {
- domainX: [-1, 1],
- domainY: [-1, 1],
- });
-
- return app.call(dispose).render().node();
-}
diff --git a/test/apps/general-pattern-symbols.js b/test/apps/general-pattern-symbols.js
deleted file mode 100644
index 8eb6dcd7..00000000
--- a/test/apps/general-pattern-symbols.js
+++ /dev/null
@@ -1,116 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-function circle(context, size) {
- const r = size / 2;
- context.moveTo(r, 0);
- context.arc(0, 0, r, 0, Math.PI * 2);
-}
-
-function cross(context, size) {
- const r = size / 6;
- context.moveTo(-3 * r, -r);
- context.lineTo(-r, -r);
- context.lineTo(-r, -3 * r);
- context.lineTo(r, -3 * r);
- context.lineTo(r, -r);
- context.lineTo(3 * r, -r);
- context.lineTo(3 * r, r);
- context.lineTo(r, r);
- context.lineTo(r, 3 * r);
- context.lineTo(-r, 3 * r);
- context.lineTo(-r, r);
- context.lineTo(-3 * r, r);
- context.closePath();
-}
-
-function square(context, size) {
- const x = -size / 2;
- context.rect(x, x, size, size);
-}
-
-function triangle(context, size) {
- const y = size / 2;
- const x = size / 2;
- context.moveTo(0, -y);
- context.lineTo(x, y);
- context.lineTo(-x, y);
- context.closePath();
-}
-
-function createMatrix(scale, rotation = 0) {
- const radian = (rotation * Math.PI) / 180;
- const matrix = {
- a: Math.cos(radian) * scale,
- b: Math.sin(radian) * scale,
- c: -Math.sin(radian) * scale,
- d: Math.cos(radian) * scale,
- e: 0,
- f: 0,
- };
- return matrix;
-}
-
-function pattern(patternElement) {
- const id = patternElement.match(/id="([^"]+)"/)[1];
- const width = patternElement.match(/width="([^"]+)"/)[1] ?? 100;
- const height = patternElement.match(/height="([^"]+)"/)[1] ?? 100;
- const canvas = document.createElement("canvas");
- const context = canvas.getContext("2d");
- const image = new Image();
- const svgContent = `
- `;
- image.src = "data:image/svg+xml," + encodeURIComponent(svgContent);
- return new Promise((resolve) => {
- image.onload = () => {
- const pattern = context.createPattern(image, "repeat");
- const matrix = createMatrix(0.5);
- pattern.setTransform(matrix);
- resolve(pattern);
- };
- });
-}
-
-export async function generalPatternSymbols() {
- const size = 640;
- const n = 2;
- const cellSize = 640 / n;
- const symbols = [circle, cross, square, triangle];
- const fill = await pattern(
- '',
- );
-
- function draw(app) {
- app
- .data(cm.range(symbols.length))
- .append(cm.group, {
- x: (d) => (d % n) * cellSize + cellSize / 2,
- y: (d) => Math.floor(d / n) * cellSize + cellSize / 2,
- })
- .append(cm.path, {
- d: (i) => {
- const context = cm.pathContext();
- symbols[i](context, cellSize * 0.9);
- return context.toArray();
- },
- fill: fill,
- stroke: "black",
- strokeWidth: 2,
- });
- }
-
- return cm
- .app({
- width: size,
- height: size,
- })
- .call(draw)
- .call(dispose)
- .render()
- .node();
-}
diff --git a/test/apps/general-regular-numbers.js b/test/apps/general-regular-numbers.js
deleted file mode 100644
index 6070dc5d..00000000
--- a/test/apps/general-regular-numbers.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-// @see https://observablehq.com/@fil/regular-numbers
-export function generalRegularNumbers() {
- const N = 511;
- const x = ([i, j, k]) => 0.5 * i + 2 * j - 3.6 * k;
- const value = ([i, j, k]) => 2 ** i * 3 ** j * 5 ** k;
-
- const numbers = cm
- .cross(
- cm.range(1 + Math.log(N) / Math.log(2)),
- cm.range(1 + Math.log(N) / Math.log(3)),
- cm.range(1 + Math.log(N) / Math.log(5)),
- )
- .filter((d) => value(d) <= N);
-
- const applyScale = (flow) => {
- flow.transform(cm.mapPosition, {
- scaleY: cm.scaleLog,
- padding: 15,
- domainX: cm.extent(numbers, x),
- domainY: cm.extent(numbers, value),
- reverseY: true,
- });
- };
-
- const app = cm.app();
-
- app
- .data(
- numbers
- .flatMap(([i, j, k]) => [
- [
- [i - 1, j, k],
- [i, j, k],
- ],
- [
- [i, j - 1, k],
- [i, j, k],
- ],
- [
- [i, j, k - 1],
- [i, j, k],
- ],
- ])
- .filter(([n]) => Math.min(...n) >= 0),
- )
- .append(cm.link, {
- x: ([n]) => x(n),
- y: ([n]) => value(n),
- x1: ([, n]) => x(n),
- y1: ([, n]) => value(n),
- stroke: "lightblue",
- })
- .call(applyScale);
-
- app
- .data(numbers)
- .append(cm.circle, {
- x: x,
- y: value,
- r: 12,
- fill: "white",
- stroke: "black",
- })
- .call(applyScale);
-
- app
- .data(numbers)
- .append(cm.text, {
- x: x,
- y: value,
- text: value,
- textAlign: "center",
- textBaseline: "middle",
- })
- .call(applyScale);
-
- return app.call(dispose).render().node();
-}
diff --git a/test/apps/general-shader-art.webgl.js b/test/apps/general-shader-art.webgl.js
deleted file mode 100644
index 719dd296..00000000
--- a/test/apps/general-shader-art.webgl.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-// @see: https://www.shadertoy.com/view/mtyGWy
-export function generalShaderArtWebGL() {
- const width = 640;
- const height = 360;
- const palette = cm.glsl`vec3 palette(float t) {
- vec3 a = vec3(0.5, 0.5, 0.5);
- vec3 b = vec3(0.5, 0.5, 0.5);
- vec3 c = vec3(1.0, 1.0, 1.0);
- vec3 d = vec3(0.263, 0.416, 0.557);
- return a + b * cos(3.1415926 * 2.0 * (c * t + d));
- }`;
-
- function update(app) {
- const time = app.prop("frameCount") / 50;
- const fill = cm.glsl`vec4 fill(vec2 coord, vec4 color) {
- vec2 uv = (coord - vec2(${width}, ${height})) / ${height};
- vec2 uv0 = uv;
- vec3 rgb = vec3(0.0);
- for (float i = 0.0; i < 4.0; i++) {
- uv = fract(uv * 1.5) - 0.5;
- float d = length(uv) * exp(-length(uv0));
- vec3 col = ${palette}(length(uv0) + i * 0.4 + ${time} * 0.4);
- d = sin(d * 8.0 + ${time}) / 8.0;
- d = abs(d);
- d = pow(0.01 / d, 1.2);
- rgb += col * d;
- }
- return vec4(rgb, 1.0);
- }`;
- app.append(cm.rect, { x: 0, y: 0, width, height, fill });
- }
-
- return cm
- .app({
- renderer: cm.webgl(),
- width,
- height,
- })
- .call(dispose)
- .call(stats)
- .on("update", update)
- .start()
- .node();
-}
diff --git a/test/apps/index.js b/test/apps/index.js
deleted file mode 100644
index a933e900..00000000
--- a/test/apps/index.js
+++ /dev/null
@@ -1,55 +0,0 @@
-export { a2Seeking } from "./a2-seeking.js";
-export { a2Arriving } from "./a2-arriving.js";
-export { a2Wandering } from "./a2-wandering.js";
-export { a2FlowRandom } from "./a2-flow-random.js";
-export { a2FlowNoise } from "./a2-flow-noise.js";
-export { forceDancing } from "./force-dancing.js";
-export { forceFalling } from "./force-falling.js";
-export { forceLiquid } from "./force-liquid.js";
-export { forceAttract } from "./force-attract.js";
-export { forceMultiple } from "./force-multiple.js";
-export { forceEverything } from "./force-everything.js";
-export { generalCircleClover } from "./general-circle-clover.js";
-export { generalLineMaurerRose } from "./general-line-maurer-rose.js";
-export { generalCircles } from "./general-circles.js";
-export { generalCirclesWebGL } from "./general-circles.webgl.js";
-export { generalCirclesGLSLWebGL } from "./general-circles-glsl.webgl.js";
-export { generalBrokenHeart } from "./general-broken-heart.js";
-export { generalPatternSymbols } from "./general-pattern-symbols.js";
-export { generalShaderArtWebGL } from "./general-shader-art.webgl.js";
-export { generalRegularNumbers } from "./general-regular-numbers.js";
-export { oscillationBaton } from "./oscillation-baton.js";
-export { oscillationBatons } from "./oscillation-batons.js";
-export { oscillationSquareAttract } from "./oscillation-square-attract.js";
-export { oscillationCar } from "./oscillation-car.js";
-export { oscillationDone } from "./oscillation-done.js";
-export { oscillationOscillate } from "./oscillation-oscillate.js";
-export { oscillationOscillateMultiple } from "./oscillation-oscillate-multiple.js";
-export { oscillationWave } from "./oscillation-wave.js";
-export { oscillationWaveSnake } from "./oscillation-wave-snake.js";
-export { oscillationSpring2 } from "./oscillation-spring2.js";
-export { oscillationSpring } from "./oscillation-spring.js";
-export { oscillationBeesSandBombs } from "./oscillation-beesandbombs.js";
-export { particleCluster } from "./particle-cluster.js";
-export { particleClusters } from "./particle-clusters.js";
-export { particleClusterShapes } from "./particle-cluster-shapes.js";
-export { particleClusterForce } from "./particle-cluster-force.js";
-export { randomWalk } from "./random-walk.js";
-export { randomDistribution } from "./random-distribution.js";
-export { randomCurve } from "./random-curve.js";
-export { randomNormal } from "./random-normal.js";
-export { randomDragon } from "./random-dragon.js";
-export { matterFallingBoxes } from "./matter-falling-boxes.js";
-export { matterFallingPolygons } from "./matter-falling-polygons.js";
-export { toxicGrid } from "./toxic-grid.js";
-export { terminalMatrix } from "./terminal-matrix.js";
-export { terminalPoint } from "./terminal-point.js";
-export { terminalText } from "./terminal-text.js";
-export { terminalWhiteNoise } from "./terminal-white-noise.js";
-export { terminalCharacterMatrix } from "./terminal-character-matrix.js";
-export { terminalFire } from "./terminal-fire.js";
-export { terminalBar } from "./terminal-bar.js";
-export { vectorCircleBouncing } from "./vector-circle-bouncing.js";
-export { vectorMoveLink } from "./vector-move-link.js";
-export { vectorFollowMe } from "./vector-follow-me.js";
-export { visDot } from "./vis-dot.js";
diff --git a/test/apps/matter-falling-boxes.js b/test/apps/matter-falling-boxes.js
deleted file mode 100644
index 5f7fe533..00000000
--- a/test/apps/matter-falling-boxes.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-import { Engine, Bodies, Composite, Body, Vector } from "matter-js";
-
-function createBoundary(parent, x, y, width, height) {
- const body = Bodies.rectangle(x, y, width, height, { isStatic: true });
- Composite.add(parent, body);
- return { body, width, height };
-}
-
-function createBox(parent, x, y) {
- const width = cm.random(8, 16);
- const height = cm.random(8, 16);
- const body = Bodies.rectangle(x, y, width, height, { restitution: 0.6 });
- Body.setVelocity(body, Vector.create(cm.random(-5, 5), 0));
- Body.setAngularVelocity(body, 0.1);
- Composite.add(parent, body);
- return { body, width, height };
-}
-
-function checkBox(d, i, array, flow) {
- const { body } = d;
- const app = flow.app();
- if (body.bounds.min.y > app.prop("height")) {
- array.splice(i, 1);
- Composite.remove(body.parent, d);
- }
-}
-
-export function matterFallingBoxes() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const engine = Engine.create();
- const w = app.prop("width");
- const h = app.prop("height");
-
- const boxes = [];
- const boundaries = [
- createBoundary(engine.world, w / 4, h - 5, w / 2 - 50, 10),
- createBoundary(engine.world, (3 * w) / 4, h - 50, w / 2 - 50, 10),
- ];
-
- const x = (d) => d.body.position.x;
- const y = (d) => d.body.position.y;
- const rotate = (d) => d.body.angle;
- const width = (d) => d.width;
- const height = (d) => d.height;
- const rectOptions = {
- x,
- y,
- width,
- height,
- rotate,
- fill: cm.rgb(127),
- stroke: cm.rgb(0),
- strokeWeight: 2,
- anchor: "center",
- };
-
- function update() {
- if (cm.random(1) < 0.1) boxes.push(createBox(engine.world, w / 2, 50));
-
- app.append(cm.clear, { fill: "#fff" });
-
- app.data(boxes).process(cm.eachRight, checkBox).append(cm.rect, rectOptions);
-
- app.data(boundaries).append(cm.rect, rectOptions);
-
- Engine.update(engine);
- }
-
- return app.on("update", update).call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/matter-falling-polygons.js b/test/apps/matter-falling-polygons.js
deleted file mode 100644
index 368e3905..00000000
--- a/test/apps/matter-falling-polygons.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-import { Engine, Bodies, Composite, Body, Vector } from "matter-js";
-
-function createBoundary(parent, x, y, width, height) {
- const body = Bodies.rectangle(x, y, width, height, { isStatic: true });
- Composite.add(parent, body);
- return { body, width, height };
-}
-
-function createBox(parent, x, y) {
- const vertices = [
- Vector.create(-10, -10),
- Vector.create(20, -15),
- Vector.create(15, 0),
- Vector.create(0, 10),
- Vector.create(-20, 15),
- ];
- const body = Bodies.fromVertices(x, y, vertices, { restitution: 0.2 });
- Body.setVelocity(body, Vector.create(cm.random(-5, 5), 0));
- Body.setAngularVelocity(body, 0.1);
- Composite.add(parent, body);
- return { body };
-}
-
-function checkBox(d, i, array, flow) {
- const { body } = d;
- const app = flow.app();
- if (body.bounds.min.y > app.prop("height")) {
- array.splice(i, 1);
- Composite.remove(body.parent, d);
- }
-}
-
-export function matterFallingPolygons() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const engine = Engine.create();
- const w = app.prop("width");
- const h = app.prop("height");
-
- const boxes = [];
- const boundaries = [
- createBoundary(engine.world, w / 4, h - 5, w / 2 - 50, 10),
- createBoundary(engine.world, (3 * w) / 4, h - 50, w / 2 - 50, 10),
- ];
-
- function update() {
- if (cm.random(1) < 0.1) boxes.push(createBox(engine.world, w / 2, 50));
-
- app.append(cm.clear, { fill: "#fff" });
-
- app
- .data(boxes)
- .process(cm.eachRight, checkBox)
- .append(cm.polygon, {
- x: (d) => d.body.vertices.map((d) => d.x),
- y: (d) => d.body.vertices.map((d) => d.y),
- fill: cm.rgb(127),
- stroke: cm.rgb(0),
- strokeWeight: 2,
- });
-
- app.data(boundaries).append(cm.rect, {
- x: (d) => d.body.position.x,
- y: (d) => d.body.position.y,
- rotate: (d) => d.body.angle,
- width: (d) => d.width,
- height: (d) => d.height,
- fill: cm.rgb(127),
- stroke: cm.rgb(0),
- strokeWeight: 2,
- anchor: "center",
- });
-
- Engine.update(engine);
- }
-
- return app.on("update", update).call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-baton.js b/test/apps/oscillation-baton.js
deleted file mode 100644
index 8a6ad333..00000000
--- a/test/apps/oscillation-baton.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationBaton() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const group = app.append(cm.group, {
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- rotate: app.prop("frameCount") / 50,
- });
- group.append(cm.link, { x: -80, y: 0, x1: 80, y1: 0 });
- group.append(cm.circle, { x: -80, y: 0, r: 5, fill: "black" });
- group.append(cm.circle, { x: 80, y: 0, r: 5, fill: "black" });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-batons.js b/test/apps/oscillation-batons.js
deleted file mode 100644
index 51233147..00000000
--- a/test/apps/oscillation-batons.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationBatons() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data([0, 1, 2])
- .append(cm.group, {
- x: (_, i) => ((i + 0.5) * app.prop("width")) / 3,
- y: app.prop("height") / 2,
- rotate: (_, i) => app.prop("frameCount") / 50 + i * 30,
- })
- .call((d) => d.append(cm.link, { x: -80, y: 0, x1: 80, y1: 0, strokeWidth: 5, strokeCap: "round" }))
- .call((d) => d.append(cm.circle, { x: -80, y: 0, r: 5, fill: "black" }));
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-beesandbombs.js b/test/apps/oscillation-beesandbombs.js
deleted file mode 100644
index e8e206d4..00000000
--- a/test/apps/oscillation-beesandbombs.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import * as d3 from "d3-geo";
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-// @see https://observablehq.com/d/a741f9a27e8c0e73
-export function oscillationBeesSandBombs() {
- const width = 640;
- const radius = 12;
- const data = cm.cross(cm.range(20, -1, 1), cm.range(20, -1, 1));
- const circle = d3.geoCircle()();
- const projection = d3.geoOrthographic().translate([0, 0]).scale(radius);
- const path = d3.geoPath(projection);
-
- function draw(app) {
- app.append(cm.clear, { fill: cm.rgb(255) });
-
- app
- .data(data)
- .append(cm.circle, {
- x: (d) => d[0],
- y: (d) => d[1],
- r: radius,
- fill: "white",
- stroke: "black",
- strokeWidth: 1.5,
- })
- .call(applyScale);
-
- app
- .data(data)
- .append(cm.path, {
- x: (d) => d[0],
- y: (d) => d[1],
- fill: "black",
- d: (d) => {
- const [x, y] = d;
- const now = app.prop("frameCount") / 200;
- const l = ((Math.hypot(x, y) + Math.atan2(y, x) / (Math.PI * 2) - now) % 1) * -360;
- projection.rotate([0, l, -l]);
- const context = cm.pathContext();
- path.context(context)(circle);
- return context.toArray();
- },
- })
- .call(applyScale);
- }
-
- function applyScale(flow) {
- const app = flow.app();
- flow.transform(cm.mapAttrs, {
- x: { range: [40, app.prop("width") - 40] },
- y: { range: [40, app.prop("height") - 40] },
- });
- }
-
- return cm.app({ width: width, height: width }).on("update", draw).call(dispose).call(stats).start().node();
-}
diff --git a/test/apps/oscillation-car.js b/test/apps/oscillation-car.js
deleted file mode 100644
index b6ba2810..00000000
--- a/test/apps/oscillation-car.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { force, object, location } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationCar() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const mover = object();
-
- const update = location();
- const toMouse = (d, i, _, flow) => {
- const app = flow.app();
- const mouse = cm.vec(app.prop("mouseX"), app.prop("mouseY"));
- return mouse.sub(d.location).mag(0.5);
- };
- const constrain = (d) => {
- d.location.clampX(app.prop("width")).clampY(app.prop("height"));
- d.velocity.clamp(4);
- };
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .datum(mover)
- .process(cm.each, force(toMouse))
- .process(cm.each, update)
- .process(cm.each, constrain)
- .append(cm.rect, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- width: 30,
- height: 10,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- rotate: (d) => d.velocity.angle(),
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-done.js b/test/apps/oscillation-done.js
deleted file mode 100644
index 46289e8b..00000000
--- a/test/apps/oscillation-done.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationDone() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const r = 75;
- const theta = app.prop("frameCount") / 50;
- const group = app.append(cm.group, {
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- });
- const x = r * Math.cos(theta);
- const y = r * Math.sin(theta);
- group.append(cm.link, { x: 0, y: 0, x1: x, y1: y });
- group.append(cm.circle, { x, y, r: 16, fill: "#000" });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-oscillate-multiple.js b/test/apps/oscillation-oscillate-multiple.js
deleted file mode 100644
index d1548510..00000000
--- a/test/apps/oscillation-oscillate-multiple.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationOscillateMultiple() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const oscillators = cm.range(20).map(() => ({
- angle: cm.vec(),
- velocity: cm.vec(cm.random(-0.05, 0.05), cm.random(-0.05, 0.05)),
- amplitude: cm.vec(cm.random(app.prop("width") / 2), cm.random(app.prop("height") / 2)),
- }));
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const group = app.append(cm.group, {
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- });
- const x = (d) => Math.sin(d.angle.x) * d.amplitude.x;
- const y = (d) => Math.sin(d.angle.y) * d.amplitude.y;
- group
- .data(oscillators)
- .process(cm.each, (d) => d.angle.add(d.velocity))
- .call((d) => d.append(cm.link, { x: 0, y: 0, x1: x, y1: y }))
- .call((d) =>
- d.append(cm.circle, {
- x,
- y,
- r: 16,
- stroke: "#000",
- fill: cm.rgb(175),
- fillOpacity: 0.5,
- }),
- );
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-oscillate.js b/test/apps/oscillation-oscillate.js
deleted file mode 100644
index a909571d..00000000
--- a/test/apps/oscillation-oscillate.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationOscillate() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const amplitude = 100;
- const period = 120;
- const group = app.append(cm.group, {
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- });
- const x = amplitude * Math.cos((cm.TWO_PI * app.prop("frameCount")) / period);
- const y = 0;
- group.append(cm.link, { x: 0, y: 0, x1: x, y1: y });
- group.append(cm.circle, { x, y, r: 16, fill: "black" });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-spring.js b/test/apps/oscillation-spring.js
deleted file mode 100644
index db074db3..00000000
--- a/test/apps/oscillation-spring.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationSpring() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const pendulum = { angle: Math.PI / 4, aVelocity: 0, r: 150, damping: 0.995 };
- const update = (d) => {
- const aAcceleration = -1 * (0.4 / d.r) * Math.sin(d.angle);
- d.aVelocity += aAcceleration;
- d.aVelocity *= d.damping;
- d.angle += d.aVelocity;
- };
- const x = (d) => d.r * Math.sin(d.angle);
- const y = (d) => d.r * Math.cos(d.angle);
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const group = app.append(cm.group, { x: app.prop("width") / 2, y: 0 });
- group
- .datum(pendulum)
- .process(cm.each, update)
- .call((d) => d.append(cm.link, { x: 0, y: 0, x1: x, y1: y }))
- .call((d) =>
- d.append(cm.circle, {
- x,
- y,
- r: 20,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- }),
- );
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-spring2.js b/test/apps/oscillation-spring2.js
deleted file mode 100644
index ddf5b8da..00000000
--- a/test/apps/oscillation-spring2.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { object, location, force } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-function updateBob(d, { dragging, anchor, spring }) {
- const applyGravity = (d) => cm.vec(0, 1).mult(d.mass);
- const applySpring = (d) => {
- const f = cm.vecSub(d.location, anchor.location);
- const length = f.mag();
- const stretch = length - spring.length;
- const m = -1 * spring.k * stretch;
- f.mag(m);
- return f;
- };
- const applyDamping = (d) => d.velocity.mult(0.98);
- const applyDrag = (d, i, _, flow) => {
- const app = flow.app();
- d.location.x = app.prop("mouseX") - app.prop("width") / 2;
- d.location.y = app.prop("mouseY");
- d.velocity.mult(0);
- d.acceleration.mult(0);
- };
-
- if (dragging) d.process(cm.each, applyDrag);
- else {
- d.process(cm.each, force(applyGravity))
- .process(cm.each, force(applySpring))
- .process(cm.each, applyDamping)
- .process(cm.each, location());
- }
-}
-
-function drawBob(d, { dragging }) {
- const x = (d) => d.location.x;
- const y = (d) => d.location.y;
- d.append(cm.link, { x: 0, y: 0, x1: x, y1: y });
- d.append(cm.circle, {
- x,
- y,
- r: 20,
- fill: dragging ? "black" : cm.rgb(175),
- stroke: cm.rgb(0),
- });
-}
-
-export function oscillationSpring2() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- let dragging = false;
-
- const spring = { k: 0.1, length: 100 };
- const bob = object({ location: cm.vec(0, spring.length) });
- const anchor = object();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const group = app.append(cm.group, { x: app.prop("width") / 2, y: 0 });
-
- group.datum(bob).call(updateBob, { dragging, spring, anchor }).call(drawBob, { dragging });
-
- group.datum(anchor).append(cm.rect, {
- x: (d) => d.location.x - 5,
- y: (d) => d.location.y,
- width: 10,
- height: 10,
- fill: cm.rgb(175),
- stroke: cm.rgb(0),
- });
- })
- .on("mouseDown", () => (dragging = true))
- .on("mouseUp", () => (dragging = false));
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-square-attract.js b/test/apps/oscillation-square-attract.js
deleted file mode 100644
index dadcf737..00000000
--- a/test/apps/oscillation-square-attract.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { location, object, attraction, rotation } from "../utils/force.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationSquareAttract() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const centerX = app.prop("width") / 2;
- const centerY = app.prop("height") / 2;
-
- const attractor = object({
- mass: 10,
- location: cm.vec(centerX, centerY),
- G: 1,
- });
-
- const movers = cm.range(20).map(() =>
- object({
- location: cm.vec(cm.random(app.prop("width")), cm.random(app.prop("height"))),
- velocity: cm.vec(cm.random(), cm.random()),
- mass: cm.random(2, 5),
- }),
- );
-
- const applyAttraction = attraction(attractor);
- const rotate = rotation();
- const move = location();
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, applyAttraction)
- .process(cm.each, rotate)
- .process(cm.each, move)
- .append(cm.rect, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- width: (d) => d.mass * 5,
- height: (d) => d.mass * 5,
- rotate: (d) => d.rotation,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 2,
- });
- })
- .on("update", () => {
- app
- .datum(attractor) // Convert to an array.
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: (d) => d.mass * 2,
- fill: cm.rgb(175),
- stroke: "#000",
- strokeWidth: 5,
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-wave-snake.js b/test/apps/oscillation-wave-snake.js
deleted file mode 100644
index b9c80a09..00000000
--- a/test/apps/oscillation-wave-snake.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationWaveSnake() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const I = cm.range(50);
- const X = cm.range(I.length, cm.TWO_PI);
- const X1 = cm.range(I.length, cm.TWO_PI * 2);
- const X2 = cm.range(I.length, cm.TWO_PI / 3);
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(I)
- .append(cm.circle, {
- x: (d) => d,
- y: (d) => {
- const offset = app.prop("frameCount") / 120;
- const x = Math.sin(X[d] + offset) * 2;
- const x1 = Math.sin(X1[d] + offset) * 1;
- const x2 = Math.sin(X2[d] + offset) * 3;
- return x * x1 * x2;
- },
- r: 20,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: "#000",
- strokeWidth: 1,
- })
- .transform(cm.mapAttrs, {
- x: { range: [0, app.prop("width")] },
- y: { range: [0, app.prop("height")] },
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/oscillation-wave.js b/test/apps/oscillation-wave.js
deleted file mode 100644
index 4f466d6b..00000000
--- a/test/apps/oscillation-wave.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function oscillationWave() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const X = cm.range(50, cm.TWO_PI);
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(X)
- .append(cm.circle, {
- x: (d) => d,
- y: (d) => Math.sin(d + app.prop("frameCount") / 30),
- r: 20,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: "#000",
- strokeWidth: 1,
- })
- .transform(cm.mapAttrs, {
- x: { range: [0, app.prop("width")] },
- y: { range: [0, app.prop("height")] },
- });
- });
-
- return app.call(dispose).call(frame).call(stats).start().node();
-}
diff --git a/test/apps/particle-cluster-force.js b/test/apps/particle-cluster-force.js
deleted file mode 100644
index 70c0cfe3..00000000
--- a/test/apps/particle-cluster-force.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { object, location, force } from "../utils/force.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function particleClusterForce() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const particles = [];
- const repeller = {
- location: cm.vec(app.prop("width") / 2, app.prop("height") - 50),
- power: 150,
- };
-
- const update = location();
- const fadeOut = (d, i, array) => {
- if (d.lifespan < 0) array.splice(i, 1);
- d.lifespan -= 2;
- };
- const applyGravity = force((d) => cm.vec(0, 0.1).mult(d.mass));
- const applyRepeller = force((d) => {
- const force = cm.vecSub(repeller.location, d.location);
- const distance = cm.clamp(force.mag(), 5, 50);
- const strength = (-1 * repeller.power) / (distance * distance);
- force.mag(strength);
- return force;
- });
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app.datum(repeller).append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: 16,
- stroke: "black",
- fill: cm.rgb(127),
- });
-
- app
- .data(particles)
- .process(
- cm.push,
- object({
- location: cm.vec(app.prop("width") / 2, 50),
- lifespan: 255,
- velocity: cm.vec(cm.random(-1, 1), cm.random(-2, 0)),
- }),
- )
- .process(cm.eachRight, fadeOut)
- .process(cm.each, applyRepeller)
- .process(cm.each, applyGravity)
- .process(cm.each, update)
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: 5,
- fill: cm.rgb(0),
- stroke: cm.rgb(0),
- fillOpacity: (d) => d.lifespan,
- strokeOpacity: (d) => d.lifespan,
- })
- .transform(cm.mapAttrs, {
- fillOpacity: { domain: [0, 255], range: [0, 0.6] },
- strokeOpacity: { domain: [0, 255], range: [0, 1] },
- });
- });
-
- return app.call(dispose).call(frame).call(stats).start().node();
-}
diff --git a/test/apps/particle-cluster-shapes.js b/test/apps/particle-cluster-shapes.js
deleted file mode 100644
index 9537bbc9..00000000
--- a/test/apps/particle-cluster-shapes.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function particleClusterShapes() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const options = {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- fill: cm.rgb(0),
- stroke: cm.rgb(0),
- rotate: (d) => d.location.x,
- fillOpacity: (d) => d.lifespan,
- strokeOpacity: (d) => d.lifespan,
- };
-
- const scaleOptions = {
- fillOpacity: { domain: [0, 255], range: [0, 0.6] },
- strokeOpacity: { domain: [0, 255], range: [0, 1] },
- rotate: { domain: [0, app.prop("width")], range: [0, cm.TWO_PI * 2] },
- };
-
- const particles = [];
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const flow = app
- .data(particles)
- .process(cm.push, {
- location: cm.vec(app.prop("width") / 2, 50),
- velocity: cm.vec(cm.random(-1, 1), cm.random(-2, 0)),
- acceleration: cm.vec(0, 0.05),
- lifespan: 255,
- type: Math.random() < 0.5 ? 1 : 0,
- })
- .process(cm.eachRight, (d, i, array) => d.lifespan < 0 && array.splice(i, 1))
- .process(cm.each, (d) => (d.lifespan -= 2))
- .process(cm.each, (d) => {
- d.velocity.add(d.acceleration);
- d.location.add(d.velocity);
- });
-
- flow
- .process(cm.filter, (d) => d.type === 1)
- .append(cm.circle, { ...options, r: 5 })
- .transform(cm.mapAttrs, scaleOptions);
-
- flow
- .process(cm.filter, (d) => d.type === 0)
- .append(cm.rect, {
- ...options,
- width: 10,
- height: 10,
- anchor: "center",
- })
- .transform(cm.mapAttrs, scaleOptions);
- });
-
- return app.call(dispose).call(frame).call(stats).start().node();
-}
diff --git a/test/apps/particle-cluster.js b/test/apps/particle-cluster.js
deleted file mode 100644
index cd8bf2d4..00000000
--- a/test/apps/particle-cluster.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-export function particleCluster() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const particles = [];
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(particles)
- .process(cm.push, {
- location: cm.vec(app.prop("width") / 2, 50),
- velocity: cm.vec(cm.random(-1, 1), cm.random(-2, 0)),
- acceleration: cm.vec(0, 0.05),
- lifespan: 255,
- })
- .process(cm.eachRight, (d, i, array) => d.lifespan < 0 && array.splice(i, 1))
- .process(cm.each, (d) => (d.lifespan -= 2))
- .process(cm.each, (d) => {
- d.velocity.add(d.acceleration);
- d.location.add(d.velocity);
- })
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: 5,
- fill: cm.rgb(0),
- stroke: cm.rgb(0),
- fillOpacity: (d) => d.lifespan,
- strokeOpacity: (d) => d.lifespan,
- })
- .transform(cm.mapAttrs, {
- fillOpacity: { domain: [0, 255], range: [0, 0.6] },
- strokeOpacity: { domain: [0, 255], range: [0, 1] },
- });
- });
-
- return app.call(dispose).call(frame).call(stats).start().node();
-}
diff --git a/test/apps/particle-clusters.js b/test/apps/particle-clusters.js
deleted file mode 100644
index db88714d..00000000
--- a/test/apps/particle-clusters.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { frame } from "../utils/frame.js";
-import { stats } from "../utils/stats.js";
-
-function updateParticles(flow) {
- flow
- .process(cm.push, () => ({
- location: cm.vec(0, 0),
- velocity: cm.vec(cm.random(-1, 1), cm.random(-2, 0)),
- acceleration: cm.vec(0, 0.05),
- lifespan: 255,
- }))
- .process(cm.eachRight, (d, i, array) => d.lifespan < 0 && array.splice(i, 1))
- .process(cm.each, (d) => (d.lifespan -= 2))
- .process(cm.each, (d) => {
- d.velocity.add(d.acceleration);
- d.location.add(d.velocity);
- });
-}
-
-function drawParticles(flow) {
- flow
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- r: 5,
- fill: cm.rgb(0),
- stroke: cm.rgb(0),
- fillOpacity: (d) => d.lifespan,
- strokeOpacity: (d) => d.lifespan,
- })
- .transform(cm.mapAttrs, {
- fillOpacity: { domain: [0, 255], range: [0, 0.6] },
- strokeOpacity: { domain: [0, 255], range: [0, 1] },
- });
-}
-
-export function particleClusters() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const PS = [];
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- const groups = app.data(PS).append(cm.group, {
- x: (d) => d.origin.x,
- y: (d) => d.origin.y,
- });
-
- groups
- .data((d) => d.particles)
- .call(updateParticles)
- .call(drawParticles);
- })
- .on("mouseDown", () => {
- PS.push({
- origin: cm.vec(app.prop("mouseX"), app.prop("mouseY")),
- particles: [],
- });
- });
-
- return app.call(dispose).call(frame).call(stats).start().node();
-}
diff --git a/test/apps/random-curve.js b/test/apps/random-curve.js
deleted file mode 100644
index 006508df..00000000
--- a/test/apps/random-curve.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-function bellCurve(mu = 0, sigma = 1) {
- const e = 2.71828183;
- const sq2pi = Math.sqrt(cm.TWO_PI);
- const sdsq = sigma * sigma;
- return (x) => {
- const xmsq = -1 * (x - mu) * (x - mu);
- return (1 / (sigma * sq2pi)) * Math.pow(e, xmsq / sdsq);
- };
-}
-
-export function randomCurve() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const X = cm.range(app.prop("width"), -3, 3);
- let sigma = 0.5;
- let step = 0.01;
-
- app.on("update", () => {
- const curve = bellCurve(0, sigma);
-
- app.append(cm.clear, { fill: "#fff" });
-
- app
- .data(X)
- .append(cm.line, {
- x: (_, i) => i,
- y: (d) => curve(d),
- stroke: "black",
- strokeWidth: 2,
- })
- .transform(cm.mapAttrs, {
- y: {
- domain: [0, 1],
- range: [app.prop("height") - 2, 2],
- },
- });
-
- sigma += step;
- if (sigma > 2 || sigma < 0.3) step *= -1;
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/random-distribution.js b/test/apps/random-distribution.js
deleted file mode 100644
index 5b6beb03..00000000
--- a/test/apps/random-distribution.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function randomDistribution() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const N = cm.range(15).map(() => 0);
- const step = app.prop("width") / N.length;
-
- app.on("update", () => {
- const index = cm.random(N.length) | 0;
- N[index] += 1;
-
- app.data(N).append(cm.rect, {
- x: (_, i) => i * step,
- y: (d) => app.prop("height") - d,
- width: step - 2,
- height: (d) => d,
- fill: cm.rgb(127),
- stroke: "black",
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/random-dragon.js b/test/apps/random-dragon.js
deleted file mode 100644
index 75827b2c..00000000
--- a/test/apps/random-dragon.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function randomDragon() {
- const width = 640,
- height = 640,
- x = cm.randomNoise(0, width),
- y = cm.randomNoise(0, height),
- size = cm.randomNoise(0, 100),
- rotate = cm.randomNoise(0, cm.TWO_PI),
- hue = cm.randomNoise(0, 360);
-
- function setup(app) {
- app.append(cm.clear, { fill: "orange" });
- }
-
- function update(app) {
- const t = app.prop("frameCount") / 100;
- app
- .append(cm.group, {
- rotate: rotate(t),
- x: x(t),
- y: y(t),
- })
- .append(cm.rect, {
- x: 0,
- y: 0,
- width: size(t),
- height: size(t),
- fill: cm.hsl(hue(t), 100, 50),
- stroke: "black",
- fillOpacity: 0.5,
- });
- }
-
- return cm
- .app({ width: 640, height: 640 })
- .on("beforeAll", setup)
- .on("update", update)
- .call(dispose)
- .call(stats)
- .start()
- .node();
-}
diff --git a/test/apps/random-normal.js b/test/apps/random-normal.js
deleted file mode 100644
index 2fe389f6..00000000
--- a/test/apps/random-normal.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function randomNormal() {
- const app = cm.app({
- width: 640,
- height: 240,
- });
-
- const random = cm.randomNormal(app.prop("width") / 2, 60);
-
- function update() {
- app.append(cm.circle, {
- x: random(),
- y: app.prop("height") / 2,
- r: 16,
- fill: "#000",
- fillOpacity: 0.1,
- });
- }
-
- app.on("update", update);
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/random-walk.js b/test/apps/random-walk.js
deleted file mode 100644
index 99f77b09..00000000
--- a/test/apps/random-walk.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function randomWalk() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- let x = app.prop("width") / 2;
- let y = app.prop("height") / 2;
- app.on("update", () => {
- app.append(cm.circle, { x, y, r: 1, fill: "#000" });
- const direction = Math.floor(cm.random(4));
- if (direction === 0) y -= 1;
- else if (direction === 1) x += 1;
- else if (direction === 2) y += 1;
- else x -= 1;
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/terminal-bar.js b/test/apps/terminal-bar.js
deleted file mode 100644
index 5cd32d7a..00000000
--- a/test/apps/terminal-bar.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-function barX(
- flow,
- {
- x: X,
- y: Y,
- width,
- height,
- step,
- ticks,
- title,
- colors = ["#CC4A6A", "#6EBD41", "#D6B152", "#56ADD3", "#AC5ADA", "#7DDEBB"],
- },
-) {
- const [data] = flow.data();
- const scaleX = cm.scaleLinear([0, 2.0], [0, width]);
-
- // Title.
- flow.datum(0).append(cm.text, {
- text: title,
- x: width / 2,
- y: -1,
- textAlign: "center",
- textBaseline: "bottom",
- });
-
- // Axis.
- const axis = flow
- .datum(0)
- .append(cm.group, {
- x: 0,
- y: height,
- })
- .data(ticks);
-
- axis.append(cm.link, {
- x: (d) => scaleX(d),
- x1: (d) => scaleX(d),
- y: 0,
- y1: -height - 1,
- stroke: cm.cfb(":"),
- });
-
- axis.datum(0).append(cm.link, {
- x: 0,
- x1: width,
- y: 0,
- y1: 0,
- stroke: cm.cfb("-"),
- });
-
- axis.append(cm.text, {
- text: (d) => (d ? d.toFixed(1) + "" : d),
- textAlign: "center",
- y: 1,
- x: (d) => scaleX(d),
- });
-
- // Bars.
- flow.data(data).append(cm.rect, {
- x: scaleX(0),
- y: Y.map((y) => y * step),
- width: X.map(scaleX),
- height: step - 1,
- fill: (_, i) => cm.cfb("#", colors[i % colors.length]),
- });
-}
-
-export async function terminalBar() {
- const data = [1.9, 1.9, 1.6, 1.0, 0.4, 0.1];
-
- // Dimensions.
- const step = 3;
- const marginX = 3;
- const marginY = 3;
- const plotWidth = 60;
- const plotHeight = step * data.length;
- const height = plotHeight + marginY * 2;
- const width = plotWidth + marginX * 2;
-
- const app = cm.app({
- renderer: await cm.terminal(),
- cols: width,
- rows: height,
- });
-
- // Bar Chart.
- app
- .append(cm.group, { x: marginX, y: marginY })
- .data(data)
- .append(barX, {
- x: (d) => d,
- y: (_, i) => i,
- step: cm.constant(step),
- width: cm.constant(plotWidth),
- height: cm.constant(plotHeight),
- title: cm.constant("Bar Chart, Terminal"),
- ticks: cm.constant([0, 0.5, 1.0, 1.5, 2.0]),
- });
-
- // Annotation.
- app.append(cm.text, {
- x: marginX + plotWidth,
- y: marginY + plotHeight,
- text: cm.figlet("2023"),
- textBaseline: "bottom",
- textAlign: "end",
- });
-
- return app.call(dispose).render().node();
-}
diff --git a/test/apps/terminal-character-matrix.js b/test/apps/terminal-character-matrix.js
deleted file mode 100644
index c41767f9..00000000
--- a/test/apps/terminal-character-matrix.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-function updateString(d, i, array, flow) {
- const app = flow.app();
- const height = app.prop("height");
- const { chars, lifespan, length } = array[i];
- const curLength = chars.length;
-
- // Create a new string if the current one is dead.
- // Fade out the string if lifespan is less than the current length.
- // Fade in the string if lifespan is greater than the current length.
- if (lifespan < 0) array[i] = createString(height);
- else if (lifespan <= curLength) chars[curLength - lifespan] = "";
- else if (lifespan > curLength) {
- for (let i = length - 1; i < curLength; i++) chars[i] = cm.randomChar();
- chars.push(cm.randomChar());
- }
-
- d.lifespan--;
-}
-
-function createString(height) {
- const lifespan = cm.randomInt(height);
- const length = cm.randomInt(lifespan);
- const chars = cm.range(length).map(cm.randomChar);
- const y = cm.randomInt(0, 15);
- return { lifespan, length, chars, y };
-}
-
-// @see https://asciinema.org/a/19942
-export async function terminalCharacterMatrix() {
- let strings = null;
-
- function update(app) {
- const width = app.prop("width");
- const height = app.prop("height");
- if (!strings) strings = cm.range(width).map(() => createString(height));
-
- app.append(cm.clear, { fill: "black" });
-
- app
- .data(strings)
- .process(cm.eachRight, updateString)
- .append(cm.group, {
- x: (_, i) => i,
- y: (d) => d.y,
- })
- .data((d) => d.chars)
- .append(cm.point, {
- x: 0,
- y: (_, i) => i,
- stroke0: (d) => d,
- stroke1: "#6EBD41",
- });
- }
-
- const app = cm.app({
- renderer: await cm.terminal(),
- frameRate: 10,
- fontWeight: "bold",
- });
-
- return app.on("update", update).call(dispose).call(stats).start().node();
-}
diff --git a/test/apps/terminal-fire.js b/test/apps/terminal-fire.js
deleted file mode 100644
index 11b41f32..00000000
--- a/test/apps/terminal-fire.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import * as cm from "../../src/index.js";
-import { interpolateCool } from "d3-scale-chromatic";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-function updateFire(fire) {
- const h = fire.length;
- const w = fire[0].length;
- const max = h;
- const noise = cm.randomNoise(0, max);
-
- for (let y = 0; y < h - 1; y++) {
- for (let x = 0; x < w; x++) {
- const decay = cm.randomInt(0, 3);
- const spread = cm.randomInt(-1, 1);
- const index = Math.min(Math.max(0, x - spread), w - 1);
- const target = fire[y + 1][index];
- fire[y][x] = Math.max(0, target - decay);
- }
- }
-
- for (let x = 0; x < w; x++) {
- fire[h - 1][x] = noise(x / 10) | 0;
- }
-}
-
-function drawFire(app, fire) {
- const max = fire.length;
-
- app.append(cm.clear, { fill: "black" });
-
- app
- .data(fire)
- .append(cm.group, { x: 0, y: (_, i) => i })
- .data((d) => d)
- .append(cm.point, {
- y: 0,
- x: (_, i) => i,
- stroke0: (d) => (d === 0 ? " " : cm.randomChar()),
- stroke2: (d) => (d === 0 ? null : d),
- })
- .transform(cm.mapAttrs, {
- stroke2: {
- domain: [0, max],
- range: [0, 1],
- interpolate: interpolateCool,
- },
- });
-}
-
-function createFire(width, height) {
- return cm.range(height).map(() => cm.range(width).map(() => 0));
-}
-
-// @see https://asciinema.org/a/28404
-export async function terminalFire() {
- let fire = null;
-
- function update(app) {
- const width = app.prop("width");
- const height = app.prop("height");
- if (!fire) fire = createFire(width, height);
- updateFire(fire);
- drawFire(app, fire);
- }
-
- const app = cm.app({
- renderer: await cm.terminal(),
- frameRate: 15,
- });
-
- return app.on("update", update).call(dispose).call(stats).start().node();
-}
diff --git a/test/apps/terminal-matrix.js b/test/apps/terminal-matrix.js
deleted file mode 100644
index b1488b1b..00000000
--- a/test/apps/terminal-matrix.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-export async function terminalMatrix() {
- const app = cm.app({
- cols: 3,
- rows: 3,
- mode: "double",
- renderer: await cm.terminal(),
- });
-
- app
- .data([
- [" +", "-", "+ "],
- [" |", cm.wch("π"), "| "],
- [" +", "-", "+ "],
- ])
- .append(cm.group, { y: (_, i) => i })
- .data((d) => d)
- .append(cm.point, {
- y: 0,
- x: (_, i) => i,
- stroke0: (d) => d,
- });
-
- return app.call(dispose).render().node();
-}
diff --git a/test/apps/terminal-point.js b/test/apps/terminal-point.js
deleted file mode 100644
index 48469776..00000000
--- a/test/apps/terminal-point.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-export async function terminalPoint() {
- const app = cm.app({
- renderer: await cm.terminal(),
- });
-
- let x = 0;
-
- app.on("update", () => {
- const i = x % app.prop("width");
- const j = (x / app.prop("width")) | 0;
- app.append(cm.clear, { fill: "#000" });
- app.append(cm.point, { x: i, y: j, stroke0: "@" });
- x += 1;
- });
-
- return app.call(dispose).start().node();
-}
diff --git a/test/apps/terminal-text.js b/test/apps/terminal-text.js
deleted file mode 100644
index 73a36da9..00000000
--- a/test/apps/terminal-text.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-export async function terminalText() {
- const app = cm.app({
- width: 1200,
- renderer: await cm.terminal(),
- });
-
- app.append(cm.text, {
- text: cm.figlet("hello world"),
- x: app.prop("width") / 2,
- y: app.prop("height") / 2,
- textAlign: "center",
- textBaseline: "middle",
- fontFamily: cm.fontGhost(),
- });
-
- return app.call(dispose).start().node();
-}
diff --git a/test/apps/terminal-white-noise.js b/test/apps/terminal-white-noise.js
deleted file mode 100644
index 1d1ab43c..00000000
--- a/test/apps/terminal-white-noise.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-// @see https://asciinema.org/a/19919
-export async function terminalWhiteNoise() {
- const app = cm.app({
- renderer: await cm.terminal(),
- frameRate: 20,
- });
-
- const w = app.prop("width");
- const h = app.prop("height");
-
- const characters = cm.cross(cm.range(w), cm.range(h)).map((d) => ({ x: d[0], y: d[1] }));
-
- const textOptions = {
- text: cm.figlet("Charming"),
- x: w / 2,
- y: h / 2,
- textAlign: "center",
- textBaseline: "middle",
- fill: cm.gradientRainBowX(),
- };
- const bbox = app.textBBox(textOptions);
- const { x: tx, y: ty, width: tw, height: th } = bbox;
- const outside = ({ x, y }) => x < tx || x > tx + tw || y < ty || y > ty + th;
-
- function update() {
- app.append(cm.clear, { fill: "black" });
-
- app.append(cm.text, textOptions);
-
- app
- .data(characters)
- .process(cm.each, (d) => {
- if (d.lifespan) return d.lifespan--;
- d.ch = cm.randomChar();
- d.lifespan = cm.randomInt(3, 10);
- })
- .process(cm.filter, (d) => {
- if (outside(d)) return true;
- const p = cm.random(10);
- if (p < 0.5) return true;
- return false;
- })
- .append(cm.point, {
- x: (d) => d.x,
- y: (d) => d.y,
- stroke0: (d) => d.ch,
- });
- }
-
- return app.on("update", update).call(dispose).call(stats).start().node();
-}
diff --git a/test/apps/toxic-grid.js b/test/apps/toxic-grid.js
deleted file mode 100644
index c3c10e74..00000000
--- a/test/apps/toxic-grid.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-import { physics2d, geom } from "toxiclibsjs";
-const { Vec2D } = geom;
-const { VerletPhysics2D, VerletParticle2D, VerletSpring2D } = physics2d;
-const { GravityBehavior } = physics2d.behaviors;
-
-export function toxicGrid() {
- const app = cm.app({
- width: 640,
- height: 240,
- });
-
- const world = new VerletPhysics2D();
-
- const gravity = new GravityBehavior(new Vec2D(0, 1));
- world.addBehavior(gravity);
-
- const cols = 65;
- const rows = 20;
- const w = app.prop("width") / (cols - 1);
- const x = (i) => i % cols;
- const y = (i) => (i / cols) | 0;
-
- const particles = cm.range(cols * rows).map((i) => {
- const m = x(i);
- const n = y(i);
- const particle = new VerletParticle2D(m * w, 0);
- if (m % 4 === 0 && n === 0) particle.lock();
- world.addParticle(particle);
- return particle;
- });
-
- const springs = particles.flatMap((d, i, array) => {
- const S = [];
- const m = x(i);
- const n = y(i);
- if (m !== cols - 1) {
- const d1 = array[i + 1];
- const s1 = new VerletSpring2D(d, d1, w, 0.25);
- S.push(s1);
- world.addSpring(s1);
- }
- if (n !== rows - 1) {
- const d2 = array[i + cols];
- const s2 = new VerletSpring2D(d, d2, w, 0.25);
- S.push(s2);
- world.addSpring(s2);
- }
- return S;
- });
-
- function update() {
- world.update();
- app.append(cm.clear, { fill: "#fff" });
- app.data(springs).append(cm.link, {
- x: (d) => d.a.x,
- y: (d) => d.a.y,
- x1: (d) => d.b.x,
- y1: (d) => d.b.y,
- stroke: cm.rgb(0),
- strokeWidth: 2,
- });
- }
-
- return app.on("update", update).call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/vector-circle-bouncing.js b/test/apps/vector-circle-bouncing.js
deleted file mode 100644
index 7be6fc9d..00000000
--- a/test/apps/vector-circle-bouncing.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function vectorCircleBouncing() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const location = cm.vec(app.prop("width") / 2, app.prop("height") / 2);
- const velocity = cm.vec(1, 2.3);
-
- app
- .on("update", () => {
- location.add(velocity);
- if (!location.inX(app.prop("width"))) velocity.negX();
- if (!location.inY(app.prop("height"))) velocity.negY();
- })
- .on("update", () => {
- app.append(cm.clear, { fill: cm.rgb(255) });
- app.append(cm.circle, {
- x: location.x,
- y: location.y,
- r: 16,
- stroke: cm.rgb(0),
- fill: cm.rgb(175),
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/vector-follow-me.js b/test/apps/vector-follow-me.js
deleted file mode 100644
index dc807324..00000000
--- a/test/apps/vector-follow-me.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function vectorFollowMe() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- const movers = cm.range(20).map(() => ({
- location: cm.vec(cm.random(app.prop("width")), cm.random(app.prop("height"))),
- velocity: cm.vec(),
- acceleration: cm.vec(),
- speed: 8,
- }));
-
- app
- .on("update", () => app.append(cm.clear, { fill: cm.rgb(255) }))
- .on("update", () => {
- app
- .data(movers)
- .process(cm.each, ({ location, velocity, acceleration, speed }) => {
- const dir = cm.vec(app.prop("mouseX"), app.prop("mouseY")).sub(location).norm().mult(0.5);
-
- acceleration.set(dir);
-
- location.add(velocity.add(acceleration).clamp(speed)).clampX(app.prop("width")).clampY(app.prop("height"));
- })
- .append(cm.circle, {
- x: (d) => d.location.x,
- y: (d) => d.location.y,
- fill: "rgba(175, 175, 175, 0.5)",
- stroke: cm.rgb(0),
- r: 16,
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/vector-move-link.js b/test/apps/vector-move-link.js
deleted file mode 100644
index 70292903..00000000
--- a/test/apps/vector-move-link.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as cm from "../../src/index.js";
-import { frame } from "../utils/frame.js";
-import { dispose } from "../utils/dispose.js";
-import { stats } from "../utils/stats.js";
-
-export function vectorMoveLink() {
- const app = cm.app({
- width: 600,
- height: 200,
- });
-
- app.on("update", () => {
- const center = cm.vec(app.prop("width") / 2, app.prop("height") / 2);
- const mouse = cm.vec(app.prop("mouseX"), app.prop("mouseY"));
- const dir = cm.vecSub(mouse, center);
- app.append(cm.clear, { fill: cm.rgb(255) });
- app.append(cm.link, {
- x: center.x,
- y: center.y,
- x1: center.x + dir.x,
- y1: center.y + dir.y,
- });
- });
-
- return app.call(dispose).call(stats).call(frame).start().node();
-}
diff --git a/test/apps/vis-dot.js b/test/apps/vis-dot.js
deleted file mode 100644
index 3fbf0e51..00000000
--- a/test/apps/vis-dot.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import * as cm from "../../src/index.js";
-import { dispose } from "../utils/dispose.js";
-
-function dot(flow, { x, y, marginLeft = 40, marginTop = 30, marginRight = 0, marginBottom = 30 }) {
- const app = flow.app();
- const [data] = flow.data();
- const I = cm.range(data.length);
- const scaleX = cm.scaleLinear(cm.extent(x), [marginLeft, app.prop("width") - marginRight]);
- const scaleY = cm.scaleLinear(cm.extent(y), [app.prop("height") - marginBottom, marginTop]);
- const X = x.map(scaleX);
- const Y = y.map(scaleY);
- flow.data(I).append(cm.circle, {
- x: (i) => X[i],
- y: (i) => Y[i],
- fill: "black",
- r: 4,
- });
-}
-
-export function visDot() {
- const data = cm.range(100).map(() => [cm.random(), cm.random()]);
-
- const app = cm.app({
- width: 640,
- height: 480,
- });
-
- app.data(data).append(dot, {
- x: (d) => d[0],
- y: (d) => d[1],
- });
-
- return app.call(dispose).render().node();
-}
diff --git a/test/dom.spec.js b/test/dom.spec.js
new file mode 100644
index 00000000..64e3de3b
--- /dev/null
+++ b/test/dom.spec.js
@@ -0,0 +1,18 @@
+import {test, expect, vi} from "vitest";
+import {svg, renderMark} from "../src/index.js";
+
+test("svg(tag, options) should set events", () => {
+ const click = vi.fn();
+ const root = renderMark(svg("svg", {onclick: click}));
+ root.dispatchEvent(new Event("click"));
+ expect(click).toHaveBeenCalled();
+});
+
+test("svg(tag, options) should pass datum to event handler", () => {
+ const click = vi.fn();
+ const root = renderMark(svg("svg", [1, 2, 3], {onclick: click}));
+ const el = root.children[0];
+ const event = new Event("click");
+ el.dispatchEvent(event);
+ expect(click).toHaveBeenCalledWith(event, 1, 0, [1, 2, 3]);
+});
diff --git a/test/index.html b/test/index.html
deleted file mode 100644
index 186537f5..00000000
--- a/test/index.html
+++ /dev/null
@@ -1,82 +0,0 @@
-
diff --git a/test/output/_canvasArrow.png b/test/output/_canvasArrow.png
deleted file mode 100644
index 79c73710..00000000
Binary files a/test/output/_canvasArrow.png and /dev/null differ
diff --git a/test/output/_canvasArrowColor.png b/test/output/_canvasArrowColor.png
deleted file mode 100644
index ff3ccd0d..00000000
Binary files a/test/output/_canvasArrowColor.png and /dev/null differ
diff --git a/test/output/_canvasCircle.png b/test/output/_canvasCircle.png
deleted file mode 100644
index 721753ce..00000000
Binary files a/test/output/_canvasCircle.png and /dev/null differ
diff --git a/test/output/_canvasNested.png b/test/output/_canvasNested.png
deleted file mode 100644
index 7ab7ecfa..00000000
Binary files a/test/output/_canvasNested.png and /dev/null differ
diff --git a/test/output/_canvasPoint.png b/test/output/_canvasPoint.png
deleted file mode 100644
index b5eddd6f..00000000
Binary files a/test/output/_canvasPoint.png and /dev/null differ
diff --git a/test/output/_canvasTriangle.png b/test/output/_canvasTriangle.png
deleted file mode 100644
index 0d432c88..00000000
Binary files a/test/output/_canvasTriangle.png and /dev/null differ
diff --git a/test/output/_webglRect.png b/test/output/_webglRect.png
deleted file mode 100644
index 2043f5dd..00000000
Binary files a/test/output/_webglRect.png and /dev/null differ
diff --git a/test/output/_webglTriangle.png b/test/output/_webglTriangle.png
deleted file mode 100644
index 6996c5d0..00000000
Binary files a/test/output/_webglTriangle.png and /dev/null differ
diff --git a/test/output/cloneDataDrivenChildren.html b/test/output/cloneDataDrivenChildren.html
new file mode 100644
index 00000000..23d463cf
--- /dev/null
+++ b/test/output/cloneDataDrivenChildren.html
@@ -0,0 +1,26 @@
+
\ No newline at end of file
diff --git a/test/output/fragmentRoot.html b/test/output/fragmentRoot.html
new file mode 100644
index 00000000..0826cb2b
--- /dev/null
+++ b/test/output/fragmentRoot.html
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/output/htmlAttributes.html b/test/output/htmlAttributes.html
new file mode 100644
index 00000000..570a7ebf
--- /dev/null
+++ b/test/output/htmlAttributes.html
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/test/output/mathXL.html b/test/output/mathXL.html
new file mode 100644
index 00000000..ef6db37a
--- /dev/null
+++ b/test/output/mathXL.html
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/test/output/setAttributes.html b/test/output/setAttributes.html
new file mode 100644
index 00000000..b6df6e43
--- /dev/null
+++ b/test/output/setAttributes.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/test/output/setChildren.html b/test/output/setChildren.html
new file mode 100644
index 00000000..06d68494
--- /dev/null
+++ b/test/output/setChildren.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/test/output/setDataDrivenAttributes.html b/test/output/setDataDrivenAttributes.html
new file mode 100644
index 00000000..3cd4c336
--- /dev/null
+++ b/test/output/setDataDrivenAttributes.html
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/test/output/setDataDrivenChildren.html b/test/output/setDataDrivenChildren.html
new file mode 100644
index 00000000..4daaa927
--- /dev/null
+++ b/test/output/setDataDrivenChildren.html
@@ -0,0 +1,26 @@
+
\ No newline at end of file
diff --git a/test/output/setDataDrivenChildrenWithoutOptions.html b/test/output/setDataDrivenChildrenWithoutOptions.html
new file mode 100644
index 00000000..30471aff
--- /dev/null
+++ b/test/output/setDataDrivenChildrenWithoutOptions.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/test/output/setDataDrivenNonMarkChildren.html b/test/output/setDataDrivenNonMarkChildren.html
new file mode 100644
index 00000000..ceaf0172
--- /dev/null
+++ b/test/output/setDataDrivenNonMarkChildren.html
@@ -0,0 +1,11 @@
+
+
+ 0-1
+
+
+ 1-2
+
+
+ 2-3
+
+
\ No newline at end of file
diff --git a/test/output/setDirectAttributes.html b/test/output/setDirectAttributes.html
new file mode 100644
index 00000000..815201d7
--- /dev/null
+++ b/test/output/setDirectAttributes.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/test/output/setFalsyChildren.html b/test/output/setFalsyChildren.html
new file mode 100644
index 00000000..06d68494
--- /dev/null
+++ b/test/output/setFalsyChildren.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/test/output/setFunctionAttributes.html b/test/output/setFunctionAttributes.html
new file mode 100644
index 00000000..b6df6e43
--- /dev/null
+++ b/test/output/setFunctionAttributes.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/test/output/setFunctionChildren.html b/test/output/setFunctionChildren.html
new file mode 100644
index 00000000..06d68494
--- /dev/null
+++ b/test/output/setFunctionChildren.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/test/output/setInnerHTML.html b/test/output/setInnerHTML.html
new file mode 100644
index 00000000..b5b26d6c
--- /dev/null
+++ b/test/output/setInnerHTML.html
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/test/output/setKebabCaseAttributes.html b/test/output/setKebabCaseAttributes.html
new file mode 100644
index 00000000..2a0aa63d
--- /dev/null
+++ b/test/output/setKebabCaseAttributes.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/test/output/setListChildren.html b/test/output/setListChildren.html
new file mode 100644
index 00000000..e09e4835
--- /dev/null
+++ b/test/output/setListChildren.html
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/test/output/setNestedCallbackDataDrivenChildren.html b/test/output/setNestedCallbackDataDrivenChildren.html
new file mode 100644
index 00000000..20e14ae9
--- /dev/null
+++ b/test/output/setNestedCallbackDataDrivenChildren.html
@@ -0,0 +1,53 @@
+
\ No newline at end of file
diff --git a/test/output/setNestedChildren.html b/test/output/setNestedChildren.html
new file mode 100644
index 00000000..db66e52b
--- /dev/null
+++ b/test/output/setNestedChildren.html
@@ -0,0 +1,32 @@
+
\ No newline at end of file
diff --git a/test/output/setNestedDataDrivenChildren.html b/test/output/setNestedDataDrivenChildren.html
new file mode 100644
index 00000000..ea2948e4
--- /dev/null
+++ b/test/output/setNestedDataDrivenChildren.html
@@ -0,0 +1,53 @@
+
\ No newline at end of file
diff --git a/test/output/setNestedListChildren.html b/test/output/setNestedListChildren.html
new file mode 100644
index 00000000..e09e4835
--- /dev/null
+++ b/test/output/setNestedListChildren.html
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/test/output/setNonMarkChildren.html b/test/output/setNonMarkChildren.html
new file mode 100644
index 00000000..8bc88c4d
--- /dev/null
+++ b/test/output/setNonMarkChildren.html
@@ -0,0 +1,7 @@
+
+ hello
+
+ world
+
+ [object Object]
+
\ No newline at end of file
diff --git a/test/output/setSnakeCaseAttributes.html b/test/output/setSnakeCaseAttributes.html
new file mode 100644
index 00000000..2a0aa63d
--- /dev/null
+++ b/test/output/setSnakeCaseAttributes.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/test/output/setStyle.html b/test/output/setStyle.html
new file mode 100644
index 00000000..55d8ab85
--- /dev/null
+++ b/test/output/setStyle.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/test/output/setTable.html b/test/output/setTable.html
new file mode 100644
index 00000000..248c93ed
--- /dev/null
+++ b/test/output/setTable.html
@@ -0,0 +1,58 @@
+
+
+
+ 11975
+
+
+ 5871
+
+
+ 8916
+
+
+ 2868
+
+
+
+
+ 1951
+
+
+ 10048
+
+
+ 2060
+
+
+ 6171
+
+
+
+
+ 8010
+
+
+ 16145
+
+
+ 8090
+
+
+ 8045
+
+
+
+
+ 1013
+
+
+ 990
+
+
+ 940
+
+
+ 6907
+
+
+
\ No newline at end of file
diff --git a/test/output/setTextContent.html b/test/output/setTextContent.html
new file mode 100644
index 00000000..256cd230
--- /dev/null
+++ b/test/output/setTextContent.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/test/output/setZeroChildren.html b/test/output/setZeroChildren.html
new file mode 100644
index 00000000..8cac90a9
--- /dev/null
+++ b/test/output/setZeroChildren.html
@@ -0,0 +1,3 @@
+