Transporte: al cargar un track el loop arranca desde cero
Some checks are pending
Build and Deploy to warm (strudel.cc) / build (push) Waiting to run
Strudel tests / build (20) (push) Waiting to run

Cargar un proyecto (pestaña Projects o anterior/siguiente de la barra) deja
pendiente un reinicio que se aplica en la siguiente evaluacion: el tema nuevo
empieza en el ciclo 0 aunque el reloj del scheduler siga corriendo. Editar y
actualizar en vivo no reinicia nada. Funciona con Cyclist y NeoCyclist (ambos
tienen setCycle).

README-HACKLAB mas breve y con enlaces: upstream, TidalCycles, algorave,
librerias de samples y video introductorio.
This commit is contained in:
hacklab 2026-07-17 02:00:08 +02:00
parent b6dbe97a62
commit 921647020c
4 changed files with 53 additions and 42 deletions

View file

@ -1,81 +1,81 @@
# Strudel · fork del hacklab
Fork de [Strudel](https://codeberg.org/uzu/strudel) (live coding de música en
el navegador, port de TidalCycles a JavaScript) con mejoras de usabilidad
pensadas para pinchar en directo y para gente que empieza.
el navegador, port de TidalCycles a JavaScript) con mejoras de usabilidad para
pinchar en directo. Misma licencia que upstream: AGPL-3.0 (ver `LICENSE`).
Upstream: <https://codeberg.org/uzu/strudel> · Licencia: AGPL-3.0 (la misma,
ver `LICENSE`). Gracias al equipo de Strudel/TidalCycles por la base ♥
## Qué añade
## Qué añade este fork
- **Interfaz multiidioma** (`website/src/i18n.mjs`): castellano, inglés,
francés, alemán, italiano, portugués y más. Se cambia desde el selector del
header y se guarda en los ajustes.
- **Pestaña Projects**: gestor de proyectos locales (localStorage) con
carpetas, buscador y una librería de tracks de fábrica lista para pinchar
(bases techno/acid originales, de la casa).
- **Barra de transporte** estilo reproductor bajo el editor: anterior /
play-pausa / siguiente, tiempo transcurrido y **barra de progreso
arrastrable** (seek real: se añadió `Cyclist.setCycle()` en
`packages/core`).
- **Pestaña Guía**: chuleta de síntesis y sonidos para tener a mano mientras
se pincha.
- **Packs de temas** para el editor (`neon-madrid` + `hacklab-pack`: 5 neón
sobre negro y 5 estándar).
- **Lanzador de escritorio** (`strudel-launcher.sh`): sirve la build estática
en local y abre el navegador. Ideal para un icono clicable.
- Interfaz multiidioma (castellano, inglés, francés, alemán, italiano,
portugués y más). Selector en el header.
- Pestaña Projects: proyectos locales con carpetas, buscador y una librería
de tracks de fábrica para empezar a pinchar.
- Barra de transporte bajo el editor: anterior / play-pausa / siguiente,
tiempo y barra de progreso arrastrable (seek real sobre el reloj del
scheduler). Al cargar un track el loop arranca desde cero; editar y
actualizar en vivo no reinicia nada.
- Pestaña Guía: chuleta de síntesis y sonidos.
- Packs de temas del editor: neon-madrid y hacklab-pack.
- Lanzador de escritorio: `strudel-launcher.sh`.
## Instalación (Linux)
Requisitos: `git`, `node` >= 20 y `pnpm` (`corepack enable` lo activa).
Requisitos: `git`, `node` >= 20 y `pnpm` (`corepack enable`).
```bash
git clone https://gitea.laenre.net/hacklab/strudel.git
cd strudel
pnpm i # instala las dependencias del monorepo
pnpm i
```
### Modo desarrollo (recarga en vivo)
Desarrollo (recarga en vivo):
```bash
pnpm dev # abre http://localhost:4321
pnpm dev # http://localhost:4321
```
### Modo "aparato" (build estática + lanzador)
Uso diario (build estática + lanzador):
```bash
pnpm build # genera la web estática
./strudel-launcher.sh # la sirve en http://127.0.0.1:3009 y abre el navegador
pnpm build
./strudel-launcher.sh # sirve http://127.0.0.1:3009 y abre el navegador
```
Para el icono de escritorio, crea `~/.local/share/applications/strudel.desktop`:
Icono de escritorio: `~/.local/share/applications/strudel.desktop`
```ini
[Desktop Entry]
Type=Application
Name=Strudel
Comment=Live coding de música
Comment=Live coding de musica
Exec=/ruta/a/strudel/strudel-launcher.sh
Icon=audio-x-generic
Terminal=false
Categories=AudioVideo;Audio;
```
## Añadir tus propios packs de proyectos
## Packs de proyectos propios
La librería de fábrica vive en
La librería de fábrica está en
`website/src/repl/components/panel/default-projects.mjs`. Para un pack
personal sin tocar el repo, crea un módulo local con el mismo formato
`{name, folder, code}` y concaténalo en `DEFAULT_PROJECTS` (los packs
personales están en `.gitignore`). También puedes crear y organizar proyectos
directamente desde la pestaña Projects de la interfaz.
personal crea un módulo con el mismo formato `{name, folder, code}` y
concaténalo en `DEFAULT_PROJECTS` (los packs personales están en
`.gitignore`). También se pueden crear proyectos desde la propia pestaña.
## Mantenerse al día con upstream
El remoto `upstream` apunta al Strudel original:
## Actualizar desde upstream
```bash
git fetch upstream
git merge upstream/main # y resolver conflictos si los hay
git merge upstream/main
```
## Enlaces
- Strudel (upstream): <https://codeberg.org/uzu/strudel>
- Documentación / tutorial: <https://strudel.cc/learn>
- TidalCycles, el original en Haskell: <https://tidalcycles.org>
- Algorave, la escena: <https://algorave.com>
- Samples para empezar a jugar:
- Dirt-Samples (la librería clásica de Tidal): <https://github.com/tidalcycles/Dirt-Samples>
- tidal-drum-machines (Roland TR-808/909 y muchas más): <https://github.com/ritchse/tidal-drum-machines>
- Para ver de qué va esto en directo: <https://www.youtube.com/watch?v=iu5rnQkfO6M>

View file

@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
import { useEffect, useRef, useState } from 'react';
import cx from '@src/cx.mjs';
import { useT } from '@src/i18n.mjs';
import { $projects, $playing, parseMeta, fmt } from './panel/ProjectsTab.jsx';
import { $projects, $playing, $resetPending, parseMeta, fmt } from './panel/ProjectsTab.jsx';
// Barra de transporte estilo Spotify, FIJA debajo del editor de código:
// · fila superior: controles CENTRADOS (anterior / play-pausa / siguiente)
@ -98,6 +98,7 @@ export function TransportBar({ context }) {
const p = displayed[idx];
e.setCode(p.code);
$playing.set(p.name);
$resetPending.set(true); // el track nuevo arranca desde el ciclo 0
e.evaluate();
};

View file

@ -64,6 +64,9 @@ const migrate = (list) => {
export const $projects = atom(migrate(load()));
// nombre del sample cargado en el editor (compartido con la barra de transporte)
export const $playing = atom(null);
// true = se acaba de cargar un track: la proxima evaluacion arranca el loop
// desde el ciclo 0 (editar y actualizar en vivo no pasa por aqui)
export const $resetPending = atom(false);
const persist = (list) => {
try {
@ -261,6 +264,7 @@ export function ProjectsTab({ context }) {
ed.setCode(p.code);
setPreviewed(p);
$playing.set(p.name);
$resetPending.set(true);
};
const del = (p) => {

View file

@ -35,6 +35,7 @@ import { audioEngineTargets } from '../settings.mjs';
import { useStore } from '@nanostores/react';
import { prebake } from './prebake.mjs';
import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs';
import { $resetPending } from './components/panel/ProjectsTab.jsx';
import './Repl.css';
import { setInterval, clearInterval } from 'worker-timers';
import { getMetadata } from '../metadata_parser';
@ -104,6 +105,11 @@ export function useReplContext() {
beforeEval: () => audioReady,
afterEval: (all) => {
const { code } = all;
// track recien cargado desde Projects: el loop arranca desde cero
if ($resetPending.get()) {
$resetPending.set(false);
editorRef.current?.repl?.scheduler?.setCycle?.(0);
}
//post to iframe parent (like Udels) if it exists...
window.parent?.postMessage(code);