Skip to content

Commit

Permalink
Update module selector
Browse files Browse the repository at this point in the history
  • Loading branch information
oamaok committed Oct 17, 2021
1 parent 87516c5 commit 7b76303
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 154 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ jobs:
run: |
TEMP=$(mktemp)
echo "${{ secrets.SSH_KEY }}" > $TEMP
ssh -o 'StrictHostKeyChecking no' -i $TEMP ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} -p${{ secrets.SSH_PORT }} 'bash -s' < scripts/deploy.sh
ssh -o 'StrictHostKeyChecking no' -i $TEMP ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} -p${{ secrets.SSH_PORT }} 'bash -s' < scripts/deploy.sh
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# [modulate](https://modulate.bitti.io)

Modular synthesis in your browser.
Modular synthesis in your browser.
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- Only accesible via hotkey?

# General UI/UX

- Load unsaved patch Y/N at startup to mask the audio init
- Hotkey configuration?
- Global volume control
5 changes: 2 additions & 3 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,14 @@ ${worklets.map((worklet) => ` ${worklet}: typeof ${worklet}`).join('\n')}

;(async () => {
try {
await Promise.all([buildWorklets(),
buildClient()])
await Promise.all([buildWorklets(), buildClient()])
} catch (err) {
console.error(err)
if (isProduction) {
process.exit(1)
}
}
if(isProduction) {
if (isProduction) {
process.exit(0)
}

Expand Down
21 changes: 11 additions & 10 deletions client/src/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ let audioContext: AudioContext | null = null

export const initializeAudio = async () => {
audioContext = new AudioContext()
for (const worklet of workletNames) {
const path = `/worklets/${worklet}.js`
try {
console.log(audioContext.audioWorklet)
await audioContext.audioWorklet.addModule(path)
} catch (err) {
console.error(err)
throw new Error(`Failed to load audio worklet: ${path}`)
}
}

await Promise.all(
workletNames.map(async (worklet) => {
const path = `/worklets/${worklet}.js`
try {
await audioContext!.audioWorklet.addModule(path)
} catch (err) {
throw new Error(`Failed to load audio worklet: ${path}`)
}
})
)
}

export const getAudioContext = () => {
Expand Down
2 changes: 0 additions & 2 deletions client/src/components/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ const App = () => {
useEffect(() => {
if (!state.initialized) return
if (state.route.name !== 'index') return

console.log('savestate')
localStorage.setItem('savestate', JSON.stringify(patch))
})

Expand Down
40 changes: 22 additions & 18 deletions client/src/components/module-selector/ModuleSelector.css
Original file line number Diff line number Diff line change
@@ -1,44 +1,48 @@
.module-list {
left: 0;
bottom: 48px;
width: 300px;
.module-selector {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
width: 400px;
height: 200px;
background-color: var(--secondary);
padding: 10px;
border-radius: 4px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
position: absolute;
box-shadow: var(--box-shadow);
z-index: 20;
opacity: 0;
pointer-events: none;
}

.module-list::after {
content: ' ';
width: 10px;
height: 10px;
background-color: var(--secondary);
bottom: -5px;
left: 50px;
position: absolute;
transform: rotate(45deg);
.module-selector.open {
opacity: 1;
pointer-events: initial;
}

.filter {
width: 100%;
display: flex;
flex-direction: row;
margin-bottom: 10px;
}

.filter input {
width: 100%;
margin-left: 10px;
}

.module-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}

.item {
background-color: var(--secondary-variant);
border-radius: 2px;
padding: 2px 4px;
margin-bottom: 10px;
text-decoration: none;
margin-right: 10px;
margin-bottom: 10px;
flex-grow: 0;
}
91 changes: 52 additions & 39 deletions client/src/components/module-selector/ModuleSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { h, useState } from 'kaiku'
import MenuBar, { VerticalDivider } from '../menu-bar/MenuBar'
import { h, unwrap, useEffect, useRef, useState } from 'kaiku'
import classNames from 'classnames/bind'
import styles from './ModuleSelector.css'
import { moduleMap } from '../../moduleMap'
Expand All @@ -12,60 +11,74 @@ type SelectorState = {
filter: string
}

const ModuleList = ({ selectorState }: { selectorState: SelectorState }) => {
if (!selectorState.open) {
return null
}
const ModuleSelector = () => {
const selectorState = useState<SelectorState>({
open: false,
filter: '',
})

const filterRef = useRef<HTMLInputElement>()

const moduleNames = Object.keys(moduleMap).filter((name) =>
name.toLowerCase().includes(selectorState.filter.toLowerCase())
)

return (
<div className={css('module-list')}>
{moduleNames.map((name) => (
<button
className={css('item')}
onClick={() => {
useEffect(() => {
const toggle = (evt: KeyboardEvent) => {
if (evt.code === 'Enter') {
if (evt.target === document.body) {
selectorState.open = true
selectorState.filter = ''
filterRef.current!.focus()
} else if (
selectorState.open &&
evt.target === unwrap(filterRef.current!)
) {
const moduleNames = Object.keys(moduleMap).filter((name) =>
name.toLowerCase().includes(selectorState.filter.toLowerCase())
)
if (moduleNames.length === 1) {
filterRef.current!.blur()
selectorState.open = false
addModule(name)
}}
>
{name}
</button>
))}

addModule(moduleNames[0])
}
}
}
}

document.addEventListener('keydown', toggle)
return () => document.removeEventListener('keydown', toggle)
})

return (
<div className={css('module-selector', { open: selectorState.open })}>
<div className={css('filter')}>
filter
Filter
<input
type="text"
ref={filterRef}
value={selectorState.filter}
onInput={(evt) => {
selectorState.filter = evt.target.value
}}
/>
</div>
<div className={css('module-list')}>
{moduleNames.map((name) => (
<button
className={css('item')}
onClick={() => {
selectorState.open = false
addModule(name)
}}
>
{name}
</button>
))}
</div>
</div>
)
}

const ModuleSelector = () => {
const selectorState = useState<SelectorState>({
open: false,
filter: '',
})

return (
<MenuBar bottom left>
<button
onClick={() => {
selectorState.open = !selectorState.open
}}
>
+ add module
</button>
<ModuleList selectorState={selectorState} />
</MenuBar>
)
}

export default ModuleSelector
26 changes: 13 additions & 13 deletions client/src/components/user-bar/user-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const SignUp = ({ open }: { open: boolean }) => {
onSubmit={register}
>
<div className={css('field')}>
email
Email
<input
required
type="email"
Expand All @@ -70,7 +70,7 @@ const SignUp = ({ open }: { open: boolean }) => {
<div className={css('error')}>Email is already in use.</div>
)}
<div className={css('field')}>
username
Username
<input
required
type="text"
Expand All @@ -84,7 +84,7 @@ const SignUp = ({ open }: { open: boolean }) => {
<div className={css('error')}>Username is already in use.</div>
)}
<div className={css('field')}>
password
Password
<input
required
type="password"
Expand All @@ -94,7 +94,7 @@ const SignUp = ({ open }: { open: boolean }) => {
}}
/>
</div>
<button type="submit">sign up</button>
<button type="submit">Sign up</button>
</form>
)
}
Expand Down Expand Up @@ -135,7 +135,7 @@ const Login = ({ open }: { open: boolean }) => {
onSubmit={login}
>
<div className={css('field')}>
email
Email
<input
required
type="email"
Expand All @@ -146,7 +146,7 @@ const Login = ({ open }: { open: boolean }) => {
/>
</div>
<div className={css('field')}>
password
Password
<input
required
type="password"
Expand All @@ -159,7 +159,7 @@ const Login = ({ open }: { open: boolean }) => {
{loginState.failed && (
<div className={css('error')}>Invalid credentials.</div>
)}
<button type="submit">login</button>
<button type="submit">Login</button>
</form>
)
}
Expand All @@ -174,27 +174,27 @@ const UserBar = () => {
return (
<MenuBar top right>
<span>
logged in as <b>{state.user.username}</b>
Logged in as <b>{state.user.username}</b>
</span>
<VerticalDivider />
<button onClick={() => {}}>settings</button>
<button onClick={() => {}}>Settings</button>
<VerticalDivider />
<button onClick={auth.reset}>logout</button>
<button onClick={auth.reset}>Logout</button>
</MenuBar>
)
}

return (
<MenuBar top right>
<span>currently not logged in</span>
<span>Currently not logged in</span>
<VerticalDivider />
<button
onClick={() => {
userBarState.loginOpen = false
userBarState.signUpOpen = !userBarState.signUpOpen
}}
>
sign up
Sign up
</button>
<VerticalDivider />
<button
Expand All @@ -203,7 +203,7 @@ const UserBar = () => {
userBarState.loginOpen = !userBarState.loginOpen
}}
>
log in
Log in
</button>

<Login open={userBarState.loginOpen} />
Expand Down
8 changes: 7 additions & 1 deletion client/src/generated/worklets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import Clock from '../../worklets/Clock'
import Gain from '../../worklets/Gain'
import ModulationHelper from '../../worklets/ModulationHelper'
import Sequencer from '../../worklets/Sequencer'
export const workletNames = ["ADSR","Clock","Gain","ModulationHelper","Sequencer"] as const
export const workletNames = [
'ADSR',
'Clock',
'Gain',
'ModulationHelper',
'Sequencer',
] as const
export type Worklets = {
ADSR: typeof ADSR
Clock: typeof Clock
Expand Down
Loading

0 comments on commit 7b76303

Please sign in to comment.