diff --git a/README-HACKLAB.md b/README-HACKLAB.md index 1bb17f64..14990708 100644 --- a/README-HACKLAB.md +++ b/README-HACKLAB.md @@ -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: · 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): +- Documentación / tutorial: +- TidalCycles, el original en Haskell: +- Algorave, la escena: +- Samples para empezar a jugar: + - Dirt-Samples (la librería clásica de Tidal): + - tidal-drum-machines (Roland TR-808/909 y muchas más): +- Para ver de qué va esto en directo: diff --git a/website/src/repl/components/TransportBar.jsx b/website/src/repl/components/TransportBar.jsx index cf8ad3a9..77f2225d 100644 --- a/website/src/repl/components/TransportBar.jsx +++ b/website/src/repl/components/TransportBar.jsx @@ -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(); }; diff --git a/website/src/repl/components/panel/ProjectsTab.jsx b/website/src/repl/components/panel/ProjectsTab.jsx index 05c76a66..b897db08 100644 --- a/website/src/repl/components/panel/ProjectsTab.jsx +++ b/website/src/repl/components/panel/ProjectsTab.jsx @@ -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) => { diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index 852c63f8..f41c9f8c 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -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);