Merge branch 'tidalcycles:main' into pattern_selection

This commit is contained in:
Jade (Rose) Rowland 2024-01-21 11:19:06 -05:00 committed by GitHub
commit d03892bbcf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 487 additions and 191 deletions

View File

@ -41,7 +41,7 @@ export class Cyclist {
this.lastEnd = end;
// query the pattern for events
const haps = this.pattern.queryArc(begin, end);
const haps = this.pattern.queryArc(begin, end, { _cps: this.cps });
const tickdeadline = phase - time; // time left until the phase is a whole number
this.lastTick = time + tickdeadline;

View File

@ -340,9 +340,9 @@ export class Pattern {
* silence
* @noAutocomplete
*/
queryArc(begin, end) {
queryArc(begin, end, controls = {}) {
try {
return this.query(new State(new TimeSpan(begin, end)));
return this.query(new State(new TimeSpan(begin, end), controls));
} catch (err) {
logger(`[query]: ${err.message}`, 'error');
return [];
@ -427,7 +427,7 @@ export class Pattern {
* @noAutocomplete
*/
withHaps(func) {
return new Pattern((state) => func(this.query(state)));
return new Pattern((state) => func(this.query(state), state));
}
/**
@ -2341,28 +2341,28 @@ export const splice = register(
'splice',
function (npat, ipat, opat) {
const sliced = slice(npat, ipat, opat);
return sliced.withHap(function (hap) {
return hap.withValue((v) => ({
...{
speed: (1 / v._slices / hap.whole.duration) * (v.speed || 1),
unit: 'c',
},
...v,
}));
return new Pattern((state) => {
// TODO - default cps to 0.5
const cps = state.controls._cps || 1;
const haps = sliced.query(state);
return haps.map((hap) =>
hap.withValue((v) => ({
...{
speed: (cps / v._slices / hap.whole.duration) * (v.speed || 1),
unit: 'c',
},
...v,
})),
);
});
},
false, // turns off auto-patternification
);
// this function will be redefined in repl.mjs to use the correct cps value.
// It is still here to work in cases where repl.mjs is not used
export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat) {
return _loopAt(factor, pat, 1);
return new Pattern((state) => _loopAt(factor, pat, state.controls._cps).query(state));
});
// the fit function will be redefined in repl.mjs to use the correct cps value.
// It is still here to work in cases where repl.mjs is not used
/**
* Makes the sample fit its event duration. Good for rhythmical loops like drum breaks.
* Similar to `loopAt`.
@ -2372,12 +2372,14 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto
* s("rhodes/4").fit()
*/
export const fit = register('fit', (pat) =>
pat.withHap((hap) =>
hap.withValue((v) => ({
...v,
speed: 1 / hap.whole.duration,
unit: 'c',
})),
pat.withHaps((haps, state) =>
haps.map((hap) =>
hap.withValue((v) => ({
...v,
speed: (state.controls._cps || 1) / hap.whole.duration,
unit: 'c',
})),
),
),
);

View File

@ -61,12 +61,63 @@ export function repl({
scheduler.setPattern(pattern, autostart);
};
setTime(() => scheduler.now()); // TODO: refactor?
const stop = () => scheduler.stop();
const start = () => scheduler.start();
const pause = () => scheduler.pause();
const toggle = () => scheduler.toggle();
const setCps = (cps) => scheduler.setCps(cps);
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
const all = function (transform) {
allTransform = transform;
return silence;
};
// set pattern methods that use this repl via closure
const injectPatternMethods = () => {
Pattern.prototype.p = function (id) {
pPatterns[id] = this;
return this;
};
Pattern.prototype.q = function (id) {
return silence;
};
try {
for (let i = 1; i < 10; ++i) {
Object.defineProperty(Pattern.prototype, `d${i}`, {
get() {
return this.p(i);
},
configurable: true,
});
Object.defineProperty(Pattern.prototype, `p${i}`, {
get() {
return this.p(i);
},
configurable: true,
});
Pattern.prototype[`q${i}`] = silence;
}
} catch (err) {
console.warn('injectPatternMethods: error:', err);
}
evalScope({
all,
hush,
setCps,
setcps: setCps,
setCpm,
setcpm: setCpm,
});
};
const evaluate = async (code, autostart = true, shouldHush = true) => {
if (!code) {
throw new Error('no code to evaluate');
}
try {
updateState({ code, pending: true });
injectPatternMethods();
await beforeEval?.({ code });
shouldHush && hush();
let { pattern, meta } = await _evaluate(code, transpiler);
@ -99,68 +150,6 @@ export function repl({
onEvalError?.(err);
}
};
const stop = () => scheduler.stop();
const start = () => scheduler.start();
const pause = () => scheduler.pause();
const toggle = () => scheduler.toggle();
const setCps = (cps) => scheduler.setCps(cps);
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
// the following functions use the cps value, which is why they are defined here..
const loopAt = register('loopAt', (cycles, pat) => {
return pat.loopAtCps(cycles, scheduler.cps);
});
Pattern.prototype.p = function (id) {
pPatterns[id] = this;
return this;
};
Pattern.prototype.q = function (id) {
return silence;
};
const all = function (transform) {
allTransform = transform;
return silence;
};
try {
for (let i = 1; i < 10; ++i) {
Object.defineProperty(Pattern.prototype, `d${i}`, {
get() {
return this.p(i);
},
});
Object.defineProperty(Pattern.prototype, `p${i}`, {
get() {
return this.p(i);
},
});
Pattern.prototype[`q${i}`] = silence;
}
} catch (err) {
// already defined..
}
const fit = register('fit', (pat) =>
pat.withHap((hap) =>
hap.withValue((v) => ({
...v,
speed: scheduler.cps / hap.whole.duration, // overwrite speed completely?
unit: 'c',
})),
),
);
evalScope({
loopAt,
fit,
all,
hush,
setCps,
setcps: setCps,
setCpm,
setcpm: setCpm,
});
const setCode = (code) => updateState({ code });
return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state };
}

View File

@ -214,6 +214,32 @@ 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));
});
/**
/** * 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.
@ -221,7 +247,7 @@ export const pickmod = register('pickmod', function (lookup, pat) {
* @param {*} xs
* @returns {Pattern}
* @example
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
b: s("cp sd")
})
* @example

View File

@ -288,48 +288,50 @@ function peg$parse(input, options) {
var peg$f0 = function() { return parseFloat(text()); };
var peg$f1 = function() { return parseInt(text()); };
var peg$f2 = function(chars) { return new AtomStub(chars.join("")) };
var peg$f3 = function(s) { return s };
var peg$f4 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
var peg$f5 = function(a) { return a };
var peg$f6 = function(s) { s.arguments_.alignment = 'polymeter_slowcat'; return s; };
var peg$f7 = function(a) { return x => x.options_['weight'] = a };
var peg$f8 = function(a) { return x => x.options_['reps'] = a };
var peg$f9 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f13 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
var peg$f15 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
var peg$f2 = function(chars) { const s = chars.join(""); return (s === ".") || (s === "_") };
var peg$f3 = function(chars) { return new AtomStub(chars.join("")) };
var peg$f4 = function(s) { return s };
var peg$f5 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
var peg$f6 = function(a) { return a };
var peg$f7 = function(s) { s.arguments_.alignment = 'polymeter_slowcat'; return s; };
var peg$f8 = function(a) { return x => x.options_['weight'] = (x.options_['weight'] ?? 1) + (a ?? 2) - 1 };
var peg$f9 = function(a) { return x => x.options_['reps'] = (x.options_['reps'] ?? 1) + (a ?? 2) - 1 };
var peg$f10 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f13 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f15 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
var peg$f16 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
for (const op of ops) {
op(result);
}
return result;
};
var peg$f16 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f17 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f18 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f19 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f20 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f21 = function(sc) { return sc; };
var peg$f22 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f23 = function(s) { return { name: "target", args : { name:s}}};
var peg$f24 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f25 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f26 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f27 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f28 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f29 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f30 = function(s, v) { return v};
var peg$f31 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f32 = function(sg) {return sg};
var peg$f33 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f34 = function(sc) { return sc };
var peg$f35 = function(c) { return c };
var peg$f36 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f37 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f38 = function() { return new CommandStub("hush")};
var peg$f17 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f18 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f19 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f20 = function(tail) { return { alignment: 'feet', list: tail, seed: seed++ }; };
var peg$f21 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f22 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f23 = function(sc) { return sc; };
var peg$f24 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f25 = function(s) { return { name: "target", args : { name:s}}};
var peg$f26 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f27 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f28 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f29 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f30 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f31 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f32 = function(s, v) { return v};
var peg$f33 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f34 = function(sg) {return sg};
var peg$f35 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f36 = function(sc) { return sc };
var peg$f37 = function(c) { return c };
var peg$f38 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f39 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f40 = function() { return new CommandStub("hush")};
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
@ -821,6 +823,30 @@ function peg$parse(input, options) {
return s0;
}
function peg$parsedot() {
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 46) {
s2 = peg$c0;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e1); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
s1 = [s1, s2, s3];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parsequote() {
var s0;
@ -913,7 +939,7 @@ function peg$parse(input, options) {
}
function peg$parsestep() {
var s0, s1, s2, s3;
var s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$parsews();
@ -929,8 +955,20 @@ function peg$parse(input, options) {
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f2(s2);
peg$savedPos = peg$currPos;
s4 = peg$f2(s2);
if (s4) {
s4 = peg$FAILED;
} else {
s4 = undefined;
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f3(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -966,7 +1004,7 @@ function peg$parse(input, options) {
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f3(s4);
s0 = peg$f4(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1014,7 +1052,7 @@ function peg$parse(input, options) {
}
s8 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f4(s4, s7);
s0 = peg$f5(s4, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1046,7 +1084,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f5(s2);
s0 = peg$f6(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1086,7 +1124,7 @@ function peg$parse(input, options) {
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f6(s4);
s0 = peg$f7(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1150,25 +1188,33 @@ function peg$parse(input, options) {
}
function peg$parseop_weight() {
var s0, s1, s2;
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c18;
s2 = peg$c18;
peg$currPos++;
} else {
s1 = peg$FAILED;
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e26); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f7(s2);
if (s2 === peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 95) {
s2 = peg$c10;
peg$currPos++;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e18); }
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parsenumber();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f8(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1178,25 +1224,24 @@ function peg$parse(input, options) {
}
function peg$parseop_replicate() {
var s0, s1, s2;
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 33) {
s1 = peg$c19;
s2 = peg$c19;
peg$currPos++;
} else {
s1 = peg$FAILED;
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e27); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f8(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
if (s2 !== peg$FAILED) {
s3 = peg$parsenumber();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f9(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1246,7 +1291,7 @@ function peg$parse(input, options) {
}
if (s13 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f9(s3, s7, s11);
s0 = peg$f10(s3, s7, s11);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1286,7 +1331,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f10(s2);
s0 = peg$f11(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1314,7 +1359,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f11(s2);
s0 = peg$f12(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1344,7 +1389,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f12(s2);
s0 = peg$f13(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1368,7 +1413,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f13(s2);
s0 = peg$f14(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1396,7 +1441,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f14(s2);
s0 = peg$f15(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1422,7 +1467,7 @@ function peg$parse(input, options) {
s3 = peg$parseslice_op();
}
peg$savedPos = s0;
s0 = peg$f15(s1, s2);
s0 = peg$f16(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1447,7 +1492,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f16(s1);
s1 = peg$f17(s1);
}
s0 = s1;
@ -1496,7 +1541,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f17(s1);
s1 = peg$f18(s1);
}
s0 = s1;
@ -1545,7 +1590,56 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f18(s1);
s1 = peg$f19(s1);
}
s0 = s1;
return s0;
}
function peg$parsedot_tail() {
var s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = [];
s2 = peg$currPos;
s3 = peg$parsedot();
if (s3 !== peg$FAILED) {
s4 = peg$parsesequence();
if (s4 !== peg$FAILED) {
s2 = s4;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$currPos;
s3 = peg$parsedot();
if (s3 !== peg$FAILED) {
s4 = peg$parsesequence();
if (s4 !== peg$FAILED) {
s2 = s4;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f20(s1);
}
s0 = s1;
@ -1561,12 +1655,15 @@ function peg$parse(input, options) {
s2 = peg$parsestack_tail();
if (s2 === peg$FAILED) {
s2 = peg$parsechoose_tail();
if (s2 === peg$FAILED) {
s2 = peg$parsedot_tail();
}
}
if (s2 === peg$FAILED) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f19(s1, s2);
s0 = peg$f21(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1586,7 +1683,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f20(s1, s2);
s0 = peg$f22(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1609,7 +1706,7 @@ function peg$parse(input, options) {
s6 = peg$parsequote();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f21(s4);
s0 = peg$f23(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1671,7 +1768,7 @@ function peg$parse(input, options) {
s3 = peg$parsemini_or_operator();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f22(s3);
s0 = peg$f24(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1704,7 +1801,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f23(s4);
s0 = peg$f25(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1749,7 +1846,7 @@ function peg$parse(input, options) {
s7 = null;
}
peg$savedPos = s0;
s0 = peg$f24(s3, s5, s7);
s0 = peg$f26(s3, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1782,7 +1879,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f25(s3);
s0 = peg$f27(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1811,7 +1908,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f26(s3);
s0 = peg$f28(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1840,7 +1937,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f27(s3);
s0 = peg$f29(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1869,7 +1966,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f28(s3);
s0 = peg$f30(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1911,7 +2008,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f29(s4);
s0 = peg$f31(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2003,7 +2100,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f30(s5, s9);
s7 = peg$f32(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@ -2020,7 +2117,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f30(s5, s9);
s7 = peg$f32(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@ -2040,7 +2137,7 @@ function peg$parse(input, options) {
}
if (s8 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f31(s5, s6);
s0 = peg$f33(s5, s6);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2086,7 +2183,7 @@ function peg$parse(input, options) {
s4 = peg$parsecomment();
}
peg$savedPos = s0;
s0 = peg$f32(s1);
s0 = peg$f34(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2108,7 +2205,7 @@ function peg$parse(input, options) {
s5 = peg$parsemini_or_operator();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f33(s1, s5);
s0 = peg$f35(s1, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2133,7 +2230,7 @@ function peg$parse(input, options) {
s1 = peg$parsemini_or_operator();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f34(s1);
s1 = peg$f36(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
@ -2166,7 +2263,7 @@ function peg$parse(input, options) {
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f35(s2);
s0 = peg$f37(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2191,7 +2288,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f36(s3);
s0 = peg$f38(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2220,7 +2317,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f37(s3);
s0 = peg$f39(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -2246,7 +2343,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f38();
s1 = peg$f40();
}
s0 = s1;

View File

@ -98,6 +98,7 @@ DIGIT = [0-9]
ws "whitespace" = [ \n\r\t\u00A0]*
comma = ws "," ws
pipe = ws "|" ws
dot = ws "." ws
quote = '"' / "'"
// ------------------ steps and cycles ---------------------------
@ -105,7 +106,8 @@ quote = '"' / "'"
// single step definition (e.g bd)
step_char "a letter, a number, \"-\", \"#\", \".\", \"^\", \"_\"" =
unicode_letter / [0-9~] / "-" / "#" / "." / "^" / "_"
step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) }
step = ws chars:step_char+ ws !{ const s = chars.join(""); return (s === ".") || (s === "_") } { return new AtomStub(chars.join("")) }
// define a sub cycle e.g. [1 2, 3 [4]]
sub_cycle = ws "[" ws s:stack_or_choose ws "]" ws { return s }
@ -129,11 +131,11 @@ slice = step / sub_cycle / polymeter / slow_sequence
// at this point, we assume we can represent them as regular sequence operators
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail / op_range
op_weight = "@" a:number
{ return x => x.options_['weight'] = a }
op_weight = ws ("@" / "_") a:number?
{ return x => x.options_['weight'] = (x.options_['weight'] ?? 1) + (a ?? 2) - 1 }
op_replicate = "!"a:number
{ return x => x.options_['reps'] = a }
op_replicate = ws "!" a:number?
{ return x => x.options_['reps'] = (x.options_['reps'] ?? 1) + (a ?? 2) - 1 }
op_bjorklund = "(" ws p:slice_with_ops ws comma ws s:slice_with_ops ws comma? ws r:slice_with_ops? ws ")"
{ return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) }
@ -175,9 +177,13 @@ stack_tail = tail:(comma @sequence)+
choose_tail = tail:(pipe @sequence)+
{ return { alignment: 'rand', list: tail, seed: seed++ }; }
// a foot separates subsequences, as an alternative to wrapping them in []
dot_tail = tail:(dot @sequence)+
{ return { alignment: 'feet', list: tail, seed: seed++ }; }
// if the stack contains only one element, we don't create a stack but return the
// underlying element
stack_or_choose = head:sequence tail:(stack_tail / choose_tail)?
stack_or_choose = head:sequence tail:(stack_tail / choose_tail / dot_tail)?
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }
polymeter_stack = head:sequence tail:stack_tail?
@ -287,4 +293,4 @@ Lt = [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FC
Lu = [\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u13A0-\u13F5\u1C90-\u1CBA\u1CBD-\u1CBF\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA-\uA7AE\uA7B0-\uA7B4\uA7B6\uA7B8\uFF21-\uFF3A]
// Number, Letter
Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]
Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]

View File

@ -107,6 +107,9 @@ export function patternifyAST(ast, code, onEnter, offset = 0) {
if (alignment === 'rand') {
return strudel.chooseInWith(strudel.rand.early(randOffset * ast.arguments_.seed).segment(1), children);
}
if (alignment === 'feet') {
return strudel.fastcat(...children);
}
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (weightedChildren) {
const weightSum = ast.source_.reduce((sum, child) => sum + (child.options_?.weight || 1), 0);

View File

@ -73,6 +73,10 @@ describe('mini', () => {
expect(minS('a!3 b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']);
expect(minS('[<a b c>]!3 d')).toEqual(minS('<a b c> <a b c> <a b c> d'));
});
it('supports replication via repeated !', () => {
expect(minS('a ! ! b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']);
expect(minS('[<a b c>]!! d')).toEqual(minS('<a b c> <a b c> <a b c> d'));
});
it('supports euclidean rhythms', () => {
expect(minS('a(3, 8)')).toEqual(['a: 0 - 1/8', 'a: 3/8 - 1/2', 'a: 3/4 - 7/8']);
});
@ -190,6 +194,16 @@ describe('mini', () => {
it('supports patterned ranges', () => {
expect(minS('[<0 1> .. <2 4>]*2')).toEqual(minS('[0 1 2] [1 2 3 4]'));
});
it('supports the . operator', () => {
expect(minS('a . b c')).toEqual(minS('a [b c]'));
expect(minS('a . b c . [d e f . g h]')).toEqual(minS('a [b c] [[d e f] [g h]]'));
});
it('supports the _ operator', () => {
expect(minS('a _ b _ _')).toEqual(minS('a@2 b@3'));
});
it('_ and @ are almost interchangeable', () => {
expect(minS('a @ b @ @')).toEqual(minS('a _2 b _3'));
});
});
describe('getLeafLocation', () => {

View File

@ -3693,6 +3693,56 @@ exports[`runs examples > example "pick" example index 3 1`] = `
]
`;
exports[`runs examples > example "pickF" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:hh ]",
"[ 1/4 → 1/2 | s:rim ]",
"[ 1/2 → 1/1 | s:bd ]",
"[ 1/1 → 5/4 | s:hh pan:1 ]",
"[ 1/1 → 3/2 | s:bd pan:0 ]",
"[ 5/4 → 3/2 | s:rim pan:1 ]",
"[ 3/2 → 7/4 | s:rim pan:0 ]",
"[ 3/2 → 2/1 | s:bd pan:1 ]",
"[ 7/4 → 2/1 | s:hh pan:0 ]",
"[ 2/1 → 9/4 | s:bd ]",
"[ 9/4 → 19/8 | s:rim ]",
"[ 19/8 → 5/2 | s:hh ]",
"[ 5/2 → 11/4 | s:bd ]",
"[ 11/4 → 23/8 | s:rim ]",
"[ 23/8 → 3/1 | s:hh ]",
"[ 3/1 → 13/4 | s:hh ]",
"[ 13/4 → 7/2 | s:rim ]",
"[ 7/2 → 4/1 | s:bd ]",
]
`;
exports[`runs examples > example "pickF" example index 1 1`] = `
[
"[ 0/1 → 1/8 | note:c2 s:square pan:0 ]",
"[ 1/8 → 1/4 | note:c2 s:square pan:1 ]",
"[ 3/8 → 1/2 | note:c2 s:square pan:0 ]",
"[ 1/2 → 9/16 | note:d2 s:square ]",
"[ 11/16 → 3/4 | note:d2 s:square ]",
"[ 7/8 → 15/16 | note:d2 s:square ]",
"[ 1/1 → 9/8 | note:d2 s:square cutoff:800 ]",
"[ 11/8 → 3/2 | note:d2 s:square cutoff:800 ]",
"[ 3/2 → 25/16 | note:d2 s:square ]",
"[ 27/16 → 7/4 | note:d2 s:square ]",
"[ 15/8 → 31/16 | note:d2 s:square ]",
"[ 2/1 → 17/8 | note:c2 s:square pan:0 ]",
"[ 17/8 → 9/4 | note:c2 s:square pan:1 ]",
"[ 19/8 → 5/2 | note:c2 s:square pan:0 ]",
"[ 5/2 → 41/16 | note:d2 s:square ]",
"[ 43/16 → 11/4 | note:d2 s:square ]",
"[ 23/8 → 47/16 | note:d2 s:square ]",
"[ 3/1 → 25/8 | note:d2 s:square cutoff:800 ]",
"[ 27/8 → 7/2 | note:d2 s:square cutoff:800 ]",
"[ 7/2 → 57/16 | note:d2 s:square ]",
"[ 59/16 → 15/4 | note:d2 s:square ]",
"[ 31/8 → 63/16 | note:d2 s:square ]",
]
`;
exports[`runs examples > example "ply" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:bd ]",

View File

@ -0,0 +1,47 @@
import { useState, useEffect } from 'react';
import { loadFeaturedPatterns, loadPublicPatterns } from '@src/repl/util.mjs';
import { MiniRepl } from '@src/docs/MiniRepl';
import { PatternLabel } from '@src/repl/panel/PatternsTab';
function PatternList({ patterns }) {
return (
<div className="space-y-4">
{/* <MiniRepl tunes={patterns.map((pat) => pat.code.trim())} /> */}
{patterns.map((pat) => (
<div key={pat.id}>
<div className="flex justify-between not-prose pb-2">
<h2 className="text-lg">
<a href={`/?${pat.hash}`} target="_blank" className="underline">
<PatternLabel pattern={pat} />
</a>
</h2>
</div>
<MiniRepl tune={pat.code.trim()} maxHeight={300} />
</div>
))}
</div>
);
}
export function Oven() {
const [featuredPatterns, setFeaturedPatterns] = useState([]);
const [publicPatterns, setPublicPatterns] = useState([]);
useEffect(() => {
loadPublicPatterns().then(({ data: pats }) => {
console.log('pats', pats);
setPublicPatterns(pats);
});
loadFeaturedPatterns().then(({ data: pats }) => {
console.log('pats', pats);
setFeaturedPatterns(pats);
});
}, []);
return (
<div>
<h2 id="featured">Featured Patterns</h2>
<PatternList patterns={featuredPatterns} />
<h2 id="latest">Last Creations</h2>
<PatternList patterns={publicPatterns} />
</div>
);
}

View File

@ -58,6 +58,7 @@ export const SIDEBAR: Sidebar = {
{ text: 'What is Strudel?', link: 'workshop/getting-started' },
{ text: 'Showcase', link: 'intro/showcase' },
{ text: 'Blog', link: 'blog' },
{ text: 'Community Bakery', link: 'bakery' },
],
Workshop: [
// { text: 'Getting Started', link: 'workshop/getting-started' },

View File

@ -27,6 +27,7 @@ export function MiniRepl({
punchcardLabels = true,
claviature,
claviatureLabels,
maxHeight,
}) {
const code = tunes ? tunes[0] : tune;
const id = useMemo(() => s4(), []);
@ -154,7 +155,7 @@ export function MiniRepl({
)}
</div>
)}
<div className="overflow-auto relative p-1">
<div className="overflow-auto relative p-1" style={maxHeight ? { maxHeight: `${maxHeight}px` } : {}}>
<div
ref={(el) => {
if (!editorRef.current) {

View File

@ -0,0 +1,60 @@
---
import HeadCommon from '../components/HeadCommon.astro';
import Header from '../components/Header/Header.astro';
import LeftSidebar from '../components/LeftSidebar/LeftSidebar.astro';
import PageContent from '../components/PageContent/PageContent.astro';
import { getCollection } from 'astro:content';
import { compareDesc } from 'date-fns';
import { Oven as CommunityOven } from '../components/Oven/Oven.jsx';
import RightSidebar from '../components/RightSidebar/RightSidebar.astro';
const currentPage = Astro.url.pathname;
const posts = (await getCollection('blog')).sort((a, b) => compareDesc(a.data.date, b.data.date));
---
<html dir={'ltr'} lang={'en'} class="initial dark">
<head>
<HeadCommon />
<title>🌀 Strudel Community Bakery</title>
</head>
<body class="h-app-height text-gray-50 bg-background">
<div class="w-full h-full space-y-4 flex flex-col">
<header class="max-w-full fixed top-0 w-full z-[100]">
<Header currentPage={currentPage} />
</header>
<main class="relative pt-16">
<div class="h-full top-0 overflow-auto min-w-[300px] flex xl:justify-center pr-4 pl-4 md:pl-[300px] xl:pl-0">
<aside title="Site Navigation" class="w-[300px] px-6 left-0 hidden md:block fixed h-full">
<LeftSidebar currentPage={currentPage} />
</aside>
<PageContent>
<h1>Community Bakery</h1>
<p>
This page contains all the strudel patterns baked by the community. Add your own by clicking the "Share"
button in the REPL. Have fun, and please share some of what you create with the community.
</p>
<CommunityOven client:only />
</PageContent>
<aside class="fixed right-0 h-full overflow-auto pr-4 pl-0 pb-16 hidden xl:block" title="Table of Contents">
<RightSidebar
headings={[
{
depth: 1,
slug: 'featured',
text: 'Featured',
},
{
depth: 1,
slug: 'latest',
text: 'Last Creations',
},
]}
/>
</aside>
</div>
</main>
</div>
</body>
</html>