Compare commits

..

2 Commits

Author SHA1 Message Date
Jade (Rose) Rowland 5fbc073f9f fix sample prefetch 2026-05-25 14:05:55 +02:00
Jade (Rose) Rowland 262fe3b516 fix ir 2026-05-23 18:47:24 +02:00
6 changed files with 31 additions and 92 deletions
+1 -2
View File
@@ -11,7 +11,6 @@ import {
keymap, keymap,
lineNumbers, lineNumbers,
} from '@codemirror/view'; } from '@codemirror/view';
import {repeatCharKeymap} from './repeatcharacter.mjs';
import { persistentAtom } from '@nanostores/persistent'; import { persistentAtom } from '@nanostores/persistent';
import { logger, registerControl, repl } from '@strudel/core'; import { logger, registerControl, repl } from '@strudel/core';
import { cleanupDraw, cleanupDrawContext, Drawer } from '@strudel/draw'; import { cleanupDraw, cleanupDrawContext, Drawer } from '@strudel/draw';
@@ -112,7 +111,6 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
syntaxHighlighting(defaultHighlightStyle), syntaxHighlighting(defaultHighlightStyle),
EditorView.updateListener.of((v) => onChange(v)), EditorView.updateListener.of((v) => onChange(v)),
drawSelection({ cursorBlinkRate: 0 }), drawSelection({ cursorBlinkRate: 0 }),
repeatCharKeymap,
Prec.highest( Prec.highest(
keymap.of([ keymap.of([
{ {
@@ -217,6 +215,7 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
}, },
}; };
}), }),
/* { /* {
key: 'Ctrl-Shift-.', key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()), run: () => (onPanic ? onPanic() : onStop?.()),
-55
View File
@@ -1,55 +0,0 @@
import { Compartment } from "@codemirror/state";
import { keymap } from "@codemirror/view";
const repeatMode = new Compartment();
function repeatPreviousChar(times) {
return ({ state, dispatch }) => {
const { from, empty } = state.selection.main;
if (!empty || from === 0) return false;
const prevChar = state.doc.sliceString(from - 1, from);
const text = Array(times).fill(prevChar).join(" ");
dispatch(state.update({
changes: {
from,
insert: " " + text
}
}));
return true;
};
}
function exitRepeatMode(view) {
view.dispatch({
effects: repeatMode.reconfigure([])
});
}
const digitBindings = Array.from({ length: 9 }, (_, i) => ({
key: String(i + 1),
run(view) {
repeatPreviousChar(i + 1)(view);
exitRepeatMode(view);
return true;
}
}));
export const repeatCharKeymap = [
repeatMode.of([]),
keymap.of([
{
key: "Alt-r",
run(view) {
view.dispatch({
effects: repeatMode.reconfigure(keymap.of(digitBindings))
});
return true;
}
}
])
];
+24 -15
View File
@@ -9,7 +9,7 @@ import {
onceEnded, onceEnded,
releaseAudioNode, releaseAudioNode,
} from './helpers.mjs'; } from './helpers.mjs';
import { logger } from './logger.mjs'; import { errorLogger, logger } from './logger.mjs';
const bufferCache = {}; // string: Promise<ArrayBuffer> const bufferCache = {}; // string: Promise<ArrayBuffer>
const loadCache = {}; // string: Promise<ArrayBuffer> const loadCache = {}; // string: Promise<ArrayBuffer>
@@ -228,22 +228,31 @@ export async function fetchSampleMap(url) {
} }
url = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`; url = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`;
} }
if (typeof fetch !== 'function') {
// not a browser
return;
}
const base = getBaseURL(url); const base = getBaseURL(url);
if (typeof fetch === 'undefined') { if (typeof fetch !== 'function') {
// skip fetch when in node / testing errorLogger(new Error(`fetch is not supported in this environment. Skipping map load for: ${url}`), 'sampler.mjs')
return; return [{}, base || ''];
} }
const json = await fetch(url)
.then((res) => res.json())
.catch((error) => { try {
console.error(error); const res = await fetch(url);
throw new Error(`error loading "${url}"`); if (!res.ok) {
}); throw new Error(`HTTP error! status: ${res.status}`);
return [json, json._base || base]; }
const json = await res.json();
return [json, json._base || base];
} catch (error) {
// Catching the failure here prevents it from bubbling up and crashing upstream
errorLogger(new Error(`Failed to fetch or parse sample map at "${url}":`), "sampler.mjs");
// Return a safe fallback structure so destructuring (e.g., [json, base]) won't break
return [{}, base || ''];
}
} }
/** /**
+2 -2
View File
@@ -963,9 +963,9 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
let url; let url;
let sample = getSound(ir); let sample = getSound(ir);
if (Array.isArray(sample)) { if (Array.isArray(sample)) {
url = sample.data.samples[i % sample.data.samples.length]; url = sample.data.samples[i % sample.data.samples.length].url;
} else if (typeof sample === 'object') { } else if (typeof sample === 'object') {
url = Object.values(sample.data.samples).flat()[i % Object.values(sample.data.samples).length]; url = Object.values(sample.data.samples).flat()[i % Object.values(sample.data.samples).length].url;
} }
roomIR = await loadBuffer(url, ac, ir, 0); roomIR = await loadBuffer(url, ac, ir, 0);
} }
@@ -9,7 +9,9 @@ import {
userPattern, userPattern,
} from '../../../user_pattern_utils.mjs'; } from '../../../user_pattern_utils.mjs';
import { useMemo, useRef } from 'react'; import { useMemo, useRef } from 'react';
import { getMetadata } from '../../../metadata_parser.js';
import { useExamplePatterns } from '../../useExamplePatterns.jsx'; import { useExamplePatterns } from '../../useExamplePatterns.jsx';
import { parseJSON, isUdels } from '../../util.mjs';
import { useSettings } from '../../../settings.mjs'; import { useSettings } from '../../../settings.mjs';
import { ActionButton } from '../button/action-button.jsx'; import { ActionButton } from '../button/action-button.jsx';
import { Pagination } from '../pagination/Pagination.jsx'; import { Pagination } from '../pagination/Pagination.jsx';
@@ -18,13 +20,8 @@ import { useDebounce } from '../usedebounce.jsx';
import cx from '@src/cx.mjs'; import cx from '@src/cx.mjs';
import { Textbox } from '@src/repl/components/panel/SettingsTab.jsx'; import { Textbox } from '@src/repl/components/panel/SettingsTab.jsx';
const PATTERN_SORT = {
"NEWEST": "most recent",
"A-Z": "A-Z",
}
export function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) { export function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) {
const meta = pattern.meta const meta = useMemo(() => getMetadata(pattern.code), [pattern]);
let title = meta.title; let title = meta.title;
if (title == null) { if (title == null) {
@@ -61,10 +58,6 @@ function PatternButtons({ patterns, activePattern, onClick, started }) {
return ( return (
<div className="p-2"> <div className="p-2">
{Object.values(patterns) {Object.values(patterns)
.sort((a, b) => {
return (b.meta.title ?? "").localeCompare(a.meta.title ?? "")
})
.reverse() .reverse()
.map((pattern) => { .map((pattern) => {
const id = pattern.id; const id = pattern.id;
@@ -100,7 +93,7 @@ export function PatternsTab({ context }) {
} }
return Object.fromEntries( return Object.fromEntries(
Object.entries(userPatterns).filter(([_key, pattern]) => { Object.entries(userPatterns).filter(([_key, pattern]) => {
const meta = pattern.meta; const meta = getMetadata(pattern.code);
// Search for specific meta keys // Search for specific meta keys
const searchLowercaseTrimmed = search.trim().toLowerCase(); const searchLowercaseTrimmed = search.trim().toLowerCase();
-7
View File
@@ -3,7 +3,6 @@ import { useStore } from '@nanostores/react';
import { register } from '@strudel/core'; import { register } from '@strudel/core';
import { isUdels } from './repl/util.mjs'; import { isUdels } from './repl/util.mjs';
import { computed } from 'nanostores'; import { computed } from 'nanostores';
import { getMetadata } from './metadata_parser';
export const audioEngineTargets = { export const audioEngineTargets = {
webaudio: 'webaudio', webaudio: 'webaudio',
@@ -19,11 +18,6 @@ export const soundFilterType = {
ALL: 'all', ALL: 'all',
}; };
export const PATTERN_SORT = {
"NEWEST": "most recent",
"A-Z": "A-Z",
}
export const defaultSettings = { export const defaultSettings = {
activeFooter: 'intro', activeFooter: 'intro',
keybindings: 'codemirror', keybindings: 'codemirror',
@@ -77,7 +71,6 @@ export const $settings = computed(settingsMap, (state) => {
Object.keys(userPatterns).forEach((key) => { Object.keys(userPatterns).forEach((key) => {
const data = userPatterns[key]; const data = userPatterns[key];
data.id = data.id ?? key; data.id = data.id ?? key;
data.meta = getMetadata(data.code)
userPatterns[key] = data; userPatterns[key] = data;
}); });
return { return {