git.iko.soy/rowtype-yoga/purescript-abcjs
on main help sign in
Initial purescript-abcjs FFI bindings: Render, Synth, Timing, Types + parseOnly spec
commit9f36644085b770303059a334c80b4c52451517d3 authorMark <mark.eibes@gmail.com> dateSat 30 May 2026, 16:21 (4 months ago)
20 files changed+2324
.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc+1 .gitignore+12 .spec-results+44 CLAUDE.md+111 README.md+15 bun.lock+30 index.ts+1 package.json+15 spago.lock+1707 spago.yaml+18 src/Abcjs/Render.js+6 src/Abcjs/Render.purs+41 src/Abcjs/Synth.js+25 src/Abcjs/Synth.purs+106 src/Abcjs/Timing.js+14 src/Abcjs/Timing.purs+61 src/Abcjs/Types.js+13 src/Abcjs/Types.purs+40 test/Test/Main.purs+35 tsconfig.json+29
.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdcnew file+1
@@ -0,0 +1 @@
1+../../CLAUDE.md
.gitignorenew file+12
@@ -0,0 +1,12 @@
1+
2+bower_components/
3+node_modules/
4+.pulp-cache/
5+output/
6+output-es/
7+generated-docs/
8+.psc-package/
9+.psc*
10+.purs*
11+.psa*
12+.spago
.spec-resultsnew file+44
@@ -0,0 +1,44 @@
1+[
2+ [
3+ "Abcjs.Render.parseOnly always yields exactly one tune object",
4+ {
5+ "timestamp": "1780150858418.0",
6+ "success": true
7+ }
8+ ],
9+ [
10+ "Abcjs.Render.parseOnly parses a game-generated phrase to at least one line of music",
11+ {
12+ "timestamp": "1780150858418.0",
13+ "success": true
14+ }
15+ ],
16+ [
17+ "Abcjs.Render.parseOnly parses a valid tune into one tune object",
18+ {
19+ "timestamp": "1780150801626.0",
20+ "success": true
21+ }
22+ ],
23+ [
24+ "Abcjs.Render.parseOnly parses empty input to a tune with no music",
25+ {
26+ "timestamp": "1780150858418.0",
27+ "success": true
28+ }
29+ ],
30+ [
31+ "Abcjs.Render.parseOnly returns no tunes for empty input",
32+ {
33+ "timestamp": "1780150801626.0",
34+ "success": false
35+ }
36+ ],
37+ [
38+ "Abcjs.Render.parseOnly yields a tune for a phrase a game would generate",
39+ {
40+ "timestamp": "1780150801626.0",
41+ "success": true
42+ }
43+ ]
44+]
CLAUDE.mdnew file+111
@@ -0,0 +1,111 @@
1+---
2+description: Use Bun instead of Node.js, npm, pnpm, or vite.
3+globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
4+alwaysApply: false
5+---
6+
7+Default to using Bun instead of Node.js.
8+
9+- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
10+- Use `bun test` instead of `jest` or `vitest`
11+- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
12+- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
13+- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
14+- Bun automatically loads .env, so don't use dotenv.
15+
16+## APIs
17+
18+- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
19+- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
20+- `Bun.redis` for Redis. Don't use `ioredis`.
21+- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
22+- `WebSocket` is built-in. Don't use `ws`.
23+- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
24+- Bun.$`ls` instead of execa.
25+
26+## Testing
27+
28+Use `bun test` to run tests.
29+
30+```ts#index.test.ts
31+import { test, expect } from "bun:test";
32+
33+test("hello world", () => {
34+ expect(1).toBe(1);
35+});
36+```
37+
38+## Frontend
39+
40+Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
41+
42+Server:
43+
44+```ts#index.ts
45+import index from "./index.html"
46+
47+Bun.serve({
48+ routes: {
49+ "/": index,
50+ "/api/users/:id": {
51+ GET: (req) => {
52+ return new Response(JSON.stringify({ id: req.params.id }));
53+ },
54+ },
55+ },
56+ // optional websocket support
57+ websocket: {
58+ open: (ws) => {
59+ ws.send("Hello, world!");
60+ },
61+ message: (ws, message) => {
62+ ws.send(message);
63+ },
64+ close: (ws) => {
65+ // handle close
66+ }
67+ },
68+ development: {
69+ hmr: true,
70+ console: true,
71+ }
72+})
73+```
74+
75+HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
76+
77+```html#index.html
78+<html>
79+ <body>
80+ <h1>Hello, world!</h1>
81+ <script type="module" src="./frontend.tsx"></script>
82+ </body>
83+</html>
84+```
85+
86+With the following `frontend.tsx`:
87+
88+```tsx#frontend.tsx
89+import React from "react";
90+
91+// import .css files directly and it works
92+import './index.css';
93+
94+import { createRoot } from "react-dom/client";
95+
96+const root = createRoot(document.body);
97+
98+export default function Frontend() {
99+ return <h1>Hello, world!</h1>;
100+}
101+
102+root.render(<Frontend />);
103+```
104+
105+Then, run index.ts
106+
107+```sh
108+bun --hot ./index.ts
109+```
110+
111+For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
README.mdnew file+15
@@ -0,0 +1,15 @@
1+# purescript-abcjs
2+
3+To install dependencies:
4+
5+```bash
6+bun install
7+```
8+
9+To run:
10+
11+```bash
12+bun run index.ts
13+```
14+
15+This project was created using `bun init` in bun v1.2.21. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
bun.locknew file+30
@@ -0,0 +1,30 @@
1+{
2+ "lockfileVersion": 1,
3+ "workspaces": {
4+ "": {
5+ "name": "purescript-abcjs",
6+ "dependencies": {
7+ "abcjs": "^6.6.3",
8+ },
9+ "devDependencies": {
10+ "@types/bun": "latest",
11+ },
12+ "peerDependencies": {
13+ "typescript": "^5",
14+ },
15+ },
16+ },
17+ "packages": {
18+ "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
19+
20+ "@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
21+
22+ "abcjs": ["abcjs@6.6.3", "", {}, "sha512-BerGJCY8+pvJV1+VxZn1Y/VNcuSAk8BysCbBICY0W8fgE5g4W6sA/zB5pKxcgqzY5/gObh8ugl++4ZoaTqUCkw=="],
23+
24+ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
25+
26+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
27+
28+ "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
29+ }
30+}
index.tsnew file+1
@@ -0,0 +1 @@
1+console.log("Hello via Bun!");
package.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "name": "purescript-abcjs",
3+ "module": "index.ts",
4+ "type": "module",
5+ "private": true,
6+ "devDependencies": {
7+ "@types/bun": "latest"
8+ },
9+ "peerDependencies": {
10+ "typescript": "^5"
11+ },
12+ "dependencies": {
13+ "abcjs": "^6.6.3"
14+ }
15+}
spago.locknew file+1707
@@ -0,0 +1,1707 @@
1+{
2+ "workspace": {
3+ "packages": {
4+ "abcjs": {
5+ "path": "./",
6+ "core": {
7+ "dependencies": [
8+ "aff",
9+ "aff-promise",
10+ "arrays",
11+ "effect",
12+ "nullable",
13+ "prelude"
14+ ]
15+ },
16+ "test": {
17+ "dependencies": [
18+ "spec",
19+ "spec-node"
20+ ]
21+ }
22+ }
23+ },
24+ "package_set": {
25+ "address": {
26+ "registry": "77.4.0"
27+ },
28+ "compiler": ">=0.15.15 <0.16.0",
29+ "content": {
30+ "abc-parser": "2.1.0",
31+ "ace": "9.1.0",
32+ "address-rfc2821": "0.1.1",
33+ "aff": "8.0.0",
34+ "aff-bus": "6.0.0",
35+ "aff-coroutines": "9.0.0",
36+ "aff-promise": "4.0.0",
37+ "aff-retry": "2.0.0",
38+ "affjax": "13.0.0",
39+ "affjax-node": "1.0.0",
40+ "affjax-web": "1.0.0",
41+ "ansi": "7.0.0",
42+ "apexcharts": "0.5.0",
43+ "applicative-phases": "1.0.0",
44+ "argonaut": "9.0.0",
45+ "argonaut-aeson-generic": "0.4.1",
46+ "argonaut-codecs": "9.1.0",
47+ "argonaut-core": "7.0.0",
48+ "argonaut-generic": "8.0.0",
49+ "argonaut-traversals": "10.0.0",
50+ "argparse-basic": "2.0.0",
51+ "array-builder": "0.1.2",
52+ "array-search": "0.6.0",
53+ "arraybuffer": "13.2.0",
54+ "arraybuffer-builder": "3.1.0",
55+ "arraybuffer-types": "3.0.2",
56+ "arrays": "7.3.0",
57+ "arrays-extra": "0.6.1",
58+ "arrays-zipper": "2.0.1",
59+ "ask": "1.0.0",
60+ "assert": "6.0.0",
61+ "assert-multiple": "0.4.0",
62+ "avar": "5.0.1",
63+ "axon": "0.0.3",
64+ "b64": "0.0.9",
65+ "barbies": "1.0.1",
66+ "barlow-lens": "0.9.0",
67+ "benchlib": "0.0.4",
68+ "bifunctors": "6.1.0",
69+ "bigints": "7.0.1",
70+ "blessed": "1.0.0",
71+ "bolson": "0.3.9",
72+ "bookhound": "0.1.7",
73+ "bower-json": "3.0.0",
74+ "call-by-name": "4.0.1",
75+ "canvas": "6.0.0",
76+ "canvas-action": "9.0.0",
77+ "cartesian": "1.0.6",
78+ "catenable-lists": "7.0.0",
79+ "cbor-stream": "1.3.0",
80+ "chameleon": "1.0.0",
81+ "chameleon-halogen": "1.0.3",
82+ "chameleon-react-basic": "1.1.0",
83+ "chameleon-styled": "2.5.0",
84+ "chameleon-transformers": "1.0.0",
85+ "channel": "1.0.0",
86+ "chartjs": "0.1.1",
87+ "chartjs-halogen": "0.1.0",
88+ "checked-exceptions": "3.1.1",
89+ "choku": "1.0.2",
90+ "classless": "0.1.1",
91+ "classless-arbitrary": "0.1.1",
92+ "classless-decode-json": "0.1.1",
93+ "classless-encode-json": "0.1.3",
94+ "classnames": "2.0.0",
95+ "codec": "6.1.0",
96+ "codec-argonaut": "10.0.0",
97+ "codec-json": "2.0.0",
98+ "colors": "7.0.1",
99+ "compile-fail": "0.4.0",
100+ "concur-core": "0.5.0",
101+ "concur-react": "0.5.0",
102+ "concurrent-queues": "3.0.0",
103+ "console": "6.1.0",
104+ "const": "6.0.0",
105+ "contravariant": "6.0.0",
106+ "control": "6.0.0",
107+ "convertable-options": "1.0.0",
108+ "coroutines": "7.0.0",
109+ "css": "6.0.0",
110+ "css-class-name-extractor": "0.0.4",
111+ "css-frameworks": "1.1.0",
112+ "csv-stream": "2.3.0",
113+ "currency": "0.1.0",
114+ "data-mvc": "0.0.2",
115+ "datetime": "6.1.0",
116+ "datetime-parsing": "0.2.0",
117+ "debounce": "0.1.0",
118+ "debug": "6.0.2",
119+ "decimals": "7.1.0",
120+ "default-values": "1.0.1",
121+ "deku": "0.9.23",
122+ "deno": "0.0.5",
123+ "dissect": "1.0.0",
124+ "distributive": "6.0.0",
125+ "dodo-printer": "2.2.3",
126+ "dom-filereader": "7.0.0",
127+ "dom-indexed": "13.0.0",
128+ "dom-simple": "0.4.0",
129+ "dotenv": "4.0.3",
130+ "droplet": "0.6.0",
131+ "dts": "1.0.0",
132+ "dual-numbers": "1.0.3",
133+ "dynamic-buffer": "3.0.1",
134+ "echarts-simple": "0.0.1",
135+ "effect": "4.0.0",
136+ "either": "6.1.0",
137+ "elmish": "0.13.0",
138+ "elmish-enzyme": "0.1.1",
139+ "elmish-hooks": "0.11.0",
140+ "elmish-html": "0.10.0",
141+ "elmish-testing-library": "0.3.2",
142+ "elmish-time-machine": "0.4.2",
143+ "email-validate": "7.0.0",
144+ "encoding": "0.0.10",
145+ "enums": "6.0.1",
146+ "env-names": "0.4.0",
147+ "environment": "2.0.0",
148+ "error": "2.0.0",
149+ "eta-conversion": "0.3.2",
150+ "exceptions": "6.1.0",
151+ "exists": "6.0.0",
152+ "exitcodes": "4.0.0",
153+ "expect-inferred": "3.0.0",
154+ "express": "0.9.1",
155+ "ezfetch": "1.1.1",
156+ "fahrtwind": "2.0.0",
157+ "faker-ffi": "0.1.1",
158+ "fakerjs": "0.0.1",
159+ "fallback": "0.1.0",
160+ "fast-vect": "1.2.0",
161+ "fetch": "4.1.0",
162+ "fetch-argonaut": "1.0.1",
163+ "fetch-core": "5.1.0",
164+ "fetch-yoga-json": "1.1.0",
165+ "ffi-simple": "0.5.1",
166+ "fft": "0.3.0",
167+ "fft-js": "0.1.0",
168+ "filterable": "5.0.0",
169+ "fix-functor": "0.1.0",
170+ "fixed-points": "7.0.0",
171+ "fixed-precision": "5.0.0",
172+ "flame": "1.6.0",
173+ "float32": "2.0.0",
174+ "fmt": "0.2.1",
175+ "foldable-traversable": "6.0.0",
176+ "foldable-traversable-extra": "0.0.6",
177+ "foreign": "7.0.0",
178+ "foreign-object": "4.1.0",
179+ "foreign-readwrite": "3.4.0",
180+ "forgetmenot": "0.1.0",
181+ "fork": "6.0.0",
182+ "form-urlencoded": "7.0.0",
183+ "formatters": "7.0.0",
184+ "framer-motion": "3.1.0",
185+ "free": "7.1.0",
186+ "freeap": "7.0.0",
187+ "freer-free": "0.0.1",
188+ "freet": "7.0.0",
189+ "functions": "6.0.0",
190+ "functor1": "3.0.0",
191+ "functors": "5.0.0",
192+ "fuzzy": "0.4.0",
193+ "gen": "4.0.0",
194+ "generate-values": "1.0.1",
195+ "generic-router": "0.0.1",
196+ "geojson": "0.0.5",
197+ "geometria": "2.2.0",
198+ "gesso": "1.0.1",
199+ "gojs": "0.1.1",
200+ "golden-test": "0.1.0",
201+ "golem-fetch": "0.1.0",
202+ "grain": "3.0.0",
203+ "grain-router": "3.0.0",
204+ "grain-virtualized": "3.0.0",
205+ "graphql-client": "10.1.1",
206+ "graphs": "8.1.0",
207+ "group": "4.1.1",
208+ "halogen": "7.0.0",
209+ "halogen-bootstrap5": "5.3.2",
210+ "halogen-canvas": "1.0.0",
211+ "halogen-css": "10.0.0",
212+ "halogen-declarative-canvas": "0.0.8",
213+ "halogen-echarts-simple": "0.0.4",
214+ "halogen-formless": "4.0.3",
215+ "halogen-helix": "1.1.0",
216+ "halogen-hooks": "0.6.3",
217+ "halogen-hooks-extra": "0.9.0",
218+ "halogen-infinite-scroll": "1.1.0",
219+ "halogen-store": "0.5.4",
220+ "halogen-storybook": "2.0.0",
221+ "halogen-subscriptions": "2.0.0",
222+ "halogen-svg-elems": "8.0.0",
223+ "halogen-typewriter": "1.0.4",
224+ "halogen-use-trigger-hooks": "1.0.0",
225+ "halogen-vdom": "8.0.0",
226+ "halogen-vdom-string-renderer": "0.5.0",
227+ "halogen-xterm": "2.0.0",
228+ "heckin": "2.0.1",
229+ "heterogeneous": "0.7.0",
230+ "homogeneous": "0.4.0",
231+ "http-methods": "6.0.0",
232+ "httpurple": "4.0.0",
233+ "huffman": "0.4.0",
234+ "humdrum": "0.0.1",
235+ "hylograph-canvas": "0.1.0",
236+ "hylograph-d3-kernel": "0.1.0",
237+ "hylograph-graph": "0.3.0",
238+ "hylograph-layout": "0.3.0",
239+ "hylograph-music": "0.4.0",
240+ "hylograph-optics": "0.1.0",
241+ "hylograph-selection": "0.5.1",
242+ "hylograph-simulation": "0.5.0",
243+ "hylograph-simulation-core": "0.1.0",
244+ "hylograph-simulation-halogen": "0.5.0",
245+ "hylograph-transitions": "0.1.0",
246+ "hylograph-wasm-kernel": "0.1.0",
247+ "hyrule": "2.3.8",
248+ "i18next": "0.1.0",
249+ "idb": "0.2.0",
250+ "identity": "6.0.0",
251+ "identy": "4.0.1",
252+ "indexed-db": "1.0.0",
253+ "indexed-monad": "3.0.0",
254+ "ink": "0.0.1",
255+ "int64": "3.0.0",
256+ "integers": "6.0.0",
257+ "interpolate": "5.0.2",
258+ "intersection-observer": "1.0.1",
259+ "invariant": "6.0.0",
260+ "jarilo": "1.0.1",
261+ "jelly": "0.10.0",
262+ "jelly-router": "0.3.0",
263+ "jelly-signal": "0.4.0",
264+ "jest": "1.0.0",
265+ "js-abort-controller": "1.0.0",
266+ "js-bigints": "2.2.1",
267+ "js-date": "8.0.0",
268+ "js-fetch": "0.2.1",
269+ "js-fileio": "3.0.0",
270+ "js-intl": "1.3.0",
271+ "js-iterators": "0.1.1",
272+ "js-maps": "0.1.2",
273+ "js-promise": "1.0.0",
274+ "js-promise-aff": "1.0.0",
275+ "js-temporal": "3.0.0",
276+ "js-timers": "6.1.0",
277+ "js-uri": "3.1.0",
278+ "jsdom": "1.0.0",
279+ "jsinc": "0.5.1",
280+ "json": "1.1.0",
281+ "json-codecs": "5.0.0",
282+ "justifill": "0.5.0",
283+ "jwt": "0.0.9",
284+ "labeled-data": "0.2.0",
285+ "language-cst-parser": "0.14.1",
286+ "lazy": "6.0.0",
287+ "lazy-joe": "1.0.0",
288+ "lcg": "4.0.0",
289+ "leibniz": "5.0.0",
290+ "leveldb": "1.0.1",
291+ "liminal": "1.0.1",
292+ "linalg": "6.0.0",
293+ "linear": "0.1.0",
294+ "lists": "7.0.0",
295+ "literals": "1.0.2",
296+ "logging": "3.0.0",
297+ "logging-journald": "0.4.0",
298+ "lumi-components": "18.0.0",
299+ "machines": "7.0.0",
300+ "maps-eager": "0.5.0",
301+ "marionette": "1.0.0",
302+ "marionette-react-basic-hooks": "0.1.1",
303+ "markdown-it-js": "0.1.0",
304+ "marked": "0.1.0",
305+ "matrices": "5.0.1",
306+ "matryoshka": "1.0.0",
307+ "maybe": "6.0.0",
308+ "media-types": "6.0.0",
309+ "meowclient": "1.0.0",
310+ "midi": "4.0.0",
311+ "milkdown": "0.1.0",
312+ "milkis": "9.0.0",
313+ "mimetype": "0.0.1",
314+ "minibench": "4.0.1",
315+ "mmorph": "7.0.0",
316+ "monad-control": "5.0.0",
317+ "monad-logger": "1.3.1",
318+ "monad-loops": "0.5.0",
319+ "monad-unlift": "1.0.1",
320+ "monoid-extras": "0.0.1",
321+ "monoidal": "0.16.0",
322+ "morello": "0.4.0",
323+ "mote": "3.0.0",
324+ "motsunabe": "2.0.0",
325+ "mvc": "0.0.1",
326+ "mysql": "6.0.1",
327+ "n3": "0.1.0",
328+ "nano-id": "1.1.0",
329+ "nanoid": "0.1.0",
330+ "naturals": "3.0.0",
331+ "nested-functor": "0.2.1",
332+ "newtype": "5.0.0",
333+ "next-purs-rsc": "0.1.0",
334+ "nextjs": "0.1.1",
335+ "nextui": "0.2.0",
336+ "node-buffer": "9.0.0",
337+ "node-child-process": "11.1.0",
338+ "node-event-emitter": "3.0.0",
339+ "node-execa": "5.0.0",
340+ "node-fs": "9.2.0",
341+ "node-glob-basic": "2.0.0",
342+ "node-http": "9.1.0",
343+ "node-http2": "1.1.1",
344+ "node-human-signals": "1.0.0",
345+ "node-net": "5.1.0",
346+ "node-os": "5.1.0",
347+ "node-path": "5.0.1",
348+ "node-process": "11.2.0",
349+ "node-readline": "8.1.1",
350+ "node-sqlite3": "8.0.0",
351+ "node-stream-pipes": "2.1.6",
352+ "node-streams": "9.0.1",
353+ "node-tls": "0.3.1",
354+ "node-url": "7.0.1",
355+ "node-workerbees": "0.3.1",
356+ "node-zlib": "0.4.0",
357+ "nonempty": "7.0.0",
358+ "now": "6.0.0",
359+ "npm-package-json": "2.0.0",
360+ "nqueens": "0.1.4",
361+ "nullable": "6.0.0",
362+ "numberfield": "0.2.2",
363+ "numbers": "9.0.1",
364+ "oak": "3.1.1",
365+ "oak-debug": "1.2.2",
366+ "object-maps": "0.3.0",
367+ "ocarina": "1.5.4",
368+ "oooooooooorrrrrrrmm-lib": "0.0.1",
369+ "open-colors-scales-and-schemes": "1.0.0",
370+ "open-drawing": "6.0.4",
371+ "open-folds": "6.4.0",
372+ "open-foreign-generic": "11.0.3",
373+ "open-memoize": "6.2.0",
374+ "open-mkdirp-aff": "1.2.0",
375+ "open-pairing": "6.2.0",
376+ "open-smolder": "12.0.2",
377+ "options": "7.0.0",
378+ "optparse": "5.0.1",
379+ "ordered-collections": "3.2.0",
380+ "ordered-set": "0.5.0",
381+ "orders": "6.0.0",
382+ "org-doc": "0.1.0",
383+ "owoify": "1.2.0",
384+ "pairs": "9.0.1",
385+ "parallel": "7.0.0",
386+ "parsing": "11.0.0",
387+ "parsing-dataview": "3.2.4",
388+ "partial": "4.0.0",
389+ "pathy": "9.0.0",
390+ "pha": "0.13.0",
391+ "phaser": "0.7.0",
392+ "phylio": "1.1.2",
393+ "pipes": "8.0.0",
394+ "pirates-charm": "0.0.1",
395+ "play": "1.0.0",
396+ "pmock": "0.9.0",
397+ "point-free": "1.0.0",
398+ "pointed-list": "0.5.1",
399+ "polymorphic-vectors": "4.0.0",
400+ "posix-types": "6.0.0",
401+ "postgresql": "2.0.21",
402+ "precise": "6.0.0",
403+ "precise-datetime": "7.0.0",
404+ "prelude": "6.0.2",
405+ "prettier-printer": "3.0.0",
406+ "printf": "0.1.0",
407+ "priority-queue": "0.1.2",
408+ "profunctor": "6.0.1",
409+ "profunctor-lenses": "8.0.0",
410+ "prospero": "0.1.0",
411+ "protobuf": "4.4.0",
412+ "ps-spa": "0.7.1",
413+ "psa-utils": "8.0.0",
414+ "psci-support": "6.0.0",
415+ "punycode": "1.0.0",
416+ "pursfmt": "0.17.0",
417+ "qualified-do": "2.2.0",
418+ "quantities": "12.2.0",
419+ "quickcheck": "8.0.1",
420+ "quickcheck-combinators": "0.1.3",
421+ "quickcheck-laws": "7.0.0",
422+ "quickcheck-utf8": "0.0.0",
423+ "random": "6.0.0",
424+ "rationals": "6.0.0",
425+ "rdf": "0.1.0",
426+ "react": "11.0.0",
427+ "react-aria": "0.2.0",
428+ "react-basic": "17.0.0",
429+ "react-basic-classic": "3.0.0",
430+ "react-basic-dnd": "10.1.0",
431+ "react-basic-dom": "7.0.0",
432+ "react-basic-dom-beta": "0.1.1",
433+ "react-basic-emotion": "7.1.0",
434+ "react-basic-hooks": "9.1.1",
435+ "react-basic-storybook": "2.0.0",
436+ "react-dnd-kit": "0.6.0",
437+ "react-dom": "8.0.0",
438+ "react-halo": "3.0.0",
439+ "react-icons": "1.1.5",
440+ "react-markdown": "0.1.0",
441+ "react-testing-library": "4.0.1",
442+ "react-virtuoso": "1.0.0",
443+ "reactix": "0.6.1",
444+ "read": "1.0.1",
445+ "recharts": "1.1.0",
446+ "record": "4.0.0",
447+ "record-extra": "5.0.1",
448+ "record-extra-srghma": "0.2.8",
449+ "record-ptional-fields": "0.1.2",
450+ "record-studio": "1.0.4",
451+ "ref-lifted": "0.0.2",
452+ "refs": "6.0.0",
453+ "remotedata": "5.0.1",
454+ "repr": "0.5.0",
455+ "resize-arrays": "0.0.1",
456+ "resize-observer": "1.0.0",
457+ "resource": "2.0.1",
458+ "resourcet": "1.0.0",
459+ "result": "1.0.3",
460+ "return": "0.2.0",
461+ "ring-modules": "5.0.1",
462+ "rito": "0.3.4",
463+ "roman": "0.4.0",
464+ "rough-notation": "1.0.2",
465+ "routing": "11.0.0",
466+ "routing-duplex": "0.7.0",
467+ "run": "5.0.0",
468+ "safe-coerce": "2.0.0",
469+ "safely": "4.0.1",
470+ "school-of-music": "1.3.0",
471+ "selection-foldable": "0.2.0",
472+ "selective-functors": "1.0.1",
473+ "semirings": "7.0.0",
474+ "shuffle": "2.0.0",
475+ "sigil": "0.3.0",
476+ "sigil-hats": "0.3.0",
477+ "signal": "13.0.0",
478+ "simple-emitter": "3.0.1",
479+ "simple-i18n": "2.0.1",
480+ "simple-json": "9.0.0",
481+ "simple-json-generics": "0.2.1",
482+ "simple-ulid": "3.0.0",
483+ "sized-matrices": "1.0.0",
484+ "sized-vectors": "5.0.2",
485+ "slug": "3.2.0",
486+ "small-ffi": "4.0.1",
487+ "soundfonts": "4.1.0",
488+ "sparse-matrices": "2.0.1",
489+ "sparse-polynomials": "3.0.1",
490+ "spec": "8.1.2",
491+ "spec-discovery": "8.4.1",
492+ "spec-mocha": "5.1.1",
493+ "spec-node": "0.0.3",
494+ "spec-quickcheck": "5.0.2",
495+ "spec-reporter-xunit": "0.7.1",
496+ "splitmix": "2.1.0",
497+ "ssrs": "1.0.0",
498+ "st": "6.2.0",
499+ "statistics": "0.3.2",
500+ "strictlypositiveint": "1.0.1",
501+ "string-parsers": "8.0.0",
502+ "strings": "6.0.1",
503+ "strings-extra": "4.0.0",
504+ "stringutils": "0.0.13",
505+ "structured-logging": "1.0.0",
506+ "substitute": "0.2.3",
507+ "supabase": "0.1.0",
508+ "supply": "0.2.0",
509+ "svg-parser": "3.0.0",
510+ "systemd-journald": "0.3.0",
511+ "tagged": "4.0.2",
512+ "tailrec": "6.1.0",
513+ "tanstack-query": "3.0.0",
514+ "tecton": "0.2.1",
515+ "tecton-halogen": "0.2.0",
516+ "temporal": "0.1.0",
517+ "test-unit": "17.0.0",
518+ "text-formatting": "0.1.0",
519+ "thermite": "6.3.1",
520+ "thermite-dom": "0.3.1",
521+ "these": "6.0.0",
522+ "threading": "0.0.3",
523+ "tidy": "0.11.1",
524+ "tidy-codegen": "4.0.1",
525+ "tldr": "0.0.0",
526+ "toestand": "0.9.0",
527+ "transformation-matrix": "1.0.1",
528+ "transformers": "6.1.0",
529+ "transit": "1.0.0",
530+ "tree-rose": "4.0.2",
531+ "trivial-unfold": "0.5.0",
532+ "ts-bridge": "4.0.0",
533+ "tuples": "7.0.0",
534+ "two-or-more": "1.0.0",
535+ "type-equality": "4.0.1",
536+ "typedenv": "2.0.1",
537+ "typelevel": "6.0.0",
538+ "typelevel-lists": "2.1.0",
539+ "typelevel-peano": "1.0.1",
540+ "typelevel-prelude": "7.0.0",
541+ "typelevel-regex": "0.0.3",
542+ "typelevel-rows": "0.1.0",
543+ "typisch": "0.4.0",
544+ "uint": "7.0.0",
545+ "ulid": "3.0.1",
546+ "uncurried-transformers": "1.1.0",
547+ "undefined": "2.0.0",
548+ "undefined-is-not-a-problem": "1.1.0",
549+ "unfoldable": "6.0.0",
550+ "unicode": "6.0.0",
551+ "unique": "0.6.1",
552+ "unlift": "1.0.1",
553+ "unordered-collections": "3.1.0",
554+ "unsafe-coerce": "6.0.0",
555+ "unsafe-reference": "5.0.0",
556+ "untagged-to-tagged": "0.1.4",
557+ "untagged-union": "1.0.0",
558+ "uri": "9.0.0",
559+ "url-immutable": "1.0.0",
560+ "url-regex-safe": "0.1.1",
561+ "uuid": "9.0.0",
562+ "uuidv4": "1.0.0",
563+ "validation": "6.0.0",
564+ "variant": "8.0.0",
565+ "variant-encodings": "2.0.0",
566+ "variant-gen": "1.0.0",
567+ "vectorfield": "1.0.1",
568+ "vectors": "2.1.0",
569+ "versions": "7.0.0",
570+ "visx": "0.0.2",
571+ "vitest": "2.0.0",
572+ "web-chain": "1.0.1",
573+ "web-clipboard": "6.0.0",
574+ "web-cssom": "2.0.0",
575+ "web-cssom-view": "0.1.0",
576+ "web-dom": "6.0.0",
577+ "web-dom-parser": "8.0.0",
578+ "web-dom-xpath": "3.0.0",
579+ "web-encoding": "3.0.0",
580+ "web-events": "4.0.0",
581+ "web-fetch": "4.0.1",
582+ "web-file": "4.0.0",
583+ "web-geometry": "0.1.0",
584+ "web-html": "4.1.1",
585+ "web-pointerevents": "2.0.0",
586+ "web-proletarian": "1.0.0",
587+ "web-promise": "3.2.0",
588+ "web-resize-observer": "2.1.0",
589+ "web-router": "1.0.0",
590+ "web-socket": "4.0.0",
591+ "web-storage": "5.0.0",
592+ "web-streams": "4.0.0",
593+ "web-touchevents": "4.0.0",
594+ "web-uievents": "5.0.0",
595+ "web-url": "2.0.0",
596+ "web-workers": "1.1.0",
597+ "web-xhr": "5.0.1",
598+ "webb-aff-list": "0.0.2",
599+ "webb-array": "0.0.2",
600+ "webb-channel": "0.0.3",
601+ "webb-commandline": "0.0.3",
602+ "webb-directory": "0.0.7",
603+ "webb-file": "0.0.1",
604+ "webb-map": "0.0.1",
605+ "webb-monad": "0.0.2",
606+ "webb-mutex": "0.0.7",
607+ "webb-parsing": "0.0.5",
608+ "webb-random": "0.0.1",
609+ "webb-refer": "0.0.3",
610+ "webb-set": "0.0.1",
611+ "webb-slot": "0.0.1",
612+ "webb-stateful": "0.0.3",
613+ "webb-string": "0.0.1",
614+ "webb-test": "0.0.1",
615+ "webb-thread": "0.0.2",
616+ "webb-writer": "0.0.3",
617+ "webextension-polyfill": "0.1.0",
618+ "webgpu": "0.0.1",
619+ "which": "2.0.0",
620+ "whine-core": "0.0.33",
621+ "xterm": "1.0.0",
622+ "yaml-next": "3.1.1",
623+ "yoga-acp-om": "0.1.1",
624+ "yoga-better-auth": "0.2.2",
625+ "yoga-bun-yoga": "0.2.1",
626+ "yoga-config": "0.2.1",
627+ "yoga-country": "3.0.0",
628+ "yoga-docker-compose": "0.1.1",
629+ "yoga-dynamodb": "0.1.1",
630+ "yoga-elasticsearch": "0.1.1",
631+ "yoga-fastify": "0.5.1",
632+ "yoga-fastify-om": "0.4.4",
633+ "yoga-fetch": "1.0.1",
634+ "yoga-fetch-om": "0.6.3",
635+ "yoga-heroui": "2.0.2",
636+ "yoga-http-api": "0.3.1",
637+ "yoga-jaeger": "0.1.1",
638+ "yoga-json": "5.2.1",
639+ "yoga-language": "1.0.0",
640+ "yoga-next-fastify": "0.1.1",
641+ "yoga-om": "2.2.0",
642+ "yoga-om-layer": "2.1.0",
643+ "yoga-om-strom": "0.4.2",
644+ "yoga-om-workerbees": "0.1.2",
645+ "yoga-opentelemetry": "0.2.0",
646+ "yoga-options": "0.1.1",
647+ "yoga-pino": "0.1.1",
648+ "yoga-postgres": "6.0.0",
649+ "yoga-react": "1.0.2",
650+ "yoga-react-dom": "2.0.1",
651+ "yoga-react-native": "0.2.1",
652+ "yoga-react-om": "0.2.0",
653+ "yoga-redis": "0.1.0",
654+ "yoga-shadcn": "1.0.5",
655+ "yoga-sql-types": "0.1.0",
656+ "yoga-sqlite": "0.3.2",
657+ "yoga-sse": "0.1.1",
658+ "yoga-subtlecrypto": "0.1.0",
659+ "yoga-test-docker": "0.1.2",
660+ "yoga-timezone": "1.0.0",
661+ "yoga-tree": "1.0.0",
662+ "yoga-tree-svg": "0.1.0",
663+ "yoga-tree-utils": "1.0.0",
664+ "z3": "0.0.2",
665+ "zipperarray": "2.0.0"
666+ }
667+ },
668+ "extra_packages": {}
669+ },
670+ "packages": {
671+ "aff": {
672+ "type": "registry",
673+ "version": "8.0.0",
674+ "integrity": "sha256-9BRIrSdRoYybfiUmBqgKJ66cPqxYHb277bVWQMn/9sg=",
675+ "dependencies": [
676+ "control",
677+ "datetime",
678+ "effect",
679+ "either",
680+ "exceptions",
681+ "functions",
682+ "newtype",
683+ "parallel",
684+ "partial",
685+ "prelude",
686+ "st",
687+ "tailrec",
688+ "transformers",
689+ "unsafe-coerce"
690+ ]
691+ },
692+ "aff-promise": {
693+ "type": "registry",
694+ "version": "4.0.0",
695+ "integrity": "sha256-Jgp3y+NWuuAmwz2V3LlWoX+AvxqfVglY77N5zTw8xnI=",
696+ "dependencies": [
697+ "aff",
698+ "control",
699+ "effect",
700+ "either",
701+ "exceptions",
702+ "foreign",
703+ "prelude",
704+ "transformers"
705+ ]
706+ },
707+ "ansi": {
708+ "type": "registry",
709+ "version": "7.0.0",
710+ "integrity": "sha256-tOUaXJFzPSfBVAqLI8hc+Tz2M1rg16cXqyHaK/3SmFg=",
711+ "dependencies": [
712+ "foldable-traversable",
713+ "lists",
714+ "prelude"
715+ ]
716+ },
717+ "argonaut-codecs": {
718+ "type": "registry",
719+ "version": "9.1.0",
720+ "integrity": "sha256-K910SBrmYallESsm8pxJYThs7lig6hxX3M4PW33Cgew=",
721+ "dependencies": [
722+ "argonaut-core",
723+ "arrays",
724+ "bifunctors",
725+ "either",
726+ "foldable-traversable",
727+ "foreign-object",
728+ "identity",
729+ "integers",
730+ "lists",
731+ "maybe",
732+ "nonempty",
733+ "ordered-collections",
734+ "prelude",
735+ "record",
736+ "strings",
737+ "tuples"
738+ ]
739+ },
740+ "argonaut-core": {
741+ "type": "registry",
742+ "version": "7.0.0",
743+ "integrity": "sha256-gYZihtBIKthVuXXoiPphMookSf8M5aHyFYkcIWqlxTM=",
744+ "dependencies": [
745+ "arrays",
746+ "control",
747+ "either",
748+ "foreign-object",
749+ "functions",
750+ "gen",
751+ "maybe",
752+ "nonempty",
753+ "prelude",
754+ "strings",
755+ "tailrec"
756+ ]
757+ },
758+ "arraybuffer-types": {
759+ "type": "registry",
760+ "version": "3.0.2",
761+ "integrity": "sha256-p05cJnSkyeoB7VHzMyc2Eb1RwUaB7Nl0sQSqEGNEUD8=",
762+ "dependencies": []
763+ },
764+ "arrays": {
765+ "type": "registry",
766+ "version": "7.3.0",
767+ "integrity": "sha256-GD4z7LCi9wzi7FCQqbWhvs8paoUK9NO+HwgXZ+KP8Rk=",
768+ "dependencies": [
769+ "bifunctors",
770+ "control",
771+ "foldable-traversable",
772+ "functions",
773+ "maybe",
774+ "nonempty",
775+ "partial",
776+ "prelude",
777+ "safe-coerce",
778+ "st",
779+ "tailrec",
780+ "tuples",
781+ "unfoldable",
782+ "unsafe-coerce"
783+ ]
784+ },
785+ "avar": {
786+ "type": "registry",
787+ "version": "5.0.1",
788+ "integrity": "sha256-8V4SxF4TIWZ9Ik1P4lPzIRUAZeFBe8w5WA2VcCEKsIk=",
789+ "dependencies": [
790+ "aff",
791+ "effect",
792+ "either",
793+ "exceptions",
794+ "functions",
795+ "maybe",
796+ "prelude"
797+ ]
798+ },
799+ "bifunctors": {
800+ "type": "registry",
801+ "version": "6.1.0",
802+ "integrity": "sha256-CvZRb8uI75eKIqiYntR+k05Wk6tKlMS5w8sCA2AFna0=",
803+ "dependencies": [
804+ "const",
805+ "either",
806+ "newtype",
807+ "prelude",
808+ "tuples"
809+ ]
810+ },
811+ "catenable-lists": {
812+ "type": "registry",
813+ "version": "7.0.0",
814+ "integrity": "sha256-7+i/MHBLIRh604iCNT4ENWRWE/wuHIotER4kpdX9Ve0=",
815+ "dependencies": [
816+ "control",
817+ "foldable-traversable",
818+ "lists",
819+ "maybe",
820+ "prelude",
821+ "tuples",
822+ "unfoldable"
823+ ]
824+ },
825+ "const": {
826+ "type": "registry",
827+ "version": "6.0.0",
828+ "integrity": "sha256-S+m0lQsxnji0z1XRUmkn+3RFuWJ5CXWuIr1u3i8Bfoc=",
829+ "dependencies": [
830+ "invariant",
831+ "newtype",
832+ "prelude"
833+ ]
834+ },
835+ "contravariant": {
836+ "type": "registry",
837+ "version": "6.0.0",
838+ "integrity": "sha256-nuckEWqL80NaPmR/l9AUyrfycNh18XcvlZV9PAamwqc=",
839+ "dependencies": [
840+ "const",
841+ "either",
842+ "newtype",
843+ "prelude",
844+ "tuples"
845+ ]
846+ },
847+ "control": {
848+ "type": "registry",
849+ "version": "6.0.0",
850+ "integrity": "sha256-0vxyYh5FL+ilxgLo954w1LqCUC0RSIivNNlb9CaNUQg=",
851+ "dependencies": [
852+ "newtype",
853+ "prelude"
854+ ]
855+ },
856+ "datetime": {
857+ "type": "registry",
858+ "version": "6.1.0",
859+ "integrity": "sha256-tbv6eDXy6QOD7YSknxYSbsDF32OC2JrNbSBpzMs7Doc=",
860+ "dependencies": [
861+ "bifunctors",
862+ "control",
863+ "either",
864+ "enums",
865+ "foldable-traversable",
866+ "functions",
867+ "gen",
868+ "integers",
869+ "lists",
870+ "maybe",
871+ "newtype",
872+ "numbers",
873+ "ordered-collections",
874+ "partial",
875+ "prelude",
876+ "tuples"
877+ ]
878+ },
879+ "distributive": {
880+ "type": "registry",
881+ "version": "6.0.0",
882+ "integrity": "sha256-netyJK4B32PDYHD0tF2AenKgo3urzbI770ycM0pArAo=",
883+ "dependencies": [
884+ "identity",
885+ "newtype",
886+ "prelude",
887+ "tuples",
888+ "type-equality"
889+ ]
890+ },
891+ "effect": {
892+ "type": "registry",
893+ "version": "4.0.0",
894+ "integrity": "sha256-DdqU3bncRTjgMVPPRlAuGHVeajSPEcRaRhNuio7if6s=",
895+ "dependencies": [
896+ "prelude"
897+ ]
898+ },
899+ "either": {
900+ "type": "registry",
901+ "version": "6.1.0",
902+ "integrity": "sha256-tBHx3PgtH4GZOApZCDkcM7szRTYJinrUrVGWazQCUUg=",
903+ "dependencies": [
904+ "control",
905+ "invariant",
906+ "maybe",
907+ "prelude"
908+ ]
909+ },
910+ "enums": {
911+ "type": "registry",
912+ "version": "6.0.1",
913+ "integrity": "sha256-sdZOmLX5+5pASGpvVyT0vSD5BBYQjZiayjV5XiPutso=",
914+ "dependencies": [
915+ "control",
916+ "either",
917+ "gen",
918+ "maybe",
919+ "newtype",
920+ "nonempty",
921+ "partial",
922+ "prelude",
923+ "tuples",
924+ "unfoldable"
925+ ]
926+ },
927+ "exceptions": {
928+ "type": "registry",
929+ "version": "6.1.0",
930+ "integrity": "sha256-nRrr+N0wk0TUFOWLR7e1n1oxcXo/X345bQUtoY9lW6A=",
931+ "dependencies": [
932+ "effect",
933+ "either",
934+ "maybe",
935+ "prelude"
936+ ]
937+ },
938+ "exists": {
939+ "type": "registry",
940+ "version": "6.0.0",
941+ "integrity": "sha256-vtLbrWNaI+pzx/2fw2AQnCovoLtcosClIhjO26QKkh8=",
942+ "dependencies": [
943+ "unsafe-coerce"
944+ ]
945+ },
946+ "exitcodes": {
947+ "type": "registry",
948+ "version": "4.0.0",
949+ "integrity": "sha256-xBl4aFSEQ05Owp1ZvO6OxxvLK3LVzU35RSbj79SsePQ=",
950+ "dependencies": [
951+ "enums",
952+ "maybe",
953+ "prelude"
954+ ]
955+ },
956+ "foldable-traversable": {
957+ "type": "registry",
958+ "version": "6.0.0",
959+ "integrity": "sha256-NGGWyCio/xjce6fPP7y3wwg7CheqllID6aJudDxbWhA=",
960+ "dependencies": [
961+ "bifunctors",
962+ "const",
963+ "control",
964+ "either",
965+ "functors",
966+ "identity",
967+ "maybe",
968+ "newtype",
969+ "orders",
970+ "prelude",
971+ "tuples"
972+ ]
973+ },
974+ "foreign": {
975+ "type": "registry",
976+ "version": "7.0.0",
977+ "integrity": "sha256-jiDRMVQfovGsbbA/FzbzYf6iWk9i2b5gNrIjz+gFfsI=",
978+ "dependencies": [
979+ "either",
980+ "functions",
981+ "integers",
982+ "lists",
983+ "maybe",
984+ "prelude",
985+ "strings",
986+ "transformers",
987+ "unsafe-coerce"
988+ ]
989+ },
990+ "foreign-object": {
991+ "type": "registry",
992+ "version": "4.1.0",
993+ "integrity": "sha256-x/Q7r80z/vmHRKvPwhYmHP/DnJciPtsyGfRTMzayKIU=",
994+ "dependencies": [
995+ "arrays",
996+ "foldable-traversable",
997+ "functions",
998+ "gen",
999+ "lists",
1000+ "maybe",
1001+ "prelude",
1002+ "st",
1003+ "tailrec",
1004+ "tuples",
1005+ "typelevel-prelude",
1006+ "unfoldable",
1007+ "unsafe-coerce"
1008+ ]
1009+ },
1010+ "fork": {
1011+ "type": "registry",
1012+ "version": "6.0.0",
1013+ "integrity": "sha256-0keBVeZZg1eCZEy/En1CohVW829gQUqKtqBNIRa93/A=",
1014+ "dependencies": [
1015+ "aff",
1016+ "prelude",
1017+ "transformers"
1018+ ]
1019+ },
1020+ "free": {
1021+ "type": "registry",
1022+ "version": "7.1.0",
1023+ "integrity": "sha256-tUBInfUpRQEw4u94GWcYW7EYdmSdDHSRn88a+C0eltU=",
1024+ "dependencies": [
1025+ "catenable-lists",
1026+ "control",
1027+ "distributive",
1028+ "either",
1029+ "exists",
1030+ "foldable-traversable",
1031+ "invariant",
1032+ "lazy",
1033+ "maybe",
1034+ "prelude",
1035+ "tailrec",
1036+ "transformers",
1037+ "tuples",
1038+ "unsafe-coerce"
1039+ ]
1040+ },
1041+ "functions": {
1042+ "type": "registry",
1043+ "version": "6.0.0",
1044+ "integrity": "sha256-0jgtOr+SFO4UScKc40oA/9Vdcf2rLn3h3vZlY0KfqVo=",
1045+ "dependencies": [
1046+ "prelude"
1047+ ]
1048+ },
1049+ "functors": {
1050+ "type": "registry",
1051+ "version": "5.0.0",
1052+ "integrity": "sha256-W1o/4wcp6S3chodYL8uixlXK2mfr1L+SzM1R+whqUco=",
1053+ "dependencies": [
1054+ "bifunctors",
1055+ "const",
1056+ "contravariant",
1057+ "control",
1058+ "distributive",
1059+ "either",
1060+ "invariant",
1061+ "maybe",
1062+ "newtype",
1063+ "prelude",
1064+ "profunctor",
1065+ "tuples",
1066+ "unsafe-coerce"
1067+ ]
1068+ },
1069+ "gen": {
1070+ "type": "registry",
1071+ "version": "4.0.0",
1072+ "integrity": "sha256-kyOateeOtQa07vfTMYeSuCKdelgKE1R65fwKkZ10wsY=",
1073+ "dependencies": [
1074+ "either",
1075+ "foldable-traversable",
1076+ "identity",
1077+ "maybe",
1078+ "newtype",
1079+ "nonempty",
1080+ "prelude",
1081+ "tailrec",
1082+ "tuples",
1083+ "unfoldable"
1084+ ]
1085+ },
1086+ "identity": {
1087+ "type": "registry",
1088+ "version": "6.0.0",
1089+ "integrity": "sha256-RY/iXPpxpqvKHYfJeFVNI1J06kohB9rbtWYuha8t0LE=",
1090+ "dependencies": [
1091+ "control",
1092+ "invariant",
1093+ "newtype",
1094+ "prelude"
1095+ ]
1096+ },
1097+ "integers": {
1098+ "type": "registry",
1099+ "version": "6.0.0",
1100+ "integrity": "sha256-Y8yozC1uHRLcruobEyv5/bMKzE1BoKJ/olHv6o+bgm8=",
1101+ "dependencies": [
1102+ "maybe",
1103+ "numbers",
1104+ "prelude"
1105+ ]
1106+ },
1107+ "invariant": {
1108+ "type": "registry",
1109+ "version": "6.0.0",
1110+ "integrity": "sha256-HyoT1I5qIHRq8RSZji6zZDvqnpVjLw748utrKwZtqsw=",
1111+ "dependencies": [
1112+ "control",
1113+ "prelude"
1114+ ]
1115+ },
1116+ "js-date": {
1117+ "type": "registry",
1118+ "version": "8.0.0",
1119+ "integrity": "sha256-DNQrp4xYc8o80jtxo0aOkUeXGi6h0Xidkk2ZQ8Y4l5Y=",
1120+ "dependencies": [
1121+ "datetime",
1122+ "effect",
1123+ "enums",
1124+ "foreign",
1125+ "functions",
1126+ "integers",
1127+ "maybe",
1128+ "prelude"
1129+ ]
1130+ },
1131+ "lazy": {
1132+ "type": "registry",
1133+ "version": "6.0.0",
1134+ "integrity": "sha256-MHRC+1Pc+AjortBp4b/e1e6KpEpsDwaJBNBB7TOL+1I=",
1135+ "dependencies": [
1136+ "control",
1137+ "foldable-traversable",
1138+ "invariant",
1139+ "prelude"
1140+ ]
1141+ },
1142+ "lists": {
1143+ "type": "registry",
1144+ "version": "7.0.0",
1145+ "integrity": "sha256-/mGd7wLoU+wsTcqii7SSUByxsvwvjnu2PjwAD47yYb4=",
1146+ "dependencies": [
1147+ "bifunctors",
1148+ "control",
1149+ "foldable-traversable",
1150+ "lazy",
1151+ "maybe",
1152+ "newtype",
1153+ "nonempty",
1154+ "partial",
1155+ "prelude",
1156+ "tailrec",
1157+ "tuples",
1158+ "unfoldable"
1159+ ]
1160+ },
1161+ "maybe": {
1162+ "type": "registry",
1163+ "version": "6.0.0",
1164+ "integrity": "sha256-8X6XEgtZ5lqfkEuc1hWOofnL6mwhk8gSJTxwvbR/SbY=",
1165+ "dependencies": [
1166+ "control",
1167+ "invariant",
1168+ "newtype",
1169+ "prelude"
1170+ ]
1171+ },
1172+ "mmorph": {
1173+ "type": "registry",
1174+ "version": "7.0.0",
1175+ "integrity": "sha256-Cfltaq+tfYjuSoSBN/b8cSQZbvZTqIRs6ebQewbIkGs=",
1176+ "dependencies": [
1177+ "bifunctors",
1178+ "either",
1179+ "free",
1180+ "functors",
1181+ "identity",
1182+ "maybe",
1183+ "newtype",
1184+ "prelude",
1185+ "transformers",
1186+ "tuples"
1187+ ]
1188+ },
1189+ "newtype": {
1190+ "type": "registry",
1191+ "version": "5.0.0",
1192+ "integrity": "sha256-dt6w0cty4OS+Pjt9fsh3iU6ottqSU83mdAZkaEEvo6c=",
1193+ "dependencies": [
1194+ "prelude",
1195+ "safe-coerce"
1196+ ]
1197+ },
1198+ "node-buffer": {
1199+ "type": "registry",
1200+ "version": "9.0.0",
1201+ "integrity": "sha256-RdQjilmEHqsT4fwpCr8Mavw8KTafYrS3ZM6zeAAVAl8=",
1202+ "dependencies": [
1203+ "arraybuffer-types",
1204+ "effect",
1205+ "functions",
1206+ "maybe",
1207+ "nullable",
1208+ "partial",
1209+ "prelude",
1210+ "st",
1211+ "unsafe-coerce"
1212+ ]
1213+ },
1214+ "node-event-emitter": {
1215+ "type": "registry",
1216+ "version": "3.0.0",
1217+ "integrity": "sha256-wodx71NxJBPXOaawFn01V1ByYB2vT+I3RuV2giOVKMg=",
1218+ "dependencies": [
1219+ "effect",
1220+ "either",
1221+ "functions",
1222+ "maybe",
1223+ "nullable",
1224+ "prelude",
1225+ "unsafe-coerce"
1226+ ]
1227+ },
1228+ "node-fs": {
1229+ "type": "registry",
1230+ "version": "9.2.0",
1231+ "integrity": "sha256-fxioTSm9y47WuYs9F/7nxCh/RVDCl3Kr3Hg779J4SJA=",
1232+ "dependencies": [
1233+ "aff",
1234+ "datetime",
1235+ "effect",
1236+ "either",
1237+ "enums",
1238+ "exceptions",
1239+ "functions",
1240+ "integers",
1241+ "js-date",
1242+ "maybe",
1243+ "node-buffer",
1244+ "node-path",
1245+ "node-streams",
1246+ "nullable",
1247+ "partial",
1248+ "prelude",
1249+ "strings"
1250+ ]
1251+ },
1252+ "node-path": {
1253+ "type": "registry",
1254+ "version": "5.0.1",
1255+ "integrity": "sha256-j7n/SmpVz0FOMJl4XZop3ofJ+MeIPkMNqUEUKHEL6Us=",
1256+ "dependencies": [
1257+ "effect"
1258+ ]
1259+ },
1260+ "node-process": {
1261+ "type": "registry",
1262+ "version": "11.2.0",
1263+ "integrity": "sha256-onzKeNmaeYfN3HSDPqSFTrrP2Yl9tnVsfKKhnf2plxM=",
1264+ "dependencies": [
1265+ "effect",
1266+ "exceptions",
1267+ "foreign",
1268+ "foreign-object",
1269+ "maybe",
1270+ "node-event-emitter",
1271+ "node-streams",
1272+ "nullable",
1273+ "posix-types",
1274+ "prelude",
1275+ "strings"
1276+ ]
1277+ },
1278+ "node-streams": {
1279+ "type": "registry",
1280+ "version": "9.0.1",
1281+ "integrity": "sha256-yuVGm/R/tBr4Nphi+a1a984Z0fkmN9Q5wWtSmbmJTpA=",
1282+ "dependencies": [
1283+ "aff",
1284+ "arrays",
1285+ "effect",
1286+ "either",
1287+ "exceptions",
1288+ "maybe",
1289+ "node-buffer",
1290+ "node-event-emitter",
1291+ "nullable",
1292+ "prelude",
1293+ "refs",
1294+ "st",
1295+ "tailrec",
1296+ "unsafe-coerce"
1297+ ]
1298+ },
1299+ "nonempty": {
1300+ "type": "registry",
1301+ "version": "7.0.0",
1302+ "integrity": "sha256-Ctkq8/+KiGqCE53Y/LvibA/coy7ev9HtRZU+GYS7yfQ=",
1303+ "dependencies": [
1304+ "control",
1305+ "foldable-traversable",
1306+ "maybe",
1307+ "prelude",
1308+ "tuples",
1309+ "unfoldable"
1310+ ]
1311+ },
1312+ "now": {
1313+ "type": "registry",
1314+ "version": "6.0.0",
1315+ "integrity": "sha256-mTyG4s8tps3XIf0jjV/lME2/QlwMzrQly2wqfSSUM3o=",
1316+ "dependencies": [
1317+ "datetime",
1318+ "effect",
1319+ "prelude"
1320+ ]
1321+ },
1322+ "nullable": {
1323+ "type": "registry",
1324+ "version": "6.0.0",
1325+ "integrity": "sha256-CDAZk+L9Sc0GCFTkE8n+qdp4Ys8avvnkU+m2hVALt7M=",
1326+ "dependencies": [
1327+ "functions",
1328+ "maybe",
1329+ "prelude"
1330+ ]
1331+ },
1332+ "numbers": {
1333+ "type": "registry",
1334+ "version": "9.0.1",
1335+ "integrity": "sha256-ZydYckgwRAnRaFC7pu6fADhzdwX+UqHZrfJyet64zls=",
1336+ "dependencies": [
1337+ "functions",
1338+ "maybe",
1339+ "prelude"
1340+ ]
1341+ },
1342+ "open-memoize": {
1343+ "type": "registry",
1344+ "version": "6.2.0",
1345+ "integrity": "sha256-yJLuVYX8FocHIJ+6q7efH/Pj4S+e1Y57DZ5Dn7qf4ww=",
1346+ "dependencies": [
1347+ "either",
1348+ "integers",
1349+ "lazy",
1350+ "lists",
1351+ "maybe",
1352+ "partial",
1353+ "prelude",
1354+ "strings",
1355+ "tuples"
1356+ ]
1357+ },
1358+ "optparse": {
1359+ "type": "registry",
1360+ "version": "5.0.1",
1361+ "integrity": "sha256-2btDjHPRR0uujUQJuZAN9AzvVoXe7yG6IwnY5evERhQ=",
1362+ "dependencies": [
1363+ "arrays",
1364+ "bifunctors",
1365+ "control",
1366+ "effect",
1367+ "either",
1368+ "enums",
1369+ "exists",
1370+ "exitcodes",
1371+ "foldable-traversable",
1372+ "free",
1373+ "integers",
1374+ "lazy",
1375+ "lists",
1376+ "maybe",
1377+ "newtype",
1378+ "node-buffer",
1379+ "node-process",
1380+ "node-streams",
1381+ "nonempty",
1382+ "numbers",
1383+ "open-memoize",
1384+ "partial",
1385+ "prelude",
1386+ "strings",
1387+ "tailrec",
1388+ "transformers",
1389+ "tuples"
1390+ ]
1391+ },
1392+ "ordered-collections": {
1393+ "type": "registry",
1394+ "version": "3.2.0",
1395+ "integrity": "sha256-NaEXc2cz/7lJfxPWEja4XlZbGxuRqJwFKQJjsJf51kQ=",
1396+ "dependencies": [
1397+ "arrays",
1398+ "control",
1399+ "foldable-traversable",
1400+ "functions",
1401+ "gen",
1402+ "lists",
1403+ "maybe",
1404+ "newtype",
1405+ "partial",
1406+ "prelude",
1407+ "safe-coerce",
1408+ "tailrec",
1409+ "tuples",
1410+ "unfoldable"
1411+ ]
1412+ },
1413+ "orders": {
1414+ "type": "registry",
1415+ "version": "6.0.0",
1416+ "integrity": "sha256-nSMxZK7wxyDULkjZqvMyB71mVo6b0AyLLHosDVbv1XM=",
1417+ "dependencies": [
1418+ "newtype",
1419+ "prelude"
1420+ ]
1421+ },
1422+ "parallel": {
1423+ "type": "registry",
1424+ "version": "7.0.0",
1425+ "integrity": "sha256-bAGpVhrFz2IyHkA4MR7+tE/19FZ7dzYBdYPgK5svkqw=",
1426+ "dependencies": [
1427+ "control",
1428+ "effect",
1429+ "either",
1430+ "foldable-traversable",
1431+ "functors",
1432+ "maybe",
1433+ "newtype",
1434+ "prelude",
1435+ "profunctor",
1436+ "refs",
1437+ "transformers"
1438+ ]
1439+ },
1440+ "partial": {
1441+ "type": "registry",
1442+ "version": "4.0.0",
1443+ "integrity": "sha256-mdrlJBAgFMh79hNRW39uv7c+BwnnaOrAMmdh7+VhEhs=",
1444+ "dependencies": []
1445+ },
1446+ "pipes": {
1447+ "type": "registry",
1448+ "version": "8.0.0",
1449+ "integrity": "sha256-dtUNpG8BGVr7pu6mQZyAnUAftUHgNS7f0QH3Kw5UTpQ=",
1450+ "dependencies": [
1451+ "aff",
1452+ "control",
1453+ "effect",
1454+ "either",
1455+ "foldable-traversable",
1456+ "identity",
1457+ "lists",
1458+ "maybe",
1459+ "mmorph",
1460+ "newtype",
1461+ "prelude",
1462+ "tailrec",
1463+ "transformers",
1464+ "tuples"
1465+ ]
1466+ },
1467+ "posix-types": {
1468+ "type": "registry",
1469+ "version": "6.0.0",
1470+ "integrity": "sha256-Je5jG439F/SFO/IvVevZnWVxDIonrehlKuJkefP2lHE=",
1471+ "dependencies": [
1472+ "maybe",
1473+ "newtype",
1474+ "prelude"
1475+ ]
1476+ },
1477+ "prelude": {
1478+ "type": "registry",
1479+ "version": "6.0.2",
1480+ "integrity": "sha256-IqykrDRquGUD/LRSad7y75hZv3bUsGQ4knc8tdez8Qw=",
1481+ "dependencies": []
1482+ },
1483+ "profunctor": {
1484+ "type": "registry",
1485+ "version": "6.0.1",
1486+ "integrity": "sha256-Ow0mJC+PIIRq/a3mzCWY5vxuTgR/e+Ee68+mWeSRnWY=",
1487+ "dependencies": [
1488+ "control",
1489+ "distributive",
1490+ "either",
1491+ "exists",
1492+ "invariant",
1493+ "newtype",
1494+ "prelude",
1495+ "tuples"
1496+ ]
1497+ },
1498+ "record": {
1499+ "type": "registry",
1500+ "version": "4.0.0",
1501+ "integrity": "sha256-WQ8fqp4X1wTA70J4qUcPW4Z7DCwx6e5KJnT/EuTdWSc=",
1502+ "dependencies": [
1503+ "functions",
1504+ "prelude",
1505+ "unsafe-coerce"
1506+ ]
1507+ },
1508+ "refs": {
1509+ "type": "registry",
1510+ "version": "6.0.0",
1511+ "integrity": "sha256-KKD5G9dR2SauuDGULUJsxjqDOEi1Jqm+2Sq5U2mY8YU=",
1512+ "dependencies": [
1513+ "effect",
1514+ "prelude"
1515+ ]
1516+ },
1517+ "safe-coerce": {
1518+ "type": "registry",
1519+ "version": "2.0.0",
1520+ "integrity": "sha256-EJrxtKt5xC7TYjBZrSBO/J7Aw3DmtilYjt4olpAXWVc=",
1521+ "dependencies": [
1522+ "unsafe-coerce"
1523+ ]
1524+ },
1525+ "spec": {
1526+ "type": "registry",
1527+ "version": "8.1.2",
1528+ "integrity": "sha256-klPPJ6lBQI1CGOAdLJ+ZoGRt8XZippkTKjn53BrSkL0=",
1529+ "dependencies": [
1530+ "aff",
1531+ "ansi",
1532+ "arrays",
1533+ "avar",
1534+ "bifunctors",
1535+ "control",
1536+ "datetime",
1537+ "effect",
1538+ "either",
1539+ "exceptions",
1540+ "foldable-traversable",
1541+ "fork",
1542+ "identity",
1543+ "integers",
1544+ "lists",
1545+ "maybe",
1546+ "newtype",
1547+ "now",
1548+ "ordered-collections",
1549+ "parallel",
1550+ "pipes",
1551+ "prelude",
1552+ "refs",
1553+ "strings",
1554+ "tailrec",
1555+ "transformers",
1556+ "tuples"
1557+ ]
1558+ },
1559+ "spec-node": {
1560+ "type": "registry",
1561+ "version": "0.0.3",
1562+ "integrity": "sha256-8/9+g0obbamV0YtqzmttbWs8HaWF7CEeW+eDtqzuAAo=",
1563+ "dependencies": [
1564+ "aff",
1565+ "argonaut-codecs",
1566+ "argonaut-core",
1567+ "arrays",
1568+ "control",
1569+ "datetime",
1570+ "effect",
1571+ "either",
1572+ "foldable-traversable",
1573+ "identity",
1574+ "integers",
1575+ "maybe",
1576+ "newtype",
1577+ "node-buffer",
1578+ "node-fs",
1579+ "node-process",
1580+ "now",
1581+ "numbers",
1582+ "optparse",
1583+ "ordered-collections",
1584+ "partial",
1585+ "prelude",
1586+ "spec",
1587+ "strings",
1588+ "tuples"
1589+ ]
1590+ },
1591+ "st": {
1592+ "type": "registry",
1593+ "version": "6.2.0",
1594+ "integrity": "sha256-WI4MEkkwUd4pnwZQ3G8VKWgX5t1RSNJludm5e9boaRo=",
1595+ "dependencies": [
1596+ "effect",
1597+ "partial",
1598+ "prelude",
1599+ "tailrec",
1600+ "unsafe-coerce"
1601+ ]
1602+ },
1603+ "strings": {
1604+ "type": "registry",
1605+ "version": "6.0.1",
1606+ "integrity": "sha256-FNto2hoNW5Da6W6crIk77YXMagBvOGHZE1kO7eZ1vLM=",
1607+ "dependencies": [
1608+ "arrays",
1609+ "control",
1610+ "either",
1611+ "enums",
1612+ "foldable-traversable",
1613+ "gen",
1614+ "integers",
1615+ "maybe",
1616+ "newtype",
1617+ "nonempty",
1618+ "partial",
1619+ "prelude",
1620+ "tailrec",
1621+ "tuples",
1622+ "unfoldable",
1623+ "unsafe-coerce"
1624+ ]
1625+ },
1626+ "tailrec": {
1627+ "type": "registry",
1628+ "version": "6.1.0",
1629+ "integrity": "sha256-xrorCfP0xV1wubs27idQCHQqdvSUuqmmL/am7Ji1wVU=",
1630+ "dependencies": [
1631+ "bifunctors",
1632+ "effect",
1633+ "either",
1634+ "identity",
1635+ "maybe",
1636+ "partial",
1637+ "prelude",
1638+ "refs"
1639+ ]
1640+ },
1641+ "transformers": {
1642+ "type": "registry",
1643+ "version": "6.1.0",
1644+ "integrity": "sha256-QJmgNT/y7ljPHSsXaW4zxF/982BFiQ90KNL1g/NtEOQ=",
1645+ "dependencies": [
1646+ "control",
1647+ "distributive",
1648+ "effect",
1649+ "either",
1650+ "exceptions",
1651+ "foldable-traversable",
1652+ "identity",
1653+ "lazy",
1654+ "maybe",
1655+ "newtype",
1656+ "prelude",
1657+ "st",
1658+ "tailrec",
1659+ "tuples",
1660+ "unfoldable"
1661+ ]
1662+ },
1663+ "tuples": {
1664+ "type": "registry",
1665+ "version": "7.0.0",
1666+ "integrity": "sha256-BKy7EfK8S1KeTCwyWqyW1wwcpSAQuh3IsczSVgS6fzY=",
1667+ "dependencies": [
1668+ "control",
1669+ "invariant",
1670+ "prelude"
1671+ ]
1672+ },
1673+ "type-equality": {
1674+ "type": "registry",
1675+ "version": "4.0.1",
1676+ "integrity": "sha256-BBqYOSnAKmayRvR5mtZoCf4SlYs2ipatD/+5nQx73aw=",
1677+ "dependencies": []
1678+ },
1679+ "typelevel-prelude": {
1680+ "type": "registry",
1681+ "version": "7.0.0",
1682+ "integrity": "sha256-+pHi14/40mTj/+lmCWqL0T7iXIXD1+NSg/KItyyoB0A=",
1683+ "dependencies": [
1684+ "prelude",
1685+ "type-equality"
1686+ ]
1687+ },
1688+ "unfoldable": {
1689+ "type": "registry",
1690+ "version": "6.0.0",
1691+ "integrity": "sha256-BK/6By1sKp/ztk+9CF3q43SV0QDTLVdsGqFerKHSmbg=",
1692+ "dependencies": [
1693+ "foldable-traversable",
1694+ "maybe",
1695+ "partial",
1696+ "prelude",
1697+ "tuples"
1698+ ]
1699+ },
1700+ "unsafe-coerce": {
1701+ "type": "registry",
1702+ "version": "6.0.0",
1703+ "integrity": "sha256-0L1QsaY20OILjfU5TV72d3U/tSjhmL9hJ32WMp857Lk=",
1704+ "dependencies": []
1705+ }
1706+ }
1707+}
spago.yamlnew file+18
@@ -0,0 +1,18 @@
1+package:
2+ name: abcjs
3+ dependencies:
4+ - aff
5+ - aff-promise
6+ - arrays
7+ - effect
8+ - nullable
9+ - prelude
10+ test:
11+ main: Test.Main
12+ dependencies:
13+ - spec
14+ - spec-node
15+workspace:
16+ packageSet:
17+ registry: 77.4.0
18+ extraPackages: {}
src/Abcjs/Render.jsnew file+6
@@ -0,0 +1,6 @@
1+import abcjs from "abcjs";
2+
3+export const renderAbcImpl = (selector) => (abc) => (params) => () =>
4+ abcjs.renderAbc(selector, abc, params);
5+
6+export const parseOnlyImpl = (abc) => abcjs.parseOnly(abc);
src/Abcjs/Render.pursnew file+41
@@ -0,0 +1,41 @@
1+module Abcjs.Render
2+ ( RenderParams
3+ , defaultRenderParams
4+ , renderAbc
5+ , parseOnly
6+ ) where
7+
8+import Effect (Effect)
9+import Abcjs.Types (TuneObject)
10+
11+-- | The subset of abcjs render options most relevant to a game.
12+-- | `responsive: "resize"` makes the SVG scale to its container,
13+-- | `add_classes: true` tags each element so notes are stylable/targetable.
14+type RenderParams =
15+ { scale :: Number
16+ , staffwidth :: Number
17+ , responsive :: String
18+ , add_classes :: Boolean
19+ }
20+
21+defaultRenderParams :: RenderParams
22+defaultRenderParams =
23+ { scale: 1.0
24+ , staffwidth: 740.0
25+ , responsive: "resize"
26+ , add_classes: true
27+ }
28+
29+-- | Draw ABC notation into the element matched by a CSS selector (or id).
30+-- | Returns the rendered tunes to feed into the synth and timing APIs.
31+renderAbc :: String -> String -> RenderParams -> Effect (Array TuneObject)
32+renderAbc = renderAbcImpl
33+
34+foreign import renderAbcImpl :: String -> String -> RenderParams -> Effect (Array TuneObject)
35+
36+-- | Parse ABC without drawing anything — no DOM required.
37+-- | Useful for generating and inspecting phrases in game logic.
38+parseOnly :: String -> Array TuneObject
39+parseOnly = parseOnlyImpl
40+
41+foreign import parseOnlyImpl :: String -> Array TuneObject
src/Abcjs/Synth.jsnew file+25
@@ -0,0 +1,25 @@
1+import abcjs from "abcjs";
2+
3+export const supportsAudio = () => abcjs.synth.supportsAudio();
4+
5+export const newSynthController = () => new abcjs.synth.SynthController();
6+
7+export const loadImpl = (selector) => (cursorControl) => (options) => (controller) => () =>
8+ controller.load(selector, cursorControl, options);
9+
10+export const setTuneImpl = (tune) => (userAction) => (controller) => () =>
11+ controller.setTune(tune, userAction);
12+
13+export const playImpl = (controller) => () => controller.play();
14+
15+export const pauseImpl = (controller) => () => controller.pause();
16+
17+export const restartImpl = (controller) => () => controller.restart();
18+
19+export const seekSecondsImpl = (seconds) => (controller) => () =>
20+ controller.seek(seconds, "seconds");
21+
22+export const setWarpImpl = (percent) => (controller) => () =>
23+ controller.setWarp(percent);
24+
25+export const toggleLoopImpl = (controller) => () => controller.toggleLoop();
src/Abcjs/Synth.pursnew file+106
@@ -0,0 +1,106 @@
1+module Abcjs.Synth
2+ ( SynthController
3+ , CursorControl
4+ , ControlOptions
5+ , defaultControlOptions
6+ , supportsAudio
7+ , newSynthController
8+ , load
9+ , setTune
10+ , play
11+ , pause
12+ , restart
13+ , seekSeconds
14+ , setWarp
15+ , toggleLoop
16+ ) where
17+
18+import Prelude
19+
20+import Abcjs.Types (TimingEvent, TuneObject)
21+import Control.Promise (Promise, toAffE)
22+import Effect (Effect)
23+import Effect.Aff (Aff)
24+import Effect.Uncurried (EffectFn1, EffectFn3)
25+
26+-- | The high-level player: a transport plus an optional on-page control panel.
27+foreign import data SynthController :: Type
28+
29+-- | The callbacks abcjs fires while a tune plays — this is the hook that
30+-- | drives the follow-along cursor and any per-note/per-beat game logic.
31+-- | `onBeat` receives (beatNumber, totalBeats, totalTime).
32+type CursorControl =
33+ { onStart :: Effect Unit
34+ , onEvent :: EffectFn1 TimingEvent Unit
35+ , onBeat :: EffectFn3 Number Number Number Unit
36+ , onFinished :: Effect Unit
37+ }
38+
39+-- | Which buttons the rendered control panel shows.
40+type ControlOptions =
41+ { displayPlay :: Boolean
42+ , displayProgress :: Boolean
43+ , displayLoop :: Boolean
44+ , displayRestart :: Boolean
45+ , displayWarp :: Boolean
46+ }
47+
48+defaultControlOptions :: ControlOptions
49+defaultControlOptions =
50+ { displayPlay: true
51+ , displayProgress: true
52+ , displayLoop: true
53+ , displayRestart: true
54+ , displayWarp: true
55+ }
56+
57+-- | Whether this browser can play synthesized audio at all.
58+foreign import supportsAudio :: Effect Boolean
59+
60+foreign import newSynthController :: Effect SynthController
61+
62+-- | Attach the controller to a control-panel element and a cursor control.
63+load :: String -> CursorControl -> ControlOptions -> SynthController -> Effect Unit
64+load = loadImpl
65+
66+foreign import loadImpl :: String -> CursorControl -> ControlOptions -> SynthController -> Effect Unit
67+
68+-- | Prime a tune for playback. The `Boolean` says whether this was triggered
69+-- | by a user gesture, which lets the browser unlock the audio context.
70+setTune :: TuneObject -> Boolean -> SynthController -> Aff Unit
71+setTune tune userAction controller = toAffE (setTuneImpl tune userAction controller)
72+
73+foreign import setTuneImpl :: TuneObject -> Boolean -> SynthController -> Effect (Promise Unit)
74+
75+-- | Toggle play/pause.
76+play :: SynthController -> Effect Unit
77+play = playImpl
78+
79+foreign import playImpl :: SynthController -> Effect Unit
80+
81+pause :: SynthController -> Effect Unit
82+pause = pauseImpl
83+
84+foreign import pauseImpl :: SynthController -> Effect Unit
85+
86+restart :: SynthController -> Effect Unit
87+restart = restartImpl
88+
89+foreign import restartImpl :: SynthController -> Effect Unit
90+
91+-- | Jump to a position given in seconds.
92+seekSeconds :: Number -> SynthController -> Effect Unit
93+seekSeconds = seekSecondsImpl
94+
95+foreign import seekSecondsImpl :: Number -> SynthController -> Effect Unit
96+
97+-- | Set tempo as a percentage of the written tempo (100.0 = as written).
98+setWarp :: Number -> SynthController -> Effect Unit
99+setWarp = setWarpImpl
100+
101+foreign import setWarpImpl :: Number -> SynthController -> Effect Unit
102+
103+toggleLoop :: SynthController -> Effect Unit
104+toggleLoop = toggleLoopImpl
105+
106+foreign import toggleLoopImpl :: SynthController -> Effect Unit
src/Abcjs/Timing.jsnew file+14
@@ -0,0 +1,14 @@
1+import abcjs from "abcjs";
2+
3+export const newTimingCallbacksImpl = (visualObj) => (options) => () =>
4+ new abcjs.TimingCallbacks(visualObj, options);
5+
6+export const startImpl = (tc) => () => tc.start();
7+
8+export const stopImpl = (tc) => () => tc.stop();
9+
10+export const pauseImpl = (tc) => () => tc.pause();
11+
12+export const resetImpl = (tc) => () => tc.reset();
13+
14+export const setProgressImpl = (percent) => (tc) => () => tc.setProgress(percent);
src/Abcjs/Timing.pursnew file+61
@@ -0,0 +1,61 @@
1+module Abcjs.Timing
2+ ( TimingCallbacks
3+ , TimingOptions
4+ , newTimingCallbacks
5+ , start
6+ , stop
7+ , pause
8+ , reset
9+ , setProgress
10+ ) where
11+
12+import Prelude
13+
14+import Abcjs.Types (TimingEvent, TuneObject)
15+import Data.Nullable (Nullable)
16+import Effect (Effect)
17+import Effect.Uncurried (EffectFn1, EffectFn3)
18+
19+-- | A standalone animation timer over a rendered tune. Drives the cursor and
20+-- | game logic off note timing, independent of whether audio is playing.
21+foreign import data TimingCallbacks :: Type
22+
23+-- | `eventCallback` fires per note/rest/bar; abcjs passes `null` at the end of
24+-- | the tune, hence `Nullable`. `beatCallback` receives
25+-- | (beatNumber, totalBeats, totalTime). `qpm` overrides the tempo.
26+type TimingOptions =
27+ { qpm :: Number
28+ , eventCallback :: EffectFn1 (Nullable TimingEvent) Unit
29+ , beatCallback :: EffectFn3 Number Number Number Unit
30+ }
31+
32+newTimingCallbacks :: TuneObject -> TimingOptions -> Effect TimingCallbacks
33+newTimingCallbacks = newTimingCallbacksImpl
34+
35+foreign import newTimingCallbacksImpl :: TuneObject -> TimingOptions -> Effect TimingCallbacks
36+
37+start :: TimingCallbacks -> Effect Unit
38+start = startImpl
39+
40+foreign import startImpl :: TimingCallbacks -> Effect Unit
41+
42+stop :: TimingCallbacks -> Effect Unit
43+stop = stopImpl
44+
45+foreign import stopImpl :: TimingCallbacks -> Effect Unit
46+
47+pause :: TimingCallbacks -> Effect Unit
48+pause = pauseImpl
49+
50+foreign import pauseImpl :: TimingCallbacks -> Effect Unit
51+
52+reset :: TimingCallbacks -> Effect Unit
53+reset = resetImpl
54+
55+foreign import resetImpl :: TimingCallbacks -> Effect Unit
56+
57+-- | Jump the cursor to a fraction of the tune (0.0 to 1.0).
58+setProgress :: Number -> TimingCallbacks -> Effect Unit
59+setProgress = setProgressImpl
60+
61+foreign import setProgressImpl :: Number -> TimingCallbacks -> Effect Unit
src/Abcjs/Types.jsnew file+13
@@ -0,0 +1,13 @@
1+export const tuneLineCount = (tune) => tune.lines.length;
2+
3+export const eventMilliseconds = (ev) => ev.milliseconds;
4+
5+export const eventMeasureStart = (ev) => ev.measureStart;
6+
7+export const eventLeft = (ev) => ev.left;
8+
9+export const eventTop = (ev) => ev.top;
10+
11+export const eventWidth = (ev) => ev.width;
12+
13+export const eventHeight = (ev) => ev.height;
src/Abcjs/Types.pursnew file+40
@@ -0,0 +1,40 @@
1+module Abcjs.Types
2+ ( TuneObject
3+ , tuneLineCount
4+ , TimingEvent
5+ , eventMilliseconds
6+ , eventMeasureStart
7+ , eventLeft
8+ , eventTop
9+ , eventWidth
10+ , eventHeight
11+ ) where
12+
13+-- | A parsed/rendered tune, as returned by `renderAbc` or `parseOnly`.
14+-- | Opaque: pass it back into the synth and timing APIs.
15+foreign import data TuneObject :: Type
16+
17+-- | How many lines of music the tune parsed to. Zero means abcjs found no
18+-- | notes (empty or unparseable input) — abcjs still returns a tune object,
19+-- | so this is how you tell a generated phrase actually parsed.
20+foreign import tuneLineCount :: TuneObject -> Int
21+
22+-- | One playback event (a note, rest, or bar) handed to your callbacks.
23+-- | The accessors below pull out the fields a follow-along game needs:
24+-- | when the event happens and where it sits on the rendered SVG.
25+foreign import data TimingEvent :: Type
26+
27+-- | Milliseconds from the start of the tune at which this event sounds.
28+foreign import eventMilliseconds :: TimingEvent -> Number
29+
30+-- | Whether this event begins a new measure (handy for bar-level scoring).
31+foreign import eventMeasureStart :: TimingEvent -> Boolean
32+
33+-- | The on-screen bounding box of the event, for drawing a cursor.
34+foreign import eventLeft :: TimingEvent -> Number
35+
36+foreign import eventTop :: TimingEvent -> Number
37+
38+foreign import eventWidth :: TimingEvent -> Number
39+
40+foreign import eventHeight :: TimingEvent -> Number
test/Test/Main.pursnew file+35
@@ -0,0 +1,35 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Abcjs.Render (parseOnly)
6+import Abcjs.Types (tuneLineCount)
7+import Data.Array (head, length)
8+import Data.Maybe (Maybe(..))
9+import Effect (Effect)
10+import Test.Spec (describe, it)
11+import Test.Spec.Assertions (fail, shouldEqual, shouldSatisfy)
12+import Test.Spec.Reporter.Console (consoleReporter)
13+import Test.Spec.Runner.Node (runSpecAndExitProcess)
14+
15+main :: Effect Unit
16+main = runSpecAndExitProcess [ consoleReporter ] do
17+ describe "Abcjs.Render.parseOnly" do
18+ it "always yields exactly one tune object" do
19+ length (parseOnly validTune) `shouldEqual` 1
20+ it "parses a game-generated phrase to at least one line of music" do
21+ case head (parseOnly validTune) of
22+ Just tune -> tuneLineCount tune `shouldSatisfy` (_ > 0)
23+ Nothing -> fail "expected a tune object"
24+ it "parses empty input to a tune with no music" do
25+ case head (parseOnly "") of
26+ Just tune -> tuneLineCount tune `shouldEqual` 0
27+ Nothing -> fail "expected a tune object even for empty input"
28+
29+validTune :: String
30+validTune = twoMeasures "C D E F" "G A B c"
31+
32+twoMeasures :: String -> String -> String
33+twoMeasures a b = header <> a <> "|" <> b <> "|"
34+ where
35+ header = "X:1\nM:4/4\nL:1/4\nK:C\n"
tsconfig.jsonnew file+29
@@ -0,0 +1,29 @@
1+{
2+ "compilerOptions": {
3+ // Environment setup & latest features
4+ "lib": ["ESNext"],
5+ "target": "ESNext",
6+ "module": "Preserve",
7+ "moduleDetection": "force",
8+ "jsx": "react-jsx",
9+ "allowJs": true,
10+
11+ // Bundler mode
12+ "moduleResolution": "bundler",
13+ "allowImportingTsExtensions": true,
14+ "verbatimModuleSyntax": true,
15+ "noEmit": true,
16+
17+ // Best practices
18+ "strict": true,
19+ "skipLibCheck": true,
20+ "noFallthroughCasesInSwitch": true,
21+ "noUncheckedIndexedAccess": true,
22+ "noImplicitOverride": true,
23+
24+ // Some stricter flags (disabled by default)
25+ "noUnusedLocals": false,
26+ "noUnusedParameters": false,
27+ "noPropertyAccessFromIndexSignature": false
28+ }
29+}