diff --git a/packages/codemirror/autocomplete.mjs b/packages/codemirror/autocomplete.mjs
index 203ab855..c0845895 100644
--- a/packages/codemirror/autocomplete.mjs
+++ b/packages/codemirror/autocomplete.mjs
@@ -1,68 +1,91 @@
import jsdoc from '../../doc.json';
-// import { javascriptLanguage } from '@codemirror/lang-javascript';
import { autocompletion } from '@codemirror/autocomplete';
import { h } from './html';
-function plaintext(str) {
+const escapeHtml = (str) => {
const div = document.createElement('div');
div.innerText = str;
return div.innerHTML;
-}
+};
-const getDocLabel = (doc) => doc.name || doc.longname;
-const getInnerText = (html) => {
- var div = document.createElement('div');
+const stripHtml = (html) => {
+ const div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
};
-export function Autocomplete({ doc, label }) {
- return h`
-
${label || getDocLabel(doc)}
-${doc.description}
-
- ${doc.params?.map(
- ({ name, type, description }) =>
- `- ${name} : ${type.names?.join(' | ')} ${description ? ` - ${getInnerText(description)}` : ''}
`,
- )}
-
-
- ${doc.examples?.map((example) => `
`)}
-
-
`[0];
- /*
- {
- console.log('ola!');
- navigator.clipboard.writeText(example);
- e.stopPropagation();
-}}
->
-{example}
-
-*/
-}
+const getDocLabel = (doc) => doc.name || doc.longname;
+
+const buildParamsList = (params) =>
+ params?.length
+ ? `
+
+ `
+ : '';
+
+const buildExamples = (examples) =>
+ examples?.length
+ ? `
+
+
Examples
+ ${examples
+ .map(
+ (example) => `
+
${escapeHtml(example)}
+ `,
+ )
+ .join('')}
+
+ `
+ : '';
+
+export const Autocomplete = ({ doc, label }) =>
+ h`
+
+`[0];
+
+const isValidDoc = (doc) => {
+ const label = getDocLabel(doc);
+ return label && !label.startsWith('_') && !['package'].includes(doc.kind);
+};
+
+const hasExcludedTags = (doc) =>
+ ['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag));
const jsdocCompletions = jsdoc.docs
- .filter(
- (doc) =>
- getDocLabel(doc) &&
- !getDocLabel(doc).startsWith('_') &&
- !['package'].includes(doc.kind) &&
- !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)),
- )
+ .filter((doc) => isValidDoc(doc) && !hasExcludedTags(doc))
// https://codemirror.net/docs/ref/#autocomplete.Completion
- .map((doc) /*: Completion */ => ({
+ .map((doc) => ({
label: getDocLabel(doc),
// detail: 'xxx', // An optional short piece of information to show (with a different style) after the label.
info: () => Autocomplete({ doc }),
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
}));
-export const strudelAutocomplete = (context /* : CompletionContext */) => {
- let word = context.matchBefore(/\w*/);
- if (word.from == word.to && !context.explicit) return null;
+export const strudelAutocomplete = (context) => {
+ const word = context.matchBefore(/\w*/);
+ if (word.from === word.to && !context.explicit) return null;
+
return {
from: word.from,
options: jsdocCompletions,
@@ -74,11 +97,5 @@ export const strudelAutocomplete = (context /* : CompletionContext */) => {
};
};
-export function isAutoCompletionEnabled(on) {
- return on
- ? [
- autocompletion({ override: [strudelAutocomplete] }),
- //javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
- ]
- : []; // autocompletion({ override: [] })
-}
+export const isAutoCompletionEnabled = (enabled) =>
+ enabled ? [autocompletion({ override: [strudelAutocomplete] })] : [];
diff --git a/packages/core/bench/pattern.bench.mjs b/packages/core/bench/pattern.bench.mjs
index 1b5be0b9..56a84980 100644
--- a/packages/core/bench/pattern.bench.mjs
+++ b/packages/core/bench/pattern.bench.mjs
@@ -1,11 +1,11 @@
import { describe, bench } from 'vitest';
-import { calculateTactus, sequence, stack } from '../index.mjs';
+import { calculateSteps, sequence, stack } from '../index.mjs';
const pat64 = sequence(...Array(64).keys());
describe('steps', () => {
- calculateTactus(true);
+ calculateSteps(true);
bench(
'+tactus',
() => {
@@ -14,7 +14,7 @@ describe('steps', () => {
{ time: 1000 },
);
- calculateTactus(false);
+ calculateSteps(false);
bench(
'-tactus',
() => {
@@ -25,7 +25,7 @@ describe('steps', () => {
});
describe('stack', () => {
- calculateTactus(true);
+ calculateSteps(true);
bench(
'+tactus',
() => {
@@ -34,7 +34,7 @@ describe('stack', () => {
{ time: 1000 },
);
- calculateTactus(false);
+ calculateSteps(false);
bench(
'-tactus',
() => {
@@ -43,4 +43,4 @@ describe('stack', () => {
{ time: 1000 },
);
});
-calculateTactus(true);
+calculateSteps(true);
diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs
index d0e09293..ae983d20 100644
--- a/packages/midi/midi.mjs
+++ b/packages/midi/midi.mjs
@@ -493,6 +493,9 @@ export async function midin(input) {
otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : ''
}`,
);
+ }
+ // ensure refs for this input are initialized
+ if (!refs[input]) {
refs[input] = {};
}
const cc = (cc) => ref(() => refs[input][cc] || 0);
diff --git a/packages/mini/bench/mini.bench.mjs b/packages/mini/bench/mini.bench.mjs
index 782ac86b..e7471bf2 100644
--- a/packages/mini/bench/mini.bench.mjs
+++ b/packages/mini/bench/mini.bench.mjs
@@ -1,10 +1,10 @@
import { describe, bench } from 'vitest';
-import { calculateTactus } from '../../core/index.mjs';
+import { calculateSteps } from '../../core/index.mjs';
import { mini } from '../index.mjs';
describe('mini', () => {
- calculateTactus(true);
+ calculateSteps(true);
bench(
'+tactus',
() => {
@@ -13,7 +13,7 @@ describe('mini', () => {
{ time: 1000 },
);
- calculateTactus(false);
+ calculateSteps(false);
bench(
'-tactus',
() => {
@@ -21,5 +21,5 @@ describe('mini', () => {
},
{ time: 1000 },
);
- calculateTactus(true);
+ calculateSteps(true);
});
diff --git a/packages/sampler/README.md b/packages/sampler/README.md
index c495c2ad..1142176b 100644
--- a/packages/sampler/README.md
+++ b/packages/sampler/README.md
@@ -20,3 +20,13 @@ samples('http://localhost:5432')
LOG=1 npx @strudel/sampler # adds logging
PORT=5555 npx @strudel/sampler # changes port
```
+
+## static json
+
+when running with `--json`, you will simply get the json logged back:
+
+```sh
+npx --yes @strudel/sampler --json > strudel.json
+```
+
+this is useful if you want to create a sample pack from the current folder.
\ No newline at end of file
diff --git a/packages/sampler/package.json b/packages/sampler/package.json
index b0f27f86..dd701bcf 100644
--- a/packages/sampler/package.json
+++ b/packages/sampler/package.json
@@ -1,6 +1,6 @@
{
"name": "@strudel/sampler",
- "version": "0.2.0",
+ "version": "0.2.2",
"description": "",
"keywords": [
"tidalcycles",
diff --git a/packages/sampler/sample-server.mjs b/packages/sampler/sample-server.mjs
index d1e56108..08456add 100644
--- a/packages/sampler/sample-server.mjs
+++ b/packages/sampler/sample-server.mjs
@@ -10,14 +10,6 @@ import os from 'os';
// eslint-disable-next-line
const LOG = !!process.env.LOG || false;
-console.log(
- cowsay.say({
- text: 'welcome to @strudel/sampler',
- e: 'oO',
- T: 'U ',
- }),
-);
-
async function getFilesInDirectory(directory) {
let files = [];
const dirents = await readdir(directory, { withFileTypes: true });
@@ -60,8 +52,26 @@ async function getBanks(directory) {
return { banks, files };
}
+const args = process.argv.slice(2);
+
// eslint-disable-next-line
const directory = process.cwd();
+
+if (args.includes('--json')) {
+ const { banks, files } = await getBanks(directory);
+ const json = JSON.stringify(banks);
+ console.log(json);
+ process.exit(0);
+}
+
+console.log(
+ cowsay.say({
+ text: 'welcome to @strudel/sampler',
+ e: 'oO',
+ T: 'U ',
+ }),
+);
+
const server = http.createServer(async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
const { banks, files } = await getBanks(directory);
diff --git a/website/package.json b/website/package.json
index 49975814..87f4025e 100644
--- a/website/package.json
+++ b/website/package.json
@@ -8,8 +8,7 @@
"start": "astro dev",
"build": "astro build",
"preview": "astro preview --port 3009 --host 0.0.0.0",
- "astro": "astro",
- "postinstall": "cp node_modules/hs2js/dist/tree-sitter.wasm public && cp node_modules/hs2js/dist/tree-sitter-haskell.wasm public"
+ "astro": "astro"
},
"dependencies": {
"@algolia/client-search": "^5.20.0",
diff --git a/website/src/pages/learn/conditional-modifiers.mdx b/website/src/pages/learn/conditional-modifiers.mdx
index c2e22595..d31ab0cf 100644
--- a/website/src/pages/learn/conditional-modifiers.mdx
+++ b/website/src/pages/learn/conditional-modifiers.mdx
@@ -34,11 +34,11 @@ import { JsDoc } from '../../docs/JsDoc';
## arp
-
+
## arpWith 🧪
-
+
## struct
@@ -58,7 +58,7 @@ import { JsDoc } from '../../docs/JsDoc';
## hush
-
+
## invert
diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx
index 8f6a3b8a..89f36fe9 100644
--- a/website/src/pages/learn/effects.mdx
+++ b/website/src/pages/learn/effects.mdx
@@ -339,4 +339,18 @@ global effects use the same chain for all events of the same orbit:
+## Duck
+
+### duckorbit
+
+
+
+### duckattack
+
+
+
+### duckdepth
+
+
+
Next, we'll look at input / output via [MIDI, OSC and other methods](/learn/input-output).
diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx
index bbe650cf..a87b8f7d 100644
--- a/website/src/pages/learn/samples.mdx
+++ b/website/src/pages/learn/samples.mdx
@@ -178,6 +178,16 @@ the version number).
It is also possible, of course, to just remove it from cache (deleting cache in browser Privacy settings,
or from the dev console if you're technically minded, or by using a cache deleting extension).
+## Generating strudel.json
+
+You can use [@strudel/sampler](https://www.npmjs.com/package/@strudel/sampler) to generate a strudel.json file for you, by running:
+
+```sh
+npx --yes @strudel/sampler --json > strudel.json
+```
+
+See other uses of strudel/sampler further below, under "From Disk via @strudel/sampler".
+
## Github Shortcut
Because loading samples from github is common, there is a shortcut:
@@ -361,6 +371,10 @@ Sampler effects are functions that can be used to change the behaviour of sample
+### scrub
+
+{' '}
+
### speed
diff --git a/website/src/repl/Repl.css b/website/src/repl/Repl.css
index 3e13ff5a..b8443081 100644
--- a/website/src/repl/Repl.css
+++ b/website/src/repl/Repl.css
@@ -69,3 +69,133 @@
text-decoration: underline 0.18rem;
text-underline-offset: 0.22rem;
}
+
+/* Override default styles from the codemirror inline css for autocomplete info tooltip*/
+.cm-tooltip.cm-completionInfo {
+ padding: 12px !important;
+ padding-bottom: 12px !important;
+ border: 1px solid var(--foreground) !important;
+ border-radius: 4px !important;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
+ max-width: 500px !important;
+ min-width: 300px !important;
+ max-height: 400px !important;
+ white-space: normal !important;
+ overflow: auto !important;
+ background-color: var(--lineHighlight) !important;
+}
+
+/* Main tooltip container */
+.autocomplete-info-tooltip {
+ border-radius: 4px !important;
+ color: var(--foreground);
+ font-family: var(--font-family, 'SF Mono', 'Monaco', monospace);
+ font-size: var(--font-size, 13px);
+ line-height: 1.4;
+ max-width: 600px;
+ max-height: 400px;
+ min-width: 400px;
+}
+
+.autocomplete-info-function-name {
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--foreground);
+ margin: 0 0 8px 0;
+}
+
+.autocomplete-info-function-description {
+ margin: 0 0 12px 0;
+ color: var(--foreground);
+ line-height: 1.5;
+ opacity: 0.8;
+}
+
+.autocomplete-info-section-title {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--foreground);
+ margin: 16px 0 6px 0;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.autocomplete-info-section-title:first-child {
+ margin-top: 0;
+}
+
+.autocomplete-info-params-section {
+ margin-top: 12px;
+}
+
+.autocomplete-info-params-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.autocomplete-info-param-item {
+ margin-bottom: 8px;
+ padding: 8px;
+ background-color: var(--lineBackground);
+ border-radius: 3px;
+ border-left: 2px solid var(--foreground, #555);
+}
+
+.autocomplete-info-param-item:last-child {
+ margin-bottom: 0;
+}
+
+.autocomplete-info-param-name {
+ font-weight: 600;
+ color: var(--variable, var(--foreground));
+ margin-right: 8px;
+}
+
+.autocomplete-info-param-type {
+ color: var(--comment);
+ font-size: 12px;
+ background-color: var(--gutterForeground);
+ padding: 1px 4px;
+ border-radius: 2px;
+}
+
+.autocomplete-info-param-desc {
+ color: var(--foreground);
+ font-size: 10px;
+ margin-top: 4px;
+ line-height: 1.4;
+ opacity: 0.7;
+}
+
+.autocomplete-info-examples-section {
+ margin-top: 12px;
+}
+
+.autocomplete-info-example-code {
+ background: var(--lineBackground);
+ color: var(--foreground);
+ padding: 8px;
+ border-radius: 3px;
+ font-family: var(--font-family, 'SF Mono', 'Monaco', monospace);
+ font-size: 12px;
+ line-height: 1.5;
+ margin: 4px 0;
+ overflow-x: auto;
+ white-space: pre;
+ border: 1px solid var(--foreground, #3a3a3a);
+}
+
+.autocomplete-info-tooltip::-webkit-scrollbar {
+ width: 4px;
+}
+
+.autocomplete-info-tooltip::-webkit-scrollbar-track {
+}
+
+.autocomplete-info-tooltip::-webkit-scrollbar-thumb {
+ border-radius: 2px;
+}
+
+.autocomplete-info-tooltip::-webkit-scrollbar-thumb:hover {
+}