mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-25 04:28:30 +00:00
Merge pull request #1203 from tidalcycles/console-colors
colorize console + tweak header
This commit is contained in:
commit
2e72c7f4ed
@ -28,11 +28,10 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
|
|||||||
<div class="flex overflow-visible items-center grow" style="overflow:visible">
|
<div class="flex overflow-visible items-center grow" style="overflow:visible">
|
||||||
<div class="flex items-center text-2xl space-x-2">
|
<div class="flex items-center text-2xl space-x-2">
|
||||||
<h1 class="font-bold flex space-x-2 items-baseline text-xl">
|
<h1 class="font-bold flex space-x-2 items-baseline text-xl">
|
||||||
<span>🌀</span>
|
<span class="block rotate-90 text-blue-500">꩜</span>
|
||||||
<div class="flex space-x-2 items-baseline">
|
<div class="flex space-x-2 items-baseline">
|
||||||
<span class="">strudel</span>
|
<span class="">strudel</span>
|
||||||
<span class="text-sm">DOCS</span>
|
<span class="text-sm font-medium">DOCS</span>
|
||||||
<a href={`${baseNoTrailing}/`} class="text-sm opacity-25">REPL</a>
|
|
||||||
</div>
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import AcademicCapIcon from '@heroicons/react/20/solid/AcademicCapIcon';
|
|
||||||
import ArrowPathIcon from '@heroicons/react/20/solid/ArrowPathIcon';
|
|
||||||
import LinkIcon from '@heroicons/react/20/solid/LinkIcon';
|
|
||||||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||||
import SparklesIcon from '@heroicons/react/20/solid/SparklesIcon';
|
|
||||||
import StopCircleIcon from '@heroicons/react/20/solid/StopCircleIcon';
|
import StopCircleIcon from '@heroicons/react/20/solid/StopCircleIcon';
|
||||||
import cx from '@src/cx.mjs';
|
import cx from '@src/cx.mjs';
|
||||||
import { useSettings, setIsZen } from '../../settings.mjs';
|
import { useSettings, setIsZen } from '../../settings.mjs';
|
||||||
@ -21,7 +17,7 @@ export function Header({ context, embedded = false }) {
|
|||||||
<header
|
<header
|
||||||
id="header"
|
id="header"
|
||||||
className={cx(
|
className={cx(
|
||||||
'flex-none text-black z-[100] text-lg select-none h-14',
|
'flex-none text-black z-[100] text-lg select-none h-20 md:h-14',
|
||||||
!isZen && !isEmbedded && 'bg-lineHighlight',
|
!isZen && !isEmbedded && 'bg-lineHighlight',
|
||||||
isZen ? 'h-12 w-8 fixed top-0 left-0' : 'sticky top-0 w-full py-1 justify-between',
|
isZen ? 'h-12 w-8 fixed top-0 left-0' : 'sticky top-0 w-full py-1 justify-between',
|
||||||
isEmbedded ? 'flex' : 'md:flex',
|
isEmbedded ? 'flex' : 'md:flex',
|
||||||
@ -41,7 +37,7 @@ export function Header({ context, embedded = false }) {
|
|||||||
className={cx(
|
className={cx(
|
||||||
'mt-[1px]',
|
'mt-[1px]',
|
||||||
started && !isCSSAnimationDisabled && 'animate-spin',
|
started && !isCSSAnimationDisabled && 'animate-spin',
|
||||||
'cursor-pointer',
|
'cursor-pointer text-blue-500',
|
||||||
isZen && 'fixed top-2 right-4',
|
isZen && 'fixed top-2 right-4',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -50,14 +46,14 @@ export function Header({ context, embedded = false }) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🌀
|
<span className="block rotate-90">꩜</span>
|
||||||
</div>
|
</div>
|
||||||
{!isZen && (
|
{!isZen && (
|
||||||
<div className={cx(started && !isCSSAnimationDisabled && 'animate-pulse', 'space-x-2')}>
|
<div className="space-x-2">
|
||||||
<span className="">strudel</span>
|
<span className="">strudel</span>
|
||||||
<span className="text-sm">REPL</span>
|
<span className="text-sm font-medium">REPL</span>
|
||||||
{!isEmbedded && (
|
{!isEmbedded && isButtonRowHidden && (
|
||||||
<a href={`${baseNoTrailing}/learn`} className="text-sm opacity-25">
|
<a href={`${baseNoTrailing}/learn`} className="text-sm opacity-25 font-medium">
|
||||||
DOCS
|
DOCS
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
@ -66,7 +62,7 @@ export function Header({ context, embedded = false }) {
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
{!isZen && !isButtonRowHidden && (
|
{!isZen && !isButtonRowHidden && (
|
||||||
<div className="flex max-w-full overflow-auto text-foreground">
|
<div className="flex max-w-full overflow-auto text-foreground px-1 md:px-2">
|
||||||
<button
|
<button
|
||||||
onClick={handleTogglePlay}
|
onClick={handleTogglePlay}
|
||||||
title={started ? 'stop' : 'play'}
|
title={started ? 'stop' : 'play'}
|
||||||
@ -77,7 +73,7 @@ export function Header({ context, embedded = false }) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!pending ? (
|
{!pending ? (
|
||||||
<span className={cx('flex items-center space-x-1', isEmbedded ? '' : 'w-16')}>
|
<span className={cx('flex items-center space-x-2')}>
|
||||||
{started ? <StopCircleIcon className="w-6 h-6" /> : <PlayCircleIcon className="w-6 h-6" />}
|
{started ? <StopCircleIcon className="w-6 h-6" /> : <PlayCircleIcon className="w-6 h-6" />}
|
||||||
{!isEmbedded && <span>{started ? 'stop' : 'play'}</span>}
|
{!isEmbedded && <span>{started ? 'stop' : 'play'}</span>}
|
||||||
</span>
|
</span>
|
||||||
@ -94,8 +90,6 @@ export function Header({ context, embedded = false }) {
|
|||||||
!isDirty || !activeCode ? 'opacity-50' : 'hover:opacity-50',
|
!isDirty || !activeCode ? 'opacity-50' : 'hover:opacity-50',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* <CommandLineIcon className="w-6 h-6" /> */}
|
|
||||||
<ArrowPathIcon className="w-6 h-6" />
|
|
||||||
{!isEmbedded && <span>update</span>}
|
{!isEmbedded && <span>update</span>}
|
||||||
</button>
|
</button>
|
||||||
{!isEmbedded && (
|
{!isEmbedded && (
|
||||||
@ -104,7 +98,6 @@ export function Header({ context, embedded = false }) {
|
|||||||
className="hover:opacity-50 p-2 flex items-center space-x-1"
|
className="hover:opacity-50 p-2 flex items-center space-x-1"
|
||||||
onClick={handleShuffle}
|
onClick={handleShuffle}
|
||||||
>
|
>
|
||||||
<SparklesIcon className="w-6 h-6" />
|
|
||||||
<span> shuffle</span>
|
<span> shuffle</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@ -117,7 +110,6 @@ export function Header({ context, embedded = false }) {
|
|||||||
)}
|
)}
|
||||||
onClick={handleShare}
|
onClick={handleShare}
|
||||||
>
|
>
|
||||||
<LinkIcon className="w-6 h-6" />
|
|
||||||
<span>share</span>
|
<span>share</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@ -127,7 +119,6 @@ export function Header({ context, embedded = false }) {
|
|||||||
href={`${baseNoTrailing}/workshop/getting-started/`}
|
href={`${baseNoTrailing}/workshop/getting-started/`}
|
||||||
className={cx('hover:opacity-50 flex items-center space-x-1', !isEmbedded ? 'p-2' : 'px-2')}
|
className={cx('hover:opacity-50 flex items-center space-x-1', !isEmbedded ? 'p-2' : 'px-2')}
|
||||||
>
|
>
|
||||||
<AcademicCapIcon className="w-6 h-6" />
|
|
||||||
<span>learn</span>
|
<span>learn</span>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,18 +1,48 @@
|
|||||||
|
import { logger } from '@strudel/core';
|
||||||
|
import useEvent from '@src/useEvent.mjs';
|
||||||
import cx from '@src/cx.mjs';
|
import cx from '@src/cx.mjs';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { useSettings } from '../../../settings.mjs';
|
||||||
|
|
||||||
export function ConsoleTab({ log }) {
|
export function ConsoleTab() {
|
||||||
|
const [log, setLog] = useState([]);
|
||||||
|
const { fontFamily, fontSize } = useSettings();
|
||||||
|
useLogger(
|
||||||
|
useCallback((e) => {
|
||||||
|
const { message, type, data } = e.detail;
|
||||||
|
setLog((l) => {
|
||||||
|
const lastLog = l.length ? l[l.length - 1] : undefined;
|
||||||
|
const id = nanoid(12);
|
||||||
|
// if (type === 'loaded-sample' && lastLog.type === 'load-sample' && lastLog.url === data.url) {
|
||||||
|
if (type === 'loaded-sample') {
|
||||||
|
// const loadIndex = l.length - 1;
|
||||||
|
const loadIndex = l.findIndex(({ data: { url }, type }) => type === 'load-sample' && url === data.url);
|
||||||
|
l[loadIndex] = { message, type, id, data };
|
||||||
|
} else if (lastLog && lastLog.message === message) {
|
||||||
|
l = l.slice(0, -1).concat([{ message, type, count: (lastLog.count ?? 1) + 1, id, data }]);
|
||||||
|
} else {
|
||||||
|
l = l.concat([{ message, type, id, data }]);
|
||||||
|
}
|
||||||
|
return l.slice(-20);
|
||||||
|
});
|
||||||
|
}, []),
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div id="console-tab" className="break-all px-4 dark:text-white text-stone-900 text-sm py-2">
|
<div
|
||||||
<pre aria-hidden="true">{`███████╗████████╗██████╗ ██╗ ██╗██████╗ ███████╗██╗
|
id="console-tab"
|
||||||
██╔════╝╚══██╔══╝██╔══██╗██║ ██║██╔══██╗██╔════╝██║
|
className="break-all px-4 dark:text-white text-stone-900 text-sm py-2 space-y-1"
|
||||||
███████╗ ██║ ██████╔╝██║ ██║██║ ██║█████╗ ██║
|
style={{ fontFamily, fontSize }}
|
||||||
╚════██║ ██║ ██╔══██╗██║ ██║██║ ██║██╔══╝ ██║
|
>
|
||||||
███████║ ██║ ██║ ██║╚██████╔╝██████╔╝███████╗███████╗
|
|
||||||
╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝`}</pre>
|
|
||||||
{log.map((l, i) => {
|
{log.map((l, i) => {
|
||||||
const message = linkify(l.message);
|
const message = linkify(l.message);
|
||||||
|
const color = l.data?.hap?.value?.color;
|
||||||
return (
|
return (
|
||||||
<div key={l.id} className={cx(l.type === 'error' && 'text-red-500', l.type === 'highlight' && 'underline')}>
|
<div
|
||||||
|
key={l.id}
|
||||||
|
className={cx(l.type === 'error' && 'text-red-500', l.type === 'highlight' && 'underline')}
|
||||||
|
style={color ? { color } : {}}
|
||||||
|
>
|
||||||
<span dangerouslySetInnerHTML={{ __html: message }} />
|
<span dangerouslySetInnerHTML={{ __html: message }} />
|
||||||
{l.count ? ` (${l.count})` : ''}
|
{l.count ? ` (${l.count})` : ''}
|
||||||
</div>
|
</div>
|
||||||
@ -42,3 +72,7 @@ function linkify(inputText) {
|
|||||||
|
|
||||||
return replacedText;
|
return replacedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useLogger(onTrigger) {
|
||||||
|
useEvent(logger.key, onTrigger);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import { logger } from '@strudel/core';
|
|
||||||
import useEvent from '@src/useEvent.mjs';
|
|
||||||
import cx from '@src/cx.mjs';
|
import cx from '@src/cx.mjs';
|
||||||
import { nanoid } from 'nanoid';
|
|
||||||
import { useCallback, useState } from 'react';
|
|
||||||
import { setPanelPinned, setActiveFooter as setTab, setIsPanelOpened, useSettings } from '../../../settings.mjs';
|
import { setPanelPinned, setActiveFooter as setTab, setIsPanelOpened, useSettings } from '../../../settings.mjs';
|
||||||
import { ConsoleTab } from './ConsoleTab';
|
import { ConsoleTab } from './ConsoleTab';
|
||||||
import { FilesTab } from './FilesTab';
|
import { FilesTab } from './FilesTab';
|
||||||
@ -119,33 +115,11 @@ function PanelNav({ children, className, settings, ...props }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function PanelContent({ context, tab }) {
|
function PanelContent({ context, tab }) {
|
||||||
const [log, setLog] = useState([]);
|
|
||||||
useLogger(
|
|
||||||
useCallback((e) => {
|
|
||||||
const { message, type, data } = e.detail;
|
|
||||||
setLog((l) => {
|
|
||||||
const lastLog = l.length ? l[l.length - 1] : undefined;
|
|
||||||
const id = nanoid(12);
|
|
||||||
// if (type === 'loaded-sample' && lastLog.type === 'load-sample' && lastLog.url === data.url) {
|
|
||||||
if (type === 'loaded-sample') {
|
|
||||||
// const loadIndex = l.length - 1;
|
|
||||||
const loadIndex = l.findIndex(({ data: { url }, type }) => type === 'load-sample' && url === data.url);
|
|
||||||
l[loadIndex] = { message, type, id, data };
|
|
||||||
} else if (lastLog && lastLog.message === message) {
|
|
||||||
l = l.slice(0, -1).concat([{ message, type, count: (lastLog.count ?? 1) + 1, id, data }]);
|
|
||||||
} else {
|
|
||||||
l = l.concat([{ message, type, id, data }]);
|
|
||||||
}
|
|
||||||
return l.slice(-20);
|
|
||||||
});
|
|
||||||
}, []),
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
case tabNames.patterns:
|
case tabNames.patterns:
|
||||||
return <PatternsTab context={context} />;
|
return <PatternsTab context={context} />;
|
||||||
case tabNames.console:
|
case tabNames.console:
|
||||||
return <ConsoleTab log={log} />;
|
return <ConsoleTab />;
|
||||||
case tabNames.sounds:
|
case tabNames.sounds:
|
||||||
return <SoundsTab />;
|
return <SoundsTab />;
|
||||||
case tabNames.reference:
|
case tabNames.reference:
|
||||||
@ -236,7 +210,3 @@ function CloseButton({ onClick }) {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function useLogger(onTrigger) {
|
|
||||||
useEvent(logger.key, onTrigger);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,9 +6,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
|
|||||||
export function WelcomeTab({ context }) {
|
export function WelcomeTab({ context }) {
|
||||||
return (
|
return (
|
||||||
<div className="prose dark:prose-invert min-w-full pt-2 font-sans pb-8 px-4 ">
|
<div className="prose dark:prose-invert min-w-full pt-2 font-sans pb-8 px-4 ">
|
||||||
<h3>
|
<h3>꩜ welcome</h3>
|
||||||
<span className={cx('animate-spin inline-block select-none')}>🌀</span> welcome
|
|
||||||
</h3>
|
|
||||||
<p>
|
<p>
|
||||||
You have found <span className="underline">strudel</span>, a new live coding platform to write dynamic music
|
You have found <span className="underline">strudel</span>, a new live coding platform to write dynamic music
|
||||||
pieces in the browser! It is free and open-source and made for beginners and experts alike. To get started:
|
pieces in the browser! It is free and open-source and made for beginners and experts alike. To get started:
|
||||||
@ -30,7 +28,7 @@ export function WelcomeTab({ context }) {
|
|||||||
</a>{' '}
|
</a>{' '}
|
||||||
to ask any questions, give feedback or just say hello.
|
to ask any questions, give feedback or just say hello.
|
||||||
</p>
|
</p>
|
||||||
<h3>about</h3>
|
<h3>꩜ about</h3>
|
||||||
<p>
|
<p>
|
||||||
strudel is a JavaScript version of{' '}
|
strudel is a JavaScript version of{' '}
|
||||||
<a href="https://tidalcycles.org/" target="_blank">
|
<a href="https://tidalcycles.org/" target="_blank">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user