Skip to content

Commit 367c8e0

Browse files
authored
Redesign layout of UI (#2)
1 parent f231827 commit 367c8e0

13 files changed

Lines changed: 1484 additions & 373 deletions

File tree

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
workflow_dispatch:
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v3
13+
14+
- name: Install pnpm
15+
uses: pnpm/action-setup@v4
16+
with:
17+
version: 9.13.2
18+
- name: Install Node.js
19+
uses: actions/setup-node@v4
20+
with:
21+
node-version: 20
22+
cache: 'pnpm'
23+
24+
- name: Install modules
25+
run: pnpm i
26+
- name: Build app
27+
run: npm run build
28+
29+
types:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v3
33+
- name: Install pnpm
34+
uses: pnpm/action-setup@v4
35+
with:
36+
version: 9.13.2
37+
- name: Install Node.js
38+
uses: actions/setup-node@v4
39+
with:
40+
node-version: 20
41+
cache: 'pnpm'
42+
- name: Install modules
43+
run: pnpm i
44+
- name: Check Types
45+
run: npm run check-types

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
22
dist
3+
midi_files

package.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,27 @@
44
"main": "index.js",
55
"scripts": {
66
"dev": "sb dev src/index.tsx",
7-
"build": "sb build src/index.tsx"
7+
"build": "sb build src/index.tsx",
8+
"check-types": "tsc --noEmit"
89
},
910
"keywords": [],
1011
"author": "",
1112
"license": "ISC",
1213
"description": "",
1314
"dependencies": {
14-
"@jamtools/core": "0.15.0-rc20",
15-
"@springboardjs/data-storage": "0.15.0-rc20",
16-
"@springboardjs/platforms-browser": "0.15.0-rc20",
17-
"@springboardjs/platforms-node": "0.15.0-rc20",
15+
"@jamtools/core": "0.0.1-dev-midipoller-4",
16+
"@springboardjs/data-storage": "0.0.1-dev-midipoller-4",
17+
"@springboardjs/platforms-browser": "0.0.1-dev-midipoller-4",
18+
"@springboardjs/platforms-node": "0.0.1-dev-midipoller-4",
1819
"hono": "4.7.6",
20+
"mic": "^2.1.2",
1921
"react": "^19.1.0",
20-
"springboard": "0.15.0-rc20",
21-
"springboard-cli": "0.15.0-rc20",
22-
"springboard-server": "0.15.0-rc20"
22+
"springboard": "0.0.1-dev-midipoller-4",
23+
"springboard-cli": "0.0.1-dev-midipoller-4",
24+
"springboard-server": "0.0.1-dev-midipoller-4"
2325
},
2426
"devDependencies": {
27+
"@types/node": "^24.0.14",
2528
"@types/react": "^19.1.2"
2629
}
2730
}

pnpm-lock.yaml

Lines changed: 209 additions & 292 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
onlyBuiltDependencies:
2+
- '@julusian/midi'
3+
- better-sqlite3
4+
- esbuild
5+
packages:
6+
- .

src/components/ConfigModal.tsx

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import React, { useRef, useEffect } from 'react';
2+
import { RecordingConfig } from '../services/recorder';
3+
4+
type ConfigModalProps = {
5+
isOpen: boolean;
6+
onClose: () => void;
7+
recordingConfig: RecordingConfig;
8+
draftInactivityTimeLimit: number;
9+
onDraftInactivityTimeLimitChange: (newLimit: number) => void;
10+
submitInactivityTimeLimitChange: () => void;
11+
draftUploaderUrl: string;
12+
onDraftUploaderUrlChange: (newUrl: string) => void;
13+
submitUploaderUrlChange: () => void;
14+
};
15+
16+
export function asModal<P extends { isOpen: boolean; onClose: () => void }>(
17+
Component: React.ComponentType<P>
18+
) {
19+
return (props: P) => {
20+
const dialogRef = useRef<HTMLDialogElement>(null);
21+
22+
useEffect(() => {
23+
const dialog = dialogRef.current;
24+
if (!dialog) return;
25+
26+
if (props.isOpen) {
27+
dialog.showModal();
28+
} else {
29+
dialog.close();
30+
}
31+
}, [props.isOpen]);
32+
33+
const handleClose = () => {
34+
props.onClose();
35+
};
36+
37+
return (
38+
<dialog ref={dialogRef} onClose={handleClose}>
39+
<Component {...props} />
40+
</dialog>
41+
);
42+
};
43+
}
44+
45+
function ConfigModalBase({
46+
onClose,
47+
recordingConfig,
48+
draftInactivityTimeLimit,
49+
onDraftInactivityTimeLimitChange,
50+
submitInactivityTimeLimitChange,
51+
draftUploaderUrl,
52+
onDraftUploaderUrlChange,
53+
submitUploaderUrlChange
54+
}: ConfigModalProps) {
55+
return (
56+
<div>
57+
<div className="modal-header">
58+
<h2 className="modal-title">⚙️ Recording Settings</h2>
59+
</div>
60+
<div className="modal-body">
61+
<div className="form-group">
62+
<label className="form-label" htmlFor="inactivity-limit">
63+
Inactivity Time Limit (seconds)
64+
</label>
65+
<input
66+
id="inactivity-limit"
67+
type='number'
68+
className='form-input'
69+
value={draftInactivityTimeLimit}
70+
onChange={(e) => onDraftInactivityTimeLimitChange(parseInt(e.target.value))}
71+
min={1}
72+
max={300}
73+
/>
74+
<p className="text-muted" style={{fontSize: '0.875rem', marginTop: '0.5rem'}}>
75+
Recording will automatically stop after this many seconds of inactivity
76+
</p>
77+
</div>
78+
79+
<div className="form-group">
80+
<label className="form-label" htmlFor="uploader-url">
81+
Uploader URL
82+
</label>
83+
<input
84+
id="uploader-url"
85+
type='text'
86+
className='form-input'
87+
value={draftUploaderUrl}
88+
onChange={(e) => onDraftUploaderUrlChange(e.target.value)}
89+
placeholder="https://example.com/upload"
90+
/>
91+
<p className="text-muted" style={{fontSize: '0.875rem', marginTop: '0.5rem'}}>
92+
URL endpoint for uploading recorded files (leave empty to disable uploads)
93+
</p>
94+
</div>
95+
</div>
96+
<div className="modal-footer">
97+
<button
98+
type='button'
99+
className='btn-secondary'
100+
onClick={onClose}
101+
>
102+
Cancel
103+
</button>
104+
<button
105+
type='button'
106+
className='btn-primary'
107+
onClick={() => {
108+
submitInactivityTimeLimitChange();
109+
submitUploaderUrlChange();
110+
onClose();
111+
}}
112+
>
113+
Save Changes
114+
</button>
115+
</div>
116+
</div>
117+
);
118+
}
119+
120+
export const ConfigModal = asModal(ConfigModalBase);

src/components/MidiDevices.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import '@jamtools/core/modules/io/io_module';
3+
import {useModule} from '../hooks/use_module';
4+
5+
export const MidiDevices: React.FC = () => {
6+
const ioModule = useModule('io');
7+
const midiDevices = ioModule.midiDeviceState.useState().midiInputDevices;
8+
9+
return (
10+
<div className="card midi-devices-card">
11+
<div className="card-header">
12+
<h2 className="card-title">🎹 MIDI Input Devices</h2>
13+
</div>
14+
{midiDevices && midiDevices.length > 0 ? (
15+
<ul className="device-list">
16+
{midiDevices.map((device, index) => (
17+
<li key={index} className="device-item fade-in">
18+
<span className="device-icon">🎹</span>
19+
<span className="device-name">{device}</span>
20+
</li>
21+
))}
22+
</ul>
23+
) : (
24+
<div className="empty-state">
25+
<div className="empty-state-icon">🎹</div>
26+
<p className="text-muted mb-0">No MIDI input devices connected</p>
27+
<p className="text-muted">Connect a MIDI device to start recording</p>
28+
</div>
29+
)}
30+
</div>
31+
);
32+
};

src/hooks/use_module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {useSpringboardEngine} from 'springboard/engine/engine';
2+
import {AllModules} from 'springboard/module_registry/module_registry';
3+
4+
export const useModule = <ModuleId extends keyof AllModules>(moduleId: ModuleId): AllModules[ModuleId] => {
5+
const engine = useSpringboardEngine();
6+
return engine.moduleRegistry.getModule(moduleId);
7+
};

0 commit comments

Comments
 (0)