diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index a02893b3..9bd26e02 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -22,7 +22,6 @@ import { setViewingPattern, createPatternID, userPattern, - examplePattern, getNextCloneID, } from '../settings.mjs'; import { Header } from './Header'; @@ -33,9 +32,12 @@ import { prebake } from './prebake.mjs'; import { getRandomTune, initCode, loadModules, shareCode, ReplContext } from './util.mjs'; import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon'; import './Repl.css'; +import { useExamplePatterns } from './useExamplePatterns'; const { code: randomTune, name } = getRandomTune(); +// + const { latestCode } = settingsMap.get(); let modulesLoading, presets, drawContext, clearCanvas, isIframe; @@ -47,10 +49,12 @@ if (typeof window !== 'undefined') { clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); isIframe = window.location !== window.parent.location; } + +let viewingPatternData = { id: '', code: null, collection: userPattern.source }; + export function Repl({ embedded = false }) { const isEmbedded = embedded || isIframe; const { panelPosition, isZen } = useSettings(); - const init = useCallback(() => { const drawTime = [-2, 2]; const drawContext = getDrawContext(); @@ -75,30 +79,29 @@ export function Repl({ embedded = false }) { onUpdateState: (state) => { setReplState({ ...state }); }, - afterEval: ({ code }) => { + afterEval: (all) => { + const { code } = all; setLatestCode(code); - const data = { code }; - let id = getViewingPattern(); window.location.hash = '#' + code2hash(code); - const examplePatternData = examplePattern.getPatternData(id); - const isExamplePattern = examplePatternData != null; + const data = { ...viewingPatternData, code }; + let id = getViewingPattern(); + const isExamplePattern = viewingPatternData.collection != userPattern.source; if (isExamplePattern) { - const codeHasChanged = code !== examplePatternData.code; + const codeHasChanged = code !== viewingPatternData.code; if (codeHasChanged) { // fork example id = getNextCloneID(id); setViewingPattern(id); - userPattern.update(id, data); + viewingPatternData = userPattern.update(id, data).data; } - setActivePattern(id); } else { id = id == null ? createPatternID() : id; - setActivePattern(id); setViewingPattern(id); - userPattern.update(id, data); + viewingPatternData = userPattern.update(id, data).data; } + setActivePattern(id); }, bgFill: false, }); @@ -172,12 +175,13 @@ export function Repl({ embedded = false }) { await prebake(); // declare default samples }; - const handleUpdate = async (id, code, reset = false) => { + const handleUpdate = async (id, data, reset = false) => { + viewingPatternData = data; if (reset) { await resetEditor(); } setViewingPattern(id); - editorRef.current.setCode(code); + editorRef.current.setCode(data.code); }; const handleEvaluate = () => { diff --git a/website/src/repl/panel/PatternsTab.jsx b/website/src/repl/panel/PatternsTab.jsx index 3276a16b..60ca4465 100644 --- a/website/src/repl/panel/PatternsTab.jsx +++ b/website/src/repl/panel/PatternsTab.jsx @@ -14,9 +14,9 @@ import { import { useMemo } from 'react'; -import * as tunes from '../tunes.mjs'; import { useStore } from '@nanostores/react'; import { getMetadata } from '../../metadata_parser'; +import { useExamplePatterns } from '../useExamplePatterns'; function classNames(...classes) { return classes.filter(Boolean).join(' '); @@ -90,27 +90,21 @@ function PatternButtons({ patterns, activePattern, onClick, viewingPattern, star } export function PatternsTab({ context }) { - const { userPatterns } = useSettings(); const activePattern = useActivePattern(); - - const featuredPatterns = useStore($featuredPatterns); - const publicPatterns = useStore($publicPatterns); - - // const otherPatterns = [ - // {source: 'Stock Examples', patterns: examplePatterns } - // ] - - const otherPatterns = useMemo(() => { - const pats = new Map(); - pats.set('Featured', featuredPatterns); - pats.set('Last Creations', publicPatterns); - pats.set('Stock Examples', examplePattern.getAll()); - return pats; - }, [featuredPatterns, publicPatterns]); - const viewingPattern = useViewingPattern(); - const updateCodeWindow = (id, code, reset = false) => { - context.handleUpdate(id, code, reset); + const { userPatterns } = useSettings(); + const examplePatterns = useExamplePatterns(); + const collections = examplePatterns.collections; + const examplesData = examplePatterns.patterns; + + const updateCodeWindow = (id, data, reset = false) => { + context.handleUpdate(id, data, reset); + // if (patternSource === userPattern.source) { + + // } else { + // const source = otherPatterns.get(patternSource); + // const data = source[id]; + // } }; const isUserPattern = userPatterns[viewingPattern] != null; @@ -151,7 +145,7 @@ export function PatternsTab({ context }) { className="hover:opacity-50" onClick={() => { const { id, data } = userPattern.delete(viewingPattern); - updateCodeWindow(id, data.code); + updateCodeWindow(id, { ...data, collection: userPattern.source }); }} title="Delete" > @@ -162,7 +156,7 @@ export function PatternsTab({ context }) { )} updateCodeWindow(id, userPatterns[id]?.code, false)} + onClick={(id) => updateCodeWindow(id, { ...userPatterns[id], collection: userPattern.source }, false)} patterns={userPatterns} started={context.started} activePattern={activePattern} @@ -202,15 +196,15 @@ export function PatternsTab({ context }) { - {Array.from(otherPatterns.keys()).map((key) => { - const patterns = otherPatterns.get(key); + {Array.from(collections.keys()).map((collection) => { + const patterns = collections.get(collection); return ( -
-

{key}

+
+

{collection}

updateCodeWindow(id, patterns[id]?.code, true)} + onClick={(id) => updateCodeWindow(id, { ...patterns[id], collection }, true)} started={context.started} patterns={patterns} activePattern={activePattern} diff --git a/website/src/repl/useExamplePatterns.jsx b/website/src/repl/useExamplePatterns.jsx new file mode 100644 index 00000000..f51d9091 --- /dev/null +++ b/website/src/repl/useExamplePatterns.jsx @@ -0,0 +1,34 @@ +import { examplePattern, $featuredPatterns, $publicPatterns } from '../settings.mjs'; +import { useStore } from '@nanostores/react'; +import { useCallback, useMemo } from 'react'; + +export const useExamplePatterns = () => { + const featuredPatterns = useStore($featuredPatterns); + const publicPatterns = useStore($publicPatterns); + const collections = useMemo(() => { + const pats = new Map(); + pats.set('Featured', featuredPatterns); + pats.set('Last Creations', publicPatterns); + pats.set(examplePattern.source, examplePattern.getAll()); + return pats; + }, [featuredPatterns, publicPatterns]); + + const patterns = useMemo(() => { + const allPatterns = Object.assign({}, ...collections.values()); + return allPatterns; + }, [collections]); + + // const examplePatterns = examplePattern.getAll(); + + // const collections = new Map(); + // collections.set('Featured', featuredPatterns); + // collections.set('Last Creations', publicPatterns); + // collections.set(examplePattern.source, examplePatterns); + // const patterns = { + // ...examplePatterns, + // ...publicPatterns, + // ...examplePatterns, + // }; + + return { patterns, collections }; +}; diff --git a/website/src/settings.mjs b/website/src/settings.mjs index 868a2ba8..9f2da640 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -46,9 +46,22 @@ export function getViewingPattern() { export function useViewingPattern() { return useStore($viewingPattern); } + +// const $viewingCollection = persistentAtom('viewingCollection', '', { listen: false }); +// export function setViewingCollection(key) { +// $viewingCollection.set(key); +// } +// export function getViewingCollection() { +// return $viewingCollection.get(); +// } + +// export function useViewingCollection() { +// return useStore($viewingCollection); +// } // active pattern is separate, because it shouldn't sync state across tabs // reason: https://github.com/tidalcycles/strudel/issues/857 const $activePattern = persistentAtom('activePattern', '', { listen: false }); + export function setActivePattern(key) { $activePattern.set(key); } @@ -135,6 +148,7 @@ export const getNextCloneID = (id) => { const examplePatterns = Object.fromEntries(Object.entries(tunes).map(([key, code], i) => [i, { id: i, code }])); export const examplePattern = { + source: 'Stock Examples', getAll() { return examplePatterns; }, @@ -149,6 +163,8 @@ export const examplePattern = { // break export const userPattern = { + source: 'user', + collection: 'user', getAll() { const patterns = JSON.parse(settingsMap.get().userPatterns); return patterns; @@ -164,22 +180,20 @@ export const userPattern = { create() { const newID = createPatternID(); const code = defaultCode; - - // const meta = getMetadata - const data = { code, created_at: Date.now(), id: newID }; - this.update(newID, data); - return { id: newID, data }; + const data = { code, created_at: Date.now(), id: newID, collection: this.collection }; + return this.update(newID, data); }, update(id, data) { const userPatterns = this.getAll(); + data = { ...data, id, collection: this.collection }; setUserPatterns({ ...userPatterns, [id]: data }); + return { id, data }; }, duplicate(id) { const examplePatternData = examplePattern.getPatternData(id); const data = examplePatternData != null ? examplePatternData : this.getPatternData(id); const newID = getNextCloneID(id); - this.update(newID, data); - return { id: newID, data }; + return this.update(newID, data); }, clearAll() { if (!confirm(`This will delete all your patterns. Are you really sure?`)) { @@ -193,7 +207,8 @@ export const userPattern = { } // setViewingPattern(null); setActivePattern(null); - return { id: null, data: { code: defaultCode } }; + + return { id: null, data: { code: defaultCode, id: null, collection: this.collection } }; }, delete(id) { const userPatterns = this.getAll();