mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-16 07:36:55 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35b9c82bdd | |||
| 0fcfc308fd | |||
| 931016143c |
@@ -0,0 +1,25 @@
|
|||||||
|
krill-parser.js
|
||||||
|
krill.pegjs
|
||||||
|
.eslintrc.json
|
||||||
|
server.js
|
||||||
|
tidal-sniffer.js
|
||||||
|
*.jsx
|
||||||
|
tunejs.js
|
||||||
|
out/**
|
||||||
|
postcss.config.js
|
||||||
|
postcss.config.cjs
|
||||||
|
tailwind.config.js
|
||||||
|
tailwind.config.cjs
|
||||||
|
vite.config.js
|
||||||
|
/**/dist/**/*
|
||||||
|
!**/*.mjs
|
||||||
|
**/*.tsx
|
||||||
|
**/*.ts
|
||||||
|
**/*.json
|
||||||
|
**/dev-dist
|
||||||
|
**/dist
|
||||||
|
/src-tauri/target/**/*
|
||||||
|
reverbGen.mjs
|
||||||
|
hydra.mjs
|
||||||
|
jsdoc-synonyms.js
|
||||||
|
packages/node/pattern.mjs
|
||||||
+1
-3
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"node": true,
|
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es2021": true
|
"es2021": true
|
||||||
},
|
},
|
||||||
@@ -14,6 +13,5 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }],
|
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }],
|
||||||
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
|
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
|
||||||
},
|
}
|
||||||
"files": ["**/*.mjs", "**/*.js"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ jobs:
|
|||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 9.12.2
|
version: 8.11.0
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@@ -39,11 +39,11 @@ jobs:
|
|||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v2
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v1
|
||||||
with:
|
with:
|
||||||
# Upload entire repository
|
# Upload entire repository
|
||||||
path: "./website/dist"
|
path: "./website/dist"
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v1
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 9.12.2
|
version: 8.11.0
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
+1
-2
@@ -11,5 +11,4 @@ pnpm-lock.yaml
|
|||||||
pnpm-workspace.yaml
|
pnpm-workspace.yaml
|
||||||
**/dev-dist
|
**/dev-dist
|
||||||
website/.astro
|
website/.astro
|
||||||
!tidal-drum-machines.json
|
packages/node/pattern.mjs
|
||||||
!tidal-drum-machines-alias.json
|
|
||||||
+6
-12
@@ -130,26 +130,20 @@ npx lerna version --no-private
|
|||||||
# publish all packages inside /packages using pnpm! don't use lerna to publish!!
|
# publish all packages inside /packages using pnpm! don't use lerna to publish!!
|
||||||
pnpm --filter "./packages/**" publish --dry-run
|
pnpm --filter "./packages/**" publish --dry-run
|
||||||
|
|
||||||
# the last command was only a dry-run. if everything looks ok, run this:
|
# the last command was only a dry-run, make sure everything looks ok, if yes, run the same command without flag
|
||||||
|
|
||||||
pnpm --filter "./packages/**" publish --access public
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`.
|
To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`.
|
||||||
Important: Always publish with `pnpm`, as `npm` does not support overriding main files in `publishConfig`, which is done in all the packages.
|
Important: Always publish with `pnpm`, as `npm` does not support overriding main files in `publishConfig`, which is done in all the packages.
|
||||||
|
|
||||||
|
### New Packages
|
||||||
|
|
||||||
|
To add a new package, you have to publish it manually the first time, using:
|
||||||
|
|
||||||
## useful commands
|
|
||||||
```sh
|
```sh
|
||||||
#regenerate the test snapshots (ex: when updating or creating new pattern functions)
|
cd packages/<package-name> && pnpm publish --access public
|
||||||
pnpm snapshot
|
|
||||||
|
|
||||||
#start the OSC server
|
|
||||||
pnpm run osc
|
|
||||||
|
|
||||||
#build the standalone version
|
|
||||||
pnpm tauri build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Have Fun
|
## Have Fun
|
||||||
|
|
||||||
Remember to have fun, and that this project is driven by the passion of volunteers!
|
Remember to have fun, and that this project is driven by the passion of volunteers!
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# strudel
|
# strudel
|
||||||
|
|
||||||
[](https://github.com/tidalcycles/strudel/actions) [](https://doi.org/10.5281/zenodo.6659278)
|
[](https://github.com/tidalcycles/strudel/actions)
|
||||||
|
|
||||||
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is a bit more stable now, but please continue to tread carefully.
|
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is a bit more stable now, but please continue to tread carefully.
|
||||||
|
|
||||||
@@ -14,16 +14,10 @@ An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using w
|
|||||||
|
|
||||||
After cloning the project, you can run the REPL locally:
|
After cloning the project, you can run the REPL locally:
|
||||||
|
|
||||||
1. Install [Node.js](https://nodejs.org/)
|
```bash
|
||||||
2. Install [pnpm](https://pnpm.io/installation)
|
pnpm i
|
||||||
3. Install dependencies by running the following command:
|
pnpm dev
|
||||||
```bash
|
```
|
||||||
pnpm i
|
|
||||||
```
|
|
||||||
4. Run the development server:
|
|
||||||
```bash
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Strudel In Your Project
|
## Using Strudel In Your Project
|
||||||
|
|
||||||
@@ -31,10 +25,6 @@ This project is organized into many [packages](./packages), which are also avail
|
|||||||
|
|
||||||
Read more about how to use these in your own project [here](https://strudel.cc/technical-manual/project-start).
|
Read more about how to use these in your own project [here](https://strudel.cc/technical-manual/project-start).
|
||||||
|
|
||||||
You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE.md). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details.
|
|
||||||
|
|
||||||
Licensing info for the default sound banks can be found over on the [dough-samples](https://github.com/felixroos/dough-samples/blob/main/README.md) repository.
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md).
|
There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md).
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import { queryCode, testCycles } from '../test/runtime.mjs';
|
|
||||||
import * as tunes from '../website/src/repl/tunes.mjs';
|
|
||||||
import { describe, bench } from 'vitest';
|
|
||||||
import { calculateSteps } from '../packages/core/index.mjs';
|
|
||||||
|
|
||||||
const tuneKeys = Object.keys(tunes);
|
|
||||||
|
|
||||||
describe('renders tunes', () => {
|
|
||||||
tuneKeys.forEach((key) => {
|
|
||||||
describe(key, () => {
|
|
||||||
calculateSteps(true);
|
|
||||||
bench(`+steps`, async () => {
|
|
||||||
await queryCode(tunes[key], testCycles[key] || 1);
|
|
||||||
});
|
|
||||||
calculateSteps(false);
|
|
||||||
bench(`-steps`, async () => {
|
|
||||||
await queryCode(tunes[key], testCycles[key] || 1);
|
|
||||||
});
|
|
||||||
calculateSteps(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import _import from 'eslint-plugin-import';
|
|
||||||
import { fixupPluginRules } from '@eslint/compat';
|
|
||||||
import globals from 'globals';
|
|
||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
import js from '@eslint/js';
|
|
||||||
import { FlatCompat } from '@eslint/eslintrc';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
const compat = new FlatCompat({
|
|
||||||
baseDirectory: __dirname,
|
|
||||||
recommendedConfig: js.configs.recommended,
|
|
||||||
allConfig: js.configs.all,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
ignores: [
|
|
||||||
'**/krill-parser.js',
|
|
||||||
'**/krill.pegjs',
|
|
||||||
'**/.eslintrc.json',
|
|
||||||
'**/server.js',
|
|
||||||
'**/tidal-sniffer.js',
|
|
||||||
'**/*.jsx',
|
|
||||||
'**/tunejs.js',
|
|
||||||
'out/**/*',
|
|
||||||
'**/postcss.config.js',
|
|
||||||
'**/postcss.config.cjs',
|
|
||||||
'**/tailwind.config.js',
|
|
||||||
'**/tailwind.config.cjs',
|
|
||||||
'**/vite.config.js',
|
|
||||||
'**/dist/**/*',
|
|
||||||
'!**/*.mjs',
|
|
||||||
'**/*.tsx',
|
|
||||||
'**/*.ts',
|
|
||||||
'**/*.json',
|
|
||||||
'**/dev-dist',
|
|
||||||
'**/dist',
|
|
||||||
'src-tauri/target/**/*',
|
|
||||||
'**/reverbGen.mjs',
|
|
||||||
'**/hydra.mjs',
|
|
||||||
'**/jsdoc-synonyms.js',
|
|
||||||
'packages/hs2js/src/hs2js.mjs',
|
|
||||||
'**/samples',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
...compat.extends('eslint:recommended').map((config) => ({
|
|
||||||
...config,
|
|
||||||
files: ['**/*.mjs', '**/*.js'],
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
files: ['**/*.mjs', '**/*.js'],
|
|
||||||
|
|
||||||
plugins: {
|
|
||||||
import: fixupPluginRules(_import),
|
|
||||||
},
|
|
||||||
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.node,
|
|
||||||
...globals.browser,
|
|
||||||
},
|
|
||||||
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'no-unused-vars': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
destructuredArrayIgnorePattern: '.',
|
|
||||||
ignoreRestSiblings: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'import/no-extraneous-dependencies': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
devDependencies: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -1,31 +1,9 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
||||||
<head>
|
<button id="play">play</button>
|
||||||
<meta charset="UTF-8" />
|
<button id="stop">stop</button>
|
||||||
<!-- <script src="../../packages/web/dist/index.js"></script> -->
|
<script>
|
||||||
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
strudel.initStrudel();
|
||||||
</head>
|
document.getElementById('play').addEventListener('click', () => evaluate('note("c a f e").jux(rev)'));
|
||||||
<body style="background: #222">
|
document.getElementById('play').addEventListener('stop', () => hush());
|
||||||
<button id="play">play</button>
|
</script>
|
||||||
<button id="stop">stop</button>
|
|
||||||
<script>
|
|
||||||
strudel.initStrudel();
|
|
||||||
document.getElementById('play').addEventListener('click', () =>
|
|
||||||
evaluate(`
|
|
||||||
//@title washover @by Switch Angel
|
|
||||||
//@social https://www.instagram.com/_switch_angel/
|
|
||||||
|
|
||||||
n("{0 1 3 5 2 }%16")
|
|
||||||
.add(n(tri.range(0, 6).slow(3)))
|
|
||||||
.scale("C4:pentatonic")
|
|
||||||
.sound("sawtooth").att(saw.range(0, 0.05).fast(6))
|
|
||||||
.release(3).pan(rand).decay(rand.range(0.1, 0.3))
|
|
||||||
.lpf(tri.range(200, 8000).slow(5)).lpq(0)
|
|
||||||
.gain(.4).vib(1).vibmod(.1)
|
|
||||||
.scope({pos:.5})
|
|
||||||
`),
|
|
||||||
);
|
|
||||||
document.getElementById('stop').addEventListener('click', () => strudel.hush());
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
||||||
<head>
|
<button id="a">A</button>
|
||||||
<meta charset="UTF-8" />
|
<button id="b">B</button>
|
||||||
<!-- <script src="../../packages/web/dist/index.js"></script> -->
|
<button id="c">C</button>
|
||||||
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
<button id="stop">stop</button>
|
||||||
</head>
|
<script>
|
||||||
<body style="background: #222">
|
initStrudel({
|
||||||
<button id="a">A</button>
|
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
||||||
<button id="b">B</button>
|
});
|
||||||
<button id="c">C</button>
|
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
|
||||||
<button id="stop">stop</button>
|
click('a', () => evaluate(`s('bd,jvbass(3,8)').jux(rev)`));
|
||||||
<script>
|
click('b', () => s('bd*2,hh(3,4),jvbass(5,8,1)').jux(rev).play());
|
||||||
initStrudel({
|
click('c', () => s('bd*2,hh(3,4),jvbass:[0 4](5,8,1)').jux(rev).stack(s('~ sd')).play());
|
||||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
click('stop', () => hush());
|
||||||
});
|
</script>
|
||||||
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
|
|
||||||
click('a', () => evaluate(`s('bd,jvbass(3,8)').jux(rev)`));
|
|
||||||
click('b', () => s('bd*2,hh(3,4),jvbass(5,8,1)').jux(rev).play());
|
|
||||||
click('c', () => s('bd*2,hh(3,4),jvbass:[0 4](5,8,1)').jux(rev).stack(s('~ sd')).play());
|
|
||||||
click('stop', () => hush());
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<script src="https://unpkg.com/hs2js@0.0.4"></script>
|
|
||||||
<button id="hello">hello</button>
|
|
||||||
<script>
|
|
||||||
hs2js.setBase('https://unpkg.com/hs2js@0.0.4/dist/');
|
|
||||||
hs2js.loadParser();
|
|
||||||
document.getElementById('hello').addEventListener('click', () => {
|
|
||||||
hs2js.evaluate('alert "hello from haskell!"');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>tidal.html</title>
|
|
||||||
</head>
|
|
||||||
<body style="margin: 0; padding: 0">
|
|
||||||
<textarea id="code" style="width: 100%; height: 200px"></textarea>
|
|
||||||
<pre id="result"></pre>
|
|
||||||
<script src="https://unpkg.com/hs2js@0.0.8"></script>
|
|
||||||
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
|
||||||
<script>
|
|
||||||
const { initStrudel } = strudel;
|
|
||||||
const { run, parse, setBase, loadParser } = hs2js;
|
|
||||||
setBase('https://unpkg.com/hs2js@0.0.4/dist/');
|
|
||||||
const ready = loadParser();
|
|
||||||
initStrudel({
|
|
||||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const textarea = document.getElementById('code');
|
|
||||||
if (window.location.hash) {
|
|
||||||
textarea.value = atob(window.location.hash.slice(1));
|
|
||||||
} else {
|
|
||||||
textarea.value = 'd1 $ s "jvbass(3,8)"';
|
|
||||||
}
|
|
||||||
textarea.addEventListener('input', (e) => {
|
|
||||||
window.location.hash = btoa(e.target.value);
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
update();
|
|
||||||
|
|
||||||
function getInfixOperators() {
|
|
||||||
let operators = {
|
|
||||||
'>': 'set',
|
|
||||||
'#': 'set',
|
|
||||||
'+': 'add',
|
|
||||||
'-': 'sub',
|
|
||||||
'*': 'mul',
|
|
||||||
'/': 'div',
|
|
||||||
};
|
|
||||||
let alignments = {
|
|
||||||
in: (s) => '|' + s,
|
|
||||||
out: (s) => s + '|',
|
|
||||||
mix: (s) => '|' + s + '|',
|
|
||||||
};
|
|
||||||
let ops = {};
|
|
||||||
Object.entries(operators).forEach(([o, name]) => {
|
|
||||||
// operator without alignment
|
|
||||||
ops[o] = (l, r) => reify(l)[name](reify(r));
|
|
||||||
Object.entries(alignments).forEach(([a, getSymbol]) => {
|
|
||||||
// get symbol with alignment
|
|
||||||
let symbol = getSymbol(o);
|
|
||||||
ops[symbol] = (l, r) => reify(l)[name][a](reify(r));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ops['~>'] = (l, r) => reify(l).late(reify(r));
|
|
||||||
ops['<~'] = (l, r) => reify(l).early(reify(r));
|
|
||||||
ops['<$>'] = (l, r) => reify(r).fmap(l).outerJoin(); // is this right?
|
|
||||||
return ops;
|
|
||||||
}
|
|
||||||
const ops = getInfixOperators();
|
|
||||||
|
|
||||||
async function update() {
|
|
||||||
let result, tree;
|
|
||||||
await ready;
|
|
||||||
try {
|
|
||||||
tree = parse(textarea.value);
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('parse error');
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
console.log('parsed tree');
|
|
||||||
console.log(tree.rootNode.toString());
|
|
||||||
try {
|
|
||||||
let patterns = {};
|
|
||||||
window.p = (name, pattern) => {
|
|
||||||
patterns[name] = pattern;
|
|
||||||
};
|
|
||||||
window.d1 = (pat) => window.p(1, pat);
|
|
||||||
window.d2 = (pat) => window.p(2, pat);
|
|
||||||
window.d3 = (pat) => window.p(3, pat);
|
|
||||||
window.rot = late;
|
|
||||||
result = run(tree.rootNode, window, ops);
|
|
||||||
if (Object.values(patterns).length) {
|
|
||||||
stack(...Object.values(patterns)).play();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('eval error');
|
|
||||||
console.error(err);
|
|
||||||
result = 'ERROR: ' + err.message;
|
|
||||||
}
|
|
||||||
document.getElementById('result').innerHTML = 'Result: ' + result;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { StrudelMirror } from '@strudel/codemirror';
|
import { StrudelMirror } from '@strudel/codemirror';
|
||||||
import { funk42 } from './tunes';
|
import { funk42 } from './tunes';
|
||||||
import { evalScope } from '@strudel/core';
|
import { drawPianoroll, evalScope } from '@strudel/core';
|
||||||
import { drawPianoroll } from '@strudel/draw';
|
|
||||||
import './style.css';
|
import './style.css';
|
||||||
import { initAudioOnFirstClick } from '@strudel/webaudio';
|
import { initAudioOnFirstClick } from '@strudel/webaudio';
|
||||||
import { transpiler } from '@strudel/transpiler';
|
import { transpiler } from '@strudel/transpiler';
|
||||||
@@ -27,7 +26,6 @@ const editor = new StrudelMirror({
|
|||||||
initAudioOnFirstClick(); // needed to make the browser happy (don't await this here..)
|
initAudioOnFirstClick(); // needed to make the browser happy (don't await this here..)
|
||||||
const loadModules = evalScope(
|
const loadModules = evalScope(
|
||||||
import('@strudel/core'),
|
import('@strudel/core'),
|
||||||
import('@strudel/draw'),
|
|
||||||
import('@strudel/mini'),
|
import('@strudel/mini'),
|
||||||
import('@strudel/tonal'),
|
import('@strudel/tonal'),
|
||||||
import('@strudel/webaudio'),
|
import('@strudel/webaudio'),
|
||||||
|
|||||||
@@ -9,12 +9,11 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/codemirror": "workspace:*",
|
"@strudel/codemirror": "workspace:*",
|
||||||
"@strudel/core": "workspace:*",
|
"@strudel/core": "workspace:*",
|
||||||
"@strudel/draw": "workspace:*",
|
|
||||||
"@strudel/mini": "workspace:*",
|
"@strudel/mini": "workspace:*",
|
||||||
"@strudel/soundfonts": "workspace:*",
|
"@strudel/soundfonts": "workspace:*",
|
||||||
"@strudel/tonal": "workspace:*",
|
"@strudel/tonal": "workspace:*",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/web": "workspace:*"
|
"@strudel/web": "workspace:*"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/core": "workspace:*",
|
"@strudel/core": "workspace:*",
|
||||||
|
|||||||
@@ -12,6 +12,6 @@
|
|||||||
"superdough": "workspace:*"
|
"superdough": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
public/tree-sitter.wasm
|
|
||||||
public/tree-sitter-haskell.wasm
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# @strudel/tidal
|
|
||||||
|
|
||||||
This is an experiment in implementing tree-sitter for parsing haskell.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pnpm i
|
|
||||||
cd haskell
|
|
||||||
pnpm copy-wasm
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Tree sitter test</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #121213;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
textarea {
|
|
||||||
padding: 10px;
|
|
||||||
color: white;
|
|
||||||
background-color: transparent;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body style="margin: 0; padding: 0">
|
|
||||||
<textarea id="code" style="width: 100%; height: 200px"></textarea>
|
|
||||||
<pre id="result"></pre>
|
|
||||||
<script
|
|
||||||
type="module"
|
|
||||||
src="/main.js
|
|
||||||
"
|
|
||||||
></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import { run, parse, loadParser } from 'hs2js';
|
|
||||||
import { initStrudel, reify, late, samples, stack } from '@strudel/web';
|
|
||||||
|
|
||||||
initStrudel({
|
|
||||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const ready = loadParser();
|
|
||||||
|
|
||||||
const textarea = document.getElementById('code');
|
|
||||||
if (window.location.hash) {
|
|
||||||
textarea.value = atob(window.location.hash.slice(1));
|
|
||||||
} else {
|
|
||||||
textarea.value = 'd1 $ s "jvbass(3,8)"';
|
|
||||||
}
|
|
||||||
textarea.addEventListener('input', (e) => {
|
|
||||||
window.location.hash = btoa(e.target.value);
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
update();
|
|
||||||
|
|
||||||
function getInfixOperators() {
|
|
||||||
let operators = {
|
|
||||||
'>': 'set',
|
|
||||||
'#': 'set',
|
|
||||||
'+': 'add',
|
|
||||||
'-': 'sub',
|
|
||||||
'*': 'mul',
|
|
||||||
'/': 'div',
|
|
||||||
};
|
|
||||||
let alignments = {
|
|
||||||
in: (s) => '|' + s,
|
|
||||||
out: (s) => s + '|',
|
|
||||||
mix: (s) => '|' + s + '|',
|
|
||||||
};
|
|
||||||
let ops = {};
|
|
||||||
Object.entries(operators).forEach(([o, name]) => {
|
|
||||||
// operator without alignment
|
|
||||||
ops[o] = (l, r) => reify(l)[name](reify(r));
|
|
||||||
Object.entries(alignments).forEach(([a, getSymbol]) => {
|
|
||||||
// get symbol with alignment
|
|
||||||
let symbol = getSymbol(o);
|
|
||||||
ops[symbol] = (l, r) => reify(l)[name][a](reify(r));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ops['~>'] = (l, r) => reify(l).late(reify(r));
|
|
||||||
ops['<~'] = (l, r) => reify(l).early(reify(r));
|
|
||||||
ops['<$>'] = (l, r) => reify(r).fmap(l).outerJoin(); // is this right?
|
|
||||||
return ops;
|
|
||||||
}
|
|
||||||
const ops = getInfixOperators();
|
|
||||||
|
|
||||||
async function update() {
|
|
||||||
let result, tree;
|
|
||||||
await ready;
|
|
||||||
try {
|
|
||||||
tree = parse(textarea.value);
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('parse error');
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
console.log('parsed tree');
|
|
||||||
console.log(tree.rootNode.toString());
|
|
||||||
try {
|
|
||||||
let patterns = {};
|
|
||||||
window.p = (name, pattern) => {
|
|
||||||
patterns[name] = pattern;
|
|
||||||
};
|
|
||||||
window.d1 = (pat) => window.p(1, pat);
|
|
||||||
window.d2 = (pat) => window.p(2, pat);
|
|
||||||
window.d3 = (pat) => window.p(3, pat);
|
|
||||||
window.rot = late;
|
|
||||||
result = run(tree.rootNode, window, ops);
|
|
||||||
if (Object.values(patterns).length) {
|
|
||||||
stack(...Object.values(patterns)).play();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('eval error');
|
|
||||||
console.error(err);
|
|
||||||
result = 'ERROR: ' + err.message;
|
|
||||||
}
|
|
||||||
document.getElementById('result').innerHTML = 'Result: ' + result;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "tidal-repl",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.1",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "pnpm copy-wasm && vite build",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"copy-wasm": "cp node_modules/hs2js/dist/tree-sitter.wasm public && cp node_modules/hs2js/dist/tree-sitter-haskell.wasm public"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"titdalcycles",
|
|
||||||
"strudel",
|
|
||||||
"pattern",
|
|
||||||
"livecoding",
|
|
||||||
"algorave"
|
|
||||||
],
|
|
||||||
"author": "Felix Roos <flix91@gmail.com>",
|
|
||||||
"license": "AGPL-3.0-or-later",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
|
||||||
"dependencies": {
|
|
||||||
"@strudel/web": "workspace:*",
|
|
||||||
"hs2js": "workspace:*"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"vite": "^6.0.11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# public
|
|
||||||
|
|
||||||
this file is just here to make sure the public folder exists
|
|
||||||
+13
-17
@@ -11,7 +11,6 @@
|
|||||||
"test": "npm run pretest && vitest run --version",
|
"test": "npm run pretest && vitest run --version",
|
||||||
"test-ui": "npm run pretest && vitest --ui",
|
"test-ui": "npm run pretest && vitest --ui",
|
||||||
"test-coverage": "npm run pretest && vitest --coverage",
|
"test-coverage": "npm run pretest && vitest --coverage",
|
||||||
"bench": "npm run pretest && vitest bench",
|
|
||||||
"snapshot": "npm run pretest && vitest run -u --silent",
|
"snapshot": "npm run pretest && vitest run -u --silent",
|
||||||
"repl": "npm run prestart && cd website && npm run dev",
|
"repl": "npm run prestart && cd website && npm run dev",
|
||||||
"start": "npm run prestart && cd website && npm run dev",
|
"start": "npm run prestart && cd website && npm run dev",
|
||||||
@@ -21,7 +20,7 @@
|
|||||||
"osc": "cd packages/osc && npm run server",
|
"osc": "cd packages/osc && npm run server",
|
||||||
"jsdoc": "jsdoc packages/ -c jsdoc/jsdoc.config.json",
|
"jsdoc": "jsdoc packages/ -c jsdoc/jsdoc.config.json",
|
||||||
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc/jsdoc.config.json",
|
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc/jsdoc.config.json",
|
||||||
"lint": "eslint . --quiet",
|
"lint": "eslint . --ext mjs,js --quiet",
|
||||||
"codeformat": "prettier --write .",
|
"codeformat": "prettier --write .",
|
||||||
"format-check": "prettier --check .",
|
"format-check": "prettier --check .",
|
||||||
"report-undocumented": "npm run jsdoc-json && node jsdoc/undocumented.mjs > undocumented.json",
|
"report-undocumented": "npm run jsdoc-json && node jsdoc/undocumented.mjs > undocumented.json",
|
||||||
@@ -55,22 +54,19 @@
|
|||||||
"@strudel/xen": "workspace:*"
|
"@strudel/xen": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.2.5",
|
"@tauri-apps/cli": "^1.5.9",
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@vitest/ui": "^1.1.0",
|
||||||
"@eslint/js": "^9.19.0",
|
"acorn": "^8.11.3",
|
||||||
"@tauri-apps/cli": "^2.2.7",
|
"dependency-tree": "^10.0.9",
|
||||||
"@vitest/coverage-v8": "3.0.4",
|
"eslint": "^8.56.0",
|
||||||
"@vitest/ui": "^3.0.4",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"acorn": "^8.14.0",
|
|
||||||
"dependency-tree": "^11.0.1",
|
|
||||||
"eslint": "^9.19.0",
|
|
||||||
"eslint-plugin-import": "^2.31.0",
|
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"globals": "^15.14.0",
|
"jsdoc": "^4.0.2",
|
||||||
"jsdoc": "^4.0.4",
|
|
||||||
"jsdoc-json": "^2.0.2",
|
"jsdoc-json": "^2.0.2",
|
||||||
"lerna": "^8.1.9",
|
"jsdoc-to-markdown": "^8.0.0",
|
||||||
"prettier": "^3.4.2",
|
"lerna": "^8.0.1",
|
||||||
"vitest": "^3.0.4"
|
"prettier": "^3.1.1",
|
||||||
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
|
"vitest": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { closeBrackets } from '@codemirror/autocomplete';
|
import { closeBrackets } from '@codemirror/autocomplete';
|
||||||
export { toggleComment, toggleBlockComment, toggleLineComment, toggleBlockCommentByLine } from '@codemirror/commands';
|
|
||||||
// import { search, highlightSelectionMatches } from '@codemirror/search';
|
// import { search, highlightSelectionMatches } from '@codemirror/search';
|
||||||
import { history } from '@codemirror/commands';
|
import { history } from '@codemirror/commands';
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
@@ -13,7 +12,7 @@ import {
|
|||||||
lineNumbers,
|
lineNumbers,
|
||||||
drawSelection,
|
drawSelection,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
import { repl, registerControl } from '@strudel/core';
|
import { Pattern, repl } from '@strudel/core';
|
||||||
import { Drawer, cleanupDraw } from '@strudel/draw';
|
import { Drawer, cleanupDraw } from '@strudel/draw';
|
||||||
import { isAutoCompletionEnabled } from './autocomplete.mjs';
|
import { isAutoCompletionEnabled } from './autocomplete.mjs';
|
||||||
import { isTooltipEnabled } from './tooltip.mjs';
|
import { isTooltipEnabled } from './tooltip.mjs';
|
||||||
@@ -67,7 +66,6 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
|
|||||||
const initialSettings = Object.keys(compartments).map((key) =>
|
const initialSettings = Object.keys(compartments).map((key) =>
|
||||||
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
|
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
|
||||||
);
|
);
|
||||||
|
|
||||||
initTheme(settings.theme);
|
initTheme(settings.theme);
|
||||||
let state = EditorState.create({
|
let state = EditorState.create({
|
||||||
doc: initialCode,
|
doc: initialCode,
|
||||||
@@ -100,8 +98,10 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Alt-.',
|
key: 'Alt-.',
|
||||||
preventDefault: true,
|
run: (_, e) => {
|
||||||
run: () => onStop?.(),
|
e.preventDefault();
|
||||||
|
onStop?.();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
/* {
|
/* {
|
||||||
key: 'Ctrl-Shift-.',
|
key: 'Ctrl-Shift-.',
|
||||||
@@ -134,50 +134,57 @@ export class StrudelMirror {
|
|||||||
autodraw,
|
autodraw,
|
||||||
prebake,
|
prebake,
|
||||||
bgFill = true,
|
bgFill = true,
|
||||||
solo = true,
|
|
||||||
...replOptions
|
...replOptions
|
||||||
} = options;
|
} = options;
|
||||||
this.code = initialCode;
|
this.code = initialCode;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.miniLocations = [];
|
this.miniLocations = [];
|
||||||
this.widgets = [];
|
this.widgets = [];
|
||||||
|
this.painters = [];
|
||||||
this.drawTime = drawTime;
|
this.drawTime = drawTime;
|
||||||
this.drawContext = drawContext;
|
this.drawContext = drawContext;
|
||||||
this.onDraw = onDraw || this.draw;
|
this.onDraw = onDraw || this.draw;
|
||||||
this.id = id || s4();
|
this.id = id || s4();
|
||||||
this.solo = solo;
|
|
||||||
|
|
||||||
this.drawer = new Drawer((haps, time, _, painters) => {
|
this.drawer = new Drawer((haps, time) => {
|
||||||
const currentFrame = haps.filter((hap) => hap.isActive(time));
|
const currentFrame = haps.filter((hap) => hap.isActive(time));
|
||||||
this.highlight(currentFrame, time);
|
this.highlight(currentFrame, time);
|
||||||
this.onDraw(haps, time, painters);
|
this.onDraw(haps, time, this.painters);
|
||||||
}, drawTime);
|
}, drawTime);
|
||||||
|
|
||||||
this.prebaked = prebake();
|
this.prebaked = prebake();
|
||||||
autodraw && this.drawFirstFrame();
|
autodraw && this.drawFirstFrame();
|
||||||
|
|
||||||
this.repl = repl({
|
this.repl = repl({
|
||||||
...replOptions,
|
...replOptions,
|
||||||
id,
|
|
||||||
onToggle: (started) => {
|
onToggle: (started) => {
|
||||||
replOptions?.onToggle?.(started);
|
replOptions?.onToggle?.(started);
|
||||||
if (started) {
|
if (started) {
|
||||||
|
this.adjustDrawTime();
|
||||||
this.drawer.start(this.repl.scheduler);
|
this.drawer.start(this.repl.scheduler);
|
||||||
if (this.solo) {
|
// stop other repls when this one is started
|
||||||
// stop other repls when this one is started
|
document.dispatchEvent(
|
||||||
document.dispatchEvent(
|
new CustomEvent('start-repl', {
|
||||||
new CustomEvent('start-repl', {
|
detail: this.id,
|
||||||
detail: this.id,
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.drawer.stop();
|
this.drawer.stop();
|
||||||
updateMiniLocations(this.editor, []);
|
updateMiniLocations(this.editor, []);
|
||||||
cleanupDraw(true, id);
|
cleanupDraw(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEval: async () => {
|
beforeEval: async () => {
|
||||||
cleanupDraw(true, id);
|
cleanupDraw();
|
||||||
|
this.painters = [];
|
||||||
|
const self = this;
|
||||||
|
// this is similar to repl.mjs > injectPatternMethods
|
||||||
|
// maybe there is a solution without prototype hacking, but hey, it works
|
||||||
|
// we need to do this befor every eval to make sure it works with multiple StrudelMirror's side by side
|
||||||
|
Pattern.prototype.onPaint = function (onPaint) {
|
||||||
|
self.painters.push(onPaint);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
await this.prebaked;
|
await this.prebaked;
|
||||||
await replOptions?.beforeEval?.();
|
await replOptions?.beforeEval?.();
|
||||||
},
|
},
|
||||||
@@ -191,11 +198,8 @@ export class StrudelMirror {
|
|||||||
updateWidgets(this.editor, widgets);
|
updateWidgets(this.editor, widgets);
|
||||||
updateMiniLocations(this.editor, this.miniLocations);
|
updateMiniLocations(this.editor, this.miniLocations);
|
||||||
replOptions?.afterEval?.(options);
|
replOptions?.afterEval?.(options);
|
||||||
// if no painters are set (.onPaint was not called), then we only need the present moment (for highlighting)
|
this.adjustDrawTime();
|
||||||
const drawTime = options.pattern.getPainters().length ? this.drawTime : [0, 0];
|
this.drawer.invalidate();
|
||||||
this.drawer.setDrawTime(drawTime);
|
|
||||||
// invalidate drawer after we've set the appropriate drawTime
|
|
||||||
this.drawer.invalidate(this.repl.scheduler);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.editor = initEditor({
|
this.editor = initEditor({
|
||||||
@@ -224,14 +228,19 @@ export class StrudelMirror {
|
|||||||
|
|
||||||
// stop this repl when another repl is started
|
// stop this repl when another repl is started
|
||||||
this.onStartRepl = (e) => {
|
this.onStartRepl = (e) => {
|
||||||
if (this.solo && e.detail !== this.id) {
|
if (e.detail !== this.id) {
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener('start-repl', this.onStartRepl);
|
document.addEventListener('start-repl', this.onStartRepl);
|
||||||
}
|
}
|
||||||
draw(haps, time, painters) {
|
// adjusts draw time depending on if there are painters
|
||||||
painters?.forEach((painter) => painter(this.drawContext, time, haps, this.drawTime));
|
adjustDrawTime() {
|
||||||
|
// when no painters are set, [0,0] is enough (just highlighting)
|
||||||
|
this.drawer.setDrawTime(this.painters.length ? this.drawTime : [0, 0]);
|
||||||
|
}
|
||||||
|
draw(haps, time) {
|
||||||
|
this.painters?.forEach((painter) => painter(this.drawContext, time, haps, this.drawTime));
|
||||||
}
|
}
|
||||||
async drawFirstFrame() {
|
async drawFirstFrame() {
|
||||||
if (!this.onDraw) {
|
if (!this.onDraw) {
|
||||||
@@ -243,7 +252,7 @@ export class StrudelMirror {
|
|||||||
await this.repl.evaluate(this.code, false);
|
await this.repl.evaluate(this.code, false);
|
||||||
this.drawer.invalidate(this.repl.scheduler, -0.001);
|
this.drawer.invalidate(this.repl.scheduler, -0.001);
|
||||||
// draw at -0.001 to avoid haps at 0 to be visualized as active
|
// draw at -0.001 to avoid haps at 0 to be visualized as active
|
||||||
this.onDraw?.(this.drawer.visibleHaps, -0.001, this.drawer.painters);
|
this.onDraw?.(this.drawer.visibleHaps, -0.001, this.painters);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('first frame could not be painted');
|
console.warn('first frame could not be painted');
|
||||||
}
|
}
|
||||||
@@ -336,17 +345,6 @@ export class StrudelMirror {
|
|||||||
clear() {
|
clear() {
|
||||||
this.onStartRepl && document.removeEventListener('start-repl', this.onStartRepl);
|
this.onStartRepl && document.removeEventListener('start-repl', this.onStartRepl);
|
||||||
}
|
}
|
||||||
getCursorLocation() {
|
|
||||||
return this.editor.state.selection.main.head;
|
|
||||||
}
|
|
||||||
setCursorLocation(col) {
|
|
||||||
return this.editor.dispatch({ selection: { anchor: col } });
|
|
||||||
}
|
|
||||||
appendCode(code) {
|
|
||||||
const cursor = this.getCursorLocation();
|
|
||||||
this.setCode(this.code + code);
|
|
||||||
this.setCursorLocation(cursor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseBooleans(value) {
|
function parseBooleans(value) {
|
||||||
@@ -359,12 +357,3 @@ function s4() {
|
|||||||
.toString(16)
|
.toString(16)
|
||||||
.substring(1);
|
.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the css of highlighted events. Make sure to use single quotes!
|
|
||||||
* @name markcss
|
|
||||||
* @example
|
|
||||||
* note("c a f e")
|
|
||||||
* .markcss('text-decoration:underline')
|
|
||||||
*/
|
|
||||||
export const markcss = registerControl('markcss');
|
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ export const flashField = StateField.define({
|
|||||||
for (let e of tr.effects) {
|
for (let e of tr.effects) {
|
||||||
if (e.is(setFlash)) {
|
if (e.is(setFlash)) {
|
||||||
if (e.value && tr.newDoc.length > 0) {
|
if (e.value && tr.newDoc.length > 0) {
|
||||||
const mark = Decoration.mark({
|
const mark = Decoration.mark({ attributes: { style: `background-color: #FFCA2880` } });
|
||||||
attributes: { style: `background-color: rgba(255,255,255, .4); filter: invert(10%)` },
|
|
||||||
});
|
|
||||||
flash = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
flash = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
||||||
} else {
|
} else {
|
||||||
flash = Decoration.set([]);
|
flash = Decoration.set([]);
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ const miniLocationHighlights = EditorView.decorations.compute([miniLocations, vi
|
|||||||
if (haps.has(id)) {
|
if (haps.has(id)) {
|
||||||
const hap = haps.get(id);
|
const hap = haps.get(id);
|
||||||
const color = hap.value?.color ?? 'var(--foreground)';
|
const color = hap.value?.color ?? 'var(--foreground)';
|
||||||
const style = hap.value?.markcss || `outline: solid 2px ${color}`;
|
|
||||||
// Get explicit channels for color values
|
// Get explicit channels for color values
|
||||||
/*
|
/*
|
||||||
const swatch = document.createElement('div');
|
const swatch = document.createElement('div');
|
||||||
@@ -115,7 +114,7 @@ const miniLocationHighlights = EditorView.decorations.compute([miniLocations, vi
|
|||||||
to,
|
to,
|
||||||
Decoration.mark({
|
Decoration.mark({
|
||||||
// attributes: { style: `outline: solid 2px rgba(${channels.join(', ')})` },
|
// attributes: { style: `outline: solid 2px rgba(${channels.join(', ')})` },
|
||||||
attributes: { style },
|
attributes: { style: `outline: solid 2px ${color}` },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/codemirror",
|
"name": "@strudel/codemirror",
|
||||||
"version": "1.2.0",
|
"version": "1.0.1",
|
||||||
"description": "Codemirror Extensions for Strudel",
|
"description": "Codemirror Extensions for Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -32,24 +32,26 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.18.4",
|
"@codemirror/autocomplete": "^6.11.1",
|
||||||
"@codemirror/commands": "^6.8.0",
|
"@codemirror/commands": "^6.3.3",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.1",
|
||||||
"@codemirror/language": "^6.10.8",
|
"@codemirror/language": "^6.10.0",
|
||||||
"@codemirror/search": "^6.5.8",
|
"@codemirror/search": "^6.5.5",
|
||||||
"@codemirror/state": "^6.5.1",
|
"@codemirror/state": "^6.4.0",
|
||||||
"@codemirror/view": "^6.36.2",
|
"@codemirror/view": "^6.23.0",
|
||||||
"@lezer/highlight": "^1.2.1",
|
"@lezer/highlight": "^1.2.0",
|
||||||
"@nanostores/persistent": "^0.10.2",
|
"@nanostores/persistent": "^0.9.1",
|
||||||
"@replit/codemirror-emacs": "^6.1.0",
|
"@replit/codemirror-emacs": "^6.0.1",
|
||||||
"@replit/codemirror-vim": "^6.2.1",
|
"@replit/codemirror-vim": "^6.1.0",
|
||||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||||
"@strudel/core": "workspace:*",
|
"@strudel/core": "workspace:*",
|
||||||
"@strudel/draw": "workspace:*",
|
"@strudel/draw": "workspace:*",
|
||||||
"@strudel/transpiler": "workspace:*",
|
"@strudel/transpiler": "workspace:*",
|
||||||
"nanostores": "^0.11.3"
|
"@uiw/codemirror-themes": "^4.21.21",
|
||||||
|
"@uiw/codemirror-themes-all": "^4.21.21",
|
||||||
|
"nanostores": "^0.9.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+411
-88
@@ -1,117 +1,440 @@
|
|||||||
import strudelTheme, { settings as strudelThemeSettings } from './themes/strudel-theme.mjs';
|
import {
|
||||||
import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen.mjs';
|
abcdef,
|
||||||
import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen.mjs';
|
|
||||||
import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen.mjs';
|
|
||||||
import teletext, { settings as teletextSettings } from './themes/teletext.mjs';
|
|
||||||
import algoboy, { settings as algoboySettings } from './themes/algoboy.mjs';
|
|
||||||
import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs';
|
|
||||||
import sonicPink, { settings as sonicPinkSettings } from './themes/sonic-pink.mjs';
|
|
||||||
import redText, { settings as redTextSettings } from './themes/red-text.mjs';
|
|
||||||
import greenText, { settings as greenTextSettings } from './themes/green-text.mjs';
|
|
||||||
import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs';
|
|
||||||
import atomone, { settings as atomOneSettings } from './themes/atomone.mjs';
|
|
||||||
import aura, { settings as auraSettings } from './themes/aura.mjs';
|
|
||||||
import darcula, { settings as darculaSettings } from './themes/darcula.mjs';
|
|
||||||
import dracula, { settings as draculaSettings } from './themes/dracula.mjs';
|
|
||||||
import duotoneDark, { settings as duotoneDarkSettings } from './themes/duotoneDark.mjs';
|
|
||||||
import eclipse, { settings as eclipseSettings } from './themes/eclipse.mjs';
|
|
||||||
import githubDark, { settings as githubDarkSettings } from './themes/githubDark.mjs';
|
|
||||||
import githubLight, { settings as githubLightSettings } from './themes/githubLight.mjs';
|
|
||||||
import gruvboxDark, { settings as gruvboxDarkSettings } from './themes/gruvboxDark.mjs';
|
|
||||||
import gruvboxLight, { settings as gruvboxLightSettings } from './themes/gruvboxLight.mjs';
|
|
||||||
import materialDark, { settings as materialDarkSettings } from './themes/materialDark.mjs';
|
|
||||||
import materialLight, { settings as materialLightSettings } from './themes/materialLight.mjs';
|
|
||||||
import nord, { settings as nordSettings } from './themes/nord.mjs';
|
|
||||||
import monokai, { settings as monokaiSettings } from './themes/monokai.mjs';
|
|
||||||
import solarizedDark, { settings as solarizedDarkSettings } from './themes/solarizedDark.mjs';
|
|
||||||
import solarizedLight, { settings as solarizedLightSettings } from './themes/solarizedLight.mjs';
|
|
||||||
import sublime, { settings as sublimeSettings } from './themes/sublime.mjs';
|
|
||||||
import tokyoNight, { settings as tokyoNightSettings } from './themes/tokyoNight.mjs';
|
|
||||||
import tokyoNightStorm, { settings as tokyoNightStormSettings } from './themes/tokioNightStorm.mjs';
|
|
||||||
import tokyoNightDay, { settings as tokyoNightDaySettings } from './themes/tokyoNightDay.mjs';
|
|
||||||
import vscodeDark, { settings as vscodeDarkSettings } from './themes/vscodeDark.mjs';
|
|
||||||
import vscodeLight, { settings as vscodeLightSettings } from './themes/vscodeLight.mjs';
|
|
||||||
import xcodeLight, { settings as xcodeLightSettings } from './themes/xcodeLight.mjs';
|
|
||||||
import bbedit, { settings as bbeditSettings } from './themes/bbedit.mjs';
|
|
||||||
import noctisLilac, { settings as noctisLilacSettings } from './themes/noctisLilac.mjs';
|
|
||||||
|
|
||||||
import { setTheme } from '@strudel/draw';
|
|
||||||
export const themes = {
|
|
||||||
strudelTheme,
|
|
||||||
algoboy,
|
|
||||||
androidstudio,
|
androidstudio,
|
||||||
atomone,
|
atomone,
|
||||||
aura,
|
aura,
|
||||||
bbedit,
|
bespin,
|
||||||
blackscreen,
|
|
||||||
bluescreen,
|
|
||||||
CutiePi,
|
|
||||||
darcula,
|
darcula,
|
||||||
dracula,
|
dracula,
|
||||||
duotoneDark,
|
duotoneDark,
|
||||||
eclipse,
|
eclipse,
|
||||||
githubDark,
|
githubDark,
|
||||||
githubLight,
|
|
||||||
greenText,
|
|
||||||
gruvboxDark,
|
gruvboxDark,
|
||||||
gruvboxLight,
|
|
||||||
sonicPink,
|
|
||||||
materialDark,
|
materialDark,
|
||||||
materialLight,
|
|
||||||
monokai,
|
|
||||||
noctisLilac,
|
|
||||||
nord,
|
nord,
|
||||||
redText,
|
okaidia,
|
||||||
solarizedDark,
|
solarizedDark,
|
||||||
solarizedLight,
|
|
||||||
sublime,
|
sublime,
|
||||||
teletext,
|
|
||||||
tokyoNight,
|
tokyoNight,
|
||||||
tokyoNightDay,
|
|
||||||
tokyoNightStorm,
|
tokyoNightStorm,
|
||||||
vscodeDark,
|
vscodeDark,
|
||||||
vscodeLight,
|
xcodeDark,
|
||||||
|
bbedit,
|
||||||
|
duotoneLight,
|
||||||
|
githubLight,
|
||||||
|
gruvboxLight,
|
||||||
|
materialLight,
|
||||||
|
noctisLilac,
|
||||||
|
solarizedLight,
|
||||||
|
tokyoNightDay,
|
||||||
|
xcodeLight,
|
||||||
|
} from '@uiw/codemirror-themes-all';
|
||||||
|
|
||||||
|
import strudelTheme from './themes/strudel-theme';
|
||||||
|
import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen';
|
||||||
|
import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen';
|
||||||
|
import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen';
|
||||||
|
import teletext, { settings as teletextSettings } from './themes/teletext';
|
||||||
|
import algoboy, { settings as algoboySettings } from './themes/algoboy';
|
||||||
|
import terminal, { settings as terminalSettings } from './themes/terminal';
|
||||||
|
import { setTheme } from '@strudel/draw';
|
||||||
|
|
||||||
|
export const themes = {
|
||||||
|
strudelTheme,
|
||||||
|
bluescreen,
|
||||||
|
blackscreen,
|
||||||
whitescreen,
|
whitescreen,
|
||||||
|
teletext,
|
||||||
|
algoboy,
|
||||||
|
terminal,
|
||||||
|
abcdef,
|
||||||
|
androidstudio,
|
||||||
|
atomone,
|
||||||
|
aura,
|
||||||
|
bespin,
|
||||||
|
darcula,
|
||||||
|
dracula,
|
||||||
|
duotoneDark,
|
||||||
|
eclipse,
|
||||||
|
githubDark,
|
||||||
|
gruvboxDark,
|
||||||
|
materialDark,
|
||||||
|
nord,
|
||||||
|
okaidia,
|
||||||
|
solarizedDark,
|
||||||
|
sublime,
|
||||||
|
tokyoNight,
|
||||||
|
tokyoNightStorm,
|
||||||
|
vscodeDark,
|
||||||
|
xcodeDark,
|
||||||
|
bbedit,
|
||||||
|
duotoneLight,
|
||||||
|
githubLight,
|
||||||
|
gruvboxLight,
|
||||||
|
materialLight,
|
||||||
|
noctisLilac,
|
||||||
|
solarizedLight,
|
||||||
|
tokyoNightDay,
|
||||||
xcodeLight,
|
xcodeLight,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// lineBackground is background with 50% opacity, to make sure the selection below is visible
|
||||||
|
|
||||||
export const settings = {
|
export const settings = {
|
||||||
strudelTheme: strudelThemeSettings,
|
strudelTheme: {
|
||||||
|
background: '#222',
|
||||||
|
lineBackground: '#22222299',
|
||||||
|
foreground: '#fff',
|
||||||
|
// foreground: '#75baff',
|
||||||
|
caret: '#ffcc00',
|
||||||
|
selection: 'rgba(128, 203, 196, 0.5)',
|
||||||
|
selectionMatch: '#036dd626',
|
||||||
|
// lineHighlight: '#8a91991a', // original
|
||||||
|
lineHighlight: '#00000050',
|
||||||
|
gutterBackground: 'transparent',
|
||||||
|
// gutterForeground: '#8a919966',
|
||||||
|
gutterForeground: '#8a919966',
|
||||||
|
},
|
||||||
bluescreen: bluescreenSettings,
|
bluescreen: bluescreenSettings,
|
||||||
blackscreen: blackscreenSettings,
|
blackscreen: blackscreenSettings,
|
||||||
whitescreen: whitescreenSettings,
|
whitescreen: whitescreenSettings,
|
||||||
teletext: teletextSettings,
|
teletext: teletextSettings,
|
||||||
algoboy: algoboySettings,
|
algoboy: algoboySettings,
|
||||||
androidstudio: androidstudioSettings,
|
terminal: terminalSettings,
|
||||||
atomone: atomOneSettings,
|
abcdef: {
|
||||||
aura: auraSettings,
|
background: '#0f0f0f',
|
||||||
bbedit: bbeditSettings,
|
lineBackground: '#0f0f0f99',
|
||||||
darcula: darculaSettings,
|
foreground: '#defdef',
|
||||||
dracula: draculaSettings,
|
caret: '#00FF00',
|
||||||
duotoneDark: duotoneDarkSettings,
|
selection: '#515151',
|
||||||
eclipse: eclipseSettings,
|
selectionMatch: '#515151',
|
||||||
CutiePi: CutiePiSettings,
|
gutterBackground: '#555',
|
||||||
sonicPink: sonicPinkSettings,
|
gutterForeground: '#FFFFFF',
|
||||||
githubLight: githubLightSettings,
|
lineHighlight: '#314151',
|
||||||
githubDark: githubDarkSettings,
|
},
|
||||||
greenText: greenTextSettings,
|
androidstudio: {
|
||||||
gruvboxDark: gruvboxDarkSettings,
|
background: '#282b2e',
|
||||||
gruvboxLight: gruvboxLightSettings,
|
lineBackground: '#282b2e99',
|
||||||
materialDark: materialDarkSettings,
|
foreground: '#a9b7c6',
|
||||||
materialLight: materialLightSettings,
|
caret: '#00FF00',
|
||||||
noctisLilac: noctisLilacSettings,
|
selection: '#343739',
|
||||||
nord: nordSettings,
|
selectionMatch: '#343739',
|
||||||
monokai: monokaiSettings,
|
lineHighlight: '#343739',
|
||||||
redText: redTextSettings,
|
},
|
||||||
solarizedLight: solarizedLightSettings,
|
atomone: {
|
||||||
solarizedDark: solarizedDarkSettings,
|
background: '#272C35',
|
||||||
sublime: sublimeSettings,
|
lineBackground: '#272C3599',
|
||||||
tokyoNight: tokyoNightSettings,
|
foreground: '#9d9b97',
|
||||||
tokyoNightStorm: tokyoNightStormSettings,
|
caret: '#797977',
|
||||||
vscodeDark: vscodeDarkSettings,
|
selection: '#ffffff30',
|
||||||
vscodeLight: vscodeLightSettings,
|
selectionMatch: '#2B323D',
|
||||||
xcodeLight: xcodeLightSettings,
|
gutterBackground: '#272C35',
|
||||||
tokyoNightDay: tokyoNightDaySettings,
|
gutterForeground: '#465063',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#2B323D',
|
||||||
|
},
|
||||||
|
aura: {
|
||||||
|
background: '#21202e',
|
||||||
|
lineBackground: '#21202e99',
|
||||||
|
foreground: '#edecee',
|
||||||
|
caret: '#a277ff',
|
||||||
|
selection: '#3d375e7f',
|
||||||
|
selectionMatch: '#3d375e7f',
|
||||||
|
gutterBackground: '#21202e',
|
||||||
|
gutterForeground: '#edecee',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#a394f033',
|
||||||
|
},
|
||||||
|
bbedit: {
|
||||||
|
light: true,
|
||||||
|
background: '#FFFFFF',
|
||||||
|
lineBackground: '#FFFFFF99',
|
||||||
|
foreground: '#000000',
|
||||||
|
caret: '#FBAC52',
|
||||||
|
selection: '#FFD420',
|
||||||
|
selectionMatch: '#FFD420',
|
||||||
|
gutterBackground: '#f5f5f5',
|
||||||
|
gutterForeground: '#4D4D4C',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#00000012',
|
||||||
|
},
|
||||||
|
bespin: {
|
||||||
|
background: '#28211c',
|
||||||
|
lineBackground: '#28211c99',
|
||||||
|
foreground: '#9d9b97',
|
||||||
|
caret: '#797977',
|
||||||
|
selection: '#36312e',
|
||||||
|
selectionMatch: '#4f382b',
|
||||||
|
gutterBackground: '#28211c',
|
||||||
|
gutterForeground: '#666666',
|
||||||
|
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
},
|
||||||
|
darcula: {
|
||||||
|
background: '#2B2B2B',
|
||||||
|
lineBackground: '#2B2B2B99',
|
||||||
|
foreground: '#f8f8f2',
|
||||||
|
caret: '#FFFFFF',
|
||||||
|
selection: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
||||||
|
gutterBackground: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
gutterForeground: '#999',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
},
|
||||||
|
dracula: {
|
||||||
|
background: '#282a36',
|
||||||
|
lineBackground: '#282a3699',
|
||||||
|
foreground: '#f8f8f2',
|
||||||
|
caret: '#f8f8f0',
|
||||||
|
selection: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
||||||
|
gutterBackground: '#282a36',
|
||||||
|
gutterForeground: '#6D8A88',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
},
|
||||||
|
duotoneLight: {
|
||||||
|
light: true,
|
||||||
|
background: '#faf8f5',
|
||||||
|
lineBackground: '#faf8f599',
|
||||||
|
foreground: '#b29762',
|
||||||
|
caret: '#93abdc',
|
||||||
|
selection: '#e3dcce',
|
||||||
|
selectionMatch: '#e3dcce',
|
||||||
|
gutterBackground: '#faf8f5',
|
||||||
|
gutterForeground: '#cdc4b1',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#EFEFEF',
|
||||||
|
},
|
||||||
|
duotoneDark: {
|
||||||
|
background: '#2a2734',
|
||||||
|
lineBackground: '#2a273499',
|
||||||
|
foreground: '#6c6783',
|
||||||
|
caret: '#ffad5c',
|
||||||
|
selection: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
gutterBackground: '#2a2734',
|
||||||
|
gutterForeground: '#545167',
|
||||||
|
lineHighlight: '#36334280',
|
||||||
|
},
|
||||||
|
eclipse: {
|
||||||
|
light: true,
|
||||||
|
background: '#fff',
|
||||||
|
lineBackground: '#ffffff99',
|
||||||
|
foreground: '#000',
|
||||||
|
caret: '#FFFFFF',
|
||||||
|
selection: '#d7d4f0',
|
||||||
|
selectionMatch: '#d7d4f0',
|
||||||
|
gutterBackground: '#f7f7f7',
|
||||||
|
gutterForeground: '#999',
|
||||||
|
lineHighlight: '#e8f2ff',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
},
|
||||||
|
githubLight: {
|
||||||
|
light: true,
|
||||||
|
background: '#fff',
|
||||||
|
lineBackground: '#ffffff99',
|
||||||
|
foreground: '#24292e',
|
||||||
|
selection: '#BBDFFF',
|
||||||
|
selectionMatch: '#BBDFFF',
|
||||||
|
gutterBackground: '#fff',
|
||||||
|
gutterForeground: '#6e7781',
|
||||||
|
},
|
||||||
|
githubDark: {
|
||||||
|
background: '#0d1117',
|
||||||
|
lineBackground: '#0d111799',
|
||||||
|
foreground: '#c9d1d9',
|
||||||
|
caret: '#c9d1d9',
|
||||||
|
selection: '#003d73',
|
||||||
|
selectionMatch: '#003d73',
|
||||||
|
lineHighlight: '#36334280',
|
||||||
|
},
|
||||||
|
gruvboxDark: {
|
||||||
|
background: '#282828',
|
||||||
|
lineBackground: '#28282899',
|
||||||
|
foreground: '#ebdbb2',
|
||||||
|
caret: '#ebdbb2',
|
||||||
|
selection: '#bdae93',
|
||||||
|
selectionMatch: '#bdae93',
|
||||||
|
lineHighlight: '#3c3836',
|
||||||
|
gutterBackground: '#282828',
|
||||||
|
gutterForeground: '#7c6f64',
|
||||||
|
},
|
||||||
|
gruvboxLight: {
|
||||||
|
light: true,
|
||||||
|
background: '#fbf1c7',
|
||||||
|
lineBackground: '#fbf1c799',
|
||||||
|
foreground: '#3c3836',
|
||||||
|
caret: '#af3a03',
|
||||||
|
selection: '#ebdbb2',
|
||||||
|
selectionMatch: '#bdae93',
|
||||||
|
lineHighlight: '#ebdbb2',
|
||||||
|
gutterBackground: '#ebdbb2',
|
||||||
|
gutterForeground: '#665c54',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
},
|
||||||
|
materialDark: {
|
||||||
|
background: '#2e3235',
|
||||||
|
lineBackground: '#2e323599',
|
||||||
|
foreground: '#bdbdbd',
|
||||||
|
caret: '#a0a4ae',
|
||||||
|
selection: '#d7d4f0',
|
||||||
|
selectionMatch: '#d7d4f0',
|
||||||
|
gutterBackground: '#2e3235',
|
||||||
|
gutterForeground: '#999',
|
||||||
|
gutterActiveForeground: '#4f5b66',
|
||||||
|
lineHighlight: '#545b61',
|
||||||
|
},
|
||||||
|
materialLight: {
|
||||||
|
light: true,
|
||||||
|
background: '#FAFAFA',
|
||||||
|
lineBackground: '#FAFAFA99',
|
||||||
|
foreground: '#90A4AE',
|
||||||
|
caret: '#272727',
|
||||||
|
selection: '#80CBC440',
|
||||||
|
selectionMatch: '#FAFAFA',
|
||||||
|
gutterBackground: '#FAFAFA',
|
||||||
|
gutterForeground: '#90A4AE',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#CCD7DA50',
|
||||||
|
},
|
||||||
|
noctisLilac: {
|
||||||
|
light: true,
|
||||||
|
background: '#f2f1f8',
|
||||||
|
lineBackground: '#f2f1f899',
|
||||||
|
foreground: '#0c006b',
|
||||||
|
caret: '#5c49e9',
|
||||||
|
selection: '#d5d1f2',
|
||||||
|
selectionMatch: '#d5d1f2',
|
||||||
|
gutterBackground: '#f2f1f8',
|
||||||
|
gutterForeground: '#0c006b70',
|
||||||
|
lineHighlight: '#e1def3',
|
||||||
|
},
|
||||||
|
nord: {
|
||||||
|
background: '#2e3440',
|
||||||
|
lineBackground: '#2e344099',
|
||||||
|
foreground: '#FFFFFF',
|
||||||
|
caret: '#FFFFFF',
|
||||||
|
selection: '#3b4252',
|
||||||
|
selectionMatch: '#e5e9f0',
|
||||||
|
gutterBackground: '#2e3440',
|
||||||
|
gutterForeground: '#4c566a',
|
||||||
|
gutterActiveForeground: '#d8dee9',
|
||||||
|
lineHighlight: '#4c566a',
|
||||||
|
},
|
||||||
|
okaidia: {
|
||||||
|
background: '#272822',
|
||||||
|
lineBackground: '#27282299',
|
||||||
|
foreground: '#FFFFFF',
|
||||||
|
caret: '#FFFFFF',
|
||||||
|
selection: '#49483E',
|
||||||
|
selectionMatch: '#49483E',
|
||||||
|
gutterBackground: '#272822',
|
||||||
|
gutterForeground: '#FFFFFF70',
|
||||||
|
lineHighlight: '#00000059',
|
||||||
|
},
|
||||||
|
solarizedLight: {
|
||||||
|
light: true,
|
||||||
|
background: '#fdf6e3',
|
||||||
|
lineBackground: '#fdf6e399',
|
||||||
|
foreground: '#657b83',
|
||||||
|
caret: '#586e75',
|
||||||
|
selection: '#dfd9c8',
|
||||||
|
selectionMatch: '#dfd9c8',
|
||||||
|
gutterBackground: '#00000010',
|
||||||
|
gutterForeground: '#657b83',
|
||||||
|
lineHighlight: '#dfd9c8',
|
||||||
|
},
|
||||||
|
solarizedDark: {
|
||||||
|
background: '#002b36',
|
||||||
|
lineBackground: '#002b3699',
|
||||||
|
foreground: '#93a1a1',
|
||||||
|
caret: '#839496',
|
||||||
|
selection: '#173541',
|
||||||
|
selectionMatch: '#aafe661a',
|
||||||
|
gutterBackground: '#00252f',
|
||||||
|
gutterForeground: '#839496',
|
||||||
|
lineHighlight: '#173541',
|
||||||
|
},
|
||||||
|
sublime: {
|
||||||
|
background: '#303841',
|
||||||
|
lineBackground: '#30384199',
|
||||||
|
foreground: '#FFFFFF',
|
||||||
|
caret: '#FBAC52',
|
||||||
|
selection: '#4C5964',
|
||||||
|
selectionMatch: '#3A546E',
|
||||||
|
gutterBackground: '#303841',
|
||||||
|
gutterForeground: '#FFFFFF70',
|
||||||
|
lineHighlight: '#00000059',
|
||||||
|
},
|
||||||
|
tokyoNightDay: {
|
||||||
|
light: true,
|
||||||
|
background: '#e1e2e7',
|
||||||
|
lineBackground: '#e1e2e799',
|
||||||
|
foreground: '#3760bf',
|
||||||
|
caret: '#3760bf',
|
||||||
|
selection: '#99a7df',
|
||||||
|
selectionMatch: '#99a7df',
|
||||||
|
gutterBackground: '#e1e2e7',
|
||||||
|
gutterForeground: '#3760bf',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#5f5faf11',
|
||||||
|
},
|
||||||
|
tokyoNightStorm: {
|
||||||
|
background: '#24283b',
|
||||||
|
lineBackground: '#24283b99',
|
||||||
|
foreground: '#7982a9',
|
||||||
|
caret: '#c0caf5',
|
||||||
|
selection: '#6f7bb630',
|
||||||
|
selectionMatch: '#1f2335',
|
||||||
|
gutterBackground: '#24283b',
|
||||||
|
gutterForeground: '#7982a9',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#292e42',
|
||||||
|
},
|
||||||
|
tokyoNight: {
|
||||||
|
background: '#1a1b26',
|
||||||
|
lineBackground: '#1a1b2699',
|
||||||
|
foreground: '#787c99',
|
||||||
|
caret: '#c0caf5',
|
||||||
|
selection: '#515c7e40',
|
||||||
|
selectionMatch: '#16161e',
|
||||||
|
gutterBackground: '#1a1b26',
|
||||||
|
gutterForeground: '#787c99',
|
||||||
|
gutterBorder: 'transparent',
|
||||||
|
lineHighlight: '#1e202e',
|
||||||
|
},
|
||||||
|
vscodeDark: {
|
||||||
|
background: '#1e1e1e',
|
||||||
|
lineBackground: '#1e1e1e99',
|
||||||
|
foreground: '#9cdcfe',
|
||||||
|
caret: '#c6c6c6',
|
||||||
|
selection: '#6199ff2f',
|
||||||
|
selectionMatch: '#72a1ff59',
|
||||||
|
lineHighlight: '#ffffff0f',
|
||||||
|
gutterBackground: '#1e1e1e',
|
||||||
|
gutterForeground: '#838383',
|
||||||
|
gutterActiveForeground: '#fff',
|
||||||
|
},
|
||||||
|
xcodeLight: {
|
||||||
|
light: true,
|
||||||
|
background: '#fff',
|
||||||
|
lineBackground: '#ffffff99',
|
||||||
|
foreground: '#3D3D3D',
|
||||||
|
selection: '#BBDFFF',
|
||||||
|
selectionMatch: '#BBDFFF',
|
||||||
|
gutterBackground: '#fff',
|
||||||
|
gutterForeground: '#AFAFAF',
|
||||||
|
lineHighlight: '#EDF4FF',
|
||||||
|
},
|
||||||
|
xcodeDark: {
|
||||||
|
background: '#292A30',
|
||||||
|
lineBackground: '#292A3099',
|
||||||
|
foreground: '#CECFD0',
|
||||||
|
caret: '#fff',
|
||||||
|
selection: '#727377',
|
||||||
|
selectionMatch: '#727377',
|
||||||
|
lineHighlight: '#2F3239',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function getColors(str) {
|
function getColors(str) {
|
||||||
|
|||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Cutie Pi
|
|
||||||
* by Switch Angel
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
const deepPurple = '#5c019a';
|
|
||||||
const yellowPink = '#fbeffc';
|
|
||||||
const grey = '#272C35';
|
|
||||||
const pinkAccent = '#fee1ff';
|
|
||||||
const lightGrey = '#465063';
|
|
||||||
const bratGreen = '#9acd3f';
|
|
||||||
const lighterGrey = '#97a1b7';
|
|
||||||
const pink = '#f6a6fd';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: 'white',
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: deepPurple,
|
|
||||||
caret: '#797977',
|
|
||||||
selection: yellowPink,
|
|
||||||
selectionMatch: '#2B323D',
|
|
||||||
gutterBackground: grey,
|
|
||||||
gutterForeground: lightGrey,
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: pinkAccent,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: deepPurple,
|
|
||||||
},
|
|
||||||
{ tag: [t.tagName, t.heading], color: settings.foreground },
|
|
||||||
{ tag: t.comment, color: lighterGrey },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: pink },
|
|
||||||
{ tag: [t.attributeName, t.number], color: '#d19a66' },
|
|
||||||
{ tag: t.className, color: grey },
|
|
||||||
{ tag: t.keyword, color: deepPurple },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: bratGreen },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+32
-51
@@ -1,61 +1,42 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
|
||||||
const palettes = {
|
|
||||||
// https://www.deviantart.com/advancedfan2020/art/Game-Boy-Palette-Set-Color-HEX-Part-09-920495662
|
|
||||||
'Central Florida A': ['#FFF630', '#B3AC22', '#666213', '#191905'],
|
|
||||||
'Central Florida B': ['#38CEBA', '#279082', '#16524A', '#061513'],
|
|
||||||
'Central Florida C': ['#FF8836', '#B35F26', '#663616', '#190E05'],
|
|
||||||
'Central Florida D': ['#E07070', '#9D4E4E', '#5A2D2D', '#160B0B'],
|
|
||||||
'Central Florida E': ['#7AA4CB', '#55738E', '#314251', '#0C1014'],
|
|
||||||
'Feminine Energy A': ['#DC5686', '#9A415E', '#582536', '#16090D'],
|
|
||||||
'Feminine Energy B': ['#D0463C', '#92312A', '#531c18', '#150706'],
|
|
||||||
'Feminine Energy C': ['#D86918', '#974A11', '#562A0A', '#160A02'],
|
|
||||||
'Feminine Energy D': ['#EFC54F', '#A78A36', '#604F20', '#181408'],
|
|
||||||
'Feminine Energy E': ['#866399', '#5e456b', '#36283d', '#0d0a0f'],
|
|
||||||
'Sour Watermelon A': ['#993366', '#6B2447', '#3D1429', '#0F050A'],
|
|
||||||
'Sour Watermelon B': ['#996666', '#6B4747', '#3D2929', '#0F0A0A'],
|
|
||||||
'Sour Watermelon C': ['#999966', '#686B47', '#3d3d29', '#0f0f0A'],
|
|
||||||
'Sour Watermelon D': ['#99cc66', '#6b8f47', '#3d5229', '#0f140a'],
|
|
||||||
'Sour Watermelon E': ['#99ff66', '#6bb347', '#3d6629', '#0f190a'],
|
|
||||||
//https://www.deviantart.com/advancedfan2020/art/Game-Boy-Palette-Set-Color-HEX-Part-02-920073260
|
|
||||||
'Peri Peaceful A': ['#909BE9', '#656DA3', '#3A3E5D', '#0e0f17'],
|
|
||||||
'Peri Peaceful B': ['#68628d', '#494563', '#2a2738', '#0a0a0e'], // pretty dim
|
|
||||||
'Peri Peaceful E': ['#b5a0a9', '#7f7076', '#484044', '#121011'],
|
|
||||||
'Hichem Palette B': ['#4fa3a5', '#377273', '#204142', '#081010'],
|
|
||||||
'Hichem Palette C': ['#Fe6f9b', '#b24e6d', '#662c3e', '#190b0f'],
|
|
||||||
'Hichem Palette D': ['#ffbb5a', '#b3833f', '#664b24', '#191309'],
|
|
||||||
'JSR2 A': ['#E0EFC0', '#9da786', '#5a604d', '#161813'],
|
|
||||||
};
|
|
||||||
const palette = palettes['Sour Watermelon B'];
|
|
||||||
export const settings = {
|
export const settings = {
|
||||||
background: palette[3],
|
background: '#9bbc0f',
|
||||||
foreground: palette[1],
|
foreground: '#0f380f', // whats that?
|
||||||
caret: palette[0],
|
caret: '#0f380f',
|
||||||
selection: palette[0],
|
selection: '#306230',
|
||||||
selectionMatch: palette[1],
|
selectionMatch: '#ffffff26',
|
||||||
lineHighlight: palette[3],
|
lineHighlight: '#8bac0f',
|
||||||
lineBackground: palette[3] + '90',
|
lineBackground: '#9bbc0f50',
|
||||||
//lineBackground: 'transparent',
|
//lineBackground: 'transparent',
|
||||||
gutterBackground: 'transparent',
|
gutterBackground: 'transparent',
|
||||||
gutterForeground: palette[0],
|
gutterForeground: '#0f380f',
|
||||||
light: false,
|
light: true,
|
||||||
// customStyle: '.cm-line { line-height: 1 }',
|
customStyle: '.cm-line { line-height: 1 }',
|
||||||
};
|
};
|
||||||
export default createTheme({
|
export default createTheme({
|
||||||
theme: 'dark',
|
theme: 'light',
|
||||||
settings,
|
settings,
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: t.comment, color: palette[2] },
|
{ tag: t.labelName, color: '#0f380f' },
|
||||||
{ tag: t.string, color: palette[1] },
|
{ tag: t.keyword, color: '#0f380f' },
|
||||||
{ tag: [t.atom, t.number], color: palette[1] },
|
{ tag: t.operator, color: '#0f380f' },
|
||||||
{ tag: [t.meta, t.labelName, t.variableName], color: palette[0] },
|
{ tag: t.special(t.variableName), color: '#0f380f' },
|
||||||
{
|
{ tag: t.typeName, color: '#0f380f' },
|
||||||
tag: [t.keyword, t.tagName, t.arithmeticOperator],
|
{ tag: t.atom, color: '#0f380f' },
|
||||||
color: palette[1],
|
{ tag: t.number, color: '#0f380f' },
|
||||||
},
|
{ tag: t.definition(t.variableName), color: '#0f380f' },
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: palette[0] },
|
{ tag: t.string, color: '#0f380f' },
|
||||||
{ tag: [t.function(t.variableName), t.propertyName], color: palette[0] },
|
{ tag: t.special(t.string), color: '#0f380f' },
|
||||||
{ tag: t.atom, color: palette[1] },
|
{ tag: t.comment, color: '#0f380f' },
|
||||||
|
{ tag: t.variableName, color: '#0f380f' },
|
||||||
|
{ tag: t.tagName, color: '#0f380f' },
|
||||||
|
{ tag: t.bracket, color: '#0f380f' },
|
||||||
|
{ tag: t.meta, color: '#0f380f' },
|
||||||
|
{ tag: t.attributeName, color: '#0f380f' },
|
||||||
|
{ tag: t.propertyName, color: '#0f380f' },
|
||||||
|
{ tag: t.className, color: '#0f380f' },
|
||||||
|
{ tag: t.invalid, color: '#0f380f' },
|
||||||
|
{ tag: [t.unit, t.punctuation], color: '#0f380f' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name androidstudio
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#282b2e',
|
|
||||||
lineBackground: '#282b2e99',
|
|
||||||
foreground: '#a9b7c6',
|
|
||||||
caret: '#00FF00',
|
|
||||||
selection: '#343739',
|
|
||||||
selectionMatch: '#343739',
|
|
||||||
lineHighlight: '#343739',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#282b2e',
|
|
||||||
foreground: '#a9b7c6',
|
|
||||||
caret: '#00FF00',
|
|
||||||
selection: '#4e5254',
|
|
||||||
selectionMatch: '#4e5254',
|
|
||||||
gutterForeground: '#cccccc50',
|
|
||||||
lineHighlight: '#7f85891f',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.labelName, color: 'inherit' },
|
|
||||||
{ tag: [t.keyword, t.deleted, t.className], color: '#a9b7c6' },
|
|
||||||
{ tag: [t.number, t.literal], color: '#6897bb' },
|
|
||||||
//{ tag: [t.link, t.variableName], color: '#629755' },
|
|
||||||
{ tag: [t.link, t.variableName], color: '#a9b7c6' },
|
|
||||||
{ tag: [t.comment, t.quote], color: 'grey' },
|
|
||||||
{ tag: [t.meta, t.documentMeta], color: '#bbb529' },
|
|
||||||
//{ tag: [t.string, t.propertyName, t.attributeValue], color: '#6a8759' },
|
|
||||||
{ tag: [t.propertyName, t.attributeValue], color: '#a9b7c6' },
|
|
||||||
{ tag: [t.string], color: '#6a8759' },
|
|
||||||
{ tag: [t.heading, t.typeName], color: '#ffc66d' },
|
|
||||||
{ tag: [t.attributeName], color: '#a9b7c6' },
|
|
||||||
{ tag: [t.emphasis], fontStyle: 'italic' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#272C35',
|
|
||||||
lineBackground: '#272C3599',
|
|
||||||
foreground: 'hsl(220, 14%, 71%)',
|
|
||||||
caret: '#797977',
|
|
||||||
selection: '#ffffff30',
|
|
||||||
selectionMatch: '#2B323D',
|
|
||||||
gutterBackground: '#272C35',
|
|
||||||
gutterForeground: '#465063',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#2B323D',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#272C35',
|
|
||||||
foreground: '#9d9b97',
|
|
||||||
caret: '#797977',
|
|
||||||
selection: '#3d4c64',
|
|
||||||
selectionMatch: '#3d4c64',
|
|
||||||
gutterBackground: '#272C35',
|
|
||||||
gutterForeground: '#465063',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#2e3f5940',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: 'hsl(207, 82%, 66%)',
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: 'hsl( 29, 54%, 61%)' },
|
|
||||||
{ tag: [t.tagName, t.heading], color: '#e06c75' },
|
|
||||||
{ tag: t.comment, color: '#54636D' },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: 'hsl(220, 14%, 71%)' },
|
|
||||||
{ tag: [t.attributeName, t.number], color: 'hsl( 29, 54%, 61%)' },
|
|
||||||
{ tag: t.className, color: 'hsl( 39, 67%, 69%)' },
|
|
||||||
{ tag: t.keyword, color: 'hsl(286, 60%, 67%)' },
|
|
||||||
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: '#98c379' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
Vendored
-51
@@ -1,51 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#21202e',
|
|
||||||
lineBackground: '#21202e99',
|
|
||||||
foreground: '#edecee',
|
|
||||||
caret: '#a277ff',
|
|
||||||
selection: '#3d375e7f',
|
|
||||||
selectionMatch: '#3d375e7f',
|
|
||||||
gutterBackground: '#21202e',
|
|
||||||
gutterForeground: '#edecee',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#a394f033',
|
|
||||||
};
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#21202e',
|
|
||||||
foreground: '#edecee',
|
|
||||||
caret: '#a277ff',
|
|
||||||
selection: '#5a51898f',
|
|
||||||
selectionMatch: '#5a51898f',
|
|
||||||
gutterBackground: '#21202e',
|
|
||||||
gutterForeground: '#edecee',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#a394f033',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#a277ff' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#edecee' },
|
|
||||||
{ tag: [t.propertyName], color: '#ffca85' },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#61ffca' },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: '#ffca85' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#61ffca' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#edecee' },
|
|
||||||
{ tag: [t.className], color: '#82e2ff' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#61ffca' },
|
|
||||||
{ tag: [t.typeName], color: '#82e2ff' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#a277ff' },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#61ffca' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#6d6d6d' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#a277ff' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#edecee' },
|
|
||||||
{ tag: t.invalid, color: '#ff6767' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#FFFFFF',
|
|
||||||
lineBackground: '#FFFFFF99',
|
|
||||||
foreground: '#000000',
|
|
||||||
caret: '#FBAC52',
|
|
||||||
selection: '#FFD420',
|
|
||||||
selectionMatch: '#FFD420',
|
|
||||||
gutterBackground: '#f5f5f5',
|
|
||||||
gutterForeground: '#4D4D4C',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#00000012',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#FFFFFF',
|
|
||||||
foreground: '#000000',
|
|
||||||
caret: '#FBAC52',
|
|
||||||
selection: '#FFD420',
|
|
||||||
selectionMatch: '#FFD420',
|
|
||||||
gutterBackground: '#f5f5f5',
|
|
||||||
gutterForeground: '#4D4D4C',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#00000012',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: [t.meta, t.comment], color: '#804000' },
|
|
||||||
{ tag: [t.keyword, t.strong], color: '#0000FF' },
|
|
||||||
{ tag: [t.number], color: '#FF0080' },
|
|
||||||
{ tag: [t.string], color: '#FF0080' },
|
|
||||||
{ tag: [t.variableName], color: '#006600' },
|
|
||||||
{ tag: [t.escape], color: '#33CC33' },
|
|
||||||
{ tag: [t.tagName], color: '#1C02FF' },
|
|
||||||
{ tag: [t.heading], color: '#0C07FF' },
|
|
||||||
{ tag: [t.quote], color: '#000000' },
|
|
||||||
{ tag: [t.list], color: '#B90690' },
|
|
||||||
{ tag: [t.documentMeta], color: '#888888' },
|
|
||||||
{ tag: [t.function(t.variableName)], color: '#0000A2' },
|
|
||||||
{ tag: [t.definition(t.typeName), t.typeName], color: '#6D79DE' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+21
-21
@@ -1,5 +1,5 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
export const settings = {
|
export const settings = {
|
||||||
background: 'black',
|
background: 'black',
|
||||||
foreground: 'white', // whats that?
|
foreground: 'white', // whats that?
|
||||||
@@ -15,25 +15,25 @@ export default createTheme({
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
settings,
|
settings,
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: t.labelName, color: 'inherit' },
|
{ tag: t.labelName, color: 'white' },
|
||||||
{ tag: t.keyword, color: 'inherit' },
|
{ tag: t.keyword, color: 'white' },
|
||||||
{ tag: t.operator, color: 'inherit' },
|
{ tag: t.operator, color: 'white' },
|
||||||
{ tag: t.special(t.variableName), color: 'inherit' },
|
{ tag: t.special(t.variableName), color: 'white' },
|
||||||
{ tag: t.typeName, color: 'inherit' },
|
{ tag: t.typeName, color: 'white' },
|
||||||
{ tag: t.atom, color: 'inherit' },
|
{ tag: t.atom, color: 'white' },
|
||||||
{ tag: t.number, color: 'inherit' },
|
{ tag: t.number, color: 'white' },
|
||||||
{ tag: t.definition(t.variableName), color: 'inherit' },
|
{ tag: t.definition(t.variableName), color: 'white' },
|
||||||
{ tag: t.string, color: 'inherit' },
|
{ tag: t.string, color: 'white' },
|
||||||
{ tag: t.special(t.string), color: 'inherit' },
|
{ tag: t.special(t.string), color: 'white' },
|
||||||
{ tag: t.comment, color: 'inherit' },
|
{ tag: t.comment, color: 'white' },
|
||||||
{ tag: t.variableName, color: 'inherit' },
|
{ tag: t.variableName, color: 'white' },
|
||||||
{ tag: t.tagName, color: 'inherit' },
|
{ tag: t.tagName, color: 'white' },
|
||||||
{ tag: t.bracket, color: 'inherit' },
|
{ tag: t.bracket, color: 'white' },
|
||||||
{ tag: t.meta, color: 'inherit' },
|
{ tag: t.meta, color: 'white' },
|
||||||
{ tag: t.attributeName, color: 'inherit' },
|
{ tag: t.attributeName, color: 'white' },
|
||||||
{ tag: t.propertyName, color: 'inherit' },
|
{ tag: t.propertyName, color: 'white' },
|
||||||
{ tag: t.className, color: 'inherit' },
|
{ tag: t.className, color: 'white' },
|
||||||
{ tag: t.invalid, color: 'inherit' },
|
{ tag: t.invalid, color: 'white' },
|
||||||
{ tag: [t.unit, t.punctuation], color: 'inherit' },
|
{ tag: [t.unit, t.punctuation], color: 'white' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
+21
-21
@@ -1,5 +1,5 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
export const settings = {
|
export const settings = {
|
||||||
background: '#051DB5',
|
background: '#051DB5',
|
||||||
lineBackground: '#051DB550',
|
lineBackground: '#051DB550',
|
||||||
@@ -18,25 +18,25 @@ export default createTheme({
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
settings,
|
settings,
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: t.labelName, color: 'inherit' },
|
{ tag: t.labelName, color: 'white' },
|
||||||
{ tag: t.keyword, color: 'inherit' },
|
{ tag: t.keyword, color: 'white' },
|
||||||
{ tag: t.operator, color: 'inherit' },
|
{ tag: t.operator, color: 'white' },
|
||||||
{ tag: t.special(t.variableName), color: 'inherit' },
|
{ tag: t.special(t.variableName), color: 'white' },
|
||||||
{ tag: t.typeName, color: 'inherit' },
|
{ tag: t.typeName, color: 'white' },
|
||||||
{ tag: t.atom, color: 'inherit' },
|
{ tag: t.atom, color: 'white' },
|
||||||
{ tag: t.number, color: 'inherit' },
|
{ tag: t.number, color: 'white' },
|
||||||
{ tag: t.definition(t.variableName), color: 'inherit' },
|
{ tag: t.definition(t.variableName), color: 'white' },
|
||||||
{ tag: t.string, color: 'inherit' },
|
{ tag: t.string, color: 'white' },
|
||||||
{ tag: t.special(t.string), color: 'inherit' },
|
{ tag: t.special(t.string), color: 'white' },
|
||||||
{ tag: t.comment, color: 'inherit' },
|
{ tag: t.comment, color: 'white' },
|
||||||
{ tag: t.variableName, color: 'inherit' },
|
{ tag: t.variableName, color: 'white' },
|
||||||
{ tag: t.tagName, color: 'inherit' },
|
{ tag: t.tagName, color: 'white' },
|
||||||
{ tag: t.bracket, color: 'inherit' },
|
{ tag: t.bracket, color: 'white' },
|
||||||
{ tag: t.meta, color: 'inherit' },
|
{ tag: t.meta, color: 'white' },
|
||||||
{ tag: t.attributeName, color: 'inherit' },
|
{ tag: t.attributeName, color: 'white' },
|
||||||
{ tag: t.propertyName, color: 'inherit' },
|
{ tag: t.propertyName, color: 'white' },
|
||||||
{ tag: t.className, color: 'inherit' },
|
{ tag: t.className, color: 'white' },
|
||||||
{ tag: t.invalid, color: 'inherit' },
|
{ tag: t.invalid, color: 'white' },
|
||||||
{ tag: [t.unit, t.punctuation], color: 'inherit' },
|
{ tag: [t.unit, t.punctuation], color: 'white' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name darcula
|
|
||||||
* @author darcula
|
|
||||||
* Name: IntelliJ IDEA darcula theme
|
|
||||||
* From IntelliJ IDEA by JetBrains
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
export const settings = {
|
|
||||||
background: '#242424',
|
|
||||||
lineBackground: '#24242499',
|
|
||||||
foreground: '#f8f8f2',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
gutterBackground: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
gutterForeground: '#999',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#242424',
|
|
||||||
foreground: '#f8f8f2',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
gutterBackground: 'transparent',
|
|
||||||
gutterForeground: '#999',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.labelName, color: '#CCCCCC' },
|
|
||||||
{ tag: [t.atom, t.number], color: '#7A9EC2' },
|
|
||||||
{ tag: [t.comment], color: '#707070' },
|
|
||||||
{ tag: [t.string], color: '#6A8759' },
|
|
||||||
{ tag: [t.variableName, t.operator], color: '#CCCCCC' },
|
|
||||||
{ tag: [t.function(t.variableName), t.propertyName], color: '#FFC66D' },
|
|
||||||
{ tag: [t.meta, t.className], color: '#FFC66D' },
|
|
||||||
{ tag: [t.propertyName], color: '#FFC66D' },
|
|
||||||
{ tag: [t.keyword], color: '#CC7832' },
|
|
||||||
{ tag: [t.tagName], color: '#ff79c6' },
|
|
||||||
{ tag: [t.typeName], color: '#ffb86c' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name dracula
|
|
||||||
* @author dracula
|
|
||||||
* Michael Kaminsky (http://github.com/mkaminsky11)
|
|
||||||
* Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)
|
|
||||||
*/
|
|
||||||
// this is different from https://thememirror.net/dracula
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#282a36',
|
|
||||||
lineBackground: '#282a3699',
|
|
||||||
foreground: '#f8f8f2',
|
|
||||||
caret: '#f8f8f0',
|
|
||||||
selection: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
gutterBackground: '#282a36',
|
|
||||||
gutterForeground: '#6272a4',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
};
|
|
||||||
|
|
||||||
const purple = '#BD93F9';
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#282a36',
|
|
||||||
foreground: '#f8f8f2',
|
|
||||||
caret: '#f8f8f0',
|
|
||||||
selection: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
gutterBackground: '#282a36',
|
|
||||||
gutterForeground: '#6272a4',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.comment, color: '#6272a4' },
|
|
||||||
{ tag: t.string, color: '#f1fa8c' },
|
|
||||||
{ tag: [t.atom, t.number], color: purple },
|
|
||||||
{ tag: [t.meta, t.labelName, t.variableName], color: '#f8f8f2' },
|
|
||||||
{
|
|
||||||
tag: [t.keyword, t.tagName, t.arithmeticOperator],
|
|
||||||
color: '#ff79c6',
|
|
||||||
},
|
|
||||||
{ tag: [t.function(t.variableName), t.propertyName], color: '#50fa7b' },
|
|
||||||
{ tag: t.atom, color: '#bd93f9' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name duotone
|
|
||||||
* @author Bram de Haan
|
|
||||||
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#2a2734',
|
|
||||||
lineBackground: '#2a273499',
|
|
||||||
foreground: '#eeebff',
|
|
||||||
caret: '#ffad5c',
|
|
||||||
selection: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
gutterBackground: '#2a2734',
|
|
||||||
gutterForeground: '#545167',
|
|
||||||
lineHighlight: '#36334280',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#2a2734',
|
|
||||||
foreground: '#6c6783',
|
|
||||||
caret: '#ffad5c',
|
|
||||||
selection: '#9a86fd',
|
|
||||||
selectionMatch: '#9a86fd',
|
|
||||||
gutterBackground: '#2a2734',
|
|
||||||
gutterForeground: '#545167',
|
|
||||||
lineHighlight: '#36334280',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: [t.comment, t.bracket, t.operator], color: '#6c6783' },
|
|
||||||
{ tag: [t.atom, t.number, t.keyword, t.link, t.attributeName, t.quote], color: '#ffcc99' },
|
|
||||||
{ tag: [t.emphasis, t.heading, t.tagName, t.propertyName, t.className, t.variableName], color: '#eeebff' },
|
|
||||||
{ tag: [t.typeName, t.url], color: '#eeebff' },
|
|
||||||
{ tag: t.string, color: '#ffb870' },
|
|
||||||
/* { tag: [t.propertyName], color: '#9a86fd' }, */
|
|
||||||
{ tag: [t.propertyName], color: '#eeebff' },
|
|
||||||
{ tag: t.labelName, color: '#eeebff' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name duotone
|
|
||||||
* @author Bram de Haan
|
|
||||||
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#faf8f5',
|
|
||||||
lineBackground: '#faf8f599',
|
|
||||||
foreground: '#b29762',
|
|
||||||
caret: '#93abdc',
|
|
||||||
selection: '#e3dcce',
|
|
||||||
selectionMatch: '#e3dcce',
|
|
||||||
gutterBackground: '#faf8f5',
|
|
||||||
gutterForeground: '#cdc4b1',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#EFEFEF',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#faf8f5',
|
|
||||||
foreground: '#b29762',
|
|
||||||
caret: '#93abdc',
|
|
||||||
selection: '#e3dcce',
|
|
||||||
selectionMatch: '#e3dcce',
|
|
||||||
gutterBackground: '#faf8f5',
|
|
||||||
gutterForeground: '#cdc4b1',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#ddceb154',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: [t.comment, t.bracket], color: '#b6ad9a' },
|
|
||||||
{ tag: [t.atom, t.number, t.keyword, t.link, t.attributeName, t.quote], color: '#063289' },
|
|
||||||
{ tag: [t.emphasis, t.heading, t.tagName, t.propertyName, t.variableName], color: '#2d2006' },
|
|
||||||
{ tag: [t.typeName, t.url, t.string], color: '#896724' },
|
|
||||||
{ tag: [t.operator, t.string], color: '#1659df' },
|
|
||||||
{ tag: [t.propertyName], color: '#b29762' },
|
|
||||||
{ tag: [t.unit, t.punctuation], color: '#063289' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#fff',
|
|
||||||
lineBackground: '#ffffff99',
|
|
||||||
foreground: '#000',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: '#d7d4f0',
|
|
||||||
selectionMatch: '#d7d4f0',
|
|
||||||
gutterBackground: '#f7f7f7',
|
|
||||||
gutterForeground: '#999',
|
|
||||||
lineHighlight: '#e8f2ff',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#fff',
|
|
||||||
foreground: '#000',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: '#d7d4f0',
|
|
||||||
selectionMatch: '#d7d4f0',
|
|
||||||
gutterBackground: '#f7f7f7',
|
|
||||||
gutterForeground: '#999',
|
|
||||||
lineHighlight: '#006fff1c',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: [t.comment], color: '#3F7F5F' },
|
|
||||||
{ tag: [t.documentMeta], color: '#FF1717' },
|
|
||||||
{ tag: t.keyword, color: '#7F0055', fontWeight: 'bold' },
|
|
||||||
{ tag: t.atom, color: '#00f' },
|
|
||||||
{ tag: t.number, color: '#164' },
|
|
||||||
{ tag: t.propertyName, color: '#164' },
|
|
||||||
{ tag: [t.variableName, t.definition(t.variableName)], color: '#0000C0' },
|
|
||||||
{ tag: t.function(t.variableName), color: '#0000C0' },
|
|
||||||
{ tag: t.string, color: '#2A00FF' },
|
|
||||||
{ tag: t.operator, color: 'black' },
|
|
||||||
{ tag: t.tagName, color: '#170' },
|
|
||||||
{ tag: t.attributeName, color: '#00c' },
|
|
||||||
{ tag: t.link, color: '#219' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name github
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#0d1117',
|
|
||||||
lineBackground: '#0d111799',
|
|
||||||
foreground: '#c9d1d9',
|
|
||||||
caret: '#c9d1d9',
|
|
||||||
selection: '#003d73',
|
|
||||||
selectionMatch: '#003d73',
|
|
||||||
lineHighlight: '#36334280',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#0d1117',
|
|
||||||
foreground: '#c9d1d9',
|
|
||||||
caret: '#c9d1d9',
|
|
||||||
selection: '#003d73',
|
|
||||||
selectionMatch: '#003d73',
|
|
||||||
lineHighlight: '#36334280',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.labelName, color: '#d2a8ff' },
|
|
||||||
{ tag: [t.standard(t.tagName), t.tagName], color: '#7ee787' },
|
|
||||||
{ tag: [t.comment, t.bracket], color: '#8b949e' },
|
|
||||||
{ tag: [t.className, t.propertyName], color: '#d2a8ff' },
|
|
||||||
{ tag: [t.variableName, t.attributeName], color: '#d2a8ff' },
|
|
||||||
{ tag: [t.number, t.operator], color: '#79c0ff' },
|
|
||||||
{ tag: [t.keyword, t.typeName, t.typeOperator, t.typeName], color: '#ff7b72' },
|
|
||||||
{ tag: [t.string, t.meta, t.regexp], color: '#a5d6ff' },
|
|
||||||
{ tag: [t.name, t.quote], color: '#7ee787' },
|
|
||||||
{ tag: [t.heading, t.strong], color: '#d2a8ff', fontWeight: 'bold' },
|
|
||||||
{ tag: [t.emphasis], color: '#d2a8ff', fontStyle: 'italic' },
|
|
||||||
{ tag: [t.deleted], color: '#ffdcd7', backgroundColor: 'ffeef0' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#ffab70' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: t.invalid, color: '#f97583' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name github
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#fff',
|
|
||||||
lineBackground: '#ffffff99',
|
|
||||||
foreground: '#24292e',
|
|
||||||
selection: '#BBDFFF',
|
|
||||||
selectionMatch: '#BBDFFF',
|
|
||||||
gutterBackground: '#fff',
|
|
||||||
gutterForeground: '#6e7781',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#fff',
|
|
||||||
foreground: '#24292e',
|
|
||||||
selection: '#BBDFFF',
|
|
||||||
selectionMatch: '#BBDFFF',
|
|
||||||
gutterBackground: '#fff',
|
|
||||||
gutterForeground: '#6e7781',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: [t.standard(t.tagName), t.tagName], color: '#116329' },
|
|
||||||
{ tag: [t.comment, t.bracket], color: '#6a737d' },
|
|
||||||
{ tag: [t.className, t.propertyName], color: '#6f42c1' },
|
|
||||||
{ tag: [t.variableName, t.attributeName, t.number, t.operator], color: '#005cc5' },
|
|
||||||
{ tag: [t.keyword, t.typeName, t.typeOperator, t.typeName], color: '#d73a49' },
|
|
||||||
{ tag: [t.string, t.meta, t.regexp], color: '#032f62' },
|
|
||||||
{ tag: [t.name, t.quote], color: '#22863a' },
|
|
||||||
{ tag: [t.heading, t.strong], color: '#24292e', fontWeight: 'bold' },
|
|
||||||
{ tag: [t.emphasis], color: '#24292e', fontStyle: 'italic' },
|
|
||||||
{ tag: [t.deleted], color: '#b31d28', backgroundColor: 'ffeef0' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#e36209' },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#032f62' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: t.invalid, color: '#cb2431' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
const hex = ['#000000', '#8ed675', '#56bd2a', '#54636D', '#171717'];
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: hex[0],
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: hex[2],
|
|
||||||
selection: hex[4],
|
|
||||||
selectionMatch: hex[0],
|
|
||||||
gutterBackground: hex[0],
|
|
||||||
gutterForeground: hex[3],
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: hex[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: hex[2],
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
|
|
||||||
{ tag: t.comment, color: hex[3] },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
|
|
||||||
{ tag: [t.attributeName, t.number], color: hex[1] },
|
|
||||||
{ tag: t.keyword, color: hex[2] },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-83
@@ -1,83 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name gruvbox-dark
|
|
||||||
* @author morhetz
|
|
||||||
* Name: Gruvbox
|
|
||||||
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-dark.css
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#282828',
|
|
||||||
lineBackground: '#28282899',
|
|
||||||
foreground: '#ebdbb2',
|
|
||||||
caret: '#ebdbb2',
|
|
||||||
selection: '#bdae93',
|
|
||||||
selectionMatch: '#bdae93',
|
|
||||||
lineHighlight: '#3c3836',
|
|
||||||
gutterBackground: '#282828',
|
|
||||||
gutterForeground: '#7c6f64',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#282828',
|
|
||||||
foreground: '#ebdbb2',
|
|
||||||
caret: '#ebdbb2',
|
|
||||||
selection: '#b99d555c',
|
|
||||||
selectionMatch: '#b99d555c',
|
|
||||||
lineHighlight: '#baa1602b',
|
|
||||||
gutterBackground: '#282828',
|
|
||||||
gutterForeground: '#7c6f64',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#fb4934' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: '#8ec07c' },
|
|
||||||
{ tag: [t.variableName], color: '#83a598' },
|
|
||||||
{ tag: [t.function(t.variableName)], color: '#8ec07c', fontStyle: 'bold' },
|
|
||||||
{ tag: [t.labelName], color: '#ebdbb2' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#d3869b' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#ebdbb2' },
|
|
||||||
{ tag: [t.brace], color: '#ebdbb2' },
|
|
||||||
{ tag: [t.annotation], color: '#fb4934d' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#d3869b' },
|
|
||||||
{ tag: [t.typeName, t.className], color: '#fabd2f' },
|
|
||||||
{ tag: [t.operatorKeyword], color: '#fb4934' },
|
|
||||||
{
|
|
||||||
tag: [t.tagName],
|
|
||||||
color: '#8ec07c',
|
|
||||||
fontStyle: 'bold',
|
|
||||||
},
|
|
||||||
{ tag: [t.squareBracket], color: '#fe8019' },
|
|
||||||
{ tag: [t.angleBracket], color: '#83a598' },
|
|
||||||
{ tag: [t.attributeName], color: '#8ec07c' },
|
|
||||||
{ tag: [t.regexp], color: '#8ec07c' },
|
|
||||||
{ tag: [t.quote], color: '#928374' },
|
|
||||||
{ tag: [t.string], color: '#ebdbb2' },
|
|
||||||
{
|
|
||||||
tag: t.link,
|
|
||||||
color: '#a89984',
|
|
||||||
textDecoration: 'underline',
|
|
||||||
textUnderlinePosition: 'under',
|
|
||||||
},
|
|
||||||
{ tag: [t.url, t.escape, t.special(t.string)], color: '#d3869b' },
|
|
||||||
{ tag: [t.meta], color: '#fabd2f' },
|
|
||||||
{ tag: [t.comment], color: '#928374', fontStyle: 'italic' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold', color: '#fe8019' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic', color: '#b8bb26' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#b8bb26' },
|
|
||||||
{ tag: [t.heading1, t.heading2], fontWeight: 'bold', color: '#b8bb26' },
|
|
||||||
{
|
|
||||||
tag: [t.heading3, t.heading4],
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#fabd2f',
|
|
||||||
},
|
|
||||||
{ tag: [t.heading5, t.heading6], color: '#fabd2f' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#d3869b' },
|
|
||||||
{ tag: [t.processingInstruction, t.inserted], color: '#83a598' },
|
|
||||||
{ tag: [t.contentSeparator], color: '#fb4934' },
|
|
||||||
{ tag: t.invalid, color: '#fe8019', borderBottom: `1px dotted #fb4934d` },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-131
@@ -1,131 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name gruvbox-light
|
|
||||||
* @author morhetz
|
|
||||||
* Name: Gruvbox
|
|
||||||
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-light.css
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#fbf1c7',
|
|
||||||
lineBackground: '#fbf1c799',
|
|
||||||
foreground: '#3c3836',
|
|
||||||
caret: '#af3a03',
|
|
||||||
selection: '#ebdbb2',
|
|
||||||
selectionMatch: '#bdae93',
|
|
||||||
lineHighlight: '#ebdbb2',
|
|
||||||
gutterBackground: '#ebdbb2',
|
|
||||||
gutterForeground: '#665c54',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#fbf1c7',
|
|
||||||
foreground: '#3c3836',
|
|
||||||
caret: '#af3a03',
|
|
||||||
selection: '#bdae9391',
|
|
||||||
selectionMatch: '#bdae9391',
|
|
||||||
lineHighlight: '#a37f2238',
|
|
||||||
gutterBackground: '#ebdbb2',
|
|
||||||
gutterForeground: '#665c54',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#9d0006' },
|
|
||||||
{
|
|
||||||
tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
|
|
||||||
color: '#427b58',
|
|
||||||
},
|
|
||||||
{ tag: [t.variableName], color: '#076678' },
|
|
||||||
{ tag: [t.function(t.variableName)], color: '#79740e', fontStyle: 'bold' },
|
|
||||||
{ tag: [t.labelName], color: '#3c3836' },
|
|
||||||
{
|
|
||||||
tag: [t.color, t.constant(t.name), t.standard(t.name)],
|
|
||||||
color: '#8f3f71',
|
|
||||||
},
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#3c3836' },
|
|
||||||
{ tag: [t.brace], color: '#3c3836' },
|
|
||||||
{
|
|
||||||
tag: [t.annotation],
|
|
||||||
color: '#9d0006',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
|
|
||||||
color: '#8f3f71',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.typeName, t.className],
|
|
||||||
color: '#b57614',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.operator, t.operatorKeyword],
|
|
||||||
color: '#9d0006',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.tagName],
|
|
||||||
color: '#427b58',
|
|
||||||
fontStyle: 'bold',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.squareBracket],
|
|
||||||
color: '#af3a03',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.angleBracket],
|
|
||||||
color: '#076678',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.attributeName],
|
|
||||||
color: '#427b58',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.regexp],
|
|
||||||
color: '#427b58',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.quote],
|
|
||||||
color: '#928374',
|
|
||||||
},
|
|
||||||
{ tag: [t.string], color: '#3c3836' },
|
|
||||||
{
|
|
||||||
tag: t.link,
|
|
||||||
color: '#7c6f64',
|
|
||||||
textDecoration: 'underline',
|
|
||||||
textUnderlinePosition: 'under',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.url, t.escape, t.special(t.string)],
|
|
||||||
color: '#8f3f71',
|
|
||||||
},
|
|
||||||
{ tag: [t.meta], color: '#b57614' },
|
|
||||||
{ tag: [t.comment], color: '#928374', fontStyle: 'italic' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold', color: '#af3a03' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic', color: '#79740e' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#79740e' },
|
|
||||||
{ tag: [t.heading1, t.heading2], fontWeight: 'bold', color: '#79740e' },
|
|
||||||
{
|
|
||||||
tag: [t.heading3, t.heading4],
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#b57614',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.heading5, t.heading6],
|
|
||||||
color: '#b57614',
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#8f3f71' },
|
|
||||||
{
|
|
||||||
tag: [t.processingInstruction, t.inserted],
|
|
||||||
color: '#076678',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: [t.contentSeparator],
|
|
||||||
color: '#9d0006',
|
|
||||||
},
|
|
||||||
{ tag: t.invalid, color: '#af3a03', borderBottom: `1px dotted #9d0006` },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-77
@@ -1,77 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#212121',
|
|
||||||
lineBackground: '#21212199',
|
|
||||||
foreground: '#bdbdbd',
|
|
||||||
caret: '#a0a4ae',
|
|
||||||
selection: '#d7d4f0',
|
|
||||||
selectionMatch: '#d7d4f0',
|
|
||||||
gutterBackground: '#212121',
|
|
||||||
gutterForeground: '#999',
|
|
||||||
gutterActiveForeground: '#4f5b66',
|
|
||||||
lineHighlight: '#111111',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#212121',
|
|
||||||
foreground: '#bdbdbd',
|
|
||||||
caret: '#a0a4ae',
|
|
||||||
selection: '#d7d4f063',
|
|
||||||
selectionMatch: '#d7d4f063',
|
|
||||||
gutterBackground: '#212121',
|
|
||||||
gutterForeground: '#999',
|
|
||||||
gutterActiveForeground: '#4f5b66',
|
|
||||||
lineHighlight: '#333333',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#cf6edf' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#56c8d8' },
|
|
||||||
{ tag: [t.propertyName], color: '#82AAFF' },
|
|
||||||
{ tag: [t.variableName], color: '#bdbdbd' },
|
|
||||||
{ tag: [t.function(t.variableName)], color: '#82AAFF' },
|
|
||||||
{ tag: [t.labelName], color: '#cf6edf' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#facf4e' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#56c8d8' },
|
|
||||||
{ tag: [t.brace], color: '#cf6edf' },
|
|
||||||
{ tag: [t.annotation], color: '#f07178' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#f07178' },
|
|
||||||
{ tag: [t.typeName, t.className], color: '#f07178' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#82AAFF' },
|
|
||||||
{ tag: [t.tagName], color: '#99d066' },
|
|
||||||
{ tag: [t.squareBracket], color: '#f07178' },
|
|
||||||
{ tag: [t.angleBracket], color: '#606f7a' },
|
|
||||||
{ tag: [t.attributeName], color: '#bdbdbd' },
|
|
||||||
{ tag: [t.regexp], color: '#f07178' },
|
|
||||||
{ tag: [t.quote], color: '#6abf69' },
|
|
||||||
{ tag: [t.string], color: '#99d066' },
|
|
||||||
{
|
|
||||||
tag: t.link,
|
|
||||||
color: '#56c8d8',
|
|
||||||
textDecoration: 'underline',
|
|
||||||
textUnderlinePosition: 'under',
|
|
||||||
},
|
|
||||||
{ tag: [t.url, t.escape, t.special(t.string)], color: '#facf4e' },
|
|
||||||
{ tag: [t.meta], color: '#707d8b' },
|
|
||||||
{ tag: [t.comment], color: '#707d8b', fontStyle: 'italic' },
|
|
||||||
{ tag: t.monospace, color: '#bdbdbd' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold', color: '#f07178' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic', color: '#99d066' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#facf4e' },
|
|
||||||
{ tag: t.heading1, fontWeight: 'bold', color: '#facf4e' },
|
|
||||||
{
|
|
||||||
tag: [t.heading2, t.heading3, t.heading4],
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#facf4e',
|
|
||||||
},
|
|
||||||
{ tag: [t.heading5, t.heading6], color: '#facf4e' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#56c8d8' },
|
|
||||||
{ tag: [t.processingInstruction, t.inserted], color: '#f07178' },
|
|
||||||
{ tag: [t.contentSeparator], color: '#56c8d8' },
|
|
||||||
{ tag: t.invalid, color: '#606f7a', borderBottom: `1px dotted #f07178` },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#FAFAFA',
|
|
||||||
lineBackground: '#FAFAFA99',
|
|
||||||
foreground: '#90A4AE',
|
|
||||||
caret: '#272727',
|
|
||||||
selection: '#80CBC440',
|
|
||||||
selectionMatch: '#FAFAFA',
|
|
||||||
gutterBackground: '#FAFAFA',
|
|
||||||
gutterForeground: '#90A4AE',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#CCD7DA50',
|
|
||||||
};
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#FAFAFA',
|
|
||||||
foreground: '#90A4AE',
|
|
||||||
caret: '#272727',
|
|
||||||
selection: '#80CBC440',
|
|
||||||
selectionMatch: '#80CBC440',
|
|
||||||
gutterBackground: '#FAFAFA',
|
|
||||||
gutterForeground: '#90A4AE',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#CCD7DA50',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#39ADB5' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#90A4AE' },
|
|
||||||
{ tag: [t.propertyName], color: '#6182B8' },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#91B859' },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: '#6182B8' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#39ADB5' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#90A4AE' },
|
|
||||||
{ tag: [t.className], color: '#E2931D' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#F76D47' },
|
|
||||||
{ tag: [t.typeName], color: '#E2931D', fontStyle: '#E2931D' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#39ADB5' },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#91B859' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#90A4AE' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#39ADB5' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#90A4AE' },
|
|
||||||
{ tag: t.invalid, color: '#E5393570' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#272822',
|
|
||||||
lineBackground: '#27282299',
|
|
||||||
foreground: '#FFFFFF',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: '#49483E',
|
|
||||||
selectionMatch: '#49483E',
|
|
||||||
gutterBackground: '#272822',
|
|
||||||
gutterForeground: '#FFFFFF70',
|
|
||||||
lineHighlight: '#00000059',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#272822',
|
|
||||||
foreground: '#FFFFFF',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: '#49483E',
|
|
||||||
selectionMatch: '#49483E',
|
|
||||||
gutterBackground: '#272822',
|
|
||||||
gutterForeground: '#FFFFFF70',
|
|
||||||
lineHighlight: '#0000003b',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.labelName, color: '#bababa' },
|
|
||||||
{ tag: [t.comment, t.documentMeta], color: '#8292a2' },
|
|
||||||
{ tag: [t.number, t.bool, t.null, t.atom], color: '#ae81ff' },
|
|
||||||
{ tag: [t.attributeValue, t.className, t.name], color: '#e6db74' },
|
|
||||||
{ tag: [t.propertyName, t.attributeName], color: '#a6e22e' },
|
|
||||||
{ tag: [t.variableName], color: '#9effff' },
|
|
||||||
{ tag: [t.squareBracket], color: '#bababa' },
|
|
||||||
{ tag: [t.string, t.special(t.brace)], color: '#e6db74' },
|
|
||||||
{ tag: [t.regexp, t.className, t.typeName, t.definition(t.typeName)], color: '#66d9ef' },
|
|
||||||
{
|
|
||||||
tag: [t.definition(t.variableName), t.definition(t.propertyName), t.function(t.variableName)],
|
|
||||||
color: '#a6e22e',
|
|
||||||
},
|
|
||||||
// { tag: t.keyword, color: '#f92672' },
|
|
||||||
{ tag: [t.keyword, t.definitionKeyword, t.modifier, t.tagName, t.angleBracket], color: '#f92672' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#f2f1f8',
|
|
||||||
lineBackground: '#f2f1f899',
|
|
||||||
foreground: '#0c006b',
|
|
||||||
caret: '#5c49e9',
|
|
||||||
selection: '#d5d1f2',
|
|
||||||
selectionMatch: '#d5d1f2',
|
|
||||||
gutterBackground: '#f2f1f8',
|
|
||||||
gutterForeground: '#0c006b70',
|
|
||||||
lineHighlight: '#e1def3',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#f2f1f8',
|
|
||||||
foreground: '#0c006b',
|
|
||||||
caret: '#5c49e9',
|
|
||||||
selection: '#d5d1f2',
|
|
||||||
selectionMatch: '#d5d1f2',
|
|
||||||
gutterBackground: '#f2f1f8',
|
|
||||||
gutterForeground: '#0c006b70',
|
|
||||||
lineHighlight: '#16067911',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.comment, color: '#9995b7' },
|
|
||||||
{
|
|
||||||
tag: t.keyword,
|
|
||||||
color: '#ff5792',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
{ tag: [t.definitionKeyword, t.modifier], color: '#ff5792' },
|
|
||||||
{ tag: [t.className, t.tagName, t.definition(t.typeName)], color: '#0094f0' },
|
|
||||||
{ tag: [t.number, t.bool, t.null, t.special(t.brace)], color: '#5842ff' },
|
|
||||||
{ tag: [t.definition(t.propertyName), t.function(t.variableName)], color: '#0095a8' },
|
|
||||||
{ tag: t.typeName, color: '#b3694d' },
|
|
||||||
{ tag: [t.propertyName, t.variableName], color: '#fa8900' },
|
|
||||||
{ tag: t.operator, color: '#ff5792' },
|
|
||||||
{ tag: t.self, color: '#e64100' },
|
|
||||||
{ tag: [t.string, t.regexp], color: '#00b368' },
|
|
||||||
{ tag: [t.paren, t.bracket], color: '#0431fa' },
|
|
||||||
{ tag: t.labelName, color: '#00bdd6' },
|
|
||||||
{ tag: t.attributeName, color: '#e64100' },
|
|
||||||
{ tag: t.angleBracket, color: '#9995b7' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
Vendored
-78
@@ -1,78 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#2e3440',
|
|
||||||
lineBackground: '#2e344099',
|
|
||||||
foreground: '#FFFFFF',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: '#3b4252',
|
|
||||||
selectionMatch: '#e5e9f0',
|
|
||||||
gutterBackground: '#2e3440',
|
|
||||||
gutterForeground: '#4c566a',
|
|
||||||
gutterActiveForeground: '#d8dee9',
|
|
||||||
lineHighlight: '#4c566a',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Colors from https://www.nordtheme.com/docs/colors-and-palettes
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#2e3440',
|
|
||||||
foreground: '#FFFFFF',
|
|
||||||
caret: '#FFFFFF',
|
|
||||||
selection: '#00000073',
|
|
||||||
selectionMatch: '#00000073',
|
|
||||||
gutterBackground: '#2e3440',
|
|
||||||
gutterForeground: '#4c566a',
|
|
||||||
gutterActiveForeground: '#d8dee9',
|
|
||||||
lineHighlight: '#4c566a29',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#5e81ac' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: '#88c0d0' },
|
|
||||||
{ tag: [t.variableName], color: '#8fbcbb' },
|
|
||||||
{ tag: [t.function(t.variableName)], color: '#8fbcbb' },
|
|
||||||
{ tag: [t.labelName], color: '#81a1c1' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#5e81ac' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#a3be8c' },
|
|
||||||
{ tag: [t.brace], color: '#8fbcbb' },
|
|
||||||
{ tag: [t.annotation], color: '#d30102' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#b48ead' },
|
|
||||||
{ tag: [t.typeName, t.className], color: '#ebcb8b' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#a3be8c' },
|
|
||||||
{ tag: [t.tagName], color: '#b48ead' },
|
|
||||||
{ tag: [t.squareBracket], color: '#bf616a' },
|
|
||||||
{ tag: [t.angleBracket], color: '#d08770' },
|
|
||||||
{ tag: [t.attributeName], color: '#ebcb8b' },
|
|
||||||
{ tag: [t.regexp], color: '#5e81ac' },
|
|
||||||
{ tag: [t.quote], color: '#b48ead' },
|
|
||||||
{ tag: [t.string], color: '#a3be8c' },
|
|
||||||
{
|
|
||||||
tag: t.link,
|
|
||||||
color: '#a3be8c',
|
|
||||||
textDecoration: 'underline',
|
|
||||||
textUnderlinePosition: 'under',
|
|
||||||
},
|
|
||||||
{ tag: [t.url, t.escape, t.special(t.string)], color: '#8fbcbb' },
|
|
||||||
{ tag: [t.meta], color: '#88c0d0' },
|
|
||||||
{ tag: [t.monospace], color: '#d8dee9', fontStyle: 'italic' },
|
|
||||||
{ tag: [t.comment], color: '#4c566a', fontStyle: 'italic' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold', color: '#5e81ac' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic', color: '#5e81ac' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#5e81ac' },
|
|
||||||
{ tag: t.special(t.heading1), fontWeight: 'bold', color: '#5e81ac' },
|
|
||||||
{ tag: t.heading1, fontWeight: 'bold', color: '#5e81ac' },
|
|
||||||
{
|
|
||||||
tag: [t.heading2, t.heading3, t.heading4],
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#5e81ac',
|
|
||||||
},
|
|
||||||
{ tag: [t.heading5, t.heading6], color: '#5e81ac' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#d08770' },
|
|
||||||
{ tag: [t.processingInstruction, t.inserted], color: '#8fbcbb' },
|
|
||||||
{ tag: [t.contentSeparator], color: '#ebcb8b' },
|
|
||||||
{ tag: t.invalid, color: '#434c5e', borderBottom: `1px dotted #d30102` },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
const hex = ['#000000', '#ff5356', '#bd312a', '#54636D', '#171717'];
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: hex[0],
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: hex[2],
|
|
||||||
selection: hex[4],
|
|
||||||
selectionMatch: hex[0],
|
|
||||||
gutterBackground: hex[0],
|
|
||||||
gutterForeground: hex[3],
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: hex[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: hex[2],
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
|
|
||||||
{ tag: t.comment, color: hex[3] },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
|
|
||||||
{ tag: [t.attributeName, t.number], color: hex[1] },
|
|
||||||
{ tag: t.keyword, color: hex[2] },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#002b36',
|
|
||||||
lineBackground: '#002b3699',
|
|
||||||
foreground: '#93a1a1',
|
|
||||||
caret: '#839496',
|
|
||||||
selection: '#173541',
|
|
||||||
selectionMatch: '#aafe661a',
|
|
||||||
gutterBackground: '#00252f',
|
|
||||||
gutterForeground: '#839496',
|
|
||||||
lineHighlight: '#173541',
|
|
||||||
};
|
|
||||||
|
|
||||||
const c = {
|
|
||||||
background: '#002B36',
|
|
||||||
foreground: '#839496',
|
|
||||||
selection: '#004454AA',
|
|
||||||
selectionMatch: '#005A6FAA',
|
|
||||||
cursor: '#D30102',
|
|
||||||
dropdownBackground: '#00212B',
|
|
||||||
dropdownBorder: '#2AA19899',
|
|
||||||
activeLine: '#00cafe11',
|
|
||||||
matchingBracket: '#073642',
|
|
||||||
keyword: '#859900',
|
|
||||||
storage: '#93A1A1',
|
|
||||||
variable: '#268BD2',
|
|
||||||
parameter: '#268BD2',
|
|
||||||
function: '#268BD2',
|
|
||||||
string: '#2AA198',
|
|
||||||
constant: '#CB4B16',
|
|
||||||
type: '#859900',
|
|
||||||
class: '#268BD2',
|
|
||||||
number: '#D33682',
|
|
||||||
comment: '#586E75',
|
|
||||||
heading: '#268BD2',
|
|
||||||
invalid: '#DC322F',
|
|
||||||
regexp: '#DC322F',
|
|
||||||
tag: '#268BD2',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: c.background,
|
|
||||||
foreground: c.foreground,
|
|
||||||
caret: c.cursor,
|
|
||||||
selection: c.selection,
|
|
||||||
selectionMatch: c.selection,
|
|
||||||
gutterBackground: c.background,
|
|
||||||
gutterForeground: c.foreground,
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: c.activeLine,
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: c.keyword },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: c.variable },
|
|
||||||
{ tag: [t.propertyName], color: c.function },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: c.string },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: c.function },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: c.constant },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: c.variable },
|
|
||||||
{ tag: [t.className], color: c.class },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: c.number },
|
|
||||||
{ tag: [t.typeName], color: c.type, fontStyle: c.type },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: c.keyword },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: c.regexp },
|
|
||||||
{ tag: [t.meta, t.comment], color: c.comment },
|
|
||||||
{ tag: t.tagName, color: c.tag },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: c.heading },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: c.variable },
|
|
||||||
{ tag: t.invalid, color: c.invalid },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-82
@@ -1,82 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
// this is slightly different from https://thememirror.net/solarized-light
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#fdf6e3',
|
|
||||||
lineBackground: '#fdf6e399',
|
|
||||||
foreground: '#657b83',
|
|
||||||
caret: '#586e75',
|
|
||||||
selection: '#dfd9c8',
|
|
||||||
selectionMatch: '#dfd9c8',
|
|
||||||
gutterBackground: '#00000010',
|
|
||||||
gutterForeground: '#657b83',
|
|
||||||
lineHighlight: '#dfd9c8',
|
|
||||||
};
|
|
||||||
|
|
||||||
const c = {
|
|
||||||
background: '#FDF6E3',
|
|
||||||
foreground: '#657B83',
|
|
||||||
selection: '#EEE8D5',
|
|
||||||
selectionMatch: '#EEE8D5',
|
|
||||||
cursor: '#657B83',
|
|
||||||
dropdownBackground: '#EEE8D5',
|
|
||||||
dropdownBorder: '#D3AF86',
|
|
||||||
activeLine: '#3d392d11',
|
|
||||||
matchingBracket: '#EEE8D5',
|
|
||||||
keyword: '#859900',
|
|
||||||
storage: '#586E75',
|
|
||||||
variable: '#268BD2',
|
|
||||||
parameter: '#268BD2',
|
|
||||||
function: '#268BD2',
|
|
||||||
string: '#2AA198',
|
|
||||||
constant: '#CB4B16',
|
|
||||||
type: '#859900',
|
|
||||||
class: '#268BD2',
|
|
||||||
number: '#D33682',
|
|
||||||
comment: '#93A1A1',
|
|
||||||
heading: '#268BD2',
|
|
||||||
invalid: '#DC322F',
|
|
||||||
regexp: '#DC322F',
|
|
||||||
tag: '#268BD2',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: c.background,
|
|
||||||
foreground: c.foreground,
|
|
||||||
caret: c.cursor,
|
|
||||||
selection: c.selection,
|
|
||||||
selectionMatch: c.selectionMatch,
|
|
||||||
gutterBackground: c.background,
|
|
||||||
gutterForeground: c.foreground,
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: c.activeLine,
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: c.keyword },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: c.variable },
|
|
||||||
{ tag: [t.propertyName], color: c.function },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: c.string },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: c.function },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: c.constant },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: c.variable },
|
|
||||||
{ tag: [t.className], color: c.class },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: c.number },
|
|
||||||
{ tag: [t.typeName], color: c.type, fontStyle: c.type },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: c.keyword },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: c.regexp },
|
|
||||||
{ tag: [t.meta, t.comment], color: c.comment },
|
|
||||||
{ tag: t.tagName, color: c.tag },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: c.heading },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: c.variable },
|
|
||||||
{ tag: t.invalid, color: c.invalid },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Atom One
|
|
||||||
* Atom One dark syntax theme
|
|
||||||
*
|
|
||||||
* https://github.com/atom/one-dark-syntax
|
|
||||||
*/
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
const hex = ['#1e1e1e', '#fbde2d', '#ff1493', '#4c83ff', '#ededed', '#cccccc', '#ffffff30', '#dc2f8c'];
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#000000',
|
|
||||||
lineBackground: 'transparent',
|
|
||||||
foreground: hex[4],
|
|
||||||
selection: hex[6],
|
|
||||||
gutterBackground: hex[0],
|
|
||||||
gutterForeground: hex[5],
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: hex[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings,
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
|
||||||
color: hex[4],
|
|
||||||
},
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[3] },
|
|
||||||
|
|
||||||
{ tag: t.comment, color: '#54636D' },
|
|
||||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[4] },
|
|
||||||
{ tag: [t.attributeName, t.number], color: hex[3] },
|
|
||||||
{ tag: t.keyword, color: hex[1] },
|
|
||||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+13
-16
@@ -1,23 +1,20 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#222',
|
|
||||||
lineBackground: '#22222299',
|
|
||||||
foreground: '#fff',
|
|
||||||
caret: '#ffcc00',
|
|
||||||
selection: 'rgba(128, 203, 196, 0.5)',
|
|
||||||
selectionMatch: '#036dd626',
|
|
||||||
lineHighlight: '#00000050',
|
|
||||||
gutterBackground: 'transparent',
|
|
||||||
gutterForeground: '#8a919966',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
export default createTheme({
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
settings,
|
settings: {
|
||||||
|
background: '#222',
|
||||||
|
foreground: '#75baff', // whats that?
|
||||||
|
caret: '#ffcc00',
|
||||||
|
selection: 'rgba(128, 203, 196, 0.5)',
|
||||||
|
selectionMatch: '#036dd626',
|
||||||
|
// lineHighlight: '#8a91991a', // original
|
||||||
|
lineHighlight: '#00000050',
|
||||||
|
gutterBackground: 'transparent',
|
||||||
|
// gutterForeground: '#8a919966',
|
||||||
|
gutterForeground: '#8a919966',
|
||||||
|
},
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#89ddff' },
|
|
||||||
{ tag: t.labelName, color: '#89ddff' },
|
{ tag: t.labelName, color: '#89ddff' },
|
||||||
{ tag: t.keyword, color: '#c792ea' },
|
{ tag: t.keyword, color: '#c792ea' },
|
||||||
{ tag: t.operator, color: '#89ddff' },
|
{ tag: t.operator, color: '#89ddff' },
|
||||||
|
|||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#303841',
|
|
||||||
lineBackground: '#30384199',
|
|
||||||
foreground: '#FFFFFF',
|
|
||||||
caret: '#FBAC52',
|
|
||||||
selection: '#4C5964',
|
|
||||||
selectionMatch: '#3A546E',
|
|
||||||
gutterBackground: '#303841',
|
|
||||||
gutterForeground: '#FFFFFF70',
|
|
||||||
lineHighlight: '#00000059',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#303841',
|
|
||||||
foreground: '#FFFFFF',
|
|
||||||
caret: '#FBAC52',
|
|
||||||
selection: '#4C5964',
|
|
||||||
selectionMatch: '#3A546E',
|
|
||||||
gutterBackground: '#303841',
|
|
||||||
gutterForeground: '#FFFFFF70',
|
|
||||||
lineHighlight: '#00000059',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.labelName, color: '#A2A9B5' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#A2A9B5' },
|
|
||||||
{ tag: [t.attributeName, t.keyword], color: '#B78FBA' },
|
|
||||||
{ tag: t.function(t.variableName), color: '#5AB0B0' },
|
|
||||||
{ tag: [t.string, t.regexp, t.attributeValue], color: '#99C592' },
|
|
||||||
{ tag: t.operator, color: '#f47954' },
|
|
||||||
// { tag: t.moduleKeyword, color: 'red' },
|
|
||||||
{ tag: [t.tagName, t.modifier], color: '#E35F63' },
|
|
||||||
{ tag: [t.number, t.definition(t.tagName), t.className, t.definition(t.variableName)], color: '#fbac52' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#E35F63' },
|
|
||||||
{ tag: t.variableName, color: '#539ac4' },
|
|
||||||
{ tag: [t.propertyName, t.typeName], color: '#629ccd' },
|
|
||||||
{ tag: t.propertyName, color: '#36b7b5' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
|
||||||
let colorA = '#6edee4';
|
let colorA = '#6edee4';
|
||||||
//let colorB = 'magenta';
|
//let colorB = 'magenta';
|
||||||
@@ -17,7 +17,7 @@ export const settings = {
|
|||||||
lineBackground: '#00000040',
|
lineBackground: '#00000040',
|
||||||
gutterBackground: 'transparent',
|
gutterBackground: 'transparent',
|
||||||
gutterForeground: '#8a919966',
|
gutterForeground: '#8a919966',
|
||||||
// customStyle: '.cm-line { line-height: 1 }',
|
customStyle: '.cm-line { line-height: 1 }',
|
||||||
};
|
};
|
||||||
|
|
||||||
let punctuation = colorD;
|
let punctuation = colorD;
|
||||||
|
|||||||
+20
-20
@@ -1,5 +1,5 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
export const settings = {
|
export const settings = {
|
||||||
background: 'black',
|
background: 'black',
|
||||||
foreground: '#41FF00', // whats that?
|
foreground: '#41FF00', // whats that?
|
||||||
@@ -14,24 +14,24 @@ export default createTheme({
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
settings,
|
settings,
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: t.labelName, color: 'inherit' },
|
{ tag: t.labelName, color: '#41FF00' },
|
||||||
{ tag: t.keyword, color: 'inherit' },
|
{ tag: t.keyword, color: '#41FF00' },
|
||||||
{ tag: t.operator, color: 'inherit' },
|
{ tag: t.operator, color: '#41FF00' },
|
||||||
{ tag: t.special(t.variableName), color: 'inherit' },
|
{ tag: t.special(t.variableName), color: '#41FF00' },
|
||||||
{ tag: t.typeName, color: 'inherit' },
|
{ tag: t.typeName, color: '#41FF00' },
|
||||||
{ tag: t.atom, color: 'inherit' },
|
{ tag: t.atom, color: '#41FF00' },
|
||||||
{ tag: t.number, color: 'inherit' },
|
{ tag: t.number, color: '#41FF00' },
|
||||||
{ tag: t.definition(t.variableName), color: 'inherit' },
|
{ tag: t.definition(t.variableName), color: '#41FF00' },
|
||||||
{ tag: t.string, color: 'inherit' },
|
{ tag: t.string, color: '#41FF00' },
|
||||||
{ tag: t.special(t.string), color: 'inherit' },
|
{ tag: t.special(t.string), color: '#41FF00' },
|
||||||
{ tag: t.comment, color: 'inherit' },
|
{ tag: t.comment, color: '#41FF00' },
|
||||||
{ tag: t.variableName, color: 'inherit' },
|
{ tag: t.variableName, color: '#41FF00' },
|
||||||
{ tag: t.tagName, color: 'inherit' },
|
{ tag: t.tagName, color: '#41FF00' },
|
||||||
{ tag: t.bracket, color: 'inherit' },
|
{ tag: t.bracket, color: '#41FF00' },
|
||||||
{ tag: t.meta, color: 'inherit' },
|
{ tag: t.meta, color: '#41FF00' },
|
||||||
{ tag: t.attributeName, color: 'inherit' },
|
{ tag: t.attributeName, color: '#41FF00' },
|
||||||
{ tag: t.propertyName, color: 'inherit' },
|
{ tag: t.propertyName, color: '#41FF00' },
|
||||||
{ tag: t.className, color: 'inherit' },
|
{ tag: t.className, color: '#41FF00' },
|
||||||
{ tag: t.invalid, color: 'inherit' },
|
{ tag: t.invalid, color: '#41FF00' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
import { EditorView } from '@codemirror/view';
|
|
||||||
import { syntaxHighlighting } from '@codemirror/language';
|
|
||||||
import { HighlightStyle } from '@codemirror/language';
|
|
||||||
|
|
||||||
export const createTheme = ({ theme, settings, styles }) => {
|
|
||||||
const _theme = EditorView.theme(
|
|
||||||
{
|
|
||||||
'&': {
|
|
||||||
color: settings.foreground,
|
|
||||||
backgroundColor: settings.background,
|
|
||||||
},
|
|
||||||
'.cm-gutters': {
|
|
||||||
backgroundColor: settings.gutterBackground,
|
|
||||||
color: settings.gutterForeground,
|
|
||||||
//borderRightColor: settings.gutterBorder
|
|
||||||
},
|
|
||||||
'.cm-content': {
|
|
||||||
caretColor: settings.caret,
|
|
||||||
},
|
|
||||||
'.cm-cursor, .cm-dropCursor': {
|
|
||||||
borderLeftColor: settings.caret,
|
|
||||||
},
|
|
||||||
'.cm-activeLineGutter': {
|
|
||||||
// color: settings.gutterActiveForeground
|
|
||||||
backgroundColor: settings.lineHighlight,
|
|
||||||
},
|
|
||||||
'.cm-activeLine': {
|
|
||||||
backgroundColor: settings.lineHighlight,
|
|
||||||
},
|
|
||||||
'&.cm-focused .cm-selectionBackground, & .cm-line::selection, & .cm-selectionLayer .cm-selectionBackground, .cm-content ::selection':
|
|
||||||
{
|
|
||||||
background: settings.selection + ' !important',
|
|
||||||
},
|
|
||||||
'& .cm-selectionMatch': {
|
|
||||||
backgroundColor: settings.selectionMatch,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ dark: theme === 'dark' },
|
|
||||||
);
|
|
||||||
const highlightStyle = HighlightStyle.define(styles);
|
|
||||||
return [_theme, syntaxHighlighting(highlightStyle)];
|
|
||||||
};
|
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#24283b',
|
|
||||||
lineBackground: '#24283b99',
|
|
||||||
foreground: '#7982a9',
|
|
||||||
caret: '#c0caf5',
|
|
||||||
selection: '#6f7bb630',
|
|
||||||
selectionMatch: '#1f2335',
|
|
||||||
gutterBackground: '#24283b',
|
|
||||||
gutterForeground: '#7982a9',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#292e42',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#24283b',
|
|
||||||
foreground: '#7982a9',
|
|
||||||
caret: '#c0caf5',
|
|
||||||
selection: '#6f7bb630',
|
|
||||||
selectionMatch: '#343b5f',
|
|
||||||
gutterBackground: '#24283b',
|
|
||||||
gutterForeground: '#7982a9',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#292e427a',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#bb9af7' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#c0caf5' },
|
|
||||||
{ tag: [t.propertyName], color: '#7aa2f7' },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#9ece6a' },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: '#7aa2f7' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#bb9af7' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#c0caf5' },
|
|
||||||
{ tag: [t.className], color: '#c0caf5' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#ff9e64' },
|
|
||||||
{ tag: [t.typeName], color: '#2ac3de', fontStyle: '#2ac3de' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#bb9af7' },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#b4f9f8' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#565f89' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#89ddff' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#c0caf5' },
|
|
||||||
{ tag: t.invalid, color: '#ff5370' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#1a1b26',
|
|
||||||
lineBackground: '#1a1b2699',
|
|
||||||
foreground: '#787c99',
|
|
||||||
caret: '#c0caf5',
|
|
||||||
selection: '#515c7e40',
|
|
||||||
selectionMatch: '#16161e',
|
|
||||||
gutterBackground: '#1a1b26',
|
|
||||||
gutterForeground: '#787c99',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#1e202e',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#1a1b26',
|
|
||||||
foreground: '#787c99',
|
|
||||||
caret: '#c0caf5',
|
|
||||||
selection: '#515c7e40',
|
|
||||||
selectionMatch: '#16161e',
|
|
||||||
gutterBackground: '#1a1b26',
|
|
||||||
gutterForeground: '#787c99',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#474b6611',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#bb9af7' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#c0caf5' },
|
|
||||||
{ tag: [t.propertyName], color: '#7aa2f7' },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#9ece6a' },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: '#7aa2f7' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#bb9af7' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#c0caf5' },
|
|
||||||
{ tag: [t.className], color: '#c0caf5' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#ff9e64' },
|
|
||||||
{ tag: [t.typeName], color: '#0db9d7' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#bb9af7' },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#b4f9f8' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#444b6a' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#89ddff' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#c0caf5' },
|
|
||||||
{ tag: t.invalid, color: '#ff5370' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#e1e2e7',
|
|
||||||
lineBackground: '#e1e2e799',
|
|
||||||
foreground: '#3760bf',
|
|
||||||
caret: '#3760bf',
|
|
||||||
selection: '#99a7df',
|
|
||||||
selectionMatch: '#99a7df',
|
|
||||||
gutterBackground: '#e1e2e7',
|
|
||||||
gutterForeground: '#3760bf',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#5f5faf11',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#e1e2e7',
|
|
||||||
foreground: '#3760bf',
|
|
||||||
caret: '#3760bf',
|
|
||||||
selection: '#99a7df',
|
|
||||||
selectionMatch: '#99a7df',
|
|
||||||
gutterBackground: '#e1e2e7',
|
|
||||||
gutterForeground: '#3760bf',
|
|
||||||
gutterBorder: 'transparent',
|
|
||||||
lineHighlight: '#5f5faf11',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.keyword, color: '#007197' },
|
|
||||||
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#3760bf' },
|
|
||||||
{ tag: [t.propertyName], color: '#3760bf' },
|
|
||||||
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#587539' },
|
|
||||||
{ tag: [t.function(t.variableName), t.labelName], color: '#3760bf' },
|
|
||||||
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#3760bf' },
|
|
||||||
{ tag: [t.definition(t.name), t.separator], color: '#3760bf' },
|
|
||||||
{ tag: [t.className], color: '#3760bf' },
|
|
||||||
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#b15c00' },
|
|
||||||
{ tag: [t.typeName], color: '#007197', fontStyle: '#007197' },
|
|
||||||
{ tag: [t.operator, t.operatorKeyword], color: '#007197' },
|
|
||||||
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#587539' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#848cb5' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.link, textDecoration: 'underline' },
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#b15c00' },
|
|
||||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#3760bf' },
|
|
||||||
{ tag: t.invalid, color: '#f52a65' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-80
@@ -1,80 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#1e1e1e',
|
|
||||||
lineBackground: '#1e1e1e99',
|
|
||||||
foreground: '#fff',
|
|
||||||
caret: '#c6c6c6',
|
|
||||||
selection: '#6199ff2f',
|
|
||||||
selectionMatch: '#72a1ff59',
|
|
||||||
lineHighlight: '#ffffff0f',
|
|
||||||
gutterBackground: '#1e1e1e',
|
|
||||||
gutterForeground: '#838383',
|
|
||||||
gutterActiveForeground: '#fff',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
background: '#1e1e1e',
|
|
||||||
foreground: '#fff',
|
|
||||||
caret: '#c6c6c6',
|
|
||||||
selection: '#6199ff2f',
|
|
||||||
selectionMatch: '#72a1ff59',
|
|
||||||
lineHighlight: '#ffffff0f',
|
|
||||||
gutterBackground: '#1e1e1e',
|
|
||||||
gutterForeground: '#838383',
|
|
||||||
gutterActiveForeground: '#fff',
|
|
||||||
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [
|
|
||||||
t.keyword,
|
|
||||||
t.operatorKeyword,
|
|
||||||
t.modifier,
|
|
||||||
t.color,
|
|
||||||
t.constant(t.name),
|
|
||||||
t.standard(t.name),
|
|
||||||
t.standard(t.tagName),
|
|
||||||
t.special(t.brace),
|
|
||||||
t.atom,
|
|
||||||
t.bool,
|
|
||||||
t.special(t.variableName),
|
|
||||||
],
|
|
||||||
color: '#569cd6',
|
|
||||||
},
|
|
||||||
{ tag: [t.controlKeyword, t.moduleKeyword], color: '#c586c0' },
|
|
||||||
{
|
|
||||||
tag: [
|
|
||||||
t.name,
|
|
||||||
t.deleted,
|
|
||||||
t.character,
|
|
||||||
t.macroName,
|
|
||||||
t.propertyName,
|
|
||||||
t.variableName,
|
|
||||||
t.labelName,
|
|
||||||
t.definition(t.name),
|
|
||||||
],
|
|
||||||
color: '#9cdcfe',
|
|
||||||
},
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#9cdcfe' },
|
|
||||||
{
|
|
||||||
tag: [t.typeName, t.className, t.tagName, t.number, t.changed, t.annotation, t.self, t.namespace],
|
|
||||||
color: '#4ec9b0',
|
|
||||||
},
|
|
||||||
{ tag: [t.function(t.variableName), t.function(t.propertyName)], color: '#dcdcaa' },
|
|
||||||
{ tag: [t.number], color: '#b5cea8' },
|
|
||||||
{ tag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp], color: '#d4d4d4' },
|
|
||||||
{ tag: [t.regexp], color: '#d16969' },
|
|
||||||
{ tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: '#ce9178' },
|
|
||||||
{ tag: [t.angleBracket], color: '#808080' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#6a9955' },
|
|
||||||
{ tag: t.link, color: '#6a9955', textDecoration: 'underline' },
|
|
||||||
{ tag: t.invalid, color: '#ff0000' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
-81
@@ -1,81 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
background: '#ffffff',
|
|
||||||
lineBackground: '#ffffff50',
|
|
||||||
foreground: '#383a42',
|
|
||||||
caret: '#000',
|
|
||||||
selection: '#add6ff',
|
|
||||||
selectionMatch: '#a8ac94',
|
|
||||||
lineHighlight: '#99999926',
|
|
||||||
gutterBackground: '#fff',
|
|
||||||
gutterForeground: '#237893',
|
|
||||||
gutterActiveForeground: '#0b216f',
|
|
||||||
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#ffffff',
|
|
||||||
foreground: '#383a42',
|
|
||||||
caret: '#000',
|
|
||||||
selection: '#add6ff',
|
|
||||||
selectionMatch: '#a8ac94',
|
|
||||||
lineHighlight: '#99999926',
|
|
||||||
gutterBackground: '#fff',
|
|
||||||
gutterForeground: '#237893',
|
|
||||||
gutterActiveForeground: '#0b216f',
|
|
||||||
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: [
|
|
||||||
t.keyword,
|
|
||||||
t.operatorKeyword,
|
|
||||||
t.modifier,
|
|
||||||
t.color,
|
|
||||||
t.constant(t.name),
|
|
||||||
t.standard(t.name),
|
|
||||||
t.standard(t.tagName),
|
|
||||||
t.special(t.brace),
|
|
||||||
t.atom,
|
|
||||||
t.bool,
|
|
||||||
t.special(t.variableName),
|
|
||||||
],
|
|
||||||
color: '#0000ff',
|
|
||||||
},
|
|
||||||
{ tag: [t.moduleKeyword, t.controlKeyword], color: '#af00db' },
|
|
||||||
{
|
|
||||||
tag: [
|
|
||||||
t.name,
|
|
||||||
t.deleted,
|
|
||||||
t.character,
|
|
||||||
t.macroName,
|
|
||||||
t.propertyName,
|
|
||||||
t.variableName,
|
|
||||||
t.labelName,
|
|
||||||
t.definition(t.name),
|
|
||||||
],
|
|
||||||
color: '#0070c1',
|
|
||||||
},
|
|
||||||
{ tag: t.heading, fontWeight: 'bold', color: '#0070c1' },
|
|
||||||
{
|
|
||||||
tag: [t.typeName, t.className, t.tagName, t.number, t.changed, t.annotation, t.self, t.namespace],
|
|
||||||
color: '#267f99',
|
|
||||||
},
|
|
||||||
{ tag: [t.function(t.variableName), t.function(t.propertyName)], color: '#795e26' },
|
|
||||||
{ tag: [t.number], color: '#098658' },
|
|
||||||
{ tag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp], color: '#383a42' },
|
|
||||||
{ tag: [t.regexp], color: '#af00db' },
|
|
||||||
{ tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: '#a31515' },
|
|
||||||
{ tag: [t.angleBracket], color: '#383a42' },
|
|
||||||
{ tag: t.strong, fontWeight: 'bold' },
|
|
||||||
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
||||||
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
||||||
{ tag: [t.meta, t.comment], color: '#008000' },
|
|
||||||
{ tag: t.link, color: '#4078f2', textDecoration: 'underline' },
|
|
||||||
{ tag: t.invalid, color: '#e45649' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+20
-20
@@ -1,5 +1,5 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from './theme-helper.mjs';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
export const settings = {
|
export const settings = {
|
||||||
background: 'white',
|
background: 'white',
|
||||||
foreground: 'black', // whats that?
|
foreground: 'black', // whats that?
|
||||||
@@ -16,24 +16,24 @@ export default createTheme({
|
|||||||
theme: 'light',
|
theme: 'light',
|
||||||
settings,
|
settings,
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: t.labelName, color: 'inherit' },
|
{ tag: t.labelName, color: 'black' },
|
||||||
{ tag: t.keyword, color: 'inherit' },
|
{ tag: t.keyword, color: 'black' },
|
||||||
{ tag: t.operator, color: 'inherit' },
|
{ tag: t.operator, color: 'black' },
|
||||||
{ tag: t.special(t.variableName), color: 'inherit' },
|
{ tag: t.special(t.variableName), color: 'black' },
|
||||||
{ tag: t.typeName, color: 'inherit' },
|
{ tag: t.typeName, color: 'black' },
|
||||||
{ tag: t.atom, color: 'inherit' },
|
{ tag: t.atom, color: 'black' },
|
||||||
{ tag: t.number, color: 'inherit' },
|
{ tag: t.number, color: 'black' },
|
||||||
{ tag: t.definition(t.variableName), color: 'inherit' },
|
{ tag: t.definition(t.variableName), color: 'black' },
|
||||||
{ tag: t.string, color: 'inherit' },
|
{ tag: t.string, color: 'black' },
|
||||||
{ tag: t.special(t.string), color: 'inherit' },
|
{ tag: t.special(t.string), color: 'black' },
|
||||||
{ tag: t.comment, color: 'inherit' },
|
{ tag: t.comment, color: 'black' },
|
||||||
{ tag: t.variableName, color: 'inherit' },
|
{ tag: t.variableName, color: 'black' },
|
||||||
{ tag: t.tagName, color: 'inherit' },
|
{ tag: t.tagName, color: 'black' },
|
||||||
{ tag: t.bracket, color: 'inherit' },
|
{ tag: t.bracket, color: 'black' },
|
||||||
{ tag: t.meta, color: 'inherit' },
|
{ tag: t.meta, color: 'black' },
|
||||||
{ tag: t.attributeName, color: 'inherit' },
|
{ tag: t.attributeName, color: 'black' },
|
||||||
{ tag: t.propertyName, color: 'inherit' },
|
{ tag: t.propertyName, color: 'black' },
|
||||||
{ tag: t.className, color: 'inherit' },
|
{ tag: t.className, color: 'black' },
|
||||||
{ tag: t.invalid, color: 'inherit' },
|
{ tag: t.invalid, color: 'black' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from './theme-helper.mjs';
|
|
||||||
|
|
||||||
export const settings = {
|
|
||||||
light: true,
|
|
||||||
background: '#fff',
|
|
||||||
lineBackground: '#ffffff99',
|
|
||||||
foreground: '#3D3D3D',
|
|
||||||
selection: '#BBDFFF',
|
|
||||||
selectionMatch: '#BBDFFF',
|
|
||||||
gutterBackground: '#fff',
|
|
||||||
gutterForeground: '#AFAFAF',
|
|
||||||
lineHighlight: '#EDF4FF',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: '#fff',
|
|
||||||
foreground: '#3D3D3D',
|
|
||||||
selection: '#BBDFFF',
|
|
||||||
selectionMatch: '#BBDFFF',
|
|
||||||
gutterBackground: '#fff',
|
|
||||||
gutterForeground: '#AFAFAF',
|
|
||||||
lineHighlight: '#d5e6ff69',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: [t.comment, t.quote], color: '#707F8D' },
|
|
||||||
{ tag: [t.typeName, t.typeOperator], color: '#aa0d91' },
|
|
||||||
{ tag: [t.keyword], color: '#aa0d91', fontWeight: 'bold' },
|
|
||||||
{ tag: [t.string, t.meta], color: '#D23423' },
|
|
||||||
{ tag: [t.name], color: '#032f62' },
|
|
||||||
{ tag: [t.typeName], color: '#522BB2' },
|
|
||||||
{ tag: [t.variableName], color: '#23575C' },
|
|
||||||
{ tag: [t.definition(t.variableName)], color: '#327A9E' },
|
|
||||||
{ tag: [t.regexp, t.link], color: '#0e0eff' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
@@ -133,10 +133,3 @@ registerWidget('_pitchwheel', (id, options = {}, pat) => {
|
|||||||
const ctx = getCanvasWidget(id, options).getContext('2d');
|
const ctx = getCanvasWidget(id, options).getContext('2d');
|
||||||
return pat.pitchwheel({ ...options, ctx, id });
|
return pat.pitchwheel({ ...options, ctx, id });
|
||||||
});
|
});
|
||||||
|
|
||||||
registerWidget('_spectrum', (id, options = {}, pat) => {
|
|
||||||
let _size = options.size || 200;
|
|
||||||
options = { width: _size, height: _size, ...options, size: _size / 5 };
|
|
||||||
const ctx = getCanvasWidget(id, options).getContext('2d');
|
|
||||||
return pat.spectrum({ ...options, ctx, id });
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import { describe, bench } from 'vitest';
|
|
||||||
|
|
||||||
import { calculateTactus, sequence, stack } from '../index.mjs';
|
|
||||||
|
|
||||||
const pat64 = sequence(...Array(64).keys());
|
|
||||||
|
|
||||||
describe('steps', () => {
|
|
||||||
calculateTactus(true);
|
|
||||||
bench(
|
|
||||||
'+tactus',
|
|
||||||
() => {
|
|
||||||
pat64.iter(64).fast(64).firstCycle();
|
|
||||||
},
|
|
||||||
{ time: 1000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
calculateTactus(false);
|
|
||||||
bench(
|
|
||||||
'-tactus',
|
|
||||||
() => {
|
|
||||||
pat64.iter(64).fast(64).firstCycle();
|
|
||||||
},
|
|
||||||
{ time: 1000 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('stack', () => {
|
|
||||||
calculateTactus(true);
|
|
||||||
bench(
|
|
||||||
'+tactus',
|
|
||||||
() => {
|
|
||||||
stack(pat64, pat64, pat64, pat64, pat64, pat64, pat64, pat64).fast(64).firstCycle();
|
|
||||||
},
|
|
||||||
{ time: 1000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
calculateTactus(false);
|
|
||||||
bench(
|
|
||||||
'-tactus',
|
|
||||||
() => {
|
|
||||||
stack(pat64, pat64, pat64, pat64, pat64, pat64, pat64, pat64).fast(64).firstCycle();
|
|
||||||
},
|
|
||||||
{ time: 1000 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
calculateTactus(true);
|
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
// import createClock from './zyklus.mjs';
|
// import createClock from './zyklus.mjs';
|
||||||
|
|
||||||
function getTime() {
|
function getTime() {
|
||||||
const seconds = performance.now() * 0.001;
|
const precision = 10 ** 4;
|
||||||
return seconds;
|
const seconds = performance.now() / 1000;
|
||||||
// return Math.round(seconds * precision) / precision;
|
return Math.round(seconds * precision) / precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_cycles_at_cps_change = 0;
|
let num_cycles_at_cps_change = 0;
|
||||||
@@ -24,20 +24,27 @@ const sendMessage = (type, payload) => {
|
|||||||
|
|
||||||
const sendTick = (phase, duration, tick, time) => {
|
const sendTick = (phase, duration, tick, time) => {
|
||||||
const num_seconds_since_cps_change = num_ticks_since_cps_change * duration;
|
const num_seconds_since_cps_change = num_ticks_since_cps_change * duration;
|
||||||
|
|
||||||
const tickdeadline = phase - time;
|
const tickdeadline = phase - time;
|
||||||
const lastTick = time + tickdeadline;
|
const lastTick = time + tickdeadline;
|
||||||
const num_cycles_since_cps_change = num_seconds_since_cps_change * cps;
|
const num_cycles_since_cps_change = num_seconds_since_cps_change * cps;
|
||||||
|
|
||||||
const begin = num_cycles_at_cps_change + num_cycles_since_cps_change;
|
const begin = num_cycles_at_cps_change + num_cycles_since_cps_change;
|
||||||
const secondsSinceLastTick = time - lastTick - duration;
|
const secondsSinceLastTick = time - lastTick - duration;
|
||||||
|
|
||||||
const eventLength = duration * cps;
|
const eventLength = duration * cps;
|
||||||
const end = begin + eventLength;
|
const end = begin + eventLength;
|
||||||
|
|
||||||
const cycle = begin + secondsSinceLastTick * cps;
|
const cycle = begin + secondsSinceLastTick * cps;
|
||||||
|
|
||||||
sendMessage('tick', {
|
sendMessage('tick', {
|
||||||
begin,
|
begin,
|
||||||
end,
|
end,
|
||||||
cps,
|
cps,
|
||||||
time,
|
tickdeadline,
|
||||||
|
num_cycles_at_cps_change,
|
||||||
|
num_seconds_at_cps_change,
|
||||||
|
num_seconds_since_cps_change,
|
||||||
cycle,
|
cycle,
|
||||||
});
|
});
|
||||||
num_ticks_since_cps_change++;
|
num_ticks_since_cps_change++;
|
||||||
|
|||||||
+36
-269
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
controls.mjs - Registers audio controls for pattern manipulation and effects.
|
controls.mjs - <short description TODO>
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/controls.mjs>
|
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/controls.mjs>
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -47,16 +47,12 @@ export function createParam(names) {
|
|||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maps control alias names to the "main" control name
|
function registerControl(names, ...aliases) {
|
||||||
const controlAlias = new Map();
|
|
||||||
|
|
||||||
export function registerControl(names, ...aliases) {
|
|
||||||
const name = Array.isArray(names) ? names[0] : names;
|
const name = Array.isArray(names) ? names[0] : names;
|
||||||
let bag = {};
|
let bag = {};
|
||||||
bag[name] = createParam(names);
|
bag[name] = createParam(names);
|
||||||
aliases.forEach((alias) => {
|
aliases.forEach((alias) => {
|
||||||
bag[alias] = bag[name];
|
bag[alias] = bag[name];
|
||||||
controlAlias.set(alias, name);
|
|
||||||
Pattern.prototype[alias] = Pattern.prototype[name];
|
Pattern.prototype[alias] = Pattern.prototype[name];
|
||||||
});
|
});
|
||||||
return bag;
|
return bag;
|
||||||
@@ -195,7 +191,7 @@ export const { attack, att } = registerControl('attack', 'att');
|
|||||||
* note("c e g b g e")
|
* note("c e g b g e")
|
||||||
* .fm(4)
|
* .fm(4)
|
||||||
* .fmh("<1 2 1.5 1.61>")
|
* .fmh("<1 2 1.5 1.61>")
|
||||||
* ._scope()
|
* .scope()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh');
|
export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh');
|
||||||
@@ -209,7 +205,7 @@ export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh');
|
|||||||
* @example
|
* @example
|
||||||
* note("c e g b g e")
|
* note("c e g b g e")
|
||||||
* .fm("<0 1 2 8 32>")
|
* .fm("<0 1 2 8 32>")
|
||||||
* ._scope()
|
* .scope()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm');
|
export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm');
|
||||||
@@ -225,7 +221,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm');
|
|||||||
* .fmdecay(.2)
|
* .fmdecay(.2)
|
||||||
* .fmsustain(0)
|
* .fmsustain(0)
|
||||||
* .fmenv("<exp lin>")
|
* .fmenv("<exp lin>")
|
||||||
* ._scope()
|
* .scope()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { fmenv } = registerControl('fmenv');
|
export const { fmenv } = registerControl('fmenv');
|
||||||
@@ -238,7 +234,7 @@ export const { fmenv } = registerControl('fmenv');
|
|||||||
* note("c e g b g e")
|
* note("c e g b g e")
|
||||||
* .fm(4)
|
* .fm(4)
|
||||||
* .fmattack("<0 .05 .1 .2>")
|
* .fmattack("<0 .05 .1 .2>")
|
||||||
* ._scope()
|
* .scope()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { fmattack } = registerControl('fmattack');
|
export const { fmattack } = registerControl('fmattack');
|
||||||
@@ -252,7 +248,7 @@ export const { fmattack } = registerControl('fmattack');
|
|||||||
* .fm(4)
|
* .fm(4)
|
||||||
* .fmdecay("<.01 .05 .1 .2>")
|
* .fmdecay("<.01 .05 .1 .2>")
|
||||||
* .fmsustain(.4)
|
* .fmsustain(.4)
|
||||||
* ._scope()
|
* .scope()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { fmdecay } = registerControl('fmdecay');
|
export const { fmdecay } = registerControl('fmdecay');
|
||||||
@@ -266,7 +262,7 @@ export const { fmdecay } = registerControl('fmdecay');
|
|||||||
* .fm(4)
|
* .fm(4)
|
||||||
* .fmdecay(.1)
|
* .fmdecay(.1)
|
||||||
* .fmsustain("<1 .75 .5 0>")
|
* .fmsustain("<1 .75 .5 0>")
|
||||||
* ._scope()
|
* .scope()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { fmsustain } = registerControl('fmsustain');
|
export const { fmsustain } = registerControl('fmsustain');
|
||||||
@@ -396,7 +392,7 @@ export const { loop } = registerControl('loop');
|
|||||||
* @synonyms loopb
|
* @synonyms loopb
|
||||||
* @example
|
* @example
|
||||||
* s("space").loop(1)
|
* s("space").loop(1)
|
||||||
* .loopBegin("<0 .125 .25>")._scope()
|
* .loopBegin("<0 .125 .25>").scope()
|
||||||
*/
|
*/
|
||||||
export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
||||||
/**
|
/**
|
||||||
@@ -409,7 +405,7 @@ export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
|||||||
* @synonyms loope
|
* @synonyms loope
|
||||||
* @example
|
* @example
|
||||||
* s("space").loop(1)
|
* s("space").loop(1)
|
||||||
* .loopEnd("<1 .75 .5 .25>")._scope()
|
* .loopEnd("<1 .75 .5 .25>").scope()
|
||||||
*/
|
*/
|
||||||
export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
||||||
/**
|
/**
|
||||||
@@ -434,17 +430,6 @@ export const { crush } = registerControl('crush');
|
|||||||
*/
|
*/
|
||||||
export const { coarse } = registerControl('coarse');
|
export const { coarse } = registerControl('coarse');
|
||||||
|
|
||||||
/**
|
|
||||||
* filter overdrive for supported filter types
|
|
||||||
*
|
|
||||||
* @name drive
|
|
||||||
* @param {number | Pattern} amount
|
|
||||||
* @example
|
|
||||||
* note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>")
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const { drive } = registerControl('drive');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows you to set the output channels on the interface
|
* Allows you to set the output channels on the interface
|
||||||
*
|
*
|
||||||
@@ -458,6 +443,9 @@ export const { drive } = registerControl('drive');
|
|||||||
*/
|
*/
|
||||||
export const { channels, ch } = registerControl('channels', 'ch');
|
export const { channels, ch } = registerControl('channels', 'ch');
|
||||||
|
|
||||||
|
// superdirt only
|
||||||
|
export const { phaserrate, phasr } = registerControl('phaserrate', 'phasr');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phaser audio effect that approximates popular guitar pedals.
|
* Phaser audio effect that approximates popular guitar pedals.
|
||||||
*
|
*
|
||||||
@@ -469,11 +457,7 @@ export const { channels, ch } = registerControl('channels', 'ch');
|
|||||||
* .phaser("<1 2 4 8>")
|
* .phaser("<1 2 4 8>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { phaserrate, ph, phaser } = registerControl(
|
export const { phaser, ph } = registerControl(['phaser', 'phaserdepth', 'phasercenter', 'phasersweep'], 'ph');
|
||||||
['phaserrate', 'phaserdepth', 'phasercenter', 'phasersweep'],
|
|
||||||
'ph',
|
|
||||||
'phaser',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The frequency sweep range of the lfo for the phaser effect. Defaults to 2000
|
* The frequency sweep range of the lfo for the phaser effect. Defaults to 2000
|
||||||
@@ -558,7 +542,7 @@ export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance',
|
|||||||
* @example
|
* @example
|
||||||
* note("c2 e2 f2 g2")
|
* note("c2 e2 f2 g2")
|
||||||
* .sound('sawtooth')
|
* .sound('sawtooth')
|
||||||
* .lpf(300)
|
* .lpf(500)
|
||||||
* .lpa(.5)
|
* .lpa(.5)
|
||||||
* .lpenv("<4 2 1 0 -1 -2 -4>/4")
|
* .lpenv("<4 2 1 0 -1 -2 -4>/4")
|
||||||
*/
|
*/
|
||||||
@@ -597,7 +581,7 @@ export const { bpenv, bpe } = registerControl('bpenv', 'bpe');
|
|||||||
* @example
|
* @example
|
||||||
* note("c2 e2 f2 g2")
|
* note("c2 e2 f2 g2")
|
||||||
* .sound('sawtooth')
|
* .sound('sawtooth')
|
||||||
* .lpf(300)
|
* .lpf(500)
|
||||||
* .lpa("<.5 .25 .1 .01>/4")
|
* .lpa("<.5 .25 .1 .01>/4")
|
||||||
* .lpenv(4)
|
* .lpenv(4)
|
||||||
*/
|
*/
|
||||||
@@ -636,8 +620,9 @@ export const { bpattack, bpa } = registerControl('bpattack', 'bpa');
|
|||||||
* @example
|
* @example
|
||||||
* note("c2 e2 f2 g2")
|
* note("c2 e2 f2 g2")
|
||||||
* .sound('sawtooth')
|
* .sound('sawtooth')
|
||||||
* .lpf(300)
|
* .lpf(500)
|
||||||
* .lpd("<.5 .25 .1 0>/4")
|
* .lpd("<.5 .25 .1 0>/4")
|
||||||
|
* .lps(0.2)
|
||||||
* .lpenv(4)
|
* .lpenv(4)
|
||||||
*/
|
*/
|
||||||
export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd');
|
export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd');
|
||||||
@@ -677,7 +662,7 @@ export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd');
|
|||||||
* @example
|
* @example
|
||||||
* note("c2 e2 f2 g2")
|
* note("c2 e2 f2 g2")
|
||||||
* .sound('sawtooth')
|
* .sound('sawtooth')
|
||||||
* .lpf(300)
|
* .lpf(500)
|
||||||
* .lpd(.5)
|
* .lpd(.5)
|
||||||
* .lps("<0 .25 .5 1>/4")
|
* .lps("<0 .25 .5 1>/4")
|
||||||
* .lpenv(4)
|
* .lpenv(4)
|
||||||
@@ -720,7 +705,7 @@ export const { bpsustain, bps } = registerControl('bpsustain', 'bps');
|
|||||||
* note("c2 e2 f2 g2")
|
* note("c2 e2 f2 g2")
|
||||||
* .sound('sawtooth')
|
* .sound('sawtooth')
|
||||||
* .clip(.5)
|
* .clip(.5)
|
||||||
* .lpf(300)
|
* .lpf(500)
|
||||||
* .lpenv(4)
|
* .lpenv(4)
|
||||||
* .lpr("<.5 .25 .1 0>/4")
|
* .lpr("<.5 .25 .1 0>/4")
|
||||||
* .release(.5)
|
* .release(.5)
|
||||||
@@ -757,28 +742,17 @@ export const { hprelease, hpr } = registerControl('hprelease', 'hpr');
|
|||||||
*/
|
*/
|
||||||
export const { bprelease, bpr } = registerControl('bprelease', 'bpr');
|
export const { bprelease, bpr } = registerControl('bprelease', 'bpr');
|
||||||
/**
|
/**
|
||||||
* Sets the filter type. The ladder filter is more aggressive. More types might be added in the future.
|
* Sets the filter type. The 24db filter is more aggressive. More types might be added in the future.
|
||||||
* @name ftype
|
* @name ftype
|
||||||
* @param {number | Pattern} type 12db (0), ladder (1), or 24db (2)
|
* @param {number | Pattern} type 12db (default) or 24db
|
||||||
* @example
|
* @example
|
||||||
* note("{f g g c d a a#}%8").s("sawtooth").lpenv(4).lpf(500).ftype("<0 1 2>").lpq(1)
|
* note("c2 e2 f2 g2")
|
||||||
* @example
|
|
||||||
* note("c f g g a c d4").fast(2)
|
|
||||||
* .sound('sawtooth')
|
* .sound('sawtooth')
|
||||||
* .lpf(200).fanchor(0)
|
* .lpf(500)
|
||||||
* .lpenv(3).lpq(1)
|
* .bpenv(4)
|
||||||
* .ftype("<ladder 12db 24db>")
|
* .ftype("12db 24db")
|
||||||
*/
|
*/
|
||||||
export const { ftype } = registerControl('ftype');
|
export const { ftype } = registerControl('ftype');
|
||||||
|
|
||||||
/**
|
|
||||||
* controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative
|
|
||||||
* @name fanchor
|
|
||||||
* @param {number | Pattern} center 0 to 1
|
|
||||||
* @example
|
|
||||||
* note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2")
|
|
||||||
* .lpenv(8).fanchor("<0 .5 1>")
|
|
||||||
*/
|
|
||||||
export const { fanchor } = registerControl('fanchor');
|
export const { fanchor } = registerControl('fanchor');
|
||||||
/**
|
/**
|
||||||
* Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter.
|
* Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter.
|
||||||
@@ -805,12 +779,10 @@ export const { fanchor } = registerControl('fanchor');
|
|||||||
* @example
|
* @example
|
||||||
* note("a e")
|
* note("a e")
|
||||||
* .vib("<.5 1 2 4 8 16>")
|
* .vib("<.5 1 2 4 8 16>")
|
||||||
* ._scope()
|
|
||||||
* @example
|
* @example
|
||||||
* // change the modulation depth with ":"
|
* // change the modulation depth with ":"
|
||||||
* note("a e")
|
* note("a e")
|
||||||
* .vib("<.5 1 2 4 8 16>:12")
|
* .vib("<.5 1 2 4 8 16>:12")
|
||||||
* ._scope()
|
|
||||||
*/
|
*/
|
||||||
export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', 'v');
|
export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', 'v');
|
||||||
/**
|
/**
|
||||||
@@ -831,12 +803,10 @@ export const { noise } = registerControl('noise');
|
|||||||
* @example
|
* @example
|
||||||
* note("a e").vib(4)
|
* note("a e").vib(4)
|
||||||
* .vibmod("<.25 .5 1 2 12>")
|
* .vibmod("<.25 .5 1 2 12>")
|
||||||
* ._scope()
|
|
||||||
* @example
|
* @example
|
||||||
* // change the vibrato frequency with ":"
|
* // change the vibrato frequency with ":"
|
||||||
* note("a e")
|
* note("a e")
|
||||||
* .vibmod("<.25 .5 1 2 12>:8")
|
* .vibmod("<.25 .5 1 2 12>:8")
|
||||||
* ._scope()
|
|
||||||
*/
|
*/
|
||||||
export const { vibmod, vmod } = registerControl(['vibmod', 'vib'], 'vmod');
|
export const { vibmod, vmod } = registerControl(['vibmod', 'vib'], 'vmod');
|
||||||
export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp');
|
export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp');
|
||||||
@@ -1388,17 +1358,6 @@ export const { compressorRelease } = registerControl('compressorRelease');
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { speed } = registerControl('speed');
|
export const { speed } = registerControl('speed');
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the speed of sample playback, i.e. a cheap way of changing pitch.
|
|
||||||
*
|
|
||||||
* @name stretch
|
|
||||||
* @param {number | Pattern} factor -inf to inf, negative numbers play the sample backwards.
|
|
||||||
* @example
|
|
||||||
* s("gm_flute").stretch("1 2 .5")
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const { stretch } = registerControl('stretch');
|
|
||||||
/**
|
/**
|
||||||
* Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.
|
* Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.
|
||||||
*
|
*
|
||||||
@@ -1409,7 +1368,6 @@ export const { stretch } = registerControl('stretch');
|
|||||||
* @superdirtOnly
|
* @superdirtOnly
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const { unit } = registerControl('unit');
|
export const { unit } = registerControl('unit');
|
||||||
/**
|
/**
|
||||||
* Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as:
|
* Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as:
|
||||||
@@ -1513,11 +1471,20 @@ export const { scram } = registerControl('scram');
|
|||||||
export const { binshift } = registerControl('binshift');
|
export const { binshift } = registerControl('binshift');
|
||||||
export const { hbrick } = registerControl('hbrick');
|
export const { hbrick } = registerControl('hbrick');
|
||||||
export const { lbrick } = registerControl('lbrick');
|
export const { lbrick } = registerControl('lbrick');
|
||||||
|
export const { midichan } = registerControl('midichan');
|
||||||
|
export const { control } = registerControl('control');
|
||||||
|
export const { ccn } = registerControl('ccn');
|
||||||
|
export const { ccv } = registerControl('ccv');
|
||||||
|
export const { polyTouch } = registerControl('polyTouch');
|
||||||
|
export const { midibend } = registerControl('midibend');
|
||||||
|
export const { miditouch } = registerControl('miditouch');
|
||||||
|
export const { ctlNum } = registerControl('ctlNum');
|
||||||
export const { frameRate } = registerControl('frameRate');
|
export const { frameRate } = registerControl('frameRate');
|
||||||
export const { frames } = registerControl('frames');
|
export const { frames } = registerControl('frames');
|
||||||
export const { hours } = registerControl('hours');
|
export const { hours } = registerControl('hours');
|
||||||
|
export const { midicmd } = registerControl('midicmd');
|
||||||
export const { minutes } = registerControl('minutes');
|
export const { minutes } = registerControl('minutes');
|
||||||
|
export const { progNum } = registerControl('progNum');
|
||||||
export const { seconds } = registerControl('seconds');
|
export const { seconds } = registerControl('seconds');
|
||||||
export const { songPtr } = registerControl('songPtr');
|
export const { songPtr } = registerControl('songPtr');
|
||||||
export const { uid } = registerControl('uid');
|
export const { uid } = registerControl('uid');
|
||||||
@@ -1609,203 +1576,3 @@ export const ar = register('ar', (t, pat) => {
|
|||||||
const [attack, release = attack] = t;
|
const [attack, release = attack] = t;
|
||||||
return pat.set({ attack, release });
|
return pat.set({ attack, release });
|
||||||
});
|
});
|
||||||
|
|
||||||
//MIDI
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI channel: Sets the MIDI channel for the event.
|
|
||||||
*
|
|
||||||
* @name midichan
|
|
||||||
* @param {number | Pattern} channel MIDI channel number (0-15)
|
|
||||||
* @example
|
|
||||||
* note("c4").midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const { midichan } = registerControl('midichan');
|
|
||||||
|
|
||||||
export const { midimap } = registerControl('midimap');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI port: Sets the MIDI port for the event.
|
|
||||||
*
|
|
||||||
* @name midiport
|
|
||||||
* @param {number | Pattern} port MIDI port
|
|
||||||
* @example
|
|
||||||
* note("c a f e").midiport("<0 1 2 3>").midi()
|
|
||||||
*/
|
|
||||||
export const { midiport } = registerControl('midiport');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI command: Sends a MIDI command message.
|
|
||||||
*
|
|
||||||
* @name midicmd
|
|
||||||
* @param {number | Pattern} command MIDI command
|
|
||||||
* @example
|
|
||||||
* midicmd("clock*48,<start stop>/2").midi()
|
|
||||||
*/
|
|
||||||
export const { midicmd } = registerControl('midicmd');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI control: Sends a MIDI control change message.
|
|
||||||
*
|
|
||||||
* @name control
|
|
||||||
* @param {number | Pattern} MIDI control number (0-127)
|
|
||||||
* @param {number | Pattern} MIDI controller value (0-127)
|
|
||||||
*/
|
|
||||||
export const control = register('control', (args, pat) => {
|
|
||||||
if (!Array.isArray(args)) {
|
|
||||||
throw new Error('control expects an array of [ccn, ccv]');
|
|
||||||
}
|
|
||||||
const [_ccn, _ccv] = args;
|
|
||||||
return pat.ccn(_ccn).ccv(_ccv);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI control number: Sends a MIDI control change message.
|
|
||||||
*
|
|
||||||
* @name ccn
|
|
||||||
* @param {number | Pattern} MIDI control number (0-127)
|
|
||||||
*/
|
|
||||||
export const { ccn } = registerControl('ccn');
|
|
||||||
/**
|
|
||||||
* MIDI control value: Sends a MIDI control change message.
|
|
||||||
*
|
|
||||||
* @name ccv
|
|
||||||
* @param {number | Pattern} MIDI control value (0-127)
|
|
||||||
*/
|
|
||||||
export const { ccv } = registerControl('ccv');
|
|
||||||
export const { ctlNum } = registerControl('ctlNum');
|
|
||||||
// TODO: ctlVal?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI NRPN non-registered parameter number: Sends a MIDI NRPN non-registered parameter number message.
|
|
||||||
* @name nrpnn
|
|
||||||
* @param {number | Pattern} nrpnn MIDI NRPN non-registered parameter number (0-127)
|
|
||||||
* @example
|
|
||||||
* note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const { nrpnn } = registerControl('nrpnn');
|
|
||||||
/**
|
|
||||||
* MIDI NRPN non-registered parameter value: Sends a MIDI NRPN non-registered parameter value message.
|
|
||||||
* @name nrpv
|
|
||||||
* @param {number | Pattern} nrpv MIDI NRPN non-registered parameter value (0-127)
|
|
||||||
* @example
|
|
||||||
* note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const { nrpv } = registerControl('nrpv');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI program number: Sends a MIDI program change message.
|
|
||||||
*
|
|
||||||
* @name progNum
|
|
||||||
* @param {number | Pattern} program MIDI program number (0-127)
|
|
||||||
* @example
|
|
||||||
* note("c4").progNum(10).midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const { progNum } = registerControl('progNum');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI sysex: Sends a MIDI sysex message.
|
|
||||||
* @name sysex
|
|
||||||
* @param {number | Pattern} id Sysex ID
|
|
||||||
* @param {number | Pattern} data Sysex data
|
|
||||||
* @example
|
|
||||||
* note("c4").sysex(["0x77", "0x01:0x02:0x03:0x04"]).midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const sysex = register('sysex', (args, pat) => {
|
|
||||||
if (!Array.isArray(args)) {
|
|
||||||
throw new Error('sysex expects an array of [id, data]');
|
|
||||||
}
|
|
||||||
const [id, data] = args;
|
|
||||||
return pat.sysexid(id).sysexdata(data);
|
|
||||||
});
|
|
||||||
/**
|
|
||||||
* MIDI sysex ID: Sends a MIDI sysex identifier message.
|
|
||||||
* @name sysexid
|
|
||||||
* @param {number | Pattern} id Sysex ID
|
|
||||||
* @example
|
|
||||||
* note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const { sysexid } = registerControl('sysexid');
|
|
||||||
/**
|
|
||||||
* MIDI sysex data: Sends a MIDI sysex message.
|
|
||||||
* @name sysexdata
|
|
||||||
* @param {number | Pattern} data Sysex data
|
|
||||||
* @example
|
|
||||||
* note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi()
|
|
||||||
*/
|
|
||||||
export const { sysexdata } = registerControl('sysexdata');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MIDI pitch bend: Sends a MIDI pitch bend message.
|
|
||||||
* @name midibend
|
|
||||||
* @param {number | Pattern} midibend MIDI pitch bend (-1 - 1)
|
|
||||||
* @example
|
|
||||||
* note("c4").midibend(sine.slow(4).range(-0.4,0.4)).midi()
|
|
||||||
*/
|
|
||||||
export const { midibend } = registerControl('midibend');
|
|
||||||
/**
|
|
||||||
* MIDI key after touch: Sends a MIDI key after touch message.
|
|
||||||
* @name miditouch
|
|
||||||
* @param {number | Pattern} miditouch MIDI key after touch (0-1)
|
|
||||||
* @example
|
|
||||||
* note("c4").miditouch(sine.slow(4).range(0,1)).midi()
|
|
||||||
*/
|
|
||||||
export const { miditouch } = registerControl('miditouch');
|
|
||||||
|
|
||||||
// TODO: what is this?
|
|
||||||
export const { polyTouch } = registerControl('polyTouch');
|
|
||||||
|
|
||||||
export const getControlName = (alias) => {
|
|
||||||
if (controlAlias.has(alias)) {
|
|
||||||
return controlAlias.get(alias);
|
|
||||||
}
|
|
||||||
return alias;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets properties in a batch.
|
|
||||||
*
|
|
||||||
* @name as
|
|
||||||
* @param {String | Array} mapping the control names that are set
|
|
||||||
* @example
|
|
||||||
* "c:.5 a:1 f:.25 e:.8".as("note:clip")
|
|
||||||
* @example
|
|
||||||
* "{0@2 0.25 0 0.5 .3 .5}%8".as("begin").s("sax_vib").clip(1)
|
|
||||||
*/
|
|
||||||
export const as = register('as', (mapping, pat) => {
|
|
||||||
mapping = Array.isArray(mapping) ? mapping : [mapping];
|
|
||||||
return pat.fmap((v) => {
|
|
||||||
v = Array.isArray(v) ? v : [v];
|
|
||||||
v = Object.fromEntries(mapping.map((prop, i) => [getControlName(prop), v[i]]));
|
|
||||||
return v;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file
|
|
||||||
* in the optional array syntax ex: "0.5:2", the second value controls the speed of playback
|
|
||||||
* @name scrub
|
|
||||||
* @memberof Pattern
|
|
||||||
* @returns Pattern
|
|
||||||
* @example
|
|
||||||
* samples('github:switchangel/pad')
|
|
||||||
* s("swpad:0").scrub("{0.1!2 .25@3 0.7!2 <0.8:1.5>}%8")
|
|
||||||
* @example
|
|
||||||
* samples('github:yaxu/clean-breaks/main');
|
|
||||||
* s("amen/4").fit().scrub("{0@3 0@2 4@3}%8".div(16))
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const scrub = register(
|
|
||||||
'scrub',
|
|
||||||
(beginPat, pat) => {
|
|
||||||
return beginPat.outerBind((v) => {
|
|
||||||
if (!Array.isArray(v)) {
|
|
||||||
v = [v];
|
|
||||||
}
|
|
||||||
const [beginVal, speedMultiplier = 1] = v;
|
|
||||||
|
|
||||||
return pat.begin(beginVal).mul(speed(speedMultiplier)).clip(1);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -8,19 +8,8 @@ import createClock from './zyklus.mjs';
|
|||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
|
|
||||||
export class Cyclist {
|
export class Cyclist {
|
||||||
constructor({
|
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) {
|
||||||
interval,
|
|
||||||
onTrigger,
|
|
||||||
onToggle,
|
|
||||||
onError,
|
|
||||||
getTime,
|
|
||||||
latency = 0.1,
|
|
||||||
setInterval,
|
|
||||||
clearInterval,
|
|
||||||
beforeStart,
|
|
||||||
}) {
|
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.beforeStart = beforeStart;
|
|
||||||
this.cps = 0.5;
|
this.cps = 0.5;
|
||||||
this.num_ticks_since_cps_change = 0;
|
this.num_ticks_since_cps_change = 0;
|
||||||
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
||||||
@@ -68,10 +57,6 @@ export class Cyclist {
|
|||||||
// see https://github.com/tidalcycles/strudel/pull/1004
|
// see https://github.com/tidalcycles/strudel/pull/1004
|
||||||
const deadline = targetTime - phase;
|
const deadline = targetTime - phase;
|
||||||
onTrigger?.(hap, deadline, duration, this.cps, targetTime);
|
onTrigger?.(hap, deadline, duration, this.cps, targetTime);
|
||||||
if (hap.value.cps !== undefined && this.cps != hap.value.cps) {
|
|
||||||
this.cps = hap.value.cps;
|
|
||||||
this.num_ticks_since_cps_change = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -97,8 +82,7 @@ export class Cyclist {
|
|||||||
this.started = v;
|
this.started = v;
|
||||||
this.onToggle?.(v);
|
this.onToggle?.(v);
|
||||||
}
|
}
|
||||||
async start() {
|
start() {
|
||||||
await this.beforeStart?.();
|
|
||||||
this.num_ticks_since_cps_change = 0;
|
this.num_ticks_since_cps_change = 0;
|
||||||
this.num_cycles_at_cps_change = 0;
|
this.num_cycles_at_cps_change = 0;
|
||||||
if (!this.pattern) {
|
if (!this.pattern) {
|
||||||
@@ -119,10 +103,10 @@ export class Cyclist {
|
|||||||
this.lastEnd = 0;
|
this.lastEnd = 0;
|
||||||
this.setStarted(false);
|
this.setStarted(false);
|
||||||
}
|
}
|
||||||
async setPattern(pat, autostart = false) {
|
setPattern(pat, autostart = false) {
|
||||||
this.pattern = pat;
|
this.pattern = pat;
|
||||||
if (autostart && !this.started) {
|
if (autostart && !this.started) {
|
||||||
await this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setCps(cps = 0.5) {
|
setCps(cps = 0.5) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { isPattern } from './index.mjs';
|
||||||
|
|
||||||
export const evalScope = async (...args) => {
|
export const evalScope = async (...args) => {
|
||||||
const results = await Promise.allSettled(args);
|
const results = await Promise.allSettled(args);
|
||||||
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
|
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
|
||||||
@@ -35,12 +37,11 @@ function safeEval(str, options = {}) {
|
|||||||
return Function(body)();
|
return Function(body)();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const evaluate = async (code, transpiler, transpilerOptions) => {
|
export const evaluate = async (code, transpiler) => {
|
||||||
let meta = {};
|
let meta = {};
|
||||||
|
|
||||||
if (transpiler) {
|
if (transpiler) {
|
||||||
// transform syntactically correct js code to semantically usable code
|
// transform syntactically correct js code to semantically usable code
|
||||||
const transpiled = transpiler(code, transpilerOptions);
|
const transpiled = transpiler(code);
|
||||||
code = transpiled.output;
|
code = transpiled.output;
|
||||||
meta = transpiled;
|
meta = transpiled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,10 +119,10 @@ export const lcm = (...fractions) => {
|
|||||||
if (fractions.length === 0) {
|
if (fractions.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const x = fractions.pop();
|
|
||||||
return fractions.reduce(
|
return fractions.reduce(
|
||||||
(lcm, fraction) => (lcm === undefined || fraction === undefined ? undefined : lcm.lcm(fraction)),
|
(lcm, fraction) => (lcm === undefined || fraction === undefined ? undefined : lcm.lcm(fraction)),
|
||||||
x,
|
fraction(1),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export * from './controls.mjs';
|
|||||||
export * from './hap.mjs';
|
export * from './hap.mjs';
|
||||||
export * from './pattern.mjs';
|
export * from './pattern.mjs';
|
||||||
export * from './signal.mjs';
|
export * from './signal.mjs';
|
||||||
export * from './pick.mjs';
|
|
||||||
export * from './state.mjs';
|
export * from './state.mjs';
|
||||||
export * from './timespan.mjs';
|
export * from './timespan.mjs';
|
||||||
export * from './util.mjs';
|
export * from './util.mjs';
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
import { ClockCollator, cycleToSeconds } from './util.mjs';
|
|
||||||
|
|
||||||
export class NeoCyclist {
|
export class NeoCyclist {
|
||||||
constructor({ onTrigger, onToggle, getTime }) {
|
constructor({ onTrigger, onToggle, getTime }) {
|
||||||
@@ -14,38 +13,79 @@ export class NeoCyclist {
|
|||||||
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
||||||
this.getTime = getTime; // get absolute time
|
this.getTime = getTime; // get absolute time
|
||||||
this.time_at_last_tick_message = 0;
|
this.time_at_last_tick_message = 0;
|
||||||
// the clock of the worker and the audio context clock can drift apart over time
|
|
||||||
// aditionally, the message time of the worker pinging the callback to process haps can be inconsistent.
|
this.num_cycles_at_cps_change = 0;
|
||||||
// we need to keep a rolling average of the time difference between the worker clock and audio context clock
|
|
||||||
// in order to schedule events consistently.
|
|
||||||
this.collator = new ClockCollator({ getTargetClockTime: getTime });
|
|
||||||
this.onToggle = onToggle;
|
this.onToggle = onToggle;
|
||||||
this.latency = 0.1; // fixed trigger time offset
|
this.latency = 0.1; // fixed trigger time offset
|
||||||
this.cycle = 0;
|
this.cycle = 0;
|
||||||
this.id = Math.round(Date.now() * Math.random());
|
this.id = Math.round(Date.now() * Math.random());
|
||||||
|
this.worker_time_dif;
|
||||||
this.worker = new SharedWorker(new URL('./clockworker.js', import.meta.url));
|
this.worker = new SharedWorker(new URL('./clockworker.js', import.meta.url));
|
||||||
this.worker.port.start();
|
this.worker.port.start();
|
||||||
|
|
||||||
this.channel = new BroadcastChannel('strudeltick');
|
this.channel = new BroadcastChannel('strudeltick');
|
||||||
const tickCallback = (payload) => {
|
let weight = 0; // the amount of weight that is applied to the current average when averaging a new time dif
|
||||||
const { cps, begin, end, cycle, time } = payload;
|
const maxWeight = 20;
|
||||||
this.cps = cps;
|
const precision = 10 ** 3; //round off time diff to prevent accumulating outliers
|
||||||
this.cycle = cycle;
|
|
||||||
const currentTime = this.collator.calculateOffset(time) + time;
|
// the clock of the worker and the audio context clock can drift apart over time
|
||||||
processHaps(begin, end, currentTime);
|
// aditionally, the message time of the worker pinging the callback to process haps can be inconsistent.
|
||||||
this.time_at_last_tick_message = currentTime;
|
// we need to keep a rolling weighted average of the time difference between the worker clock and audio context clock
|
||||||
|
// in order to schedule events consistently.
|
||||||
|
const setTimeReference = (num_seconds_at_cps_change, num_seconds_since_cps_change, tickdeadline) => {
|
||||||
|
const time_dif = getTime() - (num_seconds_at_cps_change + num_seconds_since_cps_change) + tickdeadline;
|
||||||
|
if (this.worker_time_dif == null) {
|
||||||
|
this.worker_time_dif = time_dif;
|
||||||
|
} else {
|
||||||
|
const w = 1; //weight of new time diff;
|
||||||
|
const new_dif =
|
||||||
|
Math.round(((this.worker_time_dif * weight + time_dif * w) / (weight + w)) * precision) / precision;
|
||||||
|
|
||||||
|
if (new_dif != this.worker_time_dif) {
|
||||||
|
// reset the weight so the clock recovers faster from an audio context freeze/dropout if it happens
|
||||||
|
weight = 4;
|
||||||
|
}
|
||||||
|
this.worker_time_dif = new_dif;
|
||||||
|
}
|
||||||
|
weight = Math.min(weight + 1, maxWeight);
|
||||||
};
|
};
|
||||||
|
|
||||||
const processHaps = (begin, end, currentTime) => {
|
const tickCallback = (payload) => {
|
||||||
|
const {
|
||||||
|
num_cycles_at_cps_change,
|
||||||
|
cps,
|
||||||
|
num_seconds_at_cps_change,
|
||||||
|
num_seconds_since_cps_change,
|
||||||
|
begin,
|
||||||
|
end,
|
||||||
|
tickdeadline,
|
||||||
|
cycle,
|
||||||
|
} = payload;
|
||||||
|
this.cps = cps;
|
||||||
|
this.cycle = cycle;
|
||||||
|
|
||||||
|
setTimeReference(num_seconds_at_cps_change, num_seconds_since_cps_change, tickdeadline);
|
||||||
|
|
||||||
|
processHaps(begin, end, num_cycles_at_cps_change, num_seconds_at_cps_change);
|
||||||
|
|
||||||
|
this.time_at_last_tick_message = this.getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
const processHaps = (begin, end, num_cycles_at_cps_change, seconds_at_cps_change) => {
|
||||||
if (this.started === false) {
|
if (this.started === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const haps = this.pattern.queryArc(begin, end, { _cps: this.cps });
|
const haps = this.pattern.queryArc(begin, end, { _cps: this.cps });
|
||||||
|
|
||||||
haps.forEach((hap) => {
|
haps.forEach((hap) => {
|
||||||
if (hap.hasOnset()) {
|
if (hap.hasOnset()) {
|
||||||
const timeUntilTrigger = cycleToSeconds(hap.whole.begin - this.cycle, this.cps);
|
const targetTime =
|
||||||
const targetTime = timeUntilTrigger + currentTime + this.latency;
|
(hap.whole.begin - num_cycles_at_cps_change) / this.cps +
|
||||||
const duration = cycleToSeconds(hap.duration, this.cps);
|
seconds_at_cps_change +
|
||||||
|
this.latency +
|
||||||
|
this.worker_time_dif;
|
||||||
|
const duration = hap.duration / this.cps;
|
||||||
onTrigger?.(hap, 0, duration, this.cps, targetTime);
|
onTrigger?.(hap, 0, duration, this.cps, targetTime);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -89,8 +129,8 @@ export class NeoCyclist {
|
|||||||
this.setStarted(true);
|
this.setStarted(true);
|
||||||
}
|
}
|
||||||
stop() {
|
stop() {
|
||||||
|
this.worker_time_dif = null;
|
||||||
logger('[cyclist] stop');
|
logger('[cyclist] stop');
|
||||||
this.collator.reset();
|
|
||||||
this.setStarted(false);
|
this.setStarted(false);
|
||||||
}
|
}
|
||||||
setPattern(pat, autostart = false) {
|
setPattern(pat, autostart = false) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/core",
|
"name": "@strudel/core",
|
||||||
"version": "1.2.0",
|
"version": "1.0.1",
|
||||||
"description": "Port of Tidal Cycles to JavaScript",
|
"description": "Port of Tidal Cycles to JavaScript",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"bench": "vitest bench",
|
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"prepublishOnly": "pnpm build"
|
"prepublishOnly": "pnpm build"
|
||||||
},
|
},
|
||||||
@@ -31,11 +30,11 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://strudel.cc",
|
"homepage": "https://strudel.cc",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fraction.js": "^5.2.1"
|
"fraction.js": "^4.3.7"
|
||||||
},
|
},
|
||||||
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
|
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11",
|
"vite": "^5.0.10",
|
||||||
"vitest": "^3.0.4"
|
"vitest": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+242
-576
File diff suppressed because it is too large
Load Diff
@@ -1,214 +0,0 @@
|
|||||||
/*
|
|
||||||
pick.mjs - methods that use one pattern to pick events from other patterns.
|
|
||||||
Copyright (C) 2024 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/signal.mjs>
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Pattern, reify, silence, register } from './pattern.mjs';
|
|
||||||
|
|
||||||
import { _mod, clamp, objectMap } from './util.mjs';
|
|
||||||
|
|
||||||
const _pick = function (lookup, pat, modulo = true) {
|
|
||||||
const array = Array.isArray(lookup);
|
|
||||||
const len = Object.keys(lookup).length;
|
|
||||||
|
|
||||||
lookup = objectMap(lookup, reify);
|
|
||||||
|
|
||||||
if (len === 0) {
|
|
||||||
return silence;
|
|
||||||
}
|
|
||||||
return pat.fmap((i) => {
|
|
||||||
let key = i;
|
|
||||||
if (array) {
|
|
||||||
key = modulo ? Math.round(key) % len : clamp(Math.round(key), 0, lookup.length - 1);
|
|
||||||
}
|
|
||||||
return lookup[key];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
|
||||||
* Similar to `inhabit`, but maintains the structure of the original patterns.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
* @example
|
|
||||||
* note("<0 1 2!2 3>".pick(["g a", "e f", "f g f g" , "g c d"]))
|
|
||||||
* @example
|
|
||||||
* sound("<0 1 [2,0]>".pick(["bd sd", "cp cp", "hh hh"]))
|
|
||||||
* @example
|
|
||||||
* sound("<0!2 [0,1] 1>".pick(["bd(3,8)", "sd sd"]))
|
|
||||||
* @example
|
|
||||||
* s("<a!2 [a,b] b>".pick({a: "bd(3,8)", b: "sd sd"}))
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const pick = function (lookup, pat) {
|
|
||||||
// backward compatibility - the args used to be flipped
|
|
||||||
if (Array.isArray(pat)) {
|
|
||||||
[pat, lookup] = [lookup, pat];
|
|
||||||
}
|
|
||||||
return __pick(lookup, pat);
|
|
||||||
};
|
|
||||||
|
|
||||||
const __pick = register('pick', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, false).innerJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * The same as `pick`, but if you pick a number greater than the size of the list,
|
|
||||||
* it wraps around, rather than sticking at the maximum value.
|
|
||||||
* For example, if you pick the fifth pattern of a list of three, you'll get the
|
|
||||||
* second one.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const pickmod = register('pickmod', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, true).innerJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * pickF lets you use a pattern of numbers to pick which function to apply to another pattern.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {Pattern} lookup a pattern of indices
|
|
||||||
* @param {function[]} funcs the array of functions from which to pull
|
|
||||||
* @returns {Pattern}
|
|
||||||
* @example
|
|
||||||
* s("bd [rim hh]").pickF("<0 1 2>", [rev,jux(rev),fast(2)])
|
|
||||||
* @example
|
|
||||||
* note("<c2 d2>(3,8)").s("square")
|
|
||||||
* .pickF("<0 2> 1", [jux(rev),fast(2),x=>x.lpf(800)])
|
|
||||||
*/
|
|
||||||
export const pickF = register('pickF', function (lookup, funcs, pat) {
|
|
||||||
return pat.apply(pick(lookup, funcs));
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * The same as `pickF`, but if you pick a number greater than the size of the functions list,
|
|
||||||
* it wraps around, rather than sticking at the maximum value.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {Pattern} lookup a pattern of indices
|
|
||||||
* @param {function[]} funcs the array of functions from which to pull
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
export const pickmodF = register('pickmodF', function (lookup, funcs, pat) {
|
|
||||||
return pat.apply(pickmod(lookup, funcs));
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * Similar to `pick`, but it applies an outerJoin instead of an innerJoin.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
export const pickOut = register('pickOut', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, false).outerJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * The same as `pickOut`, but if you pick a number greater than the size of the list,
|
|
||||||
* it wraps around, rather than sticking at the maximum value.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
export const pickmodOut = register('pickmodOut', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, true).outerJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * Similar to `pick`, but the choosen pattern is restarted when its index is triggered.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
export const pickRestart = register('pickRestart', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, false).restartJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * The same as `pickRestart`, but if you pick a number greater than the size of the list,
|
|
||||||
* it wraps around, rather than sticking at the maximum value.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
* @example
|
|
||||||
* "<a@2 b@2 c@2 d@2>".pickRestart({
|
|
||||||
a: n("0 1 2 0"),
|
|
||||||
b: n("2 3 4 ~"),
|
|
||||||
c: n("[4 5] [4 3] 2 0"),
|
|
||||||
d: n("0 -3 0 ~")
|
|
||||||
}).scale("C:major").s("piano")
|
|
||||||
*/
|
|
||||||
export const pickmodRestart = register('pickmodRestart', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, true).restartJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * Similar to `pick`, but the choosen pattern is reset when its index is triggered.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
export const pickReset = register('pickReset', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, false).resetJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * The same as `pickReset`, but if you pick a number greater than the size of the list,
|
|
||||||
* it wraps around, rather than sticking at the maximum value.
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
export const pickmodReset = register('pickmodReset', function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, true).resetJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
|
||||||
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
|
|
||||||
* @name inhabit
|
|
||||||
* @synonyms pickSqueeze
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
* @example
|
|
||||||
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
|
|
||||||
b: s("cp sd")
|
|
||||||
})
|
|
||||||
* @example
|
|
||||||
* s("a@2 [a b] a".inhabit({a: "bd(3,8)", b: "sd sd"})).slow(4)
|
|
||||||
*/
|
|
||||||
export const { inhabit, pickSqueeze } = register(['inhabit', 'pickSqueeze'], function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, false).squeezeJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/** * The same as `inhabit`, but if you pick a number greater than the size of the list,
|
|
||||||
* it wraps around, rather than sticking at the maximum value.
|
|
||||||
* For example, if you pick the fifth pattern of a list of three, you'll get the
|
|
||||||
* second one.
|
|
||||||
* @name inhabitmod
|
|
||||||
* @synonyms pickmodSqueeze
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const { inhabitmod, pickmodSqueeze } = register(['inhabitmod', 'pickmodSqueeze'], function (lookup, pat) {
|
|
||||||
return _pick(lookup, pat, true).squeezeJoin();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick from the list of values (or patterns of values) via the index using the given
|
|
||||||
* pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event
|
|
||||||
* @param {Pattern} pat
|
|
||||||
* @param {*} xs
|
|
||||||
* @returns {Pattern}
|
|
||||||
* @example
|
|
||||||
* note(squeeze("<0@2 [1!2] 2>", ["g a", "f g f g" , "g a c d"]))
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const squeeze = (pat, xs) => {
|
|
||||||
xs = xs.map(reify);
|
|
||||||
if (xs.length == 0) {
|
|
||||||
return silence;
|
|
||||||
}
|
|
||||||
return pat
|
|
||||||
.fmap((i) => {
|
|
||||||
const key = _mod(Math.round(i), xs.length);
|
|
||||||
return xs[key];
|
|
||||||
})
|
|
||||||
.squeezeJoin();
|
|
||||||
};
|
|
||||||
+6
-52
@@ -10,7 +10,6 @@ export function repl({
|
|||||||
defaultOutput,
|
defaultOutput,
|
||||||
onEvalError,
|
onEvalError,
|
||||||
beforeEval,
|
beforeEval,
|
||||||
beforeStart,
|
|
||||||
afterEval,
|
afterEval,
|
||||||
getTime,
|
getTime,
|
||||||
transpiler,
|
transpiler,
|
||||||
@@ -20,7 +19,6 @@ export function repl({
|
|||||||
sync = false,
|
sync = false,
|
||||||
setInterval,
|
setInterval,
|
||||||
clearInterval,
|
clearInterval,
|
||||||
id,
|
|
||||||
}) {
|
}) {
|
||||||
const state = {
|
const state = {
|
||||||
schedulerError: undefined,
|
schedulerError: undefined,
|
||||||
@@ -34,10 +32,6 @@ export function repl({
|
|||||||
started: false,
|
started: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const transpilerOptions = {
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateState = (update) => {
|
const updateState = (update) => {
|
||||||
Object.assign(state, update);
|
Object.assign(state, update);
|
||||||
state.isDirty = state.code !== state.activeCode;
|
state.isDirty = state.code !== state.activeCode;
|
||||||
@@ -54,7 +48,6 @@ export function repl({
|
|||||||
},
|
},
|
||||||
setInterval,
|
setInterval,
|
||||||
clearInterval,
|
clearInterval,
|
||||||
beforeStart,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// NeoCyclist uses a shared worker to communicate between instances, which is not supported on mobile chrome
|
// NeoCyclist uses a shared worker to communicate between instances, which is not supported on mobile chrome
|
||||||
@@ -63,20 +56,17 @@ export function repl({
|
|||||||
let pPatterns = {};
|
let pPatterns = {};
|
||||||
let anonymousIndex = 0;
|
let anonymousIndex = 0;
|
||||||
let allTransform;
|
let allTransform;
|
||||||
let eachTransform;
|
|
||||||
|
|
||||||
const hush = function () {
|
const hush = function () {
|
||||||
pPatterns = {};
|
pPatterns = {};
|
||||||
anonymousIndex = 0;
|
anonymousIndex = 0;
|
||||||
allTransform = undefined;
|
allTransform = undefined;
|
||||||
eachTransform = undefined;
|
|
||||||
return silence;
|
return silence;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPattern = async (pattern, autostart = true) => {
|
const setPattern = (pattern, autostart = true) => {
|
||||||
pattern = editPattern?.(pattern) || pattern;
|
pattern = editPattern?.(pattern) || pattern;
|
||||||
await scheduler.setPattern(pattern, autostart);
|
scheduler.setPattern(pattern, autostart);
|
||||||
return pattern;
|
|
||||||
};
|
};
|
||||||
setTime(() => scheduler.now()); // TODO: refactor?
|
setTime(() => scheduler.now()); // TODO: refactor?
|
||||||
|
|
||||||
@@ -86,42 +76,15 @@ export function repl({
|
|||||||
const toggle = () => scheduler.toggle();
|
const toggle = () => scheduler.toggle();
|
||||||
const setCps = (cps) => scheduler.setCps(cps);
|
const setCps = (cps) => scheduler.setCps(cps);
|
||||||
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
|
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
|
||||||
|
|
||||||
// TODO - not documented as jsdoc examples as the test framework doesn't simulate enough context for `each` and `all`..
|
|
||||||
|
|
||||||
/** Applies a function to all the running patterns. Note that the patterns are groups together into a single `stack` before the function is applied. This is probably what you want, but see `each` for
|
|
||||||
* a version that applies the function to each pattern separately.
|
|
||||||
* ```
|
|
||||||
* $: sound("bd - cp sd")
|
|
||||||
* $: sound("hh*8")
|
|
||||||
* all(fast("<2 3>"))
|
|
||||||
* ```
|
|
||||||
* ```
|
|
||||||
* $: sound("bd - cp sd")
|
|
||||||
* $: sound("hh*8")
|
|
||||||
* all(x => x.pianoroll())
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const all = function (transform) {
|
const all = function (transform) {
|
||||||
allTransform = transform;
|
allTransform = transform;
|
||||||
return silence;
|
return silence;
|
||||||
};
|
};
|
||||||
/** Applies a function to each of the running patterns separately. This is intended for future use with upcoming 'stepwise' features. See `all` for a version that applies the function to all the patterns stacked together into a single pattern.
|
|
||||||
* ```
|
|
||||||
* $: sound("bd - cp sd")
|
|
||||||
* $: sound("hh*8")
|
|
||||||
* each(fast("<2 3>"))
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const each = function (transform) {
|
|
||||||
eachTransform = transform;
|
|
||||||
return silence;
|
|
||||||
};
|
|
||||||
|
|
||||||
// set pattern methods that use this repl via closure
|
// set pattern methods that use this repl via closure
|
||||||
const injectPatternMethods = () => {
|
const injectPatternMethods = () => {
|
||||||
Pattern.prototype.p = function (id) {
|
Pattern.prototype.p = function (id) {
|
||||||
if (typeof id === 'string' && (id.startsWith('_') || id.endsWith('_'))) {
|
if (id.startsWith('_') || id.endsWith('_')) {
|
||||||
// allows muting a pattern x with x_ or _x
|
// allows muting a pattern x with x_ or _x
|
||||||
return silence;
|
return silence;
|
||||||
}
|
}
|
||||||
@@ -160,7 +123,6 @@ export function repl({
|
|||||||
});
|
});
|
||||||
return evalScope({
|
return evalScope({
|
||||||
all,
|
all,
|
||||||
each,
|
|
||||||
hush,
|
hush,
|
||||||
cpm,
|
cpm,
|
||||||
setCps,
|
setCps,
|
||||||
@@ -177,19 +139,11 @@ export function repl({
|
|||||||
try {
|
try {
|
||||||
updateState({ code, pending: true });
|
updateState({ code, pending: true });
|
||||||
await injectPatternMethods();
|
await injectPatternMethods();
|
||||||
setTime(() => scheduler.now()); // TODO: refactor?
|
|
||||||
await beforeEval?.({ code });
|
await beforeEval?.({ code });
|
||||||
shouldHush && hush();
|
shouldHush && hush();
|
||||||
let { pattern, meta } = await _evaluate(code, transpiler, transpilerOptions);
|
let { pattern, meta } = await _evaluate(code, transpiler);
|
||||||
if (Object.keys(pPatterns).length) {
|
if (Object.keys(pPatterns).length) {
|
||||||
let patterns = Object.values(pPatterns);
|
pattern = stack(...Object.values(pPatterns));
|
||||||
if (eachTransform) {
|
|
||||||
// Explicit lambda so only element (not index and array) are passed
|
|
||||||
patterns = patterns.map((x) => eachTransform(x));
|
|
||||||
}
|
|
||||||
pattern = stack(...patterns);
|
|
||||||
} else if (eachTransform) {
|
|
||||||
pattern = eachTransform(pattern);
|
|
||||||
}
|
}
|
||||||
if (allTransform) {
|
if (allTransform) {
|
||||||
pattern = allTransform(pattern);
|
pattern = allTransform(pattern);
|
||||||
@@ -199,7 +153,7 @@ export function repl({
|
|||||||
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
||||||
}
|
}
|
||||||
logger(`[eval] code updated`);
|
logger(`[eval] code updated`);
|
||||||
pattern = await setPattern(pattern, autostart);
|
setPattern(pattern, autostart);
|
||||||
updateState({
|
updateState({
|
||||||
miniLocations: meta?.miniLocations || [],
|
miniLocations: meta?.miniLocations || [],
|
||||||
widgets: meta?.widgets || [],
|
widgets: meta?.widgets || [],
|
||||||
|
|||||||
+230
-284
@@ -1,14 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
signal.mjs - continuous patterns
|
signal.mjs - <short description TODO>
|
||||||
Copyright (C) 2024 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/signal.mjs>
|
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/signal.mjs>
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Hap } from './hap.mjs';
|
import { Hap } from './hap.mjs';
|
||||||
import { Pattern, fastcat, pure, register, reify, silence, stack, sequenceP } from './pattern.mjs';
|
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
|
||||||
import Fraction from './fraction.mjs';
|
import Fraction from './fraction.mjs';
|
||||||
|
import { id, _mod, clamp, objectMap } from './util.mjs';
|
||||||
import { id, keyAlias, getCurrentKeyboardState } from './util.mjs';
|
|
||||||
|
|
||||||
export function steady(value) {
|
export function steady(value) {
|
||||||
// A continuous value
|
// A continuous value
|
||||||
@@ -16,10 +15,13 @@ export function steady(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const signal = (func) => {
|
export const signal = (func) => {
|
||||||
const query = (state) => [new Hap(undefined, state.span, func(state.span.begin))];
|
const query = (state) => [new Hap(undefined, state.span, func(state.span.midpoint()))];
|
||||||
return new Pattern(query);
|
return new Pattern(query);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isaw = signal((t) => 1 - (t % 1));
|
||||||
|
export const isaw2 = isaw.toBipolar();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sawtooth signal between 0 and 1.
|
* A sawtooth signal between 0 and 1.
|
||||||
*
|
*
|
||||||
@@ -33,40 +35,8 @@ export const signal = (func) => {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const saw = signal((t) => t % 1);
|
export const saw = signal((t) => t % 1);
|
||||||
|
|
||||||
/**
|
|
||||||
* A sawtooth signal between -1 and 1 (like `saw`, but bipolar).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const saw2 = saw.toBipolar();
|
export const saw2 = saw.toBipolar();
|
||||||
|
|
||||||
/**
|
|
||||||
* A sawtooth signal between 1 and 0 (like `saw`, but flipped).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
* @example
|
|
||||||
* note("<c3 [eb3,g3] g2 [g3,bb3]>*8")
|
|
||||||
* .clip(isaw.slow(2))
|
|
||||||
* @example
|
|
||||||
* n(isaw.range(0,8).segment(8))
|
|
||||||
* .scale('C major')
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const isaw = signal((t) => 1 - (t % 1));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sawtooth signal between 1 and -1 (like `saw2`, but flipped).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const isaw2 = isaw.toBipolar();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sine signal between -1 and 1 (like `sine`, but bipolar).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
|
export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,12 +60,6 @@ export const sine = sine2.fromBipolar();
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const cosine = sine._early(Fraction(1).div(4));
|
export const cosine = sine._early(Fraction(1).div(4));
|
||||||
|
|
||||||
/**
|
|
||||||
* A cosine signal between -1 and 1 (like `cosine`, but bipolar).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const cosine2 = sine2._early(Fraction(1).div(4));
|
export const cosine2 = sine2._early(Fraction(1).div(4));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,12 +71,6 @@ export const cosine2 = sine2._early(Fraction(1).div(4));
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const square = signal((t) => Math.floor((t * 2) % 2));
|
export const square = signal((t) => Math.floor((t * 2) % 2));
|
||||||
|
|
||||||
/**
|
|
||||||
* A square signal between -1 and 1 (like `square`, but bipolar).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const square2 = square.toBipolar();
|
export const square2 = square.toBipolar();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,71 +81,11 @@ export const square2 = square.toBipolar();
|
|||||||
* n(tri.segment(8).range(0,7)).scale("C:minor")
|
* n(tri.segment(8).range(0,7)).scale("C:minor")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const tri = fastcat(saw, isaw);
|
export const tri = fastcat(isaw, saw);
|
||||||
|
export const tri2 = fastcat(isaw2, saw2);
|
||||||
|
|
||||||
/**
|
|
||||||
* A triangle signal between -1 and 1 (like `tri`, but bipolar).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const tri2 = fastcat(saw2, isaw2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An inverted triangle signal between 1 and 0 (like `tri`, but flipped).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
* @example
|
|
||||||
* n(itri.segment(8).range(0,7)).scale("C:minor")
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const itri = fastcat(isaw, saw);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An inverted triangle signal between -1 and 1 (like `itri`, but bipolar).
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const itri2 = fastcat(isaw2, saw2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A signal representing the cycle time.
|
|
||||||
*
|
|
||||||
* @return {Pattern}
|
|
||||||
*/
|
|
||||||
export const time = signal(id);
|
export const time = signal(id);
|
||||||
|
|
||||||
/**
|
|
||||||
* The mouse's x position value ranges from 0 to 1.
|
|
||||||
* @name mousex
|
|
||||||
* @return {Pattern}
|
|
||||||
* @example
|
|
||||||
* n(mousex.segment(4).range(0,7)).scale("C:minor")
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The mouse's y position value ranges from 0 to 1.
|
|
||||||
* @name mousey
|
|
||||||
* @return {Pattern}
|
|
||||||
* @example
|
|
||||||
* n(mousey.segment(4).range(0,7)).scale("C:minor")
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let _mouseY = 0,
|
|
||||||
_mouseX = 0;
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
//document.onmousemove = (e) => {
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
|
||||||
_mouseY = e.clientY / document.body.clientHeight;
|
|
||||||
_mouseX = e.clientX / document.body.clientWidth;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mousey = signal(() => _mouseY);
|
|
||||||
export const mouseY = signal(() => _mouseY);
|
|
||||||
export const mousex = signal(() => _mouseX);
|
|
||||||
export const mouseX = signal(() => _mouseX);
|
|
||||||
|
|
||||||
// random signals
|
// random signals
|
||||||
|
|
||||||
const xorwise = (x) => {
|
const xorwise = (x) => {
|
||||||
@@ -208,7 +106,7 @@ const timeToRand = (x) => Math.abs(intSeedToRand(timeToIntSeed(x)));
|
|||||||
const timeToRandsPrime = (seed, n) => {
|
const timeToRandsPrime = (seed, n) => {
|
||||||
const result = [];
|
const result = [];
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
for (let i = 0; i < n; ++i) {
|
for (let i = 0; i < n; ++n) {
|
||||||
result.push(intSeedToRand(seed));
|
result.push(intSeedToRand(seed));
|
||||||
seed = xorwise(seed);
|
seed = xorwise(seed);
|
||||||
}
|
}
|
||||||
@@ -227,82 +125,7 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
|
|||||||
* n(run(4)).scale("C4:pentatonic")
|
* n(run(4)).scale("C4:pentatonic")
|
||||||
* // n("0 1 2 3").scale("C4:pentatonic")
|
* // n("0 1 2 3").scale("C4:pentatonic")
|
||||||
*/
|
*/
|
||||||
export const run = (n) => saw.range(0, n).round().segment(n);
|
export const run = (n) => saw.range(0, n).floor().segment(n);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a pattern from a binary number.
|
|
||||||
*
|
|
||||||
* @name binary
|
|
||||||
* @param {number} n - input number to convert to binary
|
|
||||||
* @example
|
|
||||||
* "hh".s().struct(binary(5))
|
|
||||||
* // "hh".s().struct("1 0 1")
|
|
||||||
*/
|
|
||||||
export const binary = (n) => {
|
|
||||||
const nBits = reify(n).log2(0).floor().add(1);
|
|
||||||
return binaryN(n, nBits);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a pattern from a binary number, padded to n bits long.
|
|
||||||
*
|
|
||||||
* @name binaryN
|
|
||||||
* @param {number} n - input number to convert to binary
|
|
||||||
* @param {number} nBits - pattern length, defaults to 16
|
|
||||||
* @example
|
|
||||||
* "hh".s().struct(binaryN(55532, 16))
|
|
||||||
* // "hh".s().struct("1 1 0 1 1 0 0 0 1 1 1 0 1 1 0 0")
|
|
||||||
*/
|
|
||||||
export const binaryN = (n, nBits = 16) => {
|
|
||||||
nBits = reify(nBits);
|
|
||||||
// Shift and mask, putting msb on the right-side
|
|
||||||
const bitPos = run(nBits).mul(-1).add(nBits.sub(1));
|
|
||||||
return reify(n).segment(nBits).brshift(bitPos).band(pure(1));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const randrun = (n) => {
|
|
||||||
return signal((t) => {
|
|
||||||
// Without adding 0.5, the first cycle is always 0,1,2,3,...
|
|
||||||
const rands = timeToRands(t.floor().add(0.5), n);
|
|
||||||
const nums = rands
|
|
||||||
.map((n, i) => [n, i])
|
|
||||||
.sort((a, b) => a[0] > b[0] - a[0] < b[0])
|
|
||||||
.map((x) => x[1]);
|
|
||||||
const i = t.cyclePos().mul(n).floor() % n;
|
|
||||||
return nums[i];
|
|
||||||
})._segment(n);
|
|
||||||
};
|
|
||||||
|
|
||||||
const _rearrangeWith = (ipat, n, pat) => {
|
|
||||||
const pats = [...Array(n).keys()].map((i) => pat.zoom(Fraction(i).div(n), Fraction(i + 1).div(n)));
|
|
||||||
return ipat.fmap((i) => pats[i].repeatCycles(n)._fast(n)).innerJoin();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name shuffle
|
|
||||||
* Slices a pattern into the given number of parts, then plays those parts in random order.
|
|
||||||
* Each part will be played exactly once per cycle.
|
|
||||||
* @example
|
|
||||||
* note("c d e f").sound("piano").shuffle(4)
|
|
||||||
* @example
|
|
||||||
* note("c d e f".shuffle(4), "g").sound("piano")
|
|
||||||
*/
|
|
||||||
export const shuffle = register('shuffle', (n, pat) => {
|
|
||||||
return _rearrangeWith(randrun(n), n, pat);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name scramble
|
|
||||||
* Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`,
|
|
||||||
* but parts might be played more than once, or not at all, per cycle.
|
|
||||||
* @example
|
|
||||||
* note("c d e f").sound("piano").scramble(4)
|
|
||||||
* @example
|
|
||||||
* note("c d e f".scramble(4), "g").sound("piano")
|
|
||||||
*/
|
|
||||||
export const scramble = register('scramble', (n, pat) => {
|
|
||||||
return _rearrangeWith(_irand(n)._segment(n), n, pat);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A continuous pattern of random numbers, between 0 and 1.
|
* A continuous pattern of random numbers, between 0 and 1.
|
||||||
@@ -354,6 +177,204 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i));
|
|||||||
*/
|
*/
|
||||||
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
|
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
|
||||||
|
|
||||||
|
const _pick = function (lookup, pat, modulo = true) {
|
||||||
|
const array = Array.isArray(lookup);
|
||||||
|
const len = Object.keys(lookup).length;
|
||||||
|
|
||||||
|
lookup = objectMap(lookup, reify);
|
||||||
|
|
||||||
|
if (len === 0) {
|
||||||
|
return silence;
|
||||||
|
}
|
||||||
|
return pat.fmap((i) => {
|
||||||
|
let key = i;
|
||||||
|
if (array) {
|
||||||
|
key = modulo ? Math.round(key) % len : clamp(Math.round(key), 0, lookup.length - 1);
|
||||||
|
}
|
||||||
|
return lookup[key];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
||||||
|
* Similar to `inhabit`, but maintains the structure of the original patterns.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
* @example
|
||||||
|
* note("<0 1 2!2 3>".pick(["g a", "e f", "f g f g" , "g c d"]))
|
||||||
|
* @example
|
||||||
|
* sound("<0 1 [2,0]>".pick(["bd sd", "cp cp", "hh hh"]))
|
||||||
|
* @example
|
||||||
|
* sound("<0!2 [0,1] 1>".pick(["bd(3,8)", "sd sd"]))
|
||||||
|
* @example
|
||||||
|
* s("<a!2 [a,b] b>".pick({a: "bd(3,8)", b: "sd sd"}))
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const pick = function (lookup, pat) {
|
||||||
|
// backward compatibility - the args used to be flipped
|
||||||
|
if (Array.isArray(pat)) {
|
||||||
|
[pat, lookup] = [lookup, pat];
|
||||||
|
}
|
||||||
|
return __pick(lookup, pat);
|
||||||
|
};
|
||||||
|
|
||||||
|
const __pick = register('pick', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, false).innerJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * The same as `pick`, but if you pick a number greater than the size of the list,
|
||||||
|
* it wraps around, rather than sticking at the maximum value.
|
||||||
|
* For example, if you pick the fifth pattern of a list of three, you'll get the
|
||||||
|
* second one.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const pickmod = register('pickmod', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, true).innerJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * pickF lets you use a pattern of numbers to pick which function to apply to another pattern.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {Pattern} lookup a pattern of indices
|
||||||
|
* @param {function[]} funcs the array of functions from which to pull
|
||||||
|
* @returns {Pattern}
|
||||||
|
* @example
|
||||||
|
* s("bd [rim hh]").pickF("<0 1 2>", [rev,jux(rev),fast(2)])
|
||||||
|
* @example
|
||||||
|
* note("<c2 d2>(3,8)").s("square")
|
||||||
|
* .pickF("<0 2> 1", [jux(rev),fast(2),x=>x.lpf(800)])
|
||||||
|
*/
|
||||||
|
export const pickF = register('pickF', function (lookup, funcs, pat) {
|
||||||
|
return pat.apply(pick(lookup, funcs));
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * The same as `pickF`, but if you pick a number greater than the size of the functions list,
|
||||||
|
* it wraps around, rather than sticking at the maximum value.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {Pattern} lookup a pattern of indices
|
||||||
|
* @param {function[]} funcs the array of functions from which to pull
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickmodF = register('pickmodF', function (lookup, funcs, pat) {
|
||||||
|
return pat.apply(pickmod(lookup, funcs));
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * Similar to `pick`, but it applies an outerJoin instead of an innerJoin.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickOut = register('pickOut', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, false).outerJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * The same as `pickOut`, but if you pick a number greater than the size of the list,
|
||||||
|
* it wraps around, rather than sticking at the maximum value.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickmodOut = register('pickmodOut', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, true).outerJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * Similar to `pick`, but the choosen pattern is restarted when its index is triggered.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickRestart = register('pickRestart', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, false).restartJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * The same as `pickRestart`, but if you pick a number greater than the size of the list,
|
||||||
|
* it wraps around, rather than sticking at the maximum value.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickmodRestart = register('pickmodRestart', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, true).restartJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * Similar to `pick`, but the choosen pattern is reset when its index is triggered.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickReset = register('pickReset', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, false).resetJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * The same as `pickReset`, but if you pick a number greater than the size of the list,
|
||||||
|
* it wraps around, rather than sticking at the maximum value.
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
export const pickmodReset = register('pickmodReset', function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, true).resetJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
|
||||||
|
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
|
||||||
|
* @name inhabit
|
||||||
|
* @synonyms pickSqueeze
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
* @example
|
||||||
|
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
|
||||||
|
b: s("cp sd")
|
||||||
|
})
|
||||||
|
* @example
|
||||||
|
* s("a@2 [a b] a".inhabit({a: "bd(3,8)", b: "sd sd"})).slow(4)
|
||||||
|
*/
|
||||||
|
export const { inhabit, pickSqueeze } = register(['inhabit', 'pickSqueeze'], function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, false).squeezeJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** * The same as `inhabit`, but if you pick a number greater than the size of the list,
|
||||||
|
* it wraps around, rather than sticking at the maximum value.
|
||||||
|
* For example, if you pick the fifth pattern of a list of three, you'll get the
|
||||||
|
* second one.
|
||||||
|
* @name inhabitmod
|
||||||
|
* @synonyms pickmodSqueeze
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const { inhabitmod, pickmodSqueeze } = register(['inhabitmod', 'pickmodSqueeze'], function (lookup, pat) {
|
||||||
|
return _pick(lookup, pat, true).squeezeJoin();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick from the list of values (or patterns of values) via the index using the given
|
||||||
|
* pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event
|
||||||
|
* @param {Pattern} pat
|
||||||
|
* @param {*} xs
|
||||||
|
* @returns {Pattern}
|
||||||
|
* @example
|
||||||
|
* note(squeeze("<0@2 [1!2] 2>", ["g a", "f g f g" , "g a c d"]))
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const squeeze = (pat, xs) => {
|
||||||
|
xs = xs.map(reify);
|
||||||
|
if (xs.length == 0) {
|
||||||
|
return silence;
|
||||||
|
}
|
||||||
|
return pat
|
||||||
|
.fmap((i) => {
|
||||||
|
const key = _mod(Math.round(i), xs.length);
|
||||||
|
return xs[key];
|
||||||
|
})
|
||||||
|
.squeezeJoin();
|
||||||
|
};
|
||||||
|
|
||||||
export const __chooseWith = (pat, xs) => {
|
export const __chooseWith = (pat, xs) => {
|
||||||
xs = xs.map(reify);
|
xs = xs.map(reify);
|
||||||
if (xs.length == 0) {
|
if (xs.length == 0) {
|
||||||
@@ -433,30 +454,19 @@ export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs);
|
|||||||
export const randcat = chooseCycles;
|
export const randcat = chooseCycles;
|
||||||
|
|
||||||
const _wchooseWith = function (pat, ...pairs) {
|
const _wchooseWith = function (pat, ...pairs) {
|
||||||
// A list of patterns of values
|
|
||||||
const values = pairs.map((pair) => reify(pair[0]));
|
const values = pairs.map((pair) => reify(pair[0]));
|
||||||
|
|
||||||
// A list of weight patterns
|
|
||||||
const weights = [];
|
const weights = [];
|
||||||
|
let accum = 0;
|
||||||
let total = pure(0);
|
|
||||||
for (const pair of pairs) {
|
for (const pair of pairs) {
|
||||||
// 'add' accepts either values or patterns of values here, so no need
|
accum += pair[1];
|
||||||
// to explicitly reify
|
weights.push(accum);
|
||||||
total = total.add(pair[1]);
|
|
||||||
// accumulate our list of weight patterns
|
|
||||||
weights.push(total);
|
|
||||||
}
|
}
|
||||||
// a pattern of lists of weights
|
const total = accum;
|
||||||
const weightspat = sequenceP(weights);
|
|
||||||
|
|
||||||
// Takes a number from 0-1, returns a pattern of patterns of values
|
|
||||||
const match = function (r) {
|
const match = function (r) {
|
||||||
const findpat = total.mul(r);
|
const find = r * total;
|
||||||
return weightspat.fmap((weights) => (find) => values[weights.findIndex((x) => x > find, weights)]).appLeft(findpat);
|
return values[weights.findIndex((x) => x > find, weights)];
|
||||||
};
|
};
|
||||||
// This returns a pattern of patterns.. The innerJoin is in wchooseCycles
|
return pat.fmap(match);
|
||||||
return pat.bind(match);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const wchooseWith = (...args) => _wchooseWith(...args).outerJoin();
|
const wchooseWith = (...args) => _wchooseWith(...args).outerJoin();
|
||||||
@@ -478,9 +488,6 @@ export const wchoose = (...pairs) => wchooseWith(rand, ...pairs);
|
|||||||
* wchooseCycles(["bd",10], ["hh",1], ["sd",1]).s().fast(8)
|
* wchooseCycles(["bd",10], ["hh",1], ["sd",1]).s().fast(8)
|
||||||
* @example
|
* @example
|
||||||
* wchooseCycles(["bd bd bd",5], ["hh hh hh",3], ["sd sd sd",1]).fast(4).s()
|
* wchooseCycles(["bd bd bd",5], ["hh hh hh",3], ["sd sd sd",1]).fast(4).s()
|
||||||
* @example
|
|
||||||
* // The probability can itself be a pattern
|
|
||||||
* wchooseCycles(["bd(3,8)","<5 0>"], ["hh hh hh",3]).fast(4).s()
|
|
||||||
*/
|
*/
|
||||||
export const wchooseCycles = (...pairs) => _wchooseWith(rand.segment(1), ...pairs).innerJoin();
|
export const wchooseCycles = (...pairs) => _wchooseWith(rand.segment(1), ...pairs).innerJoin();
|
||||||
|
|
||||||
@@ -506,11 +513,8 @@ export const perlinWith = (pat) => {
|
|||||||
*/
|
*/
|
||||||
export const perlin = perlinWith(time.fmap((v) => Number(v)));
|
export const perlin = perlinWith(time.fmap((v) => Number(v)));
|
||||||
|
|
||||||
export const degradeByWith = register(
|
export const degradeByWith = register('degradeByWith', (withPat, x, pat) =>
|
||||||
'degradeByWith',
|
pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)),
|
||||||
(withPat, x, pat) => pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)),
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -526,18 +530,10 @@ export const degradeByWith = register(
|
|||||||
* s("hh*8").degradeBy(0.2)
|
* s("hh*8").degradeBy(0.2)
|
||||||
* @example
|
* @example
|
||||||
* s("[hh?0.2]*8")
|
* s("[hh?0.2]*8")
|
||||||
* @example
|
|
||||||
* //beat generator
|
|
||||||
* s("bd").segment(16).degradeBy(.5).ribbon(16,1)
|
|
||||||
*/
|
*/
|
||||||
export const degradeBy = register(
|
export const degradeBy = register('degradeBy', function (x, pat) {
|
||||||
'degradeBy',
|
return pat._degradeByWith(rand, x);
|
||||||
function (x, pat) {
|
});
|
||||||
return pat._degradeByWith(rand, x);
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -551,7 +547,7 @@ export const degradeBy = register(
|
|||||||
* @example
|
* @example
|
||||||
* s("[hh?]*8")
|
* s("[hh?]*8")
|
||||||
*/
|
*/
|
||||||
export const degrade = register('degrade', (pat) => pat._degradeBy(0.5), true, true);
|
export const degrade = register('degrade', (pat) => pat._degradeBy(0.5));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inverse of `degradeBy`: Randomly removes events from the pattern by a given amount.
|
* Inverse of `degradeBy`: Randomly removes events from the pattern by a given amount.
|
||||||
@@ -571,17 +567,12 @@ export const degrade = register('degrade', (pat) => pat._degradeBy(0.5), true, t
|
|||||||
* x => x.undegradeBy(0.8).pan(1)
|
* x => x.undegradeBy(0.8).pan(1)
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
export const undegradeBy = register(
|
export const undegradeBy = register('undegradeBy', function (x, pat) {
|
||||||
'undegradeBy',
|
return pat._degradeByWith(
|
||||||
function (x, pat) {
|
rand.fmap((r) => 1 - r),
|
||||||
return pat._degradeByWith(
|
x,
|
||||||
rand.fmap((r) => 1 - r),
|
);
|
||||||
x,
|
});
|
||||||
);
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inverse of `degrade`: Randomly removes 50% of events from the pattern. Shorthand for `.undegradeBy(0.5)`
|
* Inverse of `degrade`: Randomly removes 50% of events from the pattern. Shorthand for `.undegradeBy(0.5)`
|
||||||
@@ -598,7 +589,7 @@ export const undegradeBy = register(
|
|||||||
* x => x.undegrade().pan(1)
|
* x => x.undegrade().pan(1)
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5), true, true);
|
export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -757,48 +748,3 @@ export const never = register('never', function (_, pat) {
|
|||||||
export const always = register('always', function (func, pat) {
|
export const always = register('always', function (func, pat) {
|
||||||
return func(pat);
|
return func(pat);
|
||||||
});
|
});
|
||||||
|
|
||||||
//keyname: string | Array<string>
|
|
||||||
//keyname reference: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
|
|
||||||
export function _keyDown(keyname) {
|
|
||||||
if (Array.isArray(keyname) === false) {
|
|
||||||
keyname = [keyname];
|
|
||||||
}
|
|
||||||
const keyState = getCurrentKeyboardState();
|
|
||||||
return keyname.every((x) => {
|
|
||||||
const keyName = keyAlias.get(x) ?? x;
|
|
||||||
return keyState[keyName];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Do something on a keypress, or array of keypresses
|
|
||||||
* [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)
|
|
||||||
*
|
|
||||||
* @name whenKey
|
|
||||||
* @memberof Pattern
|
|
||||||
* @returns Pattern
|
|
||||||
* @example
|
|
||||||
* s("bd(5,8)").whenKey("Control:j", x => x.segment(16).color("red")).whenKey("Control:i", x => x.fast(2).color("blue"))
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const whenKey = register('whenKey', function (input, func, pat) {
|
|
||||||
return pat.when(_keyDown(input), func);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* returns true when a key or array of keys is held
|
|
||||||
* [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)
|
|
||||||
*
|
|
||||||
* @name keyDown
|
|
||||||
* @memberof Pattern
|
|
||||||
* @returns Pattern
|
|
||||||
* @example
|
|
||||||
* keyDown("Control:j").pick([s("bd(5,8)"), s("cp(3,8)")])
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const keyDown = register('keyDown', function (pat) {
|
|
||||||
return pat.fmap(_keyDown);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Copyright (C) 2023 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { s, pan, getControlName } from '../controls.mjs';
|
import { s, pan } from '../controls.mjs';
|
||||||
import { mini } from '../../mini/mini.mjs';
|
import { mini } from '../../mini/mini.mjs';
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import Fraction from '../fraction.mjs';
|
import Fraction from '../fraction.mjs';
|
||||||
@@ -30,17 +30,13 @@ describe('controls', () => {
|
|||||||
expect(s(mini('bd').pan(1)).firstCycleValues).toEqual([{ s: 'bd', pan: 1 }]);
|
expect(s(mini('bd').pan(1)).firstCycleValues).toEqual([{ s: 'bd', pan: 1 }]);
|
||||||
expect(s(mini('bd:1').pan(1)).firstCycleValues).toEqual([{ s: 'bd', n: 1, pan: 1 }]);
|
expect(s(mini('bd:1').pan(1)).firstCycleValues).toEqual([{ s: 'bd', n: 1, pan: 1 }]);
|
||||||
});
|
});
|
||||||
it('preserves step count of the left pattern', () => {
|
it('preserves tactus of the left pattern', () => {
|
||||||
expect(s(mini('bd cp mt').pan(mini('1 2 3 4')))._steps).toEqual(Fraction(3));
|
expect(s(mini('bd cp mt').pan(mini('1 2 3 4'))).tactus).toEqual(Fraction(3));
|
||||||
});
|
});
|
||||||
it('preserves step count of the right pattern for .out', () => {
|
it('preserves tactus of the right pattern for .out', () => {
|
||||||
expect(s(mini('bd cp mt').set.out(pan(mini('1 2 3 4'))))._steps).toEqual(Fraction(4));
|
expect(s(mini('bd cp mt').set.out(pan(mini('1 2 3 4')))).tactus).toEqual(Fraction(4));
|
||||||
});
|
});
|
||||||
it('combines step count of the pattern for .mix as lcm', () => {
|
it('combines tactus of the pattern for .mix as lcm', () => {
|
||||||
expect(s(mini('bd cp mt').set.mix(pan(mini('1 2 3 4'))))._steps).toEqual(Fraction(12));
|
expect(s(mini('bd cp mt').set.mix(pan(mini('1 2 3 4')))).tactus).toEqual(Fraction(12));
|
||||||
});
|
|
||||||
it('finds control name by alias', () => {
|
|
||||||
expect(getControlName('lpf')).toEqual('cutoff');
|
|
||||||
expect(getControlName('cutoff')).toEqual('cutoff');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import {
|
|||||||
cat,
|
cat,
|
||||||
sequence,
|
sequence,
|
||||||
palindrome,
|
palindrome,
|
||||||
polymeter,
|
s_polymeter,
|
||||||
|
s_polymeterSteps,
|
||||||
polyrhythm,
|
polyrhythm,
|
||||||
silence,
|
silence,
|
||||||
fast,
|
fast,
|
||||||
@@ -45,13 +46,12 @@ import {
|
|||||||
rev,
|
rev,
|
||||||
time,
|
time,
|
||||||
run,
|
run,
|
||||||
binaryN,
|
|
||||||
pick,
|
pick,
|
||||||
stackLeft,
|
stackLeft,
|
||||||
stackRight,
|
stackRight,
|
||||||
stackCentre,
|
stackCentre,
|
||||||
stepcat,
|
s_cat,
|
||||||
sometimes,
|
calculateTactus,
|
||||||
} from '../index.mjs';
|
} from '../index.mjs';
|
||||||
|
|
||||||
import { steady } from '../signal.mjs';
|
import { steady } from '../signal.mjs';
|
||||||
@@ -608,12 +608,19 @@ describe('Pattern', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('polymeter()', () => {
|
describe('s_polymeter()', () => {
|
||||||
it('Can layer up cycles, stepwise, with lists', () => {
|
it('Can layer up cycles, stepwise, with lists', () => {
|
||||||
expect(polymeter(['a', 'b', 'c'], ['d', 'e']).fast(2).firstCycle()).toStrictEqual(
|
expect(s_polymeterSteps(3, ['d', 'e']).firstCycle()).toStrictEqual(
|
||||||
|
fastcat(pure('d'), pure('e'), pure('d')).firstCycle(),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(s_polymeter(['a', 'b', 'c'], ['d', 'e']).fast(2).firstCycle()).toStrictEqual(
|
||||||
stack(sequence('a', 'b', 'c', 'a', 'b', 'c'), sequence('d', 'e', 'd', 'e', 'd', 'e')).firstCycle(),
|
stack(sequence('a', 'b', 'c', 'a', 'b', 'c'), sequence('d', 'e', 'd', 'e', 'd', 'e')).firstCycle(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('Can layer up cycles, stepwise, with weighted patterns', () => {
|
||||||
|
sameFirst(s_polymeterSteps(3, sequence('a', 'b')).fast(2), sequence('a', 'b', 'a', 'b', 'a', 'b'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('firstOf()', () => {
|
describe('firstOf()', () => {
|
||||||
@@ -728,15 +735,21 @@ describe('Pattern', () => {
|
|||||||
describe('signal()', () => {
|
describe('signal()', () => {
|
||||||
it('Can make saw/saw2', () => {
|
it('Can make saw/saw2', () => {
|
||||||
expect(saw.struct(true, true, true, true).firstCycle()).toStrictEqual(
|
expect(saw.struct(true, true, true, true).firstCycle()).toStrictEqual(
|
||||||
sequence(0, 1 / 4, 1 / 2, 3 / 4).firstCycle(),
|
sequence(1 / 8, 3 / 8, 5 / 8, 7 / 8).firstCycle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(saw2.struct(true, true, true, true).firstCycle()).toStrictEqual(sequence(-1, -0.5, 0, 0.5).firstCycle());
|
expect(saw2.struct(true, true, true, true).firstCycle()).toStrictEqual(
|
||||||
|
sequence(-3 / 4, -1 / 4, 1 / 4, 3 / 4).firstCycle(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Can make isaw/isaw2', () => {
|
it('Can make isaw/isaw2', () => {
|
||||||
expect(isaw.struct(true, true, true, true).firstCycle()).toStrictEqual(sequence(1, 0.75, 0.5, 0.25).firstCycle());
|
expect(isaw.struct(true, true, true, true).firstCycle()).toStrictEqual(
|
||||||
|
sequence(7 / 8, 5 / 8, 3 / 8, 1 / 8).firstCycle(),
|
||||||
|
);
|
||||||
|
|
||||||
expect(isaw2.struct(true, true, true, true).firstCycle()).toStrictEqual(sequence(1, 0.5, 0, -0.5).firstCycle());
|
expect(isaw2.struct(true, true, true, true).firstCycle()).toStrictEqual(
|
||||||
|
sequence(3 / 4, 1 / 4, -1 / 4, -3 / 4).firstCycle(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('_setContext()', () => {
|
describe('_setContext()', () => {
|
||||||
@@ -874,7 +887,7 @@ describe('Pattern', () => {
|
|||||||
.squeezeJoin()
|
.squeezeJoin()
|
||||||
.queryArc(3, 4)
|
.queryArc(3, 4)
|
||||||
.map((x) => x.value),
|
.map((x) => x.value),
|
||||||
).toStrictEqual([Fraction(3)]);
|
).toStrictEqual([Fraction(3.5)]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('ply', () => {
|
describe('ply', () => {
|
||||||
@@ -924,9 +937,6 @@ describe('Pattern', () => {
|
|||||||
.firstCycle(),
|
.firstCycle(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('Can chop chops', () => {
|
|
||||||
expect(pure({ s: 'bev' }).chop(2).chop(2).firstCycle()).toStrictEqual(pure({ s: 'bev' }).chop(4).firstCycle());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
describe('range', () => {
|
describe('range', () => {
|
||||||
it('Can be patterned', () => {
|
it('Can be patterned', () => {
|
||||||
@@ -945,18 +955,6 @@ describe('Pattern', () => {
|
|||||||
expect(run(4).firstCycle()).toStrictEqual(sequence(0, 1, 2, 3).firstCycle());
|
expect(run(4).firstCycle()).toStrictEqual(sequence(0, 1, 2, 3).firstCycle());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('binaryN', () => {
|
|
||||||
it('Can make a binary pattern from a decimal', () => {
|
|
||||||
expect(binaryN(55532).firstCycle()).toStrictEqual(
|
|
||||||
sequence(1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0).firstCycle(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('Can make a binary pattern from patterned inputs', () => {
|
|
||||||
expect(binaryN(pure(0x1337), pure(14)).firstCycle()).toStrictEqual(
|
|
||||||
sequence(0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1).firstCycle(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('ribbon', () => {
|
describe('ribbon', () => {
|
||||||
it('Can ribbon', () => {
|
it('Can ribbon', () => {
|
||||||
expect(cat(0, 1, 2, 3, 4, 5, 6, 7).ribbon(2, 4).fast(4).firstCycle()).toStrictEqual(
|
expect(cat(0, 1, 2, 3, 4, 5, 6, 7).ribbon(2, 4).fast(4).firstCycle()).toStrictEqual(
|
||||||
@@ -1126,145 +1124,89 @@ describe('Pattern', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('_steps', () => {
|
describe('tactus', () => {
|
||||||
it('Is correctly preserved/calculated through transformations', () => {
|
it('Is correctly preserved/calculated through transformations', () => {
|
||||||
expect(sequence(0, 1, 2, 3).linger(4)._steps).toStrictEqual(Fraction(4));
|
expect(sequence(0, 1, 2, 3).linger(4).tactus).toStrictEqual(Fraction(4));
|
||||||
expect(sequence(0, 1, 2, 3).iter(4)._steps).toStrictEqual(Fraction(4));
|
expect(sequence(0, 1, 2, 3).iter(4).tactus).toStrictEqual(Fraction(4));
|
||||||
expect(sequence(0, 1, 2, 3).fast(4)._steps).toStrictEqual(Fraction(4));
|
expect(sequence(0, 1, 2, 3).fast(4).tactus).toStrictEqual(Fraction(4));
|
||||||
expect(sequence(0, 1, 2, 3).hurry(4)._steps).toStrictEqual(Fraction(4));
|
expect(sequence(0, 1, 2, 3).hurry(4).tactus).toStrictEqual(Fraction(4));
|
||||||
expect(sequence(0, 1, 2, 3).rev()._steps).toStrictEqual(Fraction(4));
|
expect(sequence(0, 1, 2, 3).rev().tactus).toStrictEqual(Fraction(4));
|
||||||
expect(sequence(1).segment(10)._steps).toStrictEqual(Fraction(10));
|
expect(sequence(1).segment(10).tactus).toStrictEqual(Fraction(10));
|
||||||
expect(sequence(1, 0, 1).invert()._steps).toStrictEqual(Fraction(3));
|
expect(sequence(1, 0, 1).invert().tactus).toStrictEqual(Fraction(3));
|
||||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).chop(4)._steps).toStrictEqual(Fraction(8));
|
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).chop(4).tactus).toStrictEqual(Fraction(8));
|
||||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).striate(4)._steps).toStrictEqual(Fraction(8));
|
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).striate(4).tactus).toStrictEqual(Fraction(8));
|
||||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).slice(4, sequence(0, 1, 2, 3))._steps).toStrictEqual(
|
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).slice(4, sequence(0, 1, 2, 3)).tactus).toStrictEqual(
|
||||||
Fraction(4),
|
Fraction(4),
|
||||||
);
|
);
|
||||||
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).splice(4, sequence(0, 1, 2, 3))._steps).toStrictEqual(
|
expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).splice(4, sequence(0, 1, 2, 3)).tactus).toStrictEqual(
|
||||||
Fraction(4),
|
Fraction(4),
|
||||||
);
|
);
|
||||||
expect(sequence({ n: 0 }, { n: 1 }, { n: 2 }).chop(4)._steps).toStrictEqual(Fraction(12));
|
expect(sequence({ n: 0 }, { n: 1 }, { n: 2 }).chop(4).tactus).toStrictEqual(Fraction(12));
|
||||||
expect(
|
expect(
|
||||||
pure((x) => x + 1)
|
pure((x) => x + 1)
|
||||||
.setSteps(3)
|
.setTactus(3)
|
||||||
.appBoth(pure(1).setSteps(2))._steps,
|
.appBoth(pure(1).setTactus(2)).tactus,
|
||||||
).toStrictEqual(Fraction(6));
|
).toStrictEqual(Fraction(6));
|
||||||
expect(
|
expect(
|
||||||
pure((x) => x + 1)
|
pure((x) => x + 1)
|
||||||
.setSteps(undefined)
|
.setTactus(undefined)
|
||||||
.appBoth(pure(1).setSteps(2))._steps,
|
.appBoth(pure(1).setTactus(2)).tactus,
|
||||||
).toStrictEqual(Fraction(2));
|
).toStrictEqual(Fraction(2));
|
||||||
expect(
|
expect(
|
||||||
pure((x) => x + 1)
|
pure((x) => x + 1)
|
||||||
.setSteps(3)
|
.setTactus(3)
|
||||||
.appBoth(pure(1).setSteps(undefined))._steps,
|
.appBoth(pure(1).setTactus(undefined)).tactus,
|
||||||
).toStrictEqual(Fraction(3));
|
).toStrictEqual(Fraction(3));
|
||||||
expect(stack(fastcat(0, 1, 2), fastcat(3, 4))._steps).toStrictEqual(Fraction(6));
|
expect(stack(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(6));
|
||||||
expect(stack(fastcat(0, 1, 2), fastcat(3, 4).setSteps(undefined))._steps).toStrictEqual(Fraction(3));
|
expect(stack(fastcat(0, 1, 2), fastcat(3, 4).setTactus(undefined)).tactus).toStrictEqual(Fraction(3));
|
||||||
expect(stackLeft(fastcat(0, 1, 2, 3), fastcat(3, 4))._steps).toStrictEqual(Fraction(4));
|
expect(stackLeft(fastcat(0, 1, 2, 3), fastcat(3, 4)).tactus).toStrictEqual(Fraction(4));
|
||||||
expect(stackRight(fastcat(0, 1, 2), fastcat(3, 4))._steps).toStrictEqual(Fraction(3));
|
expect(stackRight(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(3));
|
||||||
// maybe this should double when they are either all even or all odd
|
// maybe this should double when they are either all even or all odd
|
||||||
expect(stackCentre(fastcat(0, 1, 2), fastcat(3, 4))._steps).toStrictEqual(Fraction(3));
|
expect(stackCentre(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(3));
|
||||||
expect(fastcat(0, 1).ply(3)._steps).toStrictEqual(Fraction(6));
|
expect(fastcat(0, 1).ply(3).tactus).toStrictEqual(Fraction(6));
|
||||||
expect(fastcat(0, 1).setSteps(undefined).ply(3)._steps).toStrictEqual(undefined);
|
expect(fastcat(0, 1).setTactus(undefined).ply(3).tactus).toStrictEqual(undefined);
|
||||||
expect(fastcat(0, 1).fast(3)._steps).toStrictEqual(Fraction(2));
|
expect(fastcat(0, 1).fast(3).tactus).toStrictEqual(Fraction(2));
|
||||||
expect(fastcat(0, 1).setSteps(undefined).fast(3)._steps).toStrictEqual(undefined);
|
expect(fastcat(0, 1).setTactus(undefined).fast(3).tactus).toStrictEqual(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('stepcat', () => {
|
describe('s_cat', () => {
|
||||||
it('can cat', () => {
|
it('can cat', () => {
|
||||||
expect(sameFirst(stepcat(fastcat(0, 1, 2, 3), fastcat(4, 5)), fastcat(0, 1, 2, 3, 4, 5)));
|
expect(sameFirst(s_cat(fastcat(0, 1, 2, 3), fastcat(4, 5)), fastcat(0, 1, 2, 3, 4, 5)));
|
||||||
expect(sameFirst(stepcat(pure(1), pure(2), pure(3)), fastcat(1, 2, 3)));
|
expect(sameFirst(s_cat(pure(1), pure(2), pure(3)), fastcat(1, 2, 3)));
|
||||||
});
|
});
|
||||||
it('calculates undefined steps as the average', () => {
|
it('calculates undefined tactuses as the average', () => {
|
||||||
expect(sameFirst(stepcat(pure(1), pure(2), pure(3).setSteps(undefined)), fastcat(1, 2, 3)));
|
expect(sameFirst(s_cat(pure(1), pure(2), pure(3).setTactus(undefined)), fastcat(1, 2, 3)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('shrink', () => {
|
describe('s_taper', () => {
|
||||||
it('can shrink', () => {
|
it('can taper', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).shrink(1), sequence(0, 1, 2, 3, 4, 1, 2, 3, 4, 2, 3, 4, 3, 4, 4)));
|
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_taper(1, 5), sequence(0, 1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 0, 1, 0)));
|
||||||
});
|
});
|
||||||
it('can shrink backwards', () => {
|
it('can taper backwards', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).shrink(-1), sequence(0, 1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 0, 1, 0)));
|
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_taper(-1, 5), sequence(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('grow', () => {
|
describe('s_add and s_sub', () => {
|
||||||
it('can grow', () => {
|
it('can add from the left', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).grow(1), sequence(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)));
|
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_add(2), sequence(0, 1)));
|
||||||
});
|
});
|
||||||
it('can grow backwards', () => {
|
it('can sub to the left', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).grow(-1), sequence(4, 3, 4, 2, 3, 4, 1, 2, 3, 4, 0, 1, 2, 3, 4)));
|
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_sub(2), sequence(0, 1, 2)));
|
||||||
});
|
});
|
||||||
});
|
it('can add from the right', () => {
|
||||||
describe('take and drop', () => {
|
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_add(-2), sequence(3, 4)));
|
||||||
it('can take from the left', () => {
|
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).take(2), sequence(0, 1)));
|
|
||||||
});
|
});
|
||||||
it('can drop from the left', () => {
|
it('can sub to the right', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).drop(2), sequence(2, 3, 4)));
|
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_sub(-2), sequence(2, 3, 4)));
|
||||||
});
|
});
|
||||||
it('can take from the right', () => {
|
it('can subtract nothing', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).take(-2), sequence(3, 4)));
|
expect(sameFirst(pure('a').s_sub(0), pure('a')));
|
||||||
});
|
});
|
||||||
it('can drop from the right', () => {
|
it('can subtract nothing, repeatedly', () => {
|
||||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).drop(-2), sequence(0, 1, 2)));
|
expect(sameFirst(pure('a').s_sub(0, 0), fastcat('a', 'a')));
|
||||||
});
|
|
||||||
it('can drop nothing', () => {
|
|
||||||
expect(sameFirst(pure('a').drop(0), pure('a')));
|
|
||||||
});
|
|
||||||
it('can drop nothing, repeatedly', () => {
|
|
||||||
expect(sameFirst(pure('a').drop(0, 0), fastcat('a', 'a')));
|
|
||||||
for (var i = 0; i < 100; ++i) {
|
for (var i = 0; i < 100; ++i) {
|
||||||
expect(sameFirst(pure('a').drop(...Array(i).fill(0)), fastcat(...Array(i).fill('a'))));
|
expect(sameFirst(pure('a').s_sub(...Array(i).fill(0)), fastcat(...Array(i).fill('a'))));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('expand', () => {
|
|
||||||
it('can expand four things in half', () => {
|
|
||||||
expect(
|
|
||||||
sameFirst(sequence(0, 1, 2, 3).expand(1, 0.5), stepcat(sequence(0, 1, 2, 3), sequence(0, 1, 2, 3).expand(0.5))),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('can expand five things in half', () => {
|
|
||||||
expect(
|
|
||||||
sameFirst(
|
|
||||||
sequence(0, 1, 2, 3, 4).expand(1, 0.5),
|
|
||||||
stepcat(sequence(0, 1, 2, 3, 4), sequence(0, 1, 2, 3, 4).expand(0.5)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('stepJoin', () => {
|
|
||||||
it('can join a pattern with steps of 2', () => {
|
|
||||||
expect(
|
|
||||||
sameFirst(
|
|
||||||
sequence(pure(pure('a')), pure(pure('b').setSteps(2))).stepJoin(),
|
|
||||||
stepcat(pure('a'), pure('b').setSteps(2)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('can join a pattern with steps of 0.5', () => {
|
|
||||||
expect(
|
|
||||||
sameFirst(
|
|
||||||
sequence(pure(pure('a')), pure(pure('b').setSteps(0.5))).stepJoin(),
|
|
||||||
stepcat(pure('a'), pure('b').setSteps(0.5)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('loopAt', () => {
|
|
||||||
it('maintains steps', () => {
|
|
||||||
expect(s('bev').chop(8).loopAt(2)._steps).toStrictEqual(Fraction(4));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('bite', () => {
|
|
||||||
it('works with uneven patterns', () => {
|
|
||||||
sameFirst(
|
|
||||||
fastcat(slowcat('a', 'b', 'c', 'd', 'e'), slowcat(1, 2, 3, 4, 5))
|
|
||||||
.bite(2, stepcat(pure(0), pure(1).expand(2)))
|
|
||||||
.fast(5),
|
|
||||||
stepcat(slowcat('a', 'b', 'c', 'd', 'e'), slowcat(1, 2, 3, 4, 5).expand(2)).fast(5),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -91,9 +91,6 @@ export const midi2note = (n) => {
|
|||||||
// modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does)
|
// modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does)
|
||||||
export const _mod = (n, m) => ((n % m) + m) % m;
|
export const _mod = (n, m) => ((n % m) + m) % m;
|
||||||
|
|
||||||
// average numbers in an array
|
|
||||||
export const averageArray = (arr) => arr.reduce((a, b) => a + b) / arr.length;
|
|
||||||
|
|
||||||
export function nanFallback(value, fallback = 0) {
|
export function nanFallback(value, fallback = 0) {
|
||||||
if (isNaN(Number(value))) {
|
if (isNaN(Number(value))) {
|
||||||
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
||||||
@@ -363,108 +360,6 @@ export function objectMap(obj, fn) {
|
|||||||
}
|
}
|
||||||
return Object.fromEntries(Object.entries(obj).map(([k, v], i) => [k, fn(v, k, i)]));
|
return Object.fromEntries(Object.entries(obj).map(([k, v], i) => [k, fn(v, k, i)]));
|
||||||
}
|
}
|
||||||
export function cycleToSeconds(cycle, cps) {
|
|
||||||
return cycle / cps;
|
|
||||||
}
|
|
||||||
|
|
||||||
// utility for averaging two clocks together to account for drift
|
|
||||||
export class ClockCollator {
|
|
||||||
constructor({
|
|
||||||
getTargetClockTime = getUnixTimeSeconds,
|
|
||||||
weight = 16,
|
|
||||||
offsetDelta = 0.005,
|
|
||||||
checkAfterTime = 2,
|
|
||||||
resetAfterTime = 8,
|
|
||||||
}) {
|
|
||||||
this.offsetTime;
|
|
||||||
this.timeAtPrevOffsetSample;
|
|
||||||
this.prevOffsetTimes = [];
|
|
||||||
this.getTargetClockTime = getTargetClockTime;
|
|
||||||
this.weight = weight;
|
|
||||||
this.offsetDelta = offsetDelta;
|
|
||||||
this.checkAfterTime = checkAfterTime;
|
|
||||||
this.resetAfterTime = resetAfterTime;
|
|
||||||
this.reset = () => {
|
|
||||||
this.prevOffsetTimes = [];
|
|
||||||
this.offsetTime = null;
|
|
||||||
this.timeAtPrevOffsetSample = null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
calculateOffset(currentTime) {
|
|
||||||
const targetClockTime = this.getTargetClockTime();
|
|
||||||
const diffBetweenTimeSamples = targetClockTime - this.timeAtPrevOffsetSample;
|
|
||||||
const newOffsetTime = targetClockTime - currentTime;
|
|
||||||
// recalcuate the diff from scratch if the clock has been paused for some time.
|
|
||||||
if (diffBetweenTimeSamples > this.resetAfterTime) {
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.offsetTime == null) {
|
|
||||||
this.offsetTime = newOffsetTime;
|
|
||||||
}
|
|
||||||
this.prevOffsetTimes.push(newOffsetTime);
|
|
||||||
if (this.prevOffsetTimes.length > this.weight) {
|
|
||||||
this.prevOffsetTimes.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
// after X time has passed, the average of the previous weight offset times is calculated and used as a stable reference
|
|
||||||
// for calculating the timestamp
|
|
||||||
if (this.timeAtPrevOffsetSample == null || diffBetweenTimeSamples > this.checkAfterTime) {
|
|
||||||
this.timeAtPrevOffsetSample = targetClockTime;
|
|
||||||
const rollingOffsetTime = averageArray(this.prevOffsetTimes);
|
|
||||||
//when the clock offsets surpass the delta, set the new reference time
|
|
||||||
if (Math.abs(rollingOffsetTime - this.offsetTime) > this.offsetDelta) {
|
|
||||||
this.offsetTime = rollingOffsetTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.offsetTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateTimestamp(currentTime, targetTime) {
|
|
||||||
return this.calculateOffset(currentTime) + targetTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPerformanceTimeSeconds() {
|
|
||||||
return performance.now() * 0.001;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUnixTimeSeconds() {
|
|
||||||
return Date.now() * 0.001;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const keyAlias = new Map([
|
|
||||||
['control', 'Control'],
|
|
||||||
['ctrl', 'Control'],
|
|
||||||
['alt', 'Alt'],
|
|
||||||
['shift', 'Shift'],
|
|
||||||
['down', 'ArrowDown'],
|
|
||||||
['up', 'ArrowUp'],
|
|
||||||
['left', 'ArrowLeft'],
|
|
||||||
['right', 'ArrowRight'],
|
|
||||||
]);
|
|
||||||
let keyState;
|
|
||||||
|
|
||||||
export function getCurrentKeyboardState() {
|
|
||||||
if (keyState == null) {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
keyState = {};
|
|
||||||
// Listen for the keydown event to mark the key as pressed
|
|
||||||
window.addEventListener('keydown', (event) => {
|
|
||||||
keyState[event.key] = true; // Mark the key as pressed
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for the keyup event to mark the key as released
|
|
||||||
window.addEventListener('keyup', (event) => {
|
|
||||||
keyState[event.key] = false; // Mark the key as released
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...keyState }; // Return a shallow copy of the key state object
|
|
||||||
}
|
|
||||||
|
|
||||||
// Floating point versions, see Fraction for rational versions
|
// Floating point versions, see Fraction for rational versions
|
||||||
// // greatest common divisor
|
// // greatest common divisor
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { resolve } from 'path';
|
|||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: './',
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/csound",
|
"name": "@strudel/csound",
|
||||||
"version": "1.2.0",
|
"version": "1.0.1",
|
||||||
"description": "csound bindings for strudel",
|
"description": "csound bindings for strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -37,6 +37,6 @@
|
|||||||
"@strudel/webaudio": "workspace:*"
|
"@strudel/webaudio": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,43 @@
|
|||||||
import { Pattern, ClockCollator } from '@strudel/core';
|
import { parseNumeral, Pattern, getEventOffsetMs } from '@strudel/core';
|
||||||
import { parseControlsFromHap } from 'node_modules/@strudel/osc/osc.mjs';
|
|
||||||
import { Invoke } from './utils.mjs';
|
import { Invoke } from './utils.mjs';
|
||||||
|
|
||||||
const collator = new ClockCollator({});
|
|
||||||
|
|
||||||
export async function oscTriggerTauri(t_deprecate, hap, currentTime, cps = 1, targetTime) {
|
|
||||||
const controls = parseControlsFromHap(hap, cps);
|
|
||||||
const params = [];
|
|
||||||
const timestamp = collator.calculateTimestamp(currentTime, targetTime);
|
|
||||||
|
|
||||||
Object.keys(controls).forEach((key) => {
|
|
||||||
const val = controls[key];
|
|
||||||
const value = typeof val === 'number' ? val.toString() : val;
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
params.push({
|
|
||||||
name: key,
|
|
||||||
value,
|
|
||||||
valueisnumber: typeof val === 'number',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (params.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const message = { target: '/dirt/play', timestamp, params };
|
|
||||||
setTimeout(() => {
|
|
||||||
Invoke('sendosc', { messagesfromjs: [message] });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Pattern.prototype.osc = function () {
|
Pattern.prototype.osc = function () {
|
||||||
return this.onTrigger(oscTriggerTauri);
|
return this.onTrigger(async (time, hap, currentTime, cps = 1, targetTime) => {
|
||||||
|
hap.ensureObjectValue();
|
||||||
|
const cycle = hap.wholeOrPart().begin.valueOf();
|
||||||
|
const delta = hap.duration.valueOf();
|
||||||
|
const controls = Object.assign({}, { cps, cycle, delta }, hap.value);
|
||||||
|
// make sure n and note are numbers
|
||||||
|
controls.n && (controls.n = parseNumeral(controls.n));
|
||||||
|
controls.note && (controls.note = parseNumeral(controls.note));
|
||||||
|
|
||||||
|
const params = [];
|
||||||
|
|
||||||
|
const timestamp = Math.round(Date.now() + getEventOffsetMs(targetTime, currentTime));
|
||||||
|
|
||||||
|
Object.keys(controls).forEach((key) => {
|
||||||
|
const val = controls[key];
|
||||||
|
const value = typeof val === 'number' ? val.toString() : val;
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.push({
|
||||||
|
name: key,
|
||||||
|
value,
|
||||||
|
valueisnumber: typeof val === 'number',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const messagesfromjs = [];
|
||||||
|
if (params.length) {
|
||||||
|
messagesfromjs.push({ target: '/dirt/play', timestamp, params });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messagesfromjs.length) {
|
||||||
|
setTimeout(() => {
|
||||||
|
Invoke('sendosc', { messagesfromjs });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/desktopbridge",
|
"name": "@strudel/desktopbridge",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "tools/shims for communicating between the JS and Tauri (Rust) sides of the Studel desktop app",
|
"description": "tools/shims for communicating between the JS and Tauri (Rust) sides of the Studel desktop app",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"tidalcycles",
|
"tidalcycles",
|
||||||
"strudel",
|
"strudel",
|
||||||
"pattern",
|
"pattern",
|
||||||
"livecoding",
|
"livecoding",
|
||||||
"algorave"
|
"algorave"
|
||||||
],
|
],
|
||||||
"author": "Jade Rowland <jaderowlanddev@gmail.com>",
|
"author": "Jade Rowland <jaderowlanddev@gmail.com>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel/core": "workspace:*",
|
"@strudel/core": "workspace:*",
|
||||||
"@tauri-apps/api": "^2.2.0"
|
"@tauri-apps/api": "^1.5.3"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme"
|
"homepage": "https://github.com/tidalcycles/strudel#readme"
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/tauri';
|
||||||
|
|
||||||
export const Invoke = invoke;
|
export const Invoke = invoke;
|
||||||
export const isTauri = () => window.__TAURI_IPC__ != null;
|
export const isTauri = () => window.__TAURI_IPC__ != null;
|
||||||
|
|||||||
+15
-34
@@ -26,7 +26,7 @@ export const getDrawContext = (id = 'test-canvas', options) => {
|
|||||||
}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return canvas.getContext(contextType, { willReadFrequently: true });
|
return canvas.getContext(contextType);
|
||||||
};
|
};
|
||||||
|
|
||||||
let animationFrames = {};
|
let animationFrames = {};
|
||||||
@@ -36,8 +36,8 @@ function stopAnimationFrame(id) {
|
|||||||
delete animationFrames[id];
|
delete animationFrames[id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function stopAllAnimations(replID) {
|
function stopAllAnimations() {
|
||||||
Object.keys(animationFrames).forEach((id) => (!replID || id.startsWith(replID)) && stopAnimationFrame(id));
|
Object.keys(animationFrames).forEach((id) => stopAnimationFrame(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
let memory = {};
|
let memory = {};
|
||||||
@@ -72,25 +72,18 @@ Pattern.prototype.draw = function (fn, options) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanupDraw = (clearScreen = true, id) => {
|
export const cleanupDraw = (clearScreen = true) => {
|
||||||
const ctx = getDrawContext();
|
const ctx = getDrawContext();
|
||||||
clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width);
|
||||||
stopAllAnimations(id);
|
stopAllAnimations();
|
||||||
|
if (window.strudelScheduler) {
|
||||||
|
clearInterval(window.strudelScheduler);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Pattern.prototype.onPaint = function (painter) {
|
Pattern.prototype.onPaint = function () {
|
||||||
return this.withState((state) => {
|
console.warn('[draw] onPaint was not overloaded. Some drawings might not work');
|
||||||
if (!state.controls.painters) {
|
return this;
|
||||||
state.controls.painters = [];
|
|
||||||
}
|
|
||||||
state.controls.painters.push(painter);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype.getPainters = function () {
|
|
||||||
let painters = [];
|
|
||||||
this.queryArc(0, 0, { painters });
|
|
||||||
return painters;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// const round = (x) => Math.round(x * 1000) / 1000;
|
// const round = (x) => Math.round(x * 1000) / 1000;
|
||||||
@@ -129,7 +122,6 @@ export class Drawer {
|
|||||||
this.visibleHaps = [];
|
this.visibleHaps = [];
|
||||||
this.lastFrame = null;
|
this.lastFrame = null;
|
||||||
this.drawTime = drawTime;
|
this.drawTime = drawTime;
|
||||||
this.painters = [];
|
|
||||||
this.framer = new Framer(
|
this.framer = new Framer(
|
||||||
() => {
|
() => {
|
||||||
if (!this.scheduler) {
|
if (!this.scheduler) {
|
||||||
@@ -154,7 +146,7 @@ export class Drawer {
|
|||||||
// add new haps with onset (think right edge bars scrolling in)
|
// add new haps with onset (think right edge bars scrolling in)
|
||||||
.concat(haps.filter((h) => h.hasOnset()));
|
.concat(haps.filter((h) => h.hasOnset()));
|
||||||
const time = phase - lookahead;
|
const time = phase - lookahead;
|
||||||
onDraw(this.visibleHaps, time, this, this.painters);
|
onDraw(this.visibleHaps, time, this);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.warn('draw error', err);
|
console.warn('draw error', err);
|
||||||
@@ -172,13 +164,11 @@ export class Drawer {
|
|||||||
t = t ?? scheduler.now();
|
t = t ?? scheduler.now();
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
let [_, lookahead] = this.drawTime;
|
let [_, lookahead] = this.drawTime;
|
||||||
// +0.1 = workaround for weird holes in query..
|
|
||||||
const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1];
|
const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1];
|
||||||
// remove all future haps
|
// remove all future haps
|
||||||
this.visibleHaps = this.visibleHaps.filter((h) => h.whole.begin < t);
|
this.visibleHaps = this.visibleHaps.filter((h) => h.whole.begin < t);
|
||||||
this.painters = []; // will get populated by .onPaint calls attached to the pattern
|
|
||||||
// query future haps
|
// query future haps
|
||||||
const futureHaps = scheduler.pattern.queryArc(begin, end, { painters: this.painters });
|
const futureHaps = scheduler.pattern.queryArc(begin, end); // +0.1 = workaround for weird holes in query..
|
||||||
// append future haps
|
// append future haps
|
||||||
this.visibleHaps = this.visibleHaps.concat(futureHaps);
|
this.visibleHaps = this.visibleHaps.concat(futureHaps);
|
||||||
}
|
}
|
||||||
@@ -201,16 +191,7 @@ export function getComputedPropertyValue(name) {
|
|||||||
return getComputedStyle(document.documentElement).getPropertyValue(name);
|
return getComputedStyle(document.documentElement).getPropertyValue(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let theme = {
|
let theme = {};
|
||||||
background: '#222',
|
|
||||||
foreground: '#75baff',
|
|
||||||
caret: '#ffcc00',
|
|
||||||
selection: 'rgba(128, 203, 196, 0.5)',
|
|
||||||
selectionMatch: '#036dd626',
|
|
||||||
lineHighlight: '#00000050',
|
|
||||||
gutterBackground: 'transparent',
|
|
||||||
gutterForeground: '#8a919966',
|
|
||||||
};
|
|
||||||
export function getTheme() {
|
export function getTheme() {
|
||||||
return theme;
|
return theme;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/draw",
|
"name": "@strudel/draw",
|
||||||
"version": "1.2.0",
|
"version": "1.0.1",
|
||||||
"description": "Helpers for drawing with Strudel",
|
"description": "Helpers for drawing with Strudel",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -32,6 +32,6 @@
|
|||||||
"@strudel/core": "workspace:*"
|
"@strudel/core": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^6.0.11"
|
"vite": "^5.0.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-52
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Pattern, noteToMidi, freqToMidi, isPattern } from '@strudel/core';
|
import { Pattern, noteToMidi, freqToMidi } from '@strudel/core';
|
||||||
import { getTheme, getDrawContext } from './draw.mjs';
|
import { getTheme, getDrawContext } from './draw.mjs';
|
||||||
|
|
||||||
const scale = (normalized, min, max) => normalized * (max - min) + min;
|
const scale = (normalized, min, max) => normalized * (max - min) + min;
|
||||||
@@ -36,12 +36,37 @@ const getValue = (e) => {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Pattern.prototype.pianoroll = function (options = {}) {
|
||||||
|
let { cycles = 4, playhead = 0.5, overscan = 0, hideNegative = false, ctx = getDrawContext(), id = 1 } = options;
|
||||||
|
|
||||||
|
let from = -cycles * playhead;
|
||||||
|
let to = cycles * (1 - playhead);
|
||||||
|
const inFrame = (hap, t) => (!hideNegative || hap.whole.begin >= 0) && hap.isWithinTime(t + from, t + to);
|
||||||
|
this.draw(
|
||||||
|
(haps, time) => {
|
||||||
|
pianoroll({
|
||||||
|
...options,
|
||||||
|
time,
|
||||||
|
ctx,
|
||||||
|
haps: haps.filter((hap) => inFrame(hap, time)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lookbehind: from - overscan,
|
||||||
|
lookahead: to + overscan,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this function allows drawing a pianoroll without ties to Pattern.prototype
|
||||||
|
// it will probably replace the above in the future
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visualises a pattern as a scrolling 'pianoroll', displayed in the background of the editor. To show a pianoroll for all running patterns, use `all(pianoroll)`. To have a pianoroll appear below
|
* Displays a midi-style piano roll
|
||||||
* a pattern instead, prefix with `_`, e.g.: `sound("bd sd")._pianoroll()`.
|
|
||||||
*
|
*
|
||||||
* @name pianoroll
|
* @name pianoroll
|
||||||
* @synonyms punchcard
|
|
||||||
* @param {Object} options Object containing all the optional following parameters as key value pairs:
|
* @param {Object} options Object containing all the optional following parameters as key value pairs:
|
||||||
* @param {integer} cycles number of cycles to be displayed at the same time - defaults to 4
|
* @param {integer} cycles number of cycles to be displayed at the same time - defaults to 4
|
||||||
* @param {number} playhead location of the active notes on the time axis - 0 to 1, defaults to 0.5
|
* @param {number} playhead location of the active notes on the time axis - 0 to 1, defaults to 0.5
|
||||||
@@ -67,51 +92,11 @@ const getValue = (e) => {
|
|||||||
* @param {integer} minMidi minimum note value to display on the value axis - defaults to 10
|
* @param {integer} minMidi minimum note value to display on the value axis - defaults to 10
|
||||||
* @param {integer} maxMidi maximum note value to display on the value axis - defaults to 90
|
* @param {integer} maxMidi maximum note value to display on the value axis - defaults to 90
|
||||||
* @param {boolean} autorange automatically calculate the minMidi and maxMidi parameters - 0 by default
|
* @param {boolean} autorange automatically calculate the minMidi and maxMidi parameters - 0 by default
|
||||||
* @see _pianoroll
|
*
|
||||||
* @example
|
* @example
|
||||||
* note("c2 a2 eb2")
|
* note("C2 A2 G2").euclid(5,8).s('piano').clip(1).color('salmon').pianoroll({vertical:1, labels:1})
|
||||||
* .euclid(5,8)
|
|
||||||
* .s('sawtooth')
|
|
||||||
* .lpenv(4).lpf(300)
|
|
||||||
* .pianoroll({ labels: 1 })
|
|
||||||
*/
|
*/
|
||||||
|
export function pianoroll({
|
||||||
Pattern.prototype.pianoroll = function (options = {}) {
|
|
||||||
let { cycles = 4, playhead = 0.5, overscan = 0, hideNegative = false, ctx = getDrawContext(), id = 1 } = options;
|
|
||||||
|
|
||||||
let from = -cycles * playhead;
|
|
||||||
let to = cycles * (1 - playhead);
|
|
||||||
const inFrame = (hap, t) => (!hideNegative || hap.whole.begin >= 0) && hap.isWithinTime(t + from, t + to);
|
|
||||||
this.draw(
|
|
||||||
(haps, time) => {
|
|
||||||
__pianoroll({
|
|
||||||
...options,
|
|
||||||
time,
|
|
||||||
ctx,
|
|
||||||
haps: haps.filter((hap) => inFrame(hap, time)),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lookbehind: from - overscan,
|
|
||||||
lookahead: to + overscan,
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function pianoroll(arg) {
|
|
||||||
if (isPattern(arg)) {
|
|
||||||
// Single argument as a pattern
|
|
||||||
// (to support `all(pianoroll)`)
|
|
||||||
return arg.pianoroll();
|
|
||||||
}
|
|
||||||
// Single argument with option - return function to get the pattern
|
|
||||||
// (to support `all(pianoroll(options))`)
|
|
||||||
return (pat) => pat.pianoroll(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function __pianoroll({
|
|
||||||
time,
|
time,
|
||||||
haps,
|
haps,
|
||||||
cycles = 4,
|
cycles = 4,
|
||||||
@@ -123,12 +108,12 @@ export function __pianoroll({
|
|||||||
active = getTheme().foreground,
|
active = getTheme().foreground,
|
||||||
background = 'transparent',
|
background = 'transparent',
|
||||||
smear = 0,
|
smear = 0,
|
||||||
playheadColor = getTheme().foreground,
|
playheadColor = 'white',
|
||||||
minMidi = 10,
|
minMidi = 10,
|
||||||
maxMidi = 90,
|
maxMidi = 90,
|
||||||
autorange = 0,
|
autorange = 0,
|
||||||
timeframe: timeframeProp,
|
timeframe: timeframeProp,
|
||||||
fold = 1,
|
fold = 0,
|
||||||
vertical = 0,
|
vertical = 0,
|
||||||
labels = false,
|
labels = false,
|
||||||
fill = 1,
|
fill = 1,
|
||||||
@@ -281,14 +266,14 @@ export function getDrawOptions(drawTime, options = {}) {
|
|||||||
let [lookbehind, lookahead] = drawTime;
|
let [lookbehind, lookahead] = drawTime;
|
||||||
lookbehind = Math.abs(lookbehind);
|
lookbehind = Math.abs(lookbehind);
|
||||||
const cycles = lookahead + lookbehind;
|
const cycles = lookahead + lookbehind;
|
||||||
const playhead = cycles !== 0 ? lookbehind / cycles : 0;
|
const playhead = lookbehind / cycles;
|
||||||
return { fold: 1, ...options, cycles, playhead };
|
return { fold: 1, ...options, cycles, playhead };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPunchcardPainter =
|
export const getPunchcardPainter =
|
||||||
(options = {}) =>
|
(options = {}) =>
|
||||||
(ctx, time, haps, drawTime) =>
|
(ctx, time, haps, drawTime) =>
|
||||||
__pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, options) });
|
pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, options) });
|
||||||
|
|
||||||
Pattern.prototype.punchcard = function (options) {
|
Pattern.prototype.punchcard = function (options) {
|
||||||
return this.onPaint(getPunchcardPainter(options));
|
return this.onPaint(getPunchcardPainter(options));
|
||||||
@@ -312,5 +297,5 @@ Pattern.prototype.wordfall = function (options) {
|
|||||||
|
|
||||||
export function drawPianoroll(options) {
|
export function drawPianoroll(options) {
|
||||||
const { drawTime, ...rest } = options;
|
const { drawTime, ...rest } = options;
|
||||||
__pianoroll({ ...getDrawOptions(drawTime), ...rest });
|
pianoroll({ ...getDrawOptions(drawTime), ...rest });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,23 +113,6 @@ export function pitchwheel({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a pitch circle to visualize frequencies within one octave
|
|
||||||
* @name pitchwheel
|
|
||||||
* @param {number} hapcircles
|
|
||||||
* @param {number} circle
|
|
||||||
* @param {number} edo
|
|
||||||
* @param {string} root
|
|
||||||
* @param {number} thickness
|
|
||||||
* @param {number} hapRadius
|
|
||||||
* @param {string} mode
|
|
||||||
* @param {number} margin
|
|
||||||
* @example
|
|
||||||
* n("0 .. 12").scale("C:chromatic")
|
|
||||||
* .s("sawtooth")
|
|
||||||
* .lpf(500)
|
|
||||||
* ._pitchwheel()
|
|
||||||
*/
|
|
||||||
Pattern.prototype.pitchwheel = function (options = {}) {
|
Pattern.prototype.pitchwheel = function (options = {}) {
|
||||||
let { ctx = getDrawContext(), id = 1 } = options;
|
let { ctx = getDrawContext(), id = 1 } = options;
|
||||||
return this.tag(id).onPaint((_, time, haps) =>
|
return this.tag(id).onPaint((_, time, haps) =>
|
||||||
|
|||||||
@@ -125,33 +125,6 @@ function drawSpiral(options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a spiral visual.
|
|
||||||
*
|
|
||||||
* @name spiral
|
|
||||||
* @param {Object} options Object containing all the optional following parameters as key value pairs:
|
|
||||||
* @param {number} stretch controls the rotations per cycle ratio, where 1 = 1 cycle / 360 degrees
|
|
||||||
* @param {number} size the diameter of the spiral
|
|
||||||
* @param {number} thickness line thickness
|
|
||||||
* @param {string} cap style of line ends: butt (default), round, square
|
|
||||||
* @param {string} inset number of rotations before spiral starts (default 3)
|
|
||||||
* @param {string} playheadColor color of playhead, defaults to white
|
|
||||||
* @param {number} playheadLength length of playhead in rotations, defaults to 0.02
|
|
||||||
* @param {number} playheadThickness thickness of playheadrotations, defaults to thickness
|
|
||||||
* @param {number} padding space around spiral
|
|
||||||
* @param {number} steady steadyness of spiral vs playhead. 1 = spiral doesn't move, playhead does.
|
|
||||||
* @param {number} activeColor color of active segment. defaults to foreground of theme
|
|
||||||
* @param {number} inactiveColor color of inactive segments. defaults to gutterForeground of theme
|
|
||||||
* @param {boolean} colorizeInactive wether or not to colorize inactive segments, defaults to 0
|
|
||||||
* @param {boolean} fade wether or not past and future should fade out. defaults to 1
|
|
||||||
* @param {boolean} logSpiral wether or not the spiral should be logarithmic. defaults to 0
|
|
||||||
* @example
|
|
||||||
* note("c2 a2 eb2")
|
|
||||||
* .euclid(5,8)
|
|
||||||
* .s('sawtooth')
|
|
||||||
* .lpenv(4).lpf(300)
|
|
||||||
* ._spiral({ steady: .96 })
|
|
||||||
*/
|
|
||||||
Pattern.prototype.spiral = function (options = {}) {
|
Pattern.prototype.spiral = function (options = {}) {
|
||||||
return this.onPaint((ctx, time, haps, drawTime) => drawSpiral({ ctx, time, haps, drawTime, ...options }));
|
return this.onPaint((ctx, time, haps, drawTime) => drawSpiral({ ctx, time, haps, drawTime, ...options }));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@strudel/embed",
|
"name": "@strudel/embed",
|
||||||
"version": "1.1.0",
|
"version": "1.0.0",
|
||||||
"description": "Embeddable Web Component to load a Strudel REPL into an iframe",
|
"description": "Embeddable Web Component to load a Strudel REPL into an iframe",
|
||||||
"main": "embed.js",
|
"main": "embed.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
# @strudel/gamepad
|
|
||||||
|
|
||||||
This package adds gamepad input functionality to strudel Patterns.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm i @strudel/gamepad --save
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { gamepad } from '@strudel/gamepad';
|
|
||||||
|
|
||||||
// Initialize gamepad (optional index parameter, defaults to 0)
|
|
||||||
const pad = gamepad(0);
|
|
||||||
|
|
||||||
// Use gamepad inputs in patterns
|
|
||||||
const pattern = sequence([
|
|
||||||
// Button inputs
|
|
||||||
pad.a, // A button value (0-1)
|
|
||||||
pad.tglA, // A button toggle (0 or 1)
|
|
||||||
|
|
||||||
// Analog stick inputs
|
|
||||||
pad.x1, // Left stick X (0-1)
|
|
||||||
pad.x1_2, // Left stick X (-1 to 1)
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Controls
|
|
||||||
|
|
||||||
### Buttons
|
|
||||||
- Face Buttons
|
|
||||||
- `a`, `b`, `x`, `y` (or uppercase `A`, `B`, `X`, `Y`)
|
|
||||||
- Toggle versions: `tglA`, `tglB`, `tglX`, `tglY`
|
|
||||||
- Shoulder Buttons
|
|
||||||
- `lb`, `rb`, `lt`, `rt` (or uppercase `LB`, `RB`, `LT`, `RT`)
|
|
||||||
- Toggle versions: `tglLB`, `tglRB`, `tglLT`, `tglRT`
|
|
||||||
- D-Pad
|
|
||||||
- `up`, `down`, `left`, `right` (or `u`, `d`, `l`, `r` or uppercase)
|
|
||||||
- Toggle versions: `tglUp`, `tglDown`, `tglLeft`, `tglRight`(or `tglU`, `tglD`, `tglL`, `tglR`)
|
|
||||||
|
|
||||||
### Analog Sticks
|
|
||||||
- Left Stick
|
|
||||||
- `x1`, `y1` (0 to 1 range)
|
|
||||||
- `x1_2`, `y1_2` (-1 to 1 range)
|
|
||||||
- Right Stick
|
|
||||||
- `x2`, `y2` (0 to 1 range)
|
|
||||||
- `x2_2`, `y2_2` (-1 to 1 range)
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Use button values to control amplitude
|
|
||||||
$: sequence([
|
|
||||||
s("bd").gain(pad.X), // X button controls gain
|
|
||||||
s("[hh oh]").gain(pad.tglY), // Y button toggles gain
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Use analog stick for continuous control
|
|
||||||
$: note("c4*4".add(pad.y1_2.range(-24,24))) // Left stick Y controls pitch shift
|
|
||||||
.pan(pad.x1_2); // Left stick X controls panning
|
|
||||||
|
|
||||||
// Use toggle buttons to switch patterns on/off
|
|
||||||
|
|
||||||
// Define button sequences
|
|
||||||
const HADOKEN = [
|
|
||||||
'd', // Down
|
|
||||||
'r', // Right
|
|
||||||
'a', // A
|
|
||||||
];
|
|
||||||
|
|
||||||
const KONAMI = 'uuddlrlrba' //Konami Code ↑↑↓↓←→←→BA
|
|
||||||
|
|
||||||
// Add these lines to enable buttons(but why?)
|
|
||||||
$:pad.D.segment(16).gain(0)
|
|
||||||
$:pad.R.segment(16).gain(0)
|
|
||||||
$:pad.A.segment(16).gain(0)
|
|
||||||
|
|
||||||
// Check button sequence (returns 1 when detected, 0 when not within last 1 second)
|
|
||||||
$: sound("hadoken").gain(pad.checkSequence(HADOKEN))
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multiple Gamepads
|
|
||||||
|
|
||||||
You can connect multiple gamepads by specifying the gamepad index:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const pad1 = gamepad(0); // First gamepad
|
|
||||||
const pad2 = gamepad(1); // Second gamepad
|
|
||||||
```
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import { MiniRepl } from '../../../website/src/docs/MiniRepl';
|
|
||||||
|
|
||||||
# Gamepad
|
|
||||||
|
|
||||||
The Gamepad module allows you to integrate gamepad input functionality into your musical patterns. This can be particularly useful for live performances or interactive installations where you want to manipulate sounds using a game controller.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
Initialize a gamepad by calling the gamepad() function with an optional index parameter.
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`// Initialize gamepad (optional index parameter, defaults to 0)
|
|
||||||
const gp = gamepad(0)
|
|
||||||
note("c a f e").mask(gp.a)`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
## Available Controls
|
|
||||||
|
|
||||||
The gamepad module provides access to buttons and analog sticks as normalized signals (0-1) that can modulate your patterns.
|
|
||||||
|
|
||||||
### Buttons
|
|
||||||
|
|
||||||
| Type | Controls |
|
|
||||||
| ---------------- | ---------------------------------------------------------------------------------------------- |
|
|
||||||
| Face Buttons | `a`, `b`, `x`, `y` (or uppercase `A`, `B`, `X`, `Y`) |
|
|
||||||
| | Toggle versions: `tglA`, `tglB`, `tglX`, `tglY` |
|
|
||||||
| Shoulder Buttons | `lb`, `rb`, `lt`, `rt` (or uppercase `LB`, `RB`, `LT`, `RT`) |
|
|
||||||
| | Toggle versions: `tglLB`, `tglRB`, `tglLT`, `tglRT` |
|
|
||||||
| D-Pad | `up`, `down`, `left`, `right` (or `u`, `d`, `l`, `r` or uppercase) |
|
|
||||||
| | Toggle versions: `tglUp`, `tglDown`, `tglLeft`, `tglRight` (or `tglU`, `tglD`, `tglL`, `tglR`) |
|
|
||||||
|
|
||||||
### Analog Sticks
|
|
||||||
|
|
||||||
| Stick | Controls |
|
|
||||||
| ----------- | ------------------------------ |
|
|
||||||
| Left Stick | `x1`, `y1` (0 to 1 range) |
|
|
||||||
| | `x1_2`, `y1_2` (-1 to 1 range) |
|
|
||||||
| Right Stick | `x2`, `y2` (0 to 1 range) |
|
|
||||||
| | `x2_2`, `y2_2` (-1 to 1 range) |
|
|
||||||
|
|
||||||
### Button Sequence
|
|
||||||
|
|
||||||
| Stick | Controls |
|
|
||||||
| --------------- | --------------------------------------- |
|
|
||||||
| Button Sequence | `btnSequence()`, `btnSeq()`, `btnseq()` |
|
|
||||||
|
|
||||||
## Using Gamepad Inputs
|
|
||||||
|
|
||||||
Once initialized, you can use various gamepad inputs in your patterns. Here are some examples:
|
|
||||||
|
|
||||||
### Button Inputs
|
|
||||||
|
|
||||||
You can use button inputs to control different aspects of your music, such as gain or triggering events.
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`const gp = gamepad(0)
|
|
||||||
// Use button values to control amplitude
|
|
||||||
$: stack(
|
|
||||||
s("[[hh hh] oh hh oh]/2").mask(gp.tglX).bank("RolandTR909"), // X btn for HH
|
|
||||||
s("cr*1").mask(gp.Y).bank("RolandTR909"), // LB btn for CR
|
|
||||||
s("bd").mask(gp.tglA).bank("RolandTR909"), // A btn for BD
|
|
||||||
s("[ht - - mt - - lt - ]/2").mask(gp.tglB).bank("RolandTR909"), // B btn for Toms
|
|
||||||
s("sd*4").mask(gp.RB).bank("RolandTR909"), // RB btn for SD
|
|
||||||
).cpm(120)
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
### Analog Stick Inputs
|
|
||||||
|
|
||||||
Analog sticks can be used for continuous control, such as pitch shifting or panning.
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`const gp = gamepad(0)
|
|
||||||
// Use analog stick for continuous control
|
|
||||||
$: note("c4 d3 a3 e3").sound("sawtooth")
|
|
||||||
.lpf(gp.x1.range(100,4000))
|
|
||||||
.lpq(gp.y1.range(5,30))
|
|
||||||
.decay(gp.y2.range(0.1,2))
|
|
||||||
.lpenv(gp.x2.range(-5,5))
|
|
||||||
.cpm(120)
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
### Button Sequences
|
|
||||||
|
|
||||||
You can define button sequences to trigger specific actions, like playing a sound when a sequence is detected.
|
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`const gp = gamepad(0)
|
|
||||||
// Define button sequences
|
|
||||||
const HADOUKEN = [
|
|
||||||
'd', // Down
|
|
||||||
'r', // Right
|
|
||||||
'a', // A
|
|
||||||
]
|
|
||||||
const KONAMI = 'uuddlrlrba' //Konami Code ↑↑↓↓←→←→BA
|
|
||||||
|
|
||||||
// Check butto-n sequence (returns 1 while detected, 0 when not within last 1 second)
|
|
||||||
$: s("free_hadouken -").slow(2)
|
|
||||||
.mask(gp.btnSequence(HADOUKEN)).room(1).cpm(120)
|
|
||||||
|
|
||||||
// hadouken.wav by Syna-Max
|
|
||||||
//https://freesound.org/people/Syna-Max/sounds/67674/
|
|
||||||
samples({free_hadouken: 'https://cdn.freesound.org/previews/67/67674_111920-lq.mp3'})
|
|
||||||
`} />
|
|
||||||
|
|
||||||
## Multiple Gamepads
|
|
||||||
|
|
||||||
Strudel supports multiple gamepads. You can specify the gamepad index to connect to different devices.
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`const pad1 = gamepad(0); // First gamepad
|
|
||||||
const pad2 = gamepad(1); // Second gamepad`}
|
|
||||||
/>
|
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
// @strudel/gamepad/index.mjs
|
|
||||||
|
|
||||||
import { signal } from '@strudel/core';
|
|
||||||
|
|
||||||
// Button mapping for Logitech Dual Action (STANDARD GAMEPAD Vendor: 046d Product: c216)
|
|
||||||
export const buttonMap = {
|
|
||||||
a: 0,
|
|
||||||
b: 1,
|
|
||||||
x: 2,
|
|
||||||
y: 3,
|
|
||||||
lb: 4,
|
|
||||||
rb: 5,
|
|
||||||
lt: 6,
|
|
||||||
rt: 7,
|
|
||||||
back: 8,
|
|
||||||
start: 9,
|
|
||||||
u: 12,
|
|
||||||
up: 12,
|
|
||||||
d: 13,
|
|
||||||
down: 13,
|
|
||||||
l: 14,
|
|
||||||
left: 14,
|
|
||||||
r: 15,
|
|
||||||
right: 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ButtonSequenceDetector {
|
|
||||||
constructor(timeWindow = 1000) {
|
|
||||||
this.sequence = [];
|
|
||||||
this.timeWindow = timeWindow;
|
|
||||||
this.lastInputTime = 0;
|
|
||||||
this.buttonStates = Array(16).fill(0); // Track previous state of each button
|
|
||||||
// Button mapping for character inputs
|
|
||||||
}
|
|
||||||
|
|
||||||
addInput(buttonIndex, buttonValue) {
|
|
||||||
const currentTime = Date.now();
|
|
||||||
|
|
||||||
// Only add input on button press (rising edge)
|
|
||||||
if (buttonValue === 1 && this.buttonStates[buttonIndex] === 0) {
|
|
||||||
// Clear sequence if too much time has passed
|
|
||||||
if (currentTime - this.lastInputTime > this.timeWindow) {
|
|
||||||
this.sequence = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the button name instead of index
|
|
||||||
const buttonName = Object.keys(buttonMap).find((key) => buttonMap[key] === buttonIndex) || buttonIndex.toString();
|
|
||||||
|
|
||||||
this.sequence.push({
|
|
||||||
input: buttonName,
|
|
||||||
timestamp: currentTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.lastInputTime = currentTime;
|
|
||||||
|
|
||||||
//console.log(this.sequence);
|
|
||||||
// Keep only inputs within the time window
|
|
||||||
this.sequence = this.sequence.filter((entry) => currentTime - entry.timestamp <= this.timeWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update button state
|
|
||||||
this.buttonStates[buttonIndex] = buttonValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkSequence(targetSequence) {
|
|
||||||
if (!Array.isArray(targetSequence) && typeof targetSequence !== 'string') {
|
|
||||||
console.error('ButtonSequenceDetector: targetSequence must be an array or string');
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sequence.length < targetSequence.length) return 0;
|
|
||||||
|
|
||||||
// Convert string input to array if needed
|
|
||||||
const sequence =
|
|
||||||
typeof targetSequence === 'string'
|
|
||||||
? targetSequence.toLowerCase().split('')
|
|
||||||
: targetSequence.map((s) => s.toString().toLowerCase());
|
|
||||||
|
|
||||||
//console.log(this.sequence);
|
|
||||||
|
|
||||||
// Get the last n inputs where n is the target sequence length
|
|
||||||
const lastInputs = this.sequence.slice(-targetSequence.length).map((entry) => entry.input);
|
|
||||||
|
|
||||||
// Compare sequences
|
|
||||||
return lastInputs.every((input, index) => {
|
|
||||||
const target = sequence[index];
|
|
||||||
// Check if either the input matches directly or they refer to the same button in the map
|
|
||||||
return (
|
|
||||||
input === target ||
|
|
||||||
buttonMap[input] === buttonMap[target] ||
|
|
||||||
// Also check if the numerical index matches
|
|
||||||
buttonMap[input] === parseInt(target)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
? 1
|
|
||||||
: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GamepadHandler {
|
|
||||||
constructor(index = 0) {
|
|
||||||
// Add index parameter
|
|
||||||
this._gamepads = {};
|
|
||||||
this._activeGamepad = index; // Use provided index
|
|
||||||
this._axes = [0, 0, 0, 0];
|
|
||||||
this._buttons = Array(16).fill(0);
|
|
||||||
this.setupEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
setupEventListeners() {
|
|
||||||
window.addEventListener('gamepadconnected', (e) => {
|
|
||||||
this._gamepads[e.gamepad.index] = e.gamepad;
|
|
||||||
if (!this._activeGamepad) {
|
|
||||||
this._activeGamepad = e.gamepad.index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('gamepaddisconnected', (e) => {
|
|
||||||
delete this._gamepads[e.gamepad.index];
|
|
||||||
if (this._activeGamepad === e.gamepad.index) {
|
|
||||||
this._activeGamepad = Object.keys(this._gamepads)[0] || null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
poll() {
|
|
||||||
if (this._activeGamepad !== null) {
|
|
||||||
const gamepad = navigator.getGamepads()[this._activeGamepad];
|
|
||||||
if (gamepad) {
|
|
||||||
// Update axes (normalized to 0-1 range)
|
|
||||||
this._axes = gamepad.axes.map((axis) => (axis + 1) / 2);
|
|
||||||
// Update buttons
|
|
||||||
this._buttons = gamepad.buttons.map((button) => button.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAxes() {
|
|
||||||
return this._axes;
|
|
||||||
}
|
|
||||||
getButtons() {
|
|
||||||
return this._buttons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Module-level state store for toggle states
|
|
||||||
const gamepadStates = new Map();
|
|
||||||
|
|
||||||
export const gamepad = (index = 0) => {
|
|
||||||
const handler = new GamepadHandler(index);
|
|
||||||
const sequenceDetector = new ButtonSequenceDetector(2000);
|
|
||||||
|
|
||||||
// Base signal that polls gamepad state and handles sequence detection
|
|
||||||
const baseSignal = signal((t) => {
|
|
||||||
handler.poll();
|
|
||||||
const axes = handler.getAxes();
|
|
||||||
const buttons = handler.getButtons();
|
|
||||||
|
|
||||||
// Add all button inputs to sequence detector
|
|
||||||
buttons.forEach((value, i) => {
|
|
||||||
sequenceDetector.addInput(i, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return { axes, buttons, t };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create axes patterns
|
|
||||||
const axes = {
|
|
||||||
x1: baseSignal.fmap((state) => state.axes[0]),
|
|
||||||
y1: baseSignal.fmap((state) => state.axes[1]),
|
|
||||||
x2: baseSignal.fmap((state) => state.axes[2]),
|
|
||||||
y2: baseSignal.fmap((state) => state.axes[3]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add bipolar versions
|
|
||||||
axes.x1_2 = axes.x1.toBipolar();
|
|
||||||
axes.y1_2 = axes.y1.toBipolar();
|
|
||||||
axes.x2_2 = axes.x2.toBipolar();
|
|
||||||
axes.y2_2 = axes.y2.toBipolar();
|
|
||||||
|
|
||||||
// Create button patterns
|
|
||||||
const buttons = Array(16)
|
|
||||||
.fill(null)
|
|
||||||
.map((_, i) => {
|
|
||||||
// Create unique key for this gamepad+button combination
|
|
||||||
const stateKey = `gamepad${index}_btn${i}`;
|
|
||||||
|
|
||||||
// Initialize toggle state if it doesn't exist
|
|
||||||
if (!gamepadStates.has(stateKey)) {
|
|
||||||
gamepadStates.set(stateKey, {
|
|
||||||
lastButtonState: 0,
|
|
||||||
toggleState: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct button value pattern (no longer needs to call addInput)
|
|
||||||
const btn = baseSignal.fmap((state) => state.buttons[i]);
|
|
||||||
|
|
||||||
// Button toggle pattern with persistent state
|
|
||||||
const toggle = baseSignal.fmap((state) => {
|
|
||||||
const currentState = state.buttons[i];
|
|
||||||
const buttonState = gamepadStates.get(stateKey);
|
|
||||||
|
|
||||||
if (currentState === 1 && buttonState.lastButtonState === 0) {
|
|
||||||
// Toggle the state on rising edge
|
|
||||||
buttonState.toggleState = buttonState.toggleState === 0 ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonState.lastButtonState = currentState;
|
|
||||||
return buttonState.toggleState;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { value: btn, toggle };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create sequence checker pattern
|
|
||||||
const btnSequence = (sequence) => {
|
|
||||||
return baseSignal.fmap(() => sequenceDetector.checkSequence(sequence));
|
|
||||||
};
|
|
||||||
const checkSequence = btnSequence;
|
|
||||||
const btnSeq = btnSequence;
|
|
||||||
const btnseq = btnSeq;
|
|
||||||
|
|
||||||
// Return an object with all controls
|
|
||||||
return {
|
|
||||||
...axes,
|
|
||||||
buttons,
|
|
||||||
...Object.fromEntries(
|
|
||||||
Object.entries(buttonMap).flatMap(([key, index]) => [
|
|
||||||
[key.toLowerCase(), buttons[index].value],
|
|
||||||
[key.toUpperCase(), buttons[index].value],
|
|
||||||
[`tgl${key.toLowerCase()}`, buttons[index].toggle],
|
|
||||||
[`tgl${key.toUpperCase()}`, buttons[index].toggle],
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
checkSequence,
|
|
||||||
btnSequence,
|
|
||||||
btnSeq,
|
|
||||||
btnseq,
|
|
||||||
raw: baseSignal,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional: Export for debugging or state management
|
|
||||||
export const getGamepadStates = () => Object.fromEntries(gamepadStates);
|
|
||||||
export const clearGamepadStates = () => gamepadStates.clear();
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user